From e51886522525c361fa10798b806b7d5ac5240b24 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Fri, 18 Jun 2021 10:49:28 +0100 Subject: [PATCH 01/14] [ML] Remove blank job definition as it is unused and out-of-sync with Elasticsearch (#102506) This a companion to elastic/elasticsearch#74188. This PR is functionally a no-op, as the removed method was not called anywhere. But it is sensible to remove it to prevent it being called in the future now that it references fields that don't exist in Elasticsearch. --- .../application/services/job_service.js | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index 3c93c8a1ae85a..8560cdd73153b 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -91,26 +91,6 @@ class JobService { }; } - getBlankJob() { - return { - job_id: '', - description: '', - groups: [], - analysis_config: { - bucket_span: '15m', - influencers: [], - detectors: [], - }, - data_description: { - time_field: '', - time_format: '', // 'epoch', - field_delimiter: '', - quote_character: '"', - format: 'delimited', - }, - }; - } - loadJobs() { return new Promise((resolve, reject) => { jobs = []; From ee1710cf395f9d9576c4db18dc9a9668bef1aaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 18 Jun 2021 14:50:58 +0200 Subject: [PATCH 02/14] [Logs UI] Add `event.original` fallback to message reconstruction rules (#102236) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../message/builtin_rules/generic.test.ts | 54 ++++++++++++++ .../message/builtin_rules/generic.ts | 73 ++++++++++--------- 2 files changed, 94 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts index ae5a45c61d3b5..ba8eab91e3456 100644 --- a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts +++ b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts @@ -186,4 +186,58 @@ describe('Generic Rules', () => { `); }); }); + + describe('event.original fallback', () => { + test('includes the event.dataset if present', () => { + const flattenedDocument = { + '@timestamp': ['2016-12-26T16:22:13.000Z'], + 'event.dataset': ['generic.test'], + 'event.original': ['TEST_MESSAGE'], + }; + + expect(format(flattenedDocument, {})).toMatchInlineSnapshot(` + Array [ + Object { + "constant": "[", + }, + Object { + "field": "event.dataset", + "highlights": Array [], + "value": Array [ + "generic.test", + ], + }, + Object { + "constant": "] ", + }, + Object { + "field": "event.original", + "highlights": Array [], + "value": Array [ + "TEST_MESSAGE", + ], + }, + ] + `); + }); + + test('includes the original message', () => { + const flattenedDocument = { + '@timestamp': ['2016-12-26T16:22:13.000Z'], + 'event.original': ['TEST_MESSAGE'], + }; + + expect(format(flattenedDocument, {})).toMatchInlineSnapshot(` + Array [ + Object { + "field": "event.original", + "highlights": Array [], + "value": Array [ + "TEST_MESSAGE", + ], + }, + ] + `); + }); + }); }); diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts index c16d65a75b3e0..07b6cf03e2c5d 100644 --- a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts +++ b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts @@ -8,40 +8,15 @@ import { LogMessageFormattingRule } from '../rule_types'; const BUILTIN_GENERIC_MESSAGE_FIELDS = ['message', '@message']; +const BUILTIN_FALLBACK_MESSAGE_FIELDS = ['log.original', 'event.original']; -export const getGenericRules = (genericMessageFields: string[]) => [ - ...Array.from(new Set([...genericMessageFields, ...BUILTIN_GENERIC_MESSAGE_FIELDS])).reduce< - LogMessageFormattingRule[] - >((genericRules, fieldName) => [...genericRules, ...createGenericRulesForField(fieldName)], []), - { - when: { - exists: ['event.dataset', 'log.original'], - }, - format: [ - { - constant: '[', - }, - { - field: 'event.dataset', - }, - { - constant: '] ', - }, - { - field: 'log.original', - }, - ], - }, - { - when: { - exists: ['log.original'], - }, - format: [ - { - field: 'log.original', - }, - ], - }, +export const getGenericRules = (genericMessageFields: string[]): LogMessageFormattingRule[] => [ + ...Array.from(new Set([...genericMessageFields, ...BUILTIN_GENERIC_MESSAGE_FIELDS])).flatMap( + createGenericRulesForField + ), + ...BUILTIN_FALLBACK_MESSAGE_FIELDS.filter( + (fieldName) => !genericMessageFields.includes(fieldName) + ).flatMap(createFallbackRulesForField), ]; const createGenericRulesForField = (fieldName: string) => [ @@ -172,3 +147,35 @@ const createGenericRulesForField = (fieldName: string) => [ ], }, ]; + +const createFallbackRulesForField = (fieldName: string) => [ + { + when: { + exists: ['event.dataset', fieldName], + }, + format: [ + { + constant: '[', + }, + { + field: 'event.dataset', + }, + { + constant: '] ', + }, + { + field: fieldName, + }, + ], + }, + { + when: { + exists: [fieldName], + }, + format: [ + { + field: fieldName, + }, + ], + }, +]; From b9f64b78259f7dd6809a16597077015114b674c4 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 18 Jun 2021 15:57:33 +0300 Subject: [PATCH 03/14] TSVB visualizations with no timefield do not render after upgrading from 7.12.1 to 7.13.0 (#102494) * TSVB visualizations with no timefield do not render after upgrading from 7.12.1 to 7.13.0 Part of: #100778 * fix CI --- .../server/lib/get_vis_data.ts | 7 +- .../lib/cached_index_pattern_fetcher.ts | 13 ++- .../get_interval_and_timefield.test.ts | 4 +- .../vis_data/get_interval_and_timefield.ts | 9 +- .../server/lib/vis_data/get_table_data.ts | 16 ++- .../series/date_histogram.js | 6 +- .../series/date_histogram.test.js | 36 +++++-- .../series/positive_rate.js | 6 +- .../series/positive_rate.test.js | 10 +- .../request_processors/series/query.js | 18 +++- .../request_processors/series/query.test.js | 102 +++++++++++++++--- .../table/date_histogram.js | 13 ++- .../request_processors/table/positive_rate.js | 13 ++- .../request_processors/table/query.js | 15 ++- .../series/build_request_body.test.ts | 5 +- .../lib/vis_data/series/get_request_params.ts | 16 ++- 16 files changed, 226 insertions(+), 63 deletions(-) diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts index 5cdea62af9536..dd45812f4ebfc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts @@ -38,10 +38,9 @@ export async function getVisData( indexPatternsService, uiSettings, searchStrategyRegistry: framework.searchStrategyRegistry, - cachedIndexPatternFetcher: getCachedIndexPatternFetcher( - indexPatternsService, - Boolean(panel.use_kibana_indexes) - ), + cachedIndexPatternFetcher: getCachedIndexPatternFetcher(indexPatternsService, { + fetchKibanaIndexForStringIndexes: Boolean(panel.use_kibana_indexes), + }), }; return panel.type === PANEL_TYPES.TABLE diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts index 26ea191ab9217..5f989a50ca639 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts @@ -13,12 +13,19 @@ import type { IndexPatternValue, FetchedIndexPattern } from '../../../../common/ export const getCachedIndexPatternFetcher = ( indexPatternsService: IndexPatternsService, - fetchKibanaIndexForStringIndexes: boolean = false + globalOptions: { + fetchKibanaIndexForStringIndexes: boolean; + } = { + fetchKibanaIndexForStringIndexes: false, + } ) => { const cache = new Map(); - return async (indexPatternValue: IndexPatternValue): Promise => { - const key = getIndexPatternKey(indexPatternValue); + return async ( + indexPatternValue: IndexPatternValue, + fetchKibanaIndexForStringIndexes: boolean = globalOptions.fetchKibanaIndexForStringIndexes + ): Promise => { + const key = `${getIndexPatternKey(indexPatternValue)}:${fetchKibanaIndexForStringIndexes}`; if (cache.has(key)) { return cache.get(key); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts index 9520b876a5810..0d1ca9cba022a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts @@ -16,7 +16,7 @@ describe('getIntervalAndTimefield(panel, series)', () => { const panel = { time_field: '@timestamp', interval: 'auto' } as Panel; const series = {} as Series; - expect(getIntervalAndTimefield(panel, series, index)).toEqual({ + expect(getIntervalAndTimefield(panel, index, series)).toEqual({ timeField: '@timestamp', interval: 'auto', }); @@ -30,7 +30,7 @@ describe('getIntervalAndTimefield(panel, series)', () => { series_time_field: 'time', } as unknown) as Series; - expect(getIntervalAndTimefield(panel, series, index)).toEqual({ + expect(getIntervalAndTimefield(panel, index, series)).toEqual({ timeField: 'time', interval: '1m', }); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts index 90fb722c54f5b..0e90dfe77e814 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts @@ -7,12 +7,13 @@ */ import { AUTO_INTERVAL } from '../../../common/constants'; -import type { FetchedIndexPattern, Panel, Series } from '../../../common/types'; import { validateField } from '../../../common/fields_utils'; -export function getIntervalAndTimefield(panel: Panel, series: Series, index: FetchedIndexPattern) { +import type { FetchedIndexPattern, Panel, Series } from '../../../common/types'; + +export function getIntervalAndTimefield(panel: Panel, index: FetchedIndexPattern, series?: Series) { const timeField = - (series.override_index_pattern ? series.series_time_field : panel.time_field) || + (series?.override_index_pattern ? series.series_time_field : panel.time_field) || index.indexPattern?.timeFieldName; if (panel.use_kibana_indexes) { @@ -22,7 +23,7 @@ export function getIntervalAndTimefield(panel: Panel, series: Series, index: Fet let interval = panel.interval; let maxBars = panel.max_bars; - if (series.override_index_pattern) { + if (series?.override_index_pattern) { interval = series.series_interval || AUTO_INTERVAL; maxBars = series.series_max_bars; } diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts index 82411ed70d8ee..db2e027f7815c 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts @@ -25,6 +25,7 @@ import type { VisTypeTimeseriesVisDataRequest, } from '../../types'; import type { Panel } from '../../../common/types'; +import { getIntervalAndTimefield } from './get_interval_and_timefield'; export async function getTableData( requestContext: VisTypeTimeseriesRequestHandlerContext, @@ -66,6 +67,18 @@ export async function getTableData( return panel.pivot_id; }; + const buildSeriesMetaParams = async () => { + let index = panelIndex; + + /** This part of code is required to try to get the default timefield for string indices. + * The rest of the functionality available for Kibana indexes should not be active **/ + if (!panel.use_kibana_indexes && index.indexPatternString) { + index = await services.cachedIndexPatternFetcher(index.indexPatternString, true); + } + + return getIntervalAndTimefield(panel, index); + }; + const meta = { type: panel.type, uiRestrictions: capabilities.uiRestrictions, @@ -78,7 +91,8 @@ export async function getTableData( services.esQueryConfig, panelIndex, capabilities, - services.uiSettings + services.uiSettings, + buildSeriesMetaParams ); const [resp] = await searchStrategy.search(requestContext, req, [ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js index 253612c0274ad..cec3e82d5e37c 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js @@ -9,7 +9,6 @@ import { overwrite } from '../../helpers'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { offsetTime } from '../../offset_time'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { isLastValueTimerangeMode } from '../../helpers/get_timerange_mode'; import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; @@ -22,13 +21,14 @@ export function dateHistogram( esQueryConfig, seriesIndex, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams ) { return (next) => async (doc) => { const maxBarsUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS); const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { timeField, interval, maxBars } = getIntervalAndTimefield(panel, series, seriesIndex); + const { timeField, interval, maxBars } = await buildSeriesMetaParams(); const { from, to } = offsetTime(req, series.offset_time); let bucketInterval; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js index 2cd7a213b273e..08b9801254c2e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js @@ -8,6 +8,7 @@ import { DefaultSearchCapabilities } from '../../../search_strategies/capabilities/default_search_capabilities'; import { dateHistogram } from './date_histogram'; +import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { UI_SETTINGS } from '../../../../../../data/common'; describe('dateHistogram(req, panel, series)', () => { @@ -18,6 +19,7 @@ describe('dateHistogram(req, panel, series)', () => { let config; let indexPattern; let uiSettings; + let buildSeriesMetaParams; beforeEach(() => { req = { @@ -44,14 +46,24 @@ describe('dateHistogram(req, panel, series)', () => { uiSettings = { get: async (key) => (key === UI_SETTINGS.HISTOGRAM_MAX_BARS ? 100 : 50), }; + buildSeriesMetaParams = jest.fn(async () => { + return getIntervalAndTimefield(panel, indexPattern, series); + }); }); test('calls next when finished', async () => { const next = jest.fn(); - await dateHistogram(req, panel, series, config, indexPattern, capabilities, uiSettings)(next)( - {} - ); + await dateHistogram( + req, + panel, + series, + config, + indexPattern, + capabilities, + uiSettings, + buildSeriesMetaParams + )(next)({}); expect(next.mock.calls.length).toEqual(1); }); @@ -65,7 +77,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ @@ -106,7 +119,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ @@ -150,7 +164,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ @@ -197,7 +212,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc.aggs.test.aggs.timeseries.auto_date_histogram).toBeUndefined(); @@ -219,7 +235,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc.aggs.test.meta).toMatchInlineSnapshot(` @@ -242,7 +259,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js index 208321a98737e..91016384794c4 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js @@ -7,7 +7,6 @@ */ import { getBucketSize } from '../../helpers/get_bucket_size'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { bucketTransform } from '../../helpers/bucket_transform'; import { overwrite } from '../../helpers'; import { UI_SETTINGS } from '../../../../../../data/common'; @@ -58,12 +57,13 @@ export function positiveRate( esQueryConfig, seriesIndex, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams ) { return (next) => async (doc) => { const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { interval } = getIntervalAndTimefield(panel, series, seriesIndex); + const { interval } = await buildSeriesMetaParams(); const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings); if (series.metrics.some(filter)) { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js index b79e8de13062c..aac0063a54bef 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js @@ -12,6 +12,7 @@ describe('positiveRate(req, panel, series)', () => { let series; let req; let uiSettings; + let buildSeriesMetaParams; beforeEach(() => { panel = { @@ -42,6 +43,9 @@ describe('positiveRate(req, panel, series)', () => { uiSettings = { get: async () => 50, }; + buildSeriesMetaParams = jest.fn().mockResolvedValue({ + interval: 'auto', + }); }); test('calls next when finished', async () => { @@ -53,7 +57,8 @@ describe('positiveRate(req, panel, series)', () => { {}, {}, { maxBucketsLimit: 2000, getValidTimeInterval: jest.fn(() => '1d') }, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(next.mock.calls.length).toEqual(1); @@ -68,7 +73,8 @@ describe('positiveRate(req, panel, series)', () => { {}, {}, { maxBucketsLimit: 2000, getValidTimeInterval: jest.fn(() => '1d') }, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js index a5f4e17289e06..5031a0f2ec185 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js @@ -7,18 +7,28 @@ */ import { offsetTime } from '../../offset_time'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { esQuery } from '../../../../../../data/server'; -export function query(req, panel, series, esQueryConfig, seriesIndex) { - return (next) => (doc) => { - const { timeField } = getIntervalAndTimefield(panel, series, seriesIndex); +export function query( + req, + panel, + series, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { + return (next) => async (doc) => { + const { timeField } = await buildSeriesMetaParams(); const { from, to } = offsetTime(req, series.offset_time); doc.size = 0; + const ignoreGlobalFilter = panel.ignore_global_filter || series.ignore_global_filter; const queries = !ignoreGlobalFilter ? req.body.query : []; const filters = !ignoreGlobalFilter ? req.body.filters : []; + doc.query = esQuery.buildEsQuery(seriesIndex.indexPattern, queries, filters, esQueryConfig); const timerange = { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js index b3e88dbf1c6b9..849ae2e71bffc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js @@ -13,6 +13,7 @@ describe('query', () => { let series; let req; let seriesIndex; + let buildSeriesMetaParams; const config = { allowLeadingWildcards: true, @@ -35,17 +36,32 @@ describe('query', () => { }; series = { id: 'test' }; seriesIndex = {}; + buildSeriesMetaParams = jest.fn().mockResolvedValue({ + timeField: panel.time_field, + interval: panel.interval, + }); }); - test('calls next when finished', () => { + test('calls next when finished', async () => { const next = jest.fn(); - query(req, panel, series, config, seriesIndex)(next)({}); + await query(req, panel, series, config, seriesIndex, null, null, buildSeriesMetaParams)(next)( + {} + ); expect(next.mock.calls.length).toEqual(1); }); - test('returns doc with query for timerange', () => { + test('returns doc with query for timerange', async () => { const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -69,10 +85,19 @@ describe('query', () => { }); }); - test('returns doc with query for timerange (offset by 1h)', () => { + test('returns doc with query for timerange (offset by 1h)', async () => { series.offset_time = '1h'; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -96,7 +121,7 @@ describe('query', () => { }); }); - test('returns doc with global query', () => { + test('returns doc with global query', async () => { req.body.filters = [ { bool: { @@ -111,7 +136,16 @@ describe('query', () => { }, ]; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -147,10 +181,19 @@ describe('query', () => { }); }); - test('returns doc with series filter', () => { + test('returns doc with series filter', async () => { series.filter = { query: 'host:web-server', language: 'lucene' }; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -188,7 +231,7 @@ describe('query', () => { }, }); }); - test('returns doc with panel filter and global', () => { + test('returns doc with panel filter and global', async () => { req.body.filters = [ { bool: { @@ -204,7 +247,16 @@ describe('query', () => { ]; panel.filter = { query: 'host:web-server', language: 'lucene' }; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -255,7 +307,7 @@ describe('query', () => { }); }); - test('returns doc with panel filter (ignoring globals)', () => { + test('returns doc with panel filter (ignoring globals)', async () => { req.body.filters = [ { bool: { @@ -272,7 +324,16 @@ describe('query', () => { panel.filter = { query: 'host:web-server', language: 'lucene' }; panel.ignore_global_filter = true; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -311,7 +372,7 @@ describe('query', () => { }); }); - test('returns doc with panel filter (ignoring globals from series)', () => { + test('returns doc with panel filter (ignoring globals from series)', async () => { req.body.filters = [ { bool: { @@ -328,7 +389,16 @@ describe('query', () => { panel.filter = { query: 'host:web-server', language: 'lucene' }; series.ignore_global_filter = true; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js index 92ac4078a3835..f37c27f74184f 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js @@ -9,17 +9,24 @@ import { overwrite } from '../../helpers'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { isLastValueTimerangeMode } from '../../helpers/get_timerange_mode'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { getTimerange } from '../../helpers/get_timerange'; import { calculateAggRoot } from './calculate_agg_root'; import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; const { dateHistogramInterval } = search.aggs; -export function dateHistogram(req, panel, esQueryConfig, seriesIndex, capabilities, uiSettings) { +export function dateHistogram( + req, + panel, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { return (next) => async (doc) => { const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { timeField, interval } = getIntervalAndTimefield(panel, {}, seriesIndex); + const { timeField, interval } = await buildSeriesMetaParams(); const { from, to } = getTimerange(req); const meta = { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js index 3390362b56115..dafb2741d6ab4 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js @@ -7,15 +7,22 @@ */ import { getBucketSize } from '../../helpers/get_bucket_size'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { calculateAggRoot } from './calculate_agg_root'; import { createPositiveRate, filter } from '../series/positive_rate'; import { UI_SETTINGS } from '../../../../../../data/common'; -export function positiveRate(req, panel, esQueryConfig, seriesIndex, capabilities, uiSettings) { +export function positiveRate( + req, + panel, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { return (next) => async (doc) => { const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { interval } = getIntervalAndTimefield(panel, {}, seriesIndex); + const { interval } = await buildSeriesMetaParams(); const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings); panel.series.forEach((column) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js index 66783e0cdfaef..7e555557ee1f7 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js @@ -7,12 +7,19 @@ */ import { getTimerange } from '../../helpers/get_timerange'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { esQuery } from '../../../../../../data/server'; -export function query(req, panel, esQueryConfig, seriesIndex) { - return (next) => (doc) => { - const { timeField } = getIntervalAndTimefield(panel, {}, seriesIndex); +export function query( + req, + panel, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { + return (next) => async (doc) => { + const { timeField } = await buildSeriesMetaParams(); const { from, to } = getTimerange(req); doc.size = 0; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts index 46acbb27e15e1..14b4fc89712e2 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts @@ -89,7 +89,10 @@ describe('buildRequestBody(req)', () => { capabilities, { get: async () => 50, - } + }, + jest.fn().mockResolvedValue({ + timeField: '@timestamp', + }) ); expect(doc).toEqual({ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts index c565e2b86eaa3..a2248308dc571 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts @@ -7,6 +7,7 @@ */ import { buildRequestBody } from './build_request_body'; +import { getIntervalAndTimefield } from '../get_interval_and_timefield'; import type { FetchedIndexPattern, Panel, Series } from '../../../../common/types'; import type { @@ -34,6 +35,18 @@ export async function getSeriesRequestParams( seriesIndex = await cachedIndexPatternFetcher(series.series_index_pattern ?? ''); } + const buildSeriesMetaParams = async () => { + let index = seriesIndex; + + /** This part of code is required to try to get the default timefield for string indices. + * The rest of the functionality available for Kibana indexes should not be active **/ + if (!panel.use_kibana_indexes && index.indexPatternString) { + index = await cachedIndexPatternFetcher(index.indexPatternString, true); + } + + return getIntervalAndTimefield(panel, index, series); + }; + const request = await buildRequestBody( req, panel, @@ -41,7 +54,8 @@ export async function getSeriesRequestParams( esQueryConfig, seriesIndex, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams ); return { From d65416c60c803092eb17b80e77836e5ef772a429 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Fri, 18 Jun 2021 10:26:10 -0400 Subject: [PATCH 04/14] [Security Solution][Endpoint][Host Isolation] Fixes bug where host isolation/unisolation works from alert details (#102581) --- .../detections/components/host_isolation/index.tsx | 10 +++++----- .../detections/components/host_isolation/isolate.tsx | 6 +++--- .../components/host_isolation/unisolate.tsx | 6 +++--- .../containers/detection_engine/alerts/api.test.ts | 6 +++--- .../containers/detection_engine/alerts/api.ts | 12 ++++++------ .../detection_engine/alerts/use_host_isolation.tsx | 8 ++++---- .../detection_engine/alerts/use_host_unisolation.tsx | 8 ++++---- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx index 2ca8416841497..42d53f97d478b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx @@ -26,9 +26,9 @@ export const HostIsolationPanel = React.memo( cancelCallback: () => void; isolateAction: string; }) => { - const agentId = useMemo(() => { - const findAgentId = find({ category: 'agent', field: 'agent.id' }, details)?.values; - return findAgentId ? findAgentId[0] : ''; + const endpointId = useMemo(() => { + const findEndpointId = find({ category: 'agent', field: 'agent.id' }, details)?.values; + return findEndpointId ? findEndpointId[0] : ''; }, [details]); const hostName = useMemo(() => { @@ -87,7 +87,7 @@ export const HostIsolationPanel = React.memo( return isolateAction === 'isolateHost' ? ( ) : ( { const hostIsolated = await isolateHost(); diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx index e72a0d2de61bc..71f7cadda2f68 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx @@ -18,13 +18,13 @@ import { useHostUnisolation } from '../../containers/detection_engine/alerts/use export const UnisolateHost = React.memo( ({ - agentId, + endpointId, hostName, cases, caseIds, cancelCallback, }: { - agentId: string; + endpointId: string; hostName: string; cases: ReactNode; caseIds: string[]; @@ -33,7 +33,7 @@ export const UnisolateHost = React.memo( const [comment, setComment] = useState(''); const [isUnIsolated, setIsUnIsolated] = useState(false); - const { loading, unIsolateHost } = useHostUnisolation({ agentId, comment, caseIds }); + const { loading, unIsolateHost } = useHostUnisolation({ endpointId, comment, caseIds }); const confirmHostUnIsolation = useCallback(async () => { const hostIsolated = await unIsolateHost(); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts index 0c3159e0719e6..b944cb640b719 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts @@ -178,19 +178,19 @@ describe('Detections Alerts API', () => { test('check parameter url', async () => { await createHostIsolation({ - agentId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', + endpointId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', comment: 'commento', caseIds: ['88c04a90-b19c-11eb-b838-bf3c7840b969'], }); expect(postMock).toHaveBeenCalledWith('/api/endpoint/isolate', { body: - '{"agent_ids":["fd8a122b-4c54-4c05-b295-e5f8381fc59d"],"comment":"commento","case_ids":["88c04a90-b19c-11eb-b838-bf3c7840b969"]}', + '{"endpoint_ids":["fd8a122b-4c54-4c05-b295-e5f8381fc59d"],"comment":"commento","case_ids":["88c04a90-b19c-11eb-b838-bf3c7840b969"]}', }); }); test('happy path', async () => { const hostIsolationResponse = await createHostIsolation({ - agentId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', + endpointId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', comment: 'commento', caseIds: ['88c04a90-b19c-11eb-b838-bf3c7840b969'], }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts index 3baa6580b36fb..71eac547dcc8e 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts @@ -118,16 +118,16 @@ export const createSignalIndex = async ({ signal }: BasicSignals): Promise => isolateHost({ - agent_ids: [agentId], + endpoint_ids: [endpointId], comment, case_ids: caseIds, }); @@ -142,16 +142,16 @@ export const createHostIsolation = async ({ * @throws An error if response is not OK */ export const createHostUnIsolation = async ({ - agentId, + endpointId, comment = '', caseIds, }: { - agentId: string; + endpointId: string; comment?: string; caseIds?: string[]; }): Promise => unIsolateHost({ - agent_ids: [agentId], + endpoint_ids: [endpointId], comment, case_ids: caseIds, }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx index ad3c6e91c03fe..12426e05ba528 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx @@ -16,13 +16,13 @@ interface HostIsolationStatus { } interface UseHostIsolationProps { - agentId: string; + endpointId: string; comment: string; caseIds?: string[]; } export const useHostIsolation = ({ - agentId, + endpointId, comment, caseIds, }: UseHostIsolationProps): HostIsolationStatus => { @@ -32,7 +32,7 @@ export const useHostIsolation = ({ const isolateHost = useCallback(async () => { try { setLoading(true); - const isolationStatus = await createHostIsolation({ agentId, comment, caseIds }); + const isolationStatus = await createHostIsolation({ endpointId, comment, caseIds }); setLoading(false); return isolationStatus.action ? true : false; } catch (error) { @@ -40,6 +40,6 @@ export const useHostIsolation = ({ addError(error.message, { title: HOST_ISOLATION_FAILURE }); return false; } - }, [agentId, comment, caseIds, addError]); + }, [endpointId, comment, caseIds, addError]); return { loading, isolateHost }; }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx index 1a0ecb0d15878..55119f7122e12 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx @@ -16,13 +16,13 @@ interface HostUnisolationStatus { } interface UseHostIsolationProps { - agentId: string; + endpointId: string; comment: string; caseIds?: string[]; } export const useHostUnisolation = ({ - agentId, + endpointId, comment, caseIds, }: UseHostIsolationProps): HostUnisolationStatus => { @@ -32,7 +32,7 @@ export const useHostUnisolation = ({ const unIsolateHost = useCallback(async () => { try { setLoading(true); - const isolationStatus = await createHostUnIsolation({ agentId, comment, caseIds }); + const isolationStatus = await createHostUnIsolation({ endpointId, comment, caseIds }); setLoading(false); return isolationStatus.action ? true : false; } catch (error) { @@ -40,6 +40,6 @@ export const useHostUnisolation = ({ addError(error.message, { title: HOST_ISOLATION_FAILURE }); return false; } - }, [agentId, comment, caseIds, addError]); + }, [endpointId, comment, caseIds, addError]); return { loading, unIsolateHost }; }; From 853de830c2c908d7c3601e76896c4edc0a9c2ff4 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Fri, 18 Jun 2021 17:32:17 +0300 Subject: [PATCH 05/14] [TSVB] Index pattern select field disappear in Annotation tab (#102314) * [TSVB] Index pattern select field disappear in Annotation tab * Refactor AnnotationsEditor and move renderRow logic into AnnotationRow * Remove duplicated license, add useCallback to functions in AnnotationRow, remove cross refecrence and update types * Refactor AnnotationEditor and AnnotationRow * refactoring * remove extra props * fix nits Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Alexey Antonov --- .../application/components/annotation_row.tsx | 280 +++++++++++++++ .../components/annotations_editor.js | 322 ------------------ .../components/annotations_editor.tsx | 113 ++++++ .../components/panel_config/timeseries.tsx | 4 +- .../public/application/components/yes_no.tsx | 8 +- 5 files changed, 398 insertions(+), 329 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx delete mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js create mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx new file mode 100644 index 0000000000000..715cf4d6709da --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx @@ -0,0 +1,280 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect, useCallback, useMemo, ChangeEvent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiCode, + EuiComboBoxOptionOption, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSpacer, + htmlIdGenerator, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { getDataStart } from '../../services'; +import { KBN_FIELD_TYPES, Query } from '../../../../../plugins/data/public'; + +import { AddDeleteButtons } from './add_delete_buttons'; +import { ColorPicker } from './color_picker'; +import { FieldSelect } from './aggs/field_select'; +import { IndexPatternSelect } from './lib/index_pattern_select'; +import { QueryBarWrapper } from './query_bar_wrapper'; +import { YesNo } from './yes_no'; +import { fetchIndexPattern } from '../../../common/index_patterns_utils'; +import { getDefaultQueryLanguage } from './lib/get_default_query_language'; + +// @ts-expect-error not typed yet +import { IconSelect } from './icon_select/icon_select'; + +import type { Annotation, FetchedIndexPattern, IndexPatternValue } from '../../../common/types'; +import type { VisFields } from '../lib/fetch_fields'; + +const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; + +const INDEX_PATTERN_KEY = 'index_pattern'; +const TIME_FIELD_KEY = 'time_field'; + +export interface AnnotationRowProps { + annotation: Annotation; + fields: VisFields; + onChange: (partialModel: Partial) => void; + handleAdd: () => void; + handleDelete: () => void; +} + +const getAnnotationDefaults = () => ({ + fields: '', + template: '', + index_pattern: '', + query_string: { query: '', language: getDefaultQueryLanguage() }, +}); + +export const AnnotationRow = ({ + annotation, + fields, + onChange, + handleAdd, + handleDelete, +}: AnnotationRowProps) => { + const model = useMemo(() => ({ ...getAnnotationDefaults(), ...annotation }), [annotation]); + const htmlId = htmlIdGenerator(model.id); + + const [fetchedIndex, setFetchedIndex] = useState(null); + + useEffect(() => { + const updateFetchedIndex = async (index: IndexPatternValue) => { + const { indexPatterns } = getDataStart(); + + setFetchedIndex( + index + ? await fetchIndexPattern(index, indexPatterns) + : { + indexPattern: undefined, + indexPatternString: undefined, + } + ); + }; + + updateFetchedIndex(model.index_pattern); + }, [model.index_pattern]); + + const togglePanelActivation = useCallback( + () => + onChange({ + hidden: !model.hidden, + }), + [model.hidden, onChange] + ); + + const handleChange = useCallback( + (name: string) => ( + event: Array> | ChangeEvent + ) => + onChange({ + [name]: Array.isArray(event) ? event?.[0]?.value : event.target.value, + }), + [onChange] + ); + + const handleQueryChange = useCallback( + (filter: Query) => + onChange({ + query_string: filter, + }), + [onChange] + ); + + return ( +
+ + + + + + + + + + + + + } + restrict={RESTRICT_FIELDS} + value={model.time_field} + onChange={handleChange(TIME_FIELD_KEY)} + indexPattern={model.index_pattern} + fields={fields} + /> + + + + + + + + + } + fullWidth + > + + + + + + + + + + + + + + + + + + + + + } + > + + + + + + } + fullWidth + > + + + + + + } + helpText={ + + {'{{field}}'} }} + /> + + } + fullWidth + > + + + + + + + + + + +
+ ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js deleted file mode 100644 index 09ce57639b952..0000000000000 --- a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js +++ /dev/null @@ -1,322 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import _ from 'lodash'; -import { collectionActions } from './lib/collection_actions'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; -import { AddDeleteButtons } from './add_delete_buttons'; -import { ColorPicker } from './color_picker'; -import { FieldSelect } from './aggs/field_select'; -import uuid from 'uuid'; -import { IconSelect } from './icon_select/icon_select'; -import { YesNo } from './yes_no'; -import { QueryBarWrapper } from './query_bar_wrapper'; -import { getDefaultQueryLanguage } from './lib/get_default_query_language'; -import { - htmlIdGenerator, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiSpacer, - EuiFieldText, - EuiTitle, - EuiButton, - EuiCode, - EuiText, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { IndexPatternSelect } from './lib/index_pattern_select'; - -function newAnnotation() { - return { - id: uuid.v1(), - color: '#F00', - index_pattern: '', - time_field: '', - icon: 'fa-tag', - ignore_global_filters: 1, - ignore_panel_filters: 1, - }; -} - -const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; - -export class AnnotationsEditor extends Component { - constructor(props) { - super(props); - this.renderRow = this.renderRow.bind(this); - } - - handleChange(item, name) { - return (e) => { - const handleChange = collectionActions.handleChange.bind(null, this.props); - const part = {}; - part[name] = _.get(e, '[0].value', _.get(e, 'target.value')); - handleChange(_.assign({}, item, part)); - }; - } - - handleQueryChange = (model, filter) => { - const part = { query_string: filter }; - collectionActions.handleChange(this.props, { - ...model, - ...part, - }); - }; - renderRow(row) { - const defaults = { - fields: '', - template: '', - index_pattern: '', - query_string: { query: '', language: getDefaultQueryLanguage() }, - }; - const model = { ...defaults, ...row }; - const handleChange = (part) => { - const fn = collectionActions.handleChange.bind(null, this.props); - fn(_.assign({}, model, part)); - }; - const togglePanelActivation = () => { - handleChange({ - hidden: !model.hidden, - }); - }; - const htmlId = htmlIdGenerator(model.id); - const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation); - const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); - - return ( -
- - - - - - - - - - - - - } - restrict={RESTRICT_FIELDS} - value={model.time_field} - onChange={this.handleChange(model, 'time_field')} - indexPattern={model.index_pattern} - fields={this.props.fields} - fullWidth - /> - - - - - - - - - } - fullWidth - > - this.handleQueryChange(model, query)} - indexPatterns={[model.index_pattern]} - /> - - - - - - - - - - - - - - - - - - - - } - > - - - - - - } - fullWidth - > - - - - - - } - helpText={ - - {'{{field}}'} }} - /> - - } - fullWidth - > - - - - - - - - - - -
- ); - } - - render() { - const { model } = this.props; - let content; - if (!model.annotations || !model.annotations.length) { - const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation); - content = ( - -

- -

- - - -
- ); - } else { - const annotations = model.annotations.map(this.renderRow); - content = ( -
- - - - - - - - {annotations} -
- ); - } - return
{content}
; - } -} - -AnnotationsEditor.defaultProps = { - name: 'annotations', -}; - -AnnotationsEditor.propTypes = { - fields: PropTypes.object, - model: PropTypes.object, - name: PropTypes.string, - onChange: PropTypes.func, - uiSettings: PropTypes.object, -}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx new file mode 100644 index 0000000000000..b3b4993d2ca06 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback } from 'react'; +import uuid from 'uuid'; +import { EuiSpacer, EuiTitle, EuiButton, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { AnnotationRow } from './annotation_row'; +import { collectionActions, CollectionActionsProps } from './lib/collection_actions'; + +import type { Panel, Annotation } from '../../../common/types'; +import type { VisFields } from '../lib/fetch_fields'; + +interface AnnotationsEditorProps { + fields: VisFields; + model: Panel; + onChange: (partialModel: Partial) => void; +} + +export const newAnnotation = () => ({ + id: uuid.v1(), + color: '#F00', + index_pattern: '', + time_field: '', + icon: 'fa-tag', + ignore_global_filters: 1, + ignore_panel_filters: 1, +}); + +const NoContent = ({ handleAdd }: { handleAdd: () => void }) => ( + +

+ +

+ + + +
+); + +const getCollectionActionsProps = (props: AnnotationsEditorProps) => + ({ + name: 'annotations', + ...props, + } as CollectionActionsProps); + +export const AnnotationsEditor = (props: AnnotationsEditorProps) => { + const { annotations } = props.model; + + const handleAdd = useCallback( + () => collectionActions.handleAdd(getCollectionActionsProps(props), newAnnotation), + [props] + ); + + const handleDelete = useCallback( + (annotation) => () => + collectionActions.handleDelete(getCollectionActionsProps(props), annotation), + [props] + ); + + const onChange = useCallback( + (annotation: Annotation) => { + return (part: Partial) => + collectionActions.handleChange(getCollectionActionsProps(props), { + ...annotation, + ...part, + }); + }, + [props] + ); + + return ( +
+ {annotations?.length ? ( +
+ + + + + + + {annotations.map((annotation) => ( + + ))} +
+ ) : ( + + )} +
+ ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx b/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx index b57d72ea6bb30..e1d94f3bf3ebe 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx @@ -28,9 +28,8 @@ import { // @ts-expect-error not typed yet import { SeriesEditor } from '../series_editor'; // @ts-expect-error not typed yet -import { AnnotationsEditor } from '../annotations_editor'; -// @ts-expect-error not typed yet import { IndexPattern } from '../index_pattern'; +import { AnnotationsEditor } from '../annotations_editor'; import { createSelectHandler } from '../lib/create_select_handler'; import { ColorPicker } from '../color_picker'; import { YesNo } from '../yes_no'; @@ -162,7 +161,6 @@ export class TimeseriesPanelConfig extends Component< ); diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx b/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx index 3c02deb177f9e..81ce4f50b0313 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx @@ -11,21 +11,21 @@ import { EuiRadio, htmlIdGenerator } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { TimeseriesVisParams } from '../../types'; -interface YesNoProps { - name: ParamName; +interface YesNoProps { + name: string; value: boolean | number | undefined; disabled?: boolean; 'data-test-subj'?: string; onChange: (partialModel: Partial) => void; } -export function YesNo({ +export function YesNo({ name, value, disabled, 'data-test-subj': dataTestSubj, onChange, -}: YesNoProps) { +}: YesNoProps) { const handleChange = useCallback( (val: number) => { return () => onChange({ [name]: val }); From cee33b004cfdabc870ab7f975396c0c70d06d2b1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 18 Jun 2021 16:09:31 +0100 Subject: [PATCH 06/14] chore(NA): moving @kbn/ui-shared-deps into bazel (#101669) * chore(NA): moving @kbn/io-ts-utils into bazel * chore(NA): moving @kbn/ui-shared-deps into bazel * chore(NA): compelte working build for @kbn/ui-shared-deps * chore(NA): solve eslint problems * chore(NA): solve typechecking * chore(NA): debugger changes * chore(NA): update optimizer basic integration tests * chore(NA): ship kbn/ui-shared-deps metrics.json from new location at shared_built_assets * chore(NA): use correct ui-shared-deps metrics file location * chore(NA): remove webpack bazel config * chore(NA): implement improvements on webpack config * chore(NA): remove extra comment * chore(NA): try esbuild-loader minimizer * Revert "chore(NA): try esbuild-loader minimizer" This reverts commit bffc49aaaed17ee769bfe1b3290979d49c63afdc. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/scripts/post_build_kibana.sh | 2 +- .eslintignore | 2 +- .eslintrc.js | 2 +- .i18nrc.json | 2 +- .../monorepo-packages.asciidoc | 1 + package.json | 3 +- packages/BUILD.bazel | 1 + packages/kbn-optimizer/package.json | 3 - .../basic_optimization.test.ts | 10 +- .../worker/populate_bundle_cache_plugin.ts | 15 +- packages/kbn-ui-shared-deps/BUILD.bazel | 145 ++++++++++++++++++ .../flot_charts/package.json | 4 + packages/kbn-ui-shared-deps/index.d.ts | 59 ------- packages/kbn-ui-shared-deps/package.json | 7 +- packages/kbn-ui-shared-deps/scripts/build.js | 98 ------------ .../kbn-ui-shared-deps/{ => src}/entry.js | 0 .../{ => src}/flot_charts/API.md | 0 .../{ => src}/flot_charts/index.js | 0 .../flot_charts/jquery_colorhelpers.js | 0 .../{ => src}/flot_charts/jquery_flot.js | 0 .../flot_charts/jquery_flot_axislabels.js | 0 .../flot_charts/jquery_flot_canvas.js | 0 .../flot_charts/jquery_flot_categories.js | 0 .../flot_charts/jquery_flot_crosshair.js | 0 .../flot_charts/jquery_flot_errorbars.js | 0 .../flot_charts/jquery_flot_fillbetween.js | 0 .../flot_charts/jquery_flot_image.js | 0 .../{ => src}/flot_charts/jquery_flot_log.js | 0 .../flot_charts/jquery_flot_navigate.js | 0 .../{ => src}/flot_charts/jquery_flot_pie.js | 0 .../flot_charts/jquery_flot_resize.js | 0 .../flot_charts/jquery_flot_selection.js | 0 .../flot_charts/jquery_flot_stack.js | 0 .../flot_charts/jquery_flot_symbol.js | 0 .../flot_charts/jquery_flot_threshold.js | 0 .../{ => src}/flot_charts/jquery_flot_time.js | 0 .../kbn-ui-shared-deps/{ => src}/index.js | 41 ++++- .../kbn-ui-shared-deps/{ => src}/polyfills.js | 0 .../{ => src}/public_path_loader.js | 0 .../{ => src}/public_path_module_creator.js | 0 .../kbn-ui-shared-deps/{ => src}/theme.ts | 0 .../kbn-ui-shared-deps/theme/package.json | 4 + packages/kbn-ui-shared-deps/tsconfig.json | 16 +- packages/kbn-ui-shared-deps/webpack.config.js | 108 ++++++------- test/scripts/jenkins_baseline.sh | 2 +- test/scripts/jenkins_build_kibana.sh | 2 +- test/scripts/jenkins_xpack_baseline.sh | 2 +- .../components/markdown_editor/types.ts | 1 - .../markdown_editor/plugins/index.ts | 1 - yarn.lock | 2 +- 50 files changed, 289 insertions(+), 244 deletions(-) create mode 100644 packages/kbn-ui-shared-deps/BUILD.bazel create mode 100644 packages/kbn-ui-shared-deps/flot_charts/package.json delete mode 100644 packages/kbn-ui-shared-deps/index.d.ts delete mode 100644 packages/kbn-ui-shared-deps/scripts/build.js rename packages/kbn-ui-shared-deps/{ => src}/entry.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/API.md (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/index.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_colorhelpers.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_axislabels.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_canvas.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_categories.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_crosshair.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_errorbars.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_fillbetween.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_image.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_log.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_navigate.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_pie.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_resize.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_selection.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_stack.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_symbol.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_threshold.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/flot_charts/jquery_flot_time.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/index.js (78%) rename packages/kbn-ui-shared-deps/{ => src}/polyfills.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/public_path_loader.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/public_path_module_creator.js (100%) rename packages/kbn-ui-shared-deps/{ => src}/theme.ts (100%) create mode 100644 packages/kbn-ui-shared-deps/theme/package.json diff --git a/.buildkite/scripts/post_build_kibana.sh b/.buildkite/scripts/post_build_kibana.sh index a4f8d71b77105..ad22a224f7c55 100755 --- a/.buildkite/scripts/post_build_kibana.sh +++ b/.buildkite/scripts/post_build_kibana.sh @@ -6,7 +6,7 @@ if [[ ! "${DISABLE_CI_STATS_SHIPPING:-}" ]]; then echo "--- Ship Kibana Distribution Metrics to CI Stats" node scripts/ship_ci_stats \ --metrics target/optimizer_bundle_metrics.json \ - --metrics packages/kbn-ui-shared-deps/target/metrics.json + --metrics node_modules/@kbn/ui-shared-deps/shared_built_assets/metrics.json fi echo "--- Upload Build Artifacts" diff --git a/.eslintignore b/.eslintignore index ce21d5bb31264..63cd01d6e90db 100644 --- a/.eslintignore +++ b/.eslintignore @@ -37,7 +37,7 @@ snapshots.js /packages/kbn-test/src/functional_test_runner/__tests__/fixtures/ /packages/kbn-test/src/functional_test_runner/lib/config/__tests__/fixtures/ /packages/kbn-ui-framework/dist -/packages/kbn-ui-shared-deps/flot_charts +/packages/kbn-ui-shared-deps/src/flot_charts /packages/kbn-monaco/src/painless/antlr # Bazel diff --git a/.eslintrc.js b/.eslintrc.js index c4883feff9b3c..40dd6a55a2a3f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1471,7 +1471,7 @@ module.exports = { }, }, { - files: ['packages/kbn-ui-shared-deps/flot_charts/**/*.js'], + files: ['packages/kbn-ui-shared-deps/src/flot_charts/**/*.js'], env: { jquery: true, }, diff --git a/.i18nrc.json b/.i18nrc.json index ad91042a2172d..0926f73722731 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -11,7 +11,7 @@ "uiActionsExamples": "examples/ui_action_examples", "share": "src/plugins/share", "home": "src/plugins/home", - "flot": "packages/kbn-ui-shared-deps/flot_charts", + "flot": "packages/kbn-ui-shared-deps/src/flot_charts", "charts": "src/plugins/charts", "esUi": "src/plugins/es_ui_shared", "devTools": "src/plugins/dev_tools", diff --git a/docs/developer/getting-started/monorepo-packages.asciidoc b/docs/developer/getting-started/monorepo-packages.asciidoc index ba484c9c2884f..c211751c09b49 100644 --- a/docs/developer/getting-started/monorepo-packages.asciidoc +++ b/docs/developer/getting-started/monorepo-packages.asciidoc @@ -102,5 +102,6 @@ yarn kbn watch-bazel - @kbn/std - @kbn/telemetry-utils - @kbn/tinymath +- @kbn/ui-shared-deps - @kbn/utility-types - @kbn/utils diff --git a/package.json b/package.json index b8a52f14c52ca..310350baf7b2d 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "@kbn/std": "link:bazel-bin/packages/kbn-std", "@kbn/tinymath": "link:bazel-bin/packages/kbn-tinymath", "@kbn/ui-framework": "link:packages/kbn-ui-framework", - "@kbn/ui-shared-deps": "link:packages/kbn-ui-shared-deps", + "@kbn/ui-shared-deps": "link:bazel-bin/packages/kbn-ui-shared-deps", "@kbn/utility-types": "link:bazel-bin/packages/kbn-utility-types", "@kbn/common-utils": "link:bazel-bin/packages/kbn-common-utils", "@kbn/utils": "link:bazel-bin/packages/kbn-utils", @@ -291,6 +291,7 @@ "mapbox-gl-draw-rectangle-mode": "1.0.4", "markdown-it": "^10.0.0", "md5": "^2.1.0", + "mdast-util-to-hast": "10.0.1", "memoize-one": "^5.0.0", "mime": "^2.4.4", "mime-types": "^2.1.27", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 7bce6c256f2f3..6208910729625 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -46,6 +46,7 @@ filegroup( "//packages/kbn-std:build", "//packages/kbn-telemetry-tools:build", "//packages/kbn-tinymath:build", + "//packages/kbn-ui-shared-deps:build", "//packages/kbn-utility-types:build", "//packages/kbn-utils:build", ], diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index 54fcdc3bb130f..a6c8284ad15f6 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -9,8 +9,5 @@ "build": "../../node_modules/.bin/tsc", "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" - }, - "dependencies": { - "@kbn/ui-shared-deps": "link:../kbn-ui-shared-deps" } } \ No newline at end of file diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts index 50c9e7e12904f..97a7f33be673d 100644 --- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts @@ -15,7 +15,7 @@ import cpy from 'cpy'; import del from 'del'; import { tap, filter } from 'rxjs/operators'; import { REPO_ROOT } from '@kbn/utils'; -import { ToolingLog } from '@kbn/dev-utils'; +import { ToolingLog, createReplaceSerializer } from '@kbn/dev-utils'; import { runOptimizer, OptimizerConfig, OptimizerUpdate, logOptimizerState } from '../index'; import { allValuesFrom } from '../common'; @@ -29,6 +29,8 @@ expect.addSnapshotSerializer({ test: (value: any) => typeof value === 'string' && value.includes(REPO_ROOT), }); +expect.addSnapshotSerializer(createReplaceSerializer(/\w+-fastbuild/, '-fastbuild')); + const log = new ToolingLog({ level: 'error', writeTo: { @@ -130,13 +132,13 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { expect(foo.cache.getModuleCount()).toBe(6); expect(foo.cache.getReferencedFiles()).toMatchInlineSnapshot(` Array [ + /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target/public_path_module_creator.js, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/kibana.json, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/lib.ts, /packages/kbn-optimizer/src/worker/entry_point_creator.ts, - /packages/kbn-ui-shared-deps/public_path_module_creator.js, ] `); @@ -153,6 +155,7 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { /node_modules/@kbn/optimizer/postcss.config.js, /node_modules/css-loader/package.json, /node_modules/style-loader/package.json, + /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target/public_path_module_creator.js, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/kibana.json, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.scss, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts, @@ -162,7 +165,6 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/core/public/core_app/styles/_globals_v8dark.scss, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/core/public/core_app/styles/_globals_v8light.scss, /packages/kbn-optimizer/src/worker/entry_point_creator.ts, - /packages/kbn-ui-shared-deps/public_path_module_creator.js, ] `); @@ -173,10 +175,10 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { expect(baz.cache.getReferencedFiles()).toMatchInlineSnapshot(` Array [ + /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/bazel-out/-fastbuild/bin/packages/kbn-ui-shared-deps/target/public_path_module_creator.js, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/x-pack/baz/kibana.json, /packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/x-pack/baz/public/index.ts, /packages/kbn-optimizer/src/worker/entry_point_creator.ts, - /packages/kbn-ui-shared-deps/public_path_module_creator.js, ] `); }); diff --git a/packages/kbn-optimizer/src/worker/populate_bundle_cache_plugin.ts b/packages/kbn-optimizer/src/worker/populate_bundle_cache_plugin.ts index 6d296b9be089c..8d890b31b639d 100644 --- a/packages/kbn-optimizer/src/worker/populate_bundle_cache_plugin.ts +++ b/packages/kbn-optimizer/src/worker/populate_bundle_cache_plugin.ts @@ -54,8 +54,19 @@ export class PopulateBundleCachePlugin { for (const module of compilation.modules) { if (isNormalModule(module)) { moduleCount += 1; - const path = getModulePath(module); - const parsedPath = parseFilePath(path); + let path = getModulePath(module); + let parsedPath = parseFilePath(path); + + if (parsedPath.dirs.includes('bazel-out')) { + const index = parsedPath.dirs.indexOf('bazel-out'); + path = Path.join( + workerConfig.repoRoot, + 'bazel-out', + ...parsedPath.dirs.slice(index + 1), + parsedPath.filename ?? '' + ); + parsedPath = parseFilePath(path); + } if (!parsedPath.dirs.includes('node_modules')) { referencedFiles.add(path); diff --git a/packages/kbn-ui-shared-deps/BUILD.bazel b/packages/kbn-ui-shared-deps/BUILD.bazel new file mode 100644 index 0000000000000..c04f88a42cce0 --- /dev/null +++ b/packages/kbn-ui-shared-deps/BUILD.bazel @@ -0,0 +1,145 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("@npm//webpack-cli:index.bzl", webpack = "webpack_cli") + +PKG_BASE_NAME = "kbn-ui-shared-deps" +PKG_REQUIRE_NAME = "@kbn/ui-shared-deps" + +SOURCE_FILES = glob( + [ + "src/**/*", + ], + exclude = [ + "**/*.md", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "flot_charts/package.json", + "theme/package.json", + "package.json", + "README.md" +] + +SRC_DEPS = [ + "//packages/elastic-datemath", + "//packages/elastic-safer-lodash-set", + "//packages/kbn-analytics", + "//packages/kbn-babel-preset", + "//packages/kbn-i18n", + "//packages/kbn-monaco", + "//packages/kbn-std", + "//packages/kbn-utils", + "@npm//@elastic/charts", + "@npm//@elastic/eui", + "@npm//@elastic/numeral", + "@npm//abortcontroller-polyfill", + "@npm//angular", + "@npm//babel-loader", + "@npm//compression-webpack-plugin", + "@npm//core-js", + "@npm//css-minimizer-webpack-plugin", + "@npm//css-loader", + "@npm//fflate", + "@npm//jquery", + "@npm//loader-utils", + # TODO: we can remove this once EUI patches the dependencies + "@npm//mdast-util-to-hast", + "@npm//mini-css-extract-plugin", + "@npm//moment", + "@npm//moment-timezone", + "@npm//raw-loader", + "@npm//react", + "@npm//react-dom", + "@npm//react-intl", + "@npm//react-is", + "@npm//react-router", + "@npm//react-router-dom", + "@npm//regenerator-runtime", + "@npm//resize-observer-polyfill", + "@npm//rison-node", + "@npm//rxjs", + "@npm//styled-components", + "@npm//symbol-observable", + "@npm//terser-webpack-plugin", + "@npm//url-loader", + "@npm//val-loader", + "@npm//whatwg-fetch" +] + +TYPES_DEPS = [ + "@npm//@types/node", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + allow_js = True, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +webpack( + name = "shared_built_assets", + data = DEPS + [ + "//:package.json", + ":srcs", + ":tsconfig", + ":webpack.config.js", + ], + output_dir = True, + args = [ + "--config", + "$(location webpack.config.js)", + "--output-path", + "$(@D)", + "--display=minimal" + ], +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = DEPS + [":tsc", ":shared_built_assets"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-ui-shared-deps/flot_charts/package.json b/packages/kbn-ui-shared-deps/flot_charts/package.json new file mode 100644 index 0000000000000..03d7ac348fcb9 --- /dev/null +++ b/packages/kbn-ui-shared-deps/flot_charts/package.json @@ -0,0 +1,4 @@ +{ + "main": "../target/flot_charts/index.js", + "types": "../target/flot_charts/index.d.ts" +} \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/index.d.ts b/packages/kbn-ui-shared-deps/index.d.ts deleted file mode 100644 index 5d2986daeeb3b..0000000000000 --- a/packages/kbn-ui-shared-deps/index.d.ts +++ /dev/null @@ -1,59 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** - * Absolute path to the distributable directory - */ -export const distDir: string; - -/** - * Filename of the main bundle file in the distributable directory - */ -export const jsFilename: string; - -/** - * Filename of files that must be loaded before the jsFilename - */ -export const jsDepFilenames: string[]; - -/** - * Filename of the unthemed css file in the distributable directory - */ -export const baseCssDistFilename: string; - -/** - * Filename of the dark-theme css file in the distributable directory - */ -export const darkCssDistFilename: string; - -/** - * Filename of the dark-theme css file in the distributable directory - */ -export const darkV8CssDistFilename: string; - -/** - * Filename of the light-theme css file in the distributable directory - */ -export const lightCssDistFilename: string; - -/** - * Filename of the light-theme css file in the distributable directory - */ -export const lightV8CssDistFilename: string; - -/** - * Externals mapping inteded to be used in a webpack config - */ -export const externals: { - [key: string]: string; -}; - -/** - * Webpack loader for configuring the public path lookup from `window.__kbnPublicPath__`. - */ -export const publicPathLoader: string; diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 162606585c43e..5ec32ca059aa1 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -3,9 +3,6 @@ "version": "1.0.0", "private": true, "license": "SSPL-1.0 OR Elastic License 2.0", - "scripts": { - "build": "node scripts/build", - "kbn:bootstrap": "node scripts/build --dev", - "kbn:watch": "node scripts/build --dev --watch" - } + "main": "target/index.js", + "types": "target/index.d.ts" } \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/scripts/build.js b/packages/kbn-ui-shared-deps/scripts/build.js deleted file mode 100644 index 0993f78590246..0000000000000 --- a/packages/kbn-ui-shared-deps/scripts/build.js +++ /dev/null @@ -1,98 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -const Path = require('path'); - -const { run, createFailError } = require('@kbn/dev-utils'); -const webpack = require('webpack'); -const Stats = require('webpack/lib/Stats'); -const del = require('del'); - -const { getWebpackConfig } = require('../webpack.config'); - -const DIST_DIR = Path.resolve(__dirname, '../target'); - -run( - async ({ log, flags }) => { - log.info('cleaning previous build output'); - await del(DIST_DIR); - - const compiler = webpack( - getWebpackConfig({ - dev: flags.dev, - }) - ); - - /** @param {webpack.Stats} stats */ - const onCompilationComplete = async (stats) => { - const took = Math.round((stats.endTime - stats.startTime) / 1000); - - if (!stats.hasErrors() && !stats.hasWarnings()) { - log.success(`webpack completed in about ${took} seconds`); - return; - } - - throw createFailError( - `webpack failure in about ${took} seconds\n${stats.toString({ - colors: true, - ...Stats.presetToOptions('minimal'), - })}` - ); - }; - - if (flags.watch) { - compiler.hooks.done.tap('report on stats', (stats) => { - onCompilationComplete(stats).catch((error) => { - log.error(error.message); - }); - }); - - compiler.hooks.watchRun.tap('report on start', () => { - if (process.stdout.isTTY) { - process.stdout.cursorTo(0, 0); - process.stdout.clearScreenDown(); - } - - log.info('Running webpack compilation...'); - }); - - compiler.watch({}, (error) => { - if (error) { - log.error('Fatal webpack error'); - log.error(error); - process.exit(1); - } - }); - - return; - } - - log.info('running webpack'); - await onCompilationComplete( - await new Promise((resolve, reject) => { - compiler.run((error, stats) => { - if (error) { - reject(error); - } else { - resolve(stats); - } - }); - }) - ); - }, - { - description: 'build @kbn/ui-shared-deps', - flags: { - boolean: ['watch', 'dev'], - help: ` - --watch Run in watch mode - --dev Build development friendly version - `, - }, - } -); diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/src/entry.js similarity index 100% rename from packages/kbn-ui-shared-deps/entry.js rename to packages/kbn-ui-shared-deps/src/entry.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/API.md b/packages/kbn-ui-shared-deps/src/flot_charts/API.md similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/API.md rename to packages/kbn-ui-shared-deps/src/flot_charts/API.md diff --git a/packages/kbn-ui-shared-deps/flot_charts/index.js b/packages/kbn-ui-shared-deps/src/flot_charts/index.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/index.js rename to packages/kbn-ui-shared-deps/src/flot_charts/index.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_colorhelpers.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_colorhelpers.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_colorhelpers.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_colorhelpers.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_axislabels.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_axislabels.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_axislabels.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_axislabels.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_canvas.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_canvas.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_canvas.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_canvas.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_categories.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_categories.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_categories.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_categories.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_crosshair.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_crosshair.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_crosshair.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_crosshair.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_errorbars.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_errorbars.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_errorbars.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_errorbars.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_fillbetween.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_fillbetween.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_fillbetween.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_fillbetween.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_image.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_image.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_image.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_image.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_log.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_log.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_log.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_log.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_navigate.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_navigate.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_navigate.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_navigate.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_pie.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_pie.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_pie.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_pie.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_resize.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_resize.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_resize.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_resize.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_selection.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_selection.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_selection.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_selection.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_stack.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_stack.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_stack.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_stack.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_symbol.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_symbol.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_symbol.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_symbol.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_threshold.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_threshold.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_threshold.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_threshold.js diff --git a/packages/kbn-ui-shared-deps/flot_charts/jquery_flot_time.js b/packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_time.js similarity index 100% rename from packages/kbn-ui-shared-deps/flot_charts/jquery_flot_time.js rename to packages/kbn-ui-shared-deps/src/flot_charts/jquery_flot_time.js diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/src/index.js similarity index 78% rename from packages/kbn-ui-shared-deps/index.js rename to packages/kbn-ui-shared-deps/src/index.js index 877bf3df6c039..c5853dc091875 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/src/index.js @@ -8,14 +8,49 @@ const Path = require('path'); -exports.distDir = Path.resolve(__dirname, 'target'); +/** + * Absolute path to the distributable directory + */ +exports.distDir = Path.resolve(__dirname, '..', 'shared_built_assets'); + +/** + * Filename of files that must be loaded before the jsFilename + */ exports.jsDepFilenames = ['kbn-ui-shared-deps.@elastic.js']; + +/** + * Filename of the main bundle file in the distributable directory + */ exports.jsFilename = 'kbn-ui-shared-deps.js'; + +/** + * Filename of the unthemed css file in the distributable directory + */ exports.baseCssDistFilename = 'kbn-ui-shared-deps.css'; + +/** + * Filename of the light-theme css file in the distributable directory + */ exports.lightCssDistFilename = 'kbn-ui-shared-deps.v7.light.css'; + +/** + * Filename of the light-theme css file in the distributable directory + */ exports.lightV8CssDistFilename = 'kbn-ui-shared-deps.v8.light.css'; + +/** + * Filename of the dark-theme css file in the distributable directory + */ exports.darkCssDistFilename = 'kbn-ui-shared-deps.v7.dark.css'; + +/** + * Filename of the dark-theme css file in the distributable directory + */ exports.darkV8CssDistFilename = 'kbn-ui-shared-deps.v8.dark.css'; + +/** + * Externals mapping inteded to be used in a webpack config + */ exports.externals = { // stateful deps angular: '__kbnSharedDeps__.Angular', @@ -63,4 +98,8 @@ exports.externals = { '@elastic/safer-lodash-set': '__kbnSharedDeps__.SaferLodashSet', 'rison-node': '__kbnSharedDeps__.RisonNode', }; + +/** + * Webpack loader for configuring the public path lookup from `window.__kbnPublicPath__`. + */ exports.publicPathLoader = require.resolve('./public_path_loader'); diff --git a/packages/kbn-ui-shared-deps/polyfills.js b/packages/kbn-ui-shared-deps/src/polyfills.js similarity index 100% rename from packages/kbn-ui-shared-deps/polyfills.js rename to packages/kbn-ui-shared-deps/src/polyfills.js diff --git a/packages/kbn-ui-shared-deps/public_path_loader.js b/packages/kbn-ui-shared-deps/src/public_path_loader.js similarity index 100% rename from packages/kbn-ui-shared-deps/public_path_loader.js rename to packages/kbn-ui-shared-deps/src/public_path_loader.js diff --git a/packages/kbn-ui-shared-deps/public_path_module_creator.js b/packages/kbn-ui-shared-deps/src/public_path_module_creator.js similarity index 100% rename from packages/kbn-ui-shared-deps/public_path_module_creator.js rename to packages/kbn-ui-shared-deps/src/public_path_module_creator.js diff --git a/packages/kbn-ui-shared-deps/theme.ts b/packages/kbn-ui-shared-deps/src/theme.ts similarity index 100% rename from packages/kbn-ui-shared-deps/theme.ts rename to packages/kbn-ui-shared-deps/src/theme.ts diff --git a/packages/kbn-ui-shared-deps/theme/package.json b/packages/kbn-ui-shared-deps/theme/package.json new file mode 100644 index 0000000000000..2d41937701a29 --- /dev/null +++ b/packages/kbn-ui-shared-deps/theme/package.json @@ -0,0 +1,4 @@ +{ + "main": "../target/theme.js", + "types": "../target/theme.d.ts" +} \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/tsconfig.json b/packages/kbn-ui-shared-deps/tsconfig.json index 88699027f85de..0fd49ede21830 100644 --- a/packages/kbn-ui-shared-deps/tsconfig.json +++ b/packages/kbn-ui-shared-deps/tsconfig.json @@ -1,10 +1,20 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "tsBuildInfoFile": "../../build/tsbuildinfo/packages/kbn-ui-shared-deps" + "allowJs": true, + "incremental": true, + "outDir": "./target", + "declaration": true, + "declarationMap": true, + "rootDir": "src", + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-ui-shared-deps/src", + "types": [ + "node", + "resize-observer-polyfill" + ] }, "include": [ - "index.d.ts", - "theme.ts" + "src/**/*", ] } diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index 76e6843bea2f8..438b1e0b2e77b 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -7,6 +7,7 @@ */ const Path = require('path'); +const Os = require('os'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); @@ -14,24 +15,23 @@ const TerserPlugin = require('terser-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); const { REPO_ROOT } = require('@kbn/utils'); -const webpack = require('webpack'); const { RawSource } = require('webpack-sources'); -const UiSharedDeps = require('./index'); +const UiSharedDeps = require('./src/index'); const MOMENT_SRC = require.resolve('moment/min/moment-with-locales.js'); -exports.getWebpackConfig = ({ dev = false } = {}) => ({ - mode: dev ? 'development' : 'production', +module.exports = { + mode: 'production', entry: { - 'kbn-ui-shared-deps': './entry.js', + 'kbn-ui-shared-deps': './src/entry.js', 'kbn-ui-shared-deps.v7.dark': ['@elastic/eui/dist/eui_theme_dark.css'], 'kbn-ui-shared-deps.v7.light': ['@elastic/eui/dist/eui_theme_light.css'], 'kbn-ui-shared-deps.v8.dark': ['@elastic/eui/dist/eui_theme_amsterdam_dark.css'], 'kbn-ui-shared-deps.v8.light': ['@elastic/eui/dist/eui_theme_amsterdam_light.css'], }, context: __dirname, - devtool: dev ? '#cheap-source-map' : false, + devtool: 'cheap-source-map', output: { path: UiSharedDeps.distDir, filename: '[name].js', @@ -39,13 +39,14 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ devtoolModuleFilenameTemplate: (info) => `kbn-ui-shared-deps/${Path.relative(REPO_ROOT, info.absoluteResourcePath)}`, library: '__kbnSharedDeps__', + futureEmitAssets: true, }, module: { noParse: [MOMENT_SRC], rules: [ { - include: [require.resolve('./entry.js')], + include: [require.resolve('./src/entry.js')], use: [ { loader: UiSharedDeps.publicPathLoader, @@ -60,7 +61,7 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ use: [MiniCssExtractPlugin.loader, 'css-loader'], }, { - include: [require.resolve('./theme.ts')], + include: [require.resolve('./src/theme.ts')], use: [ { loader: 'babel-loader', @@ -71,7 +72,7 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ ], }, { - test: !dev ? /[\\\/]@elastic[\\\/]eui[\\\/].*\.js$/ : () => false, + test: /[\\\/]@elastic[\\\/]eui[\\\/].*\.js$/, use: [ { loader: 'babel-loader', @@ -110,6 +111,7 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ optimization: { minimizer: [ new CssMinimizerPlugin({ + parallel: Math.min(Os.cpus().length, 2), minimizerOptions: { preset: [ 'default', @@ -123,7 +125,7 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ cache: false, sourceMap: false, extractComments: false, - parallel: false, + parallel: Math.min(Os.cpus().length, 2), terserOptions: { compress: true, mangle: true, @@ -154,54 +156,44 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ new MiniCssExtractPlugin({ filename: '[name].css', }), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': dev ? '"development"' : '"production"', + new CompressionPlugin({ + algorithm: 'brotliCompress', + filename: '[path].br', + test: /\.(js|css)$/, + cache: false, }), - ...(dev - ? [] - : [ - new CompressionPlugin({ - algorithm: 'brotliCompress', - filename: '[path].br', - test: /\.(js|css)$/, - cache: false, - }), - new CompressionPlugin({ - algorithm: 'gzip', - filename: '[path].gz', - test: /\.(js|css)$/, - cache: false, - }), - new (class MetricsPlugin { - apply(compiler) { - compiler.hooks.emit.tap('MetricsPlugin', (compilation) => { - const metrics = [ - { - group: 'page load bundle size', - id: 'kbnUiSharedDeps-js', - value: compilation.assets['kbn-ui-shared-deps.js'].size(), - }, - { - group: 'page load bundle size', - id: 'kbnUiSharedDeps-css', - value: - compilation.assets['kbn-ui-shared-deps.css'].size() + - compilation.assets['kbn-ui-shared-deps.v7.light.css'].size(), - }, - { - group: 'page load bundle size', - id: 'kbnUiSharedDeps-elastic', - value: compilation.assets['kbn-ui-shared-deps.@elastic.js'].size(), - }, - ]; + new CompressionPlugin({ + algorithm: 'gzip', + filename: '[path].gz', + test: /\.(js|css)$/, + cache: false, + }), + new (class MetricsPlugin { + apply(compiler) { + compiler.hooks.emit.tap('MetricsPlugin', (compilation) => { + const metrics = [ + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-js', + value: compilation.assets['kbn-ui-shared-deps.js'].size(), + }, + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-css', + value: + compilation.assets['kbn-ui-shared-deps.css'].size() + + compilation.assets['kbn-ui-shared-deps.v7.light.css'].size(), + }, + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-elastic', + value: compilation.assets['kbn-ui-shared-deps.@elastic.js'].size(), + }, + ]; - compilation.emitAsset( - 'metrics.json', - new RawSource(JSON.stringify(metrics, null, 2)) - ); - }); - } - })(), - ]), + compilation.emitAsset('metrics.json', new RawSource(JSON.stringify(metrics, null, 2))); + }); + } + })(), ], -}); +}; diff --git a/test/scripts/jenkins_baseline.sh b/test/scripts/jenkins_baseline.sh index 58d86cddf65fa..40bfc6e83ad1b 100755 --- a/test/scripts/jenkins_baseline.sh +++ b/test/scripts/jenkins_baseline.sh @@ -9,7 +9,7 @@ node scripts/build --debug --oss echo " -> shipping metrics from build to ci-stats" node scripts/ship_ci_stats \ --metrics target/optimizer_bundle_metrics.json \ - --metrics packages/kbn-ui-shared-deps/target/metrics.json + --metrics node_modules/@kbn/ui-shared-deps/shared_built_assets/metrics.json linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" installDir="$PARENT_DIR/install/kibana" diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh index 32fb98929e21c..198723908cf48 100755 --- a/test/scripts/jenkins_build_kibana.sh +++ b/test/scripts/jenkins_build_kibana.sh @@ -38,7 +38,7 @@ if [[ -z "$CODE_COVERAGE" ]] ; then echo " -> shipping metrics from build to ci-stats" node scripts/ship_ci_stats \ --metrics target/optimizer_bundle_metrics.json \ - --metrics packages/kbn-ui-shared-deps/target/metrics.json + --metrics node_modules/@kbn/ui-shared-deps/shared_built_assets/metrics.json linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" installDir="$KIBANA_DIR/install/kibana" diff --git a/test/scripts/jenkins_xpack_baseline.sh b/test/scripts/jenkins_xpack_baseline.sh index 93363687b39a9..8d5624949505a 100755 --- a/test/scripts/jenkins_xpack_baseline.sh +++ b/test/scripts/jenkins_xpack_baseline.sh @@ -10,7 +10,7 @@ node scripts/build --debug --no-oss echo " -> shipping metrics from build to ci-stats" node scripts/ship_ci_stats \ --metrics target/optimizer_bundle_metrics.json \ - --metrics packages/kbn-ui-shared-deps/target/metrics.json + --metrics node_modules/@kbn/ui-shared-deps/shared_built_assets/metrics.json linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" installDir="$KIBANA_DIR/install/kibana" diff --git a/x-pack/plugins/cases/public/components/markdown_editor/types.ts b/x-pack/plugins/cases/public/components/markdown_editor/types.ts index bb932f2fcfe22..ccc3c59c8977e 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/types.ts +++ b/x-pack/plugins/cases/public/components/markdown_editor/types.ts @@ -8,7 +8,6 @@ import { FunctionComponent } from 'react'; import { Plugin, PluggableList } from 'unified'; // Remove after this issue is resolved: https://github.com/elastic/eui/issues/4688 -// eslint-disable-next-line import/no-extraneous-dependencies import { Options as Remark2RehypeOptions } from 'mdast-util-to-hast'; // eslint-disable-next-line import/no-extraneous-dependencies import rehype2react from 'rehype-react'; diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts index c744ace91f434..d5846a9f9ea50 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts @@ -12,7 +12,6 @@ import { getDefaultEuiMarkdownUiPlugins, } from '@elastic/eui'; // Remove after this issue is resolved: https://github.com/elastic/eui/issues/4688 -// eslint-disable-next-line import/no-extraneous-dependencies import { Options as Remark2RehypeOptions } from 'mdast-util-to-hast'; import { FunctionComponent } from 'react'; // eslint-disable-next-line import/no-extraneous-dependencies diff --git a/yarn.lock b/yarn.lock index d0531ebfbaede..c9e139e68b592 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2792,7 +2792,7 @@ version "0.0.0" uid "" -"@kbn/ui-shared-deps@link:packages/kbn-ui-shared-deps": +"@kbn/ui-shared-deps@link:bazel-bin/packages/kbn-ui-shared-deps": version "0.0.0" uid "" From 303806de65bedfe1accef0eb6d346be53a1ed1f7 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Fri, 18 Jun 2021 17:30:53 +0200 Subject: [PATCH 07/14] [Exploratory View] Mobile experience (#99565) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Bryce Buchanan Co-authored-by: Alexander Wert --- .../apm_observability_overview_fetchers.ts | 4 +- .../lib/observability_overview/has_data.ts | 10 +- .../settings/apm_indices/get_apm_indices.ts | 16 +- .../server/routes/observability_overview.ts | 3 +- .../plugins/observability/common/typings.ts | 13 ++ .../components/app/empty_sections/index.tsx | 10 +- .../components/app/section/apm/index.test.tsx | 2 +- .../components/app/section/apm/index.tsx | 4 +- .../components/app/section/logs/index.tsx | 4 +- .../components/app/section/metrics/index.tsx | 4 +- .../components/app/section/uptime/index.tsx | 4 +- .../components/app/section/ux/index.test.tsx | 5 +- .../components/app/section/ux/index.tsx | 7 +- .../configurations/apm/field_formats.ts | 15 +- .../configurations/constants/constants.ts | 9 +- .../constants/elasticsearch_fieldnames.ts | 2 + .../configurations/constants/labels.ts | 97 +++++++++++- .../configurations/default_configs.ts | 12 +- .../configurations/lens_attributes.ts | 92 +++++++++-- .../mobile/device_distribution_config.ts | 49 ++++++ .../mobile/distribution_config.ts | 81 ++++++++++ .../mobile/kpi_over_time_config.ts | 102 ++++++++++++ .../configurations/mobile/mobile_fields.ts | 26 ++++ .../exploratory_view.test.tsx | 2 +- .../hooks/use_app_index_pattern.tsx | 31 ++-- .../series_builder/columns/data_types_col.tsx | 1 + .../series_builder/columns/report_filters.tsx | 2 + .../series_builder/series_builder.tsx | 21 ++- .../columns/filter_expanded.test.tsx | 4 + .../series_editor/columns/filter_expanded.tsx | 39 ++++- .../series_editor/columns/series_filter.tsx | 16 +- .../series_editor/series_editor.tsx | 7 +- .../shared/exploratory_view/types.ts | 12 +- .../utils/observability_index_patterns.ts | 4 + .../public/context/has_data_context.test.tsx | 146 +++++++++++------- .../public/context/has_data_context.tsx | 76 ++++++--- .../observability/public/data_handler.test.ts | 10 +- .../public/pages/home/index.test.tsx | 42 ++--- .../public/pages/overview/index.tsx | 4 +- .../pages/overview/overview.stories.tsx | 20 ++- .../typings/fetch_overview_data/index.ts | 19 ++- 41 files changed, 822 insertions(+), 205 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_fields.ts diff --git a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts index 3a02efd05e5a5..ef61e25af4fc2 100644 --- a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts +++ b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts @@ -53,10 +53,8 @@ export const fetchObservabilityOverviewPageData = async ({ }; export async function getHasData() { - const res = await callApmApi({ + return await callApmApi({ endpoint: 'GET /api/apm/observability_overview/has_data', signal: null, }); - - return res.hasData; } diff --git a/x-pack/plugins/apm/server/lib/observability_overview/has_data.ts b/x-pack/plugins/apm/server/lib/observability_overview/has_data.ts index 5c1a33e750e12..3b6993695f3de 100644 --- a/x-pack/plugins/apm/server/lib/observability_overview/has_data.ts +++ b/x-pack/plugins/apm/server/lib/observability_overview/has_data.ts @@ -29,8 +29,14 @@ export async function getHasData({ setup }: { setup: Setup }) { 'observability_overview_has_apm_data', params ); - return response.hits.total.value > 0; + return { + hasData: response.hits.total.value > 0, + indices: setup.indices, + }; } catch (e) { - return false; + return { + hasData: false, + indices: setup.indices, + }; } } diff --git a/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts b/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts index d8dbc242986a6..0ade96682b362 100644 --- a/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts +++ b/x-pack/plugins/apm/server/lib/settings/apm_indices/get_apm_indices.ts @@ -16,21 +16,11 @@ import { import { APMConfig } from '../../..'; import { APMRouteHandlerResources } from '../../../routes/typings'; import { withApmSpan } from '../../../utils/with_apm_span'; +import { ApmIndicesConfig } from '../../../../../observability/common/typings'; -type ISavedObjectsClient = Pick; +export { ApmIndicesConfig }; -export interface ApmIndicesConfig { - /* eslint-disable @typescript-eslint/naming-convention */ - 'apm_oss.sourcemapIndices': string; - 'apm_oss.errorIndices': string; - 'apm_oss.onboardingIndices': string; - 'apm_oss.spanIndices': string; - 'apm_oss.transactionIndices': string; - 'apm_oss.metricsIndices': string; - /* eslint-enable @typescript-eslint/naming-convention */ - apmAgentConfigurationIndex: string; - apmCustomLinkIndex: string; -} +type ISavedObjectsClient = Pick; export type ApmIndicesName = keyof ApmIndicesConfig; diff --git a/x-pack/plugins/apm/server/routes/observability_overview.ts b/x-pack/plugins/apm/server/routes/observability_overview.ts index d459570cf7337..c2e3d0e81ce0a 100644 --- a/x-pack/plugins/apm/server/routes/observability_overview.ts +++ b/x-pack/plugins/apm/server/routes/observability_overview.ts @@ -21,8 +21,7 @@ const observabilityOverviewHasDataRoute = createApmServerRoute({ options: { tags: ['access:apm'] }, handler: async (resources) => { const setup = await setupRequest(resources); - const res = await getHasData({ setup }); - return { hasData: res }; + return await getHasData({ setup }); }, }); diff --git a/x-pack/plugins/observability/common/typings.ts b/x-pack/plugins/observability/common/typings.ts index bd10543ef389b..305a18903fe7e 100644 --- a/x-pack/plugins/observability/common/typings.ts +++ b/x-pack/plugins/observability/common/typings.ts @@ -10,3 +10,16 @@ export type Maybe = T | null | undefined; export const alertStatusRt = t.union([t.literal('all'), t.literal('open'), t.literal('closed')]); export type AlertStatus = t.TypeOf; + +export interface ApmIndicesConfig { + /* eslint-disable @typescript-eslint/naming-convention */ + 'apm_oss.sourcemapIndices': string; + 'apm_oss.errorIndices': string; + 'apm_oss.onboardingIndices': string; + 'apm_oss.spanIndices': string; + 'apm_oss.transactionIndices': string; + 'apm_oss.metricsIndices': string; + /* eslint-enable @typescript-eslint/naming-convention */ + apmAgentConfigurationIndex: string; + apmCustomLinkIndex: string; +} diff --git a/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx b/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx index f7ce8675d8a45..47417a2bbb545 100644 --- a/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx +++ b/x-pack/plugins/observability/public/components/app/empty_sections/index.tsx @@ -13,26 +13,24 @@ import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { useHasData } from '../../../hooks/use_has_data'; import { usePluginContext } from '../../../hooks/use_plugin_context'; import { getEmptySections } from '../../../pages/overview/empty_section'; -import { UXHasDataResponse } from '../../../typings'; import { EmptySection } from './empty_section'; export function EmptySections() { const { core } = usePluginContext(); const theme = useContext(ThemeContext); - const { hasData } = useHasData(); + const { hasDataMap } = useHasData(); const appEmptySections = getEmptySections({ core }).filter(({ id }) => { if (id === 'alert') { - const { status, hasData: alerts } = hasData.alert || {}; + const { status, hasData: alerts } = hasDataMap.alert || {}; return ( status === FETCH_STATUS.FAILURE || (status === FETCH_STATUS.SUCCESS && (alerts as Alert[]).length === 0) ); } else { - const app = hasData[id]; + const app = hasDataMap[id]; if (app) { - const _hasData = id === 'ux' ? (app.hasData as UXHasDataResponse)?.hasData : app.hasData; - return app.status === FETCH_STATUS.FAILURE || !_hasData; + return app.status === FETCH_STATUS.FAILURE || !app.hasData; } } return false; diff --git a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx index ad3ecd2740802..16eb8dd24d3c2 100644 --- a/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/apm/index.test.tsx @@ -29,7 +29,7 @@ jest.mock('react-router-dom', () => ({ describe('APMSection', () => { beforeAll(() => { jest.spyOn(hasDataHook, 'useHasData').mockReturnValue({ - hasData: { + hasDataMap: { apm: { status: fetcherHook.FETCH_STATUS.SUCCESS, hasData: true, diff --git a/x-pack/plugins/observability/public/components/app/section/apm/index.tsx b/x-pack/plugins/observability/public/components/app/section/apm/index.tsx index e71468d3b028c..7a42e96c3823d 100644 --- a/x-pack/plugins/observability/public/components/app/section/apm/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/apm/index.tsx @@ -48,7 +48,7 @@ export function APMSection({ bucketSize }: Props) { const theme = useContext(ThemeContext); const chartTheme = useChartTheme(); const history = useHistory(); - const { forceUpdate, hasData } = useHasData(); + const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useTimeRange(); const { data, status } = useFetcher( @@ -66,7 +66,7 @@ export function APMSection({ bucketSize }: Props) { [bucketSize, relativeStart, relativeEnd, forceUpdate] ); - if (!hasData.apm?.hasData) { + if (!hasDataMap.apm?.hasData) { return null; } diff --git a/x-pack/plugins/observability/public/components/app/section/logs/index.tsx b/x-pack/plugins/observability/public/components/app/section/logs/index.tsx index cb4c831d25022..da5a8f25045a5 100644 --- a/x-pack/plugins/observability/public/components/app/section/logs/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/logs/index.tsx @@ -47,7 +47,7 @@ function getColorPerItem(series?: LogsFetchDataResponse['series']) { export function LogsSection({ bucketSize }: Props) { const history = useHistory(); const chartTheme = useChartTheme(); - const { forceUpdate, hasData } = useHasData(); + const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useTimeRange(); const { data, status } = useFetcher( @@ -65,7 +65,7 @@ export function LogsSection({ bucketSize }: Props) { [bucketSize, relativeStart, relativeEnd, forceUpdate] ); - if (!hasData.infra_logs?.hasData) { + if (!hasDataMap.infra_logs?.hasData) { return null; } diff --git a/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx b/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx index 5a642084733c7..2f5bb9bac9348 100644 --- a/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/metrics/index.tsx @@ -50,7 +50,7 @@ const bytesPerSecondFormatter = (value: NumberOrNull) => value === null ? '' : numeral(value).format('0b') + '/s'; export function MetricsSection({ bucketSize }: Props) { - const { forceUpdate, hasData } = useHasData(); + const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useTimeRange(); const [sortDirection, setSortDirection] = useState('asc'); const [sortField, setSortField] = useState('uptime'); @@ -88,7 +88,7 @@ export function MetricsSection({ bucketSize }: Props) { [data, setSortField, setSortDirection] ); - if (!hasData.infra_metrics?.hasData) { + if (!hasDataMap.infra_metrics?.hasData) { return null; } diff --git a/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx b/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx index 1dbcdeaee800a..28cbd12663c1b 100644 --- a/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/uptime/index.tsx @@ -40,7 +40,7 @@ export function UptimeSection({ bucketSize }: Props) { const theme = useContext(ThemeContext); const chartTheme = useChartTheme(); const history = useHistory(); - const { forceUpdate, hasData } = useHasData(); + const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useTimeRange(); const { data, status } = useFetcher( @@ -58,7 +58,7 @@ export function UptimeSection({ bucketSize }: Props) { [bucketSize, relativeStart, relativeEnd, forceUpdate] ); - if (!hasData.synthetics?.hasData) { + if (!hasDataMap.synthetics?.hasData) { return null; } diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx index fab461476e713..61bce8aaf845d 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx @@ -28,10 +28,11 @@ jest.mock('react-router-dom', () => ({ describe('UXSection', () => { beforeAll(() => { jest.spyOn(hasDataHook, 'useHasData').mockReturnValue({ - hasData: { + hasDataMap: { ux: { status: fetcherHook.FETCH_STATUS.SUCCESS, - hasData: { hasData: true, serviceName: 'elastic-co-frontend' }, + hasData: true, + serviceName: 'elastic-co-frontend', }, }, } as HasDataContextValue); diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx index 0ac337e5ba0b1..5aa89eb2d3074 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.tsx @@ -12,7 +12,6 @@ import { getDataHandler } from '../../../../data_handler'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useHasData } from '../../../../hooks/use_has_data'; import { useTimeRange } from '../../../../hooks/use_time_range'; -import { UXHasDataResponse } from '../../../../typings'; import CoreVitals from '../../../shared/core_web_vitals'; interface Props { @@ -20,10 +19,10 @@ interface Props { } export function UXSection({ bucketSize }: Props) { - const { forceUpdate, hasData } = useHasData(); + const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useTimeRange(); - const uxHasDataResponse = (hasData.ux?.hasData as UXHasDataResponse) || {}; - const serviceName = uxHasDataResponse.serviceName as string; + const uxHasDataResponse = hasDataMap.ux; + const serviceName = uxHasDataResponse?.serviceName as string; const { data, status } = useFetcher( () => { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/apm/field_formats.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/apm/field_formats.ts index 8d33dfbab2c62..5c1afbca2a776 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/apm/field_formats.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/apm/field_formats.ts @@ -6,7 +6,11 @@ */ import { FieldFormat } from '../../types'; -import { TRANSACTION_DURATION } from '../constants/elasticsearch_fieldnames'; +import { + METRIC_SYSTEM_CPU_USAGE, + METRIC_SYSTEM_MEMORY_USAGE, + TRANSACTION_DURATION, +} from '../constants/elasticsearch_fieldnames'; export const apmFieldFormats: FieldFormat[] = [ { @@ -18,7 +22,16 @@ export const apmFieldFormats: FieldFormat[] = [ outputFormat: 'asMilliseconds', outputPrecision: 0, showSuffix: true, + useShortSuffix: true, }, }, }, + { + field: METRIC_SYSTEM_MEMORY_USAGE, + format: { id: 'bytes', params: {} }, + }, + { + field: METRIC_SYSTEM_CPU_USAGE, + format: { id: 'percent', params: {} }, + }, ]; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts index 26459e676de08..e119507860c5c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts @@ -13,12 +13,13 @@ import { BROWSER_VERSION_LABEL, CLS_LABEL, CORE_WEB_VITALS_LABEL, + DEVICE_DISTRIBUTION_LABEL, DEVICE_LABEL, ENVIRONMENT_LABEL, FCP_LABEL, FID_LABEL, HOST_NAME_LABEL, - KIP_OVER_TIME_LABEL, + KPI_OVER_TIME_LABEL, KPI_LABEL, LCP_LABEL, LOCATION_LABEL, @@ -31,6 +32,7 @@ import { OS_LABEL, PERF_DIST_LABEL, PORT_LABEL, + REQUEST_METHOD, SERVICE_NAME_LABEL, TAGS_LABEL, TBT_LABEL, @@ -72,14 +74,17 @@ export const FieldLabels: Record = { 'performance.metric': METRIC_LABEL, 'Business.KPI': KPI_LABEL, + 'http.request.method': REQUEST_METHOD, }; export const DataViewLabels: Record = { dist: PERF_DIST_LABEL, - kpi: KIP_OVER_TIME_LABEL, + kpi: KPI_OVER_TIME_LABEL, cwv: CORE_WEB_VITALS_LABEL, + mdd: DEVICE_DISTRIBUTION_LABEL, }; export const USE_BREAK_DOWN_COLUMN = 'USE_BREAK_DOWN_COLUMN'; export const FILTER_RECORDS = 'FILTER_RECORDS'; +export const TERMS_COLUMN = 'TERMS_COLUMN'; export const OPERATION_COLUMN = 'operation'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/elasticsearch_fieldnames.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/elasticsearch_fieldnames.ts index 5ecc5b758de84..01dd2a49b9be0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/elasticsearch_fieldnames.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/elasticsearch_fieldnames.ts @@ -86,6 +86,8 @@ export const ERROR_PAGE_URL = 'error.page.url'; // METRICS export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; +export const METRIC_SYSTEM_MEMORY_USAGE = 'system.memory.usage'; +export const METRIC_SYSTEM_CPU_USAGE = 'system.cpu.usage'; export const METRIC_SYSTEM_TOTAL_MEMORY = 'system.memory.total'; export const METRIC_SYSTEM_CPU_PERCENT = 'system.cpu.total.norm.pct'; export const METRIC_PROCESS_CPU_PERCENT = 'system.process.cpu.total.norm.pct'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts index b5816daa419df..73739b7db12ef 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts @@ -165,7 +165,7 @@ export const KPI_LABEL = i18n.translate('xpack.observability.expView.fieldLabels export const PERF_DIST_LABEL = i18n.translate( 'xpack.observability.expView.fieldLabels.performanceDistribution', { - defaultMessage: 'Performance Distribution', + defaultMessage: 'Performance distribution', } ); @@ -176,6 +176,20 @@ export const CORE_WEB_VITALS_LABEL = i18n.translate( } ); +export const DEVICE_DISTRIBUTION_LABEL = i18n.translate( + 'xpack.observability.expView.fieldLabels.deviceDistribution', + { + defaultMessage: 'Device distribution', + } +); + +export const MOBILE_RESPONSE_LABEL = i18n.translate( + 'xpack.observability.expView.fieldLabels.mobileResponse', + { + defaultMessage: 'Mobile response', + } +); + export const MEMORY_USAGE_LABEL = i18n.translate( 'xpack.observability.expView.fieldLabels.memoryUsage', { @@ -183,7 +197,7 @@ export const MEMORY_USAGE_LABEL = i18n.translate( } ); -export const KIP_OVER_TIME_LABEL = i18n.translate( +export const KPI_OVER_TIME_LABEL = i18n.translate( 'xpack.observability.expView.fieldLabels.kpiOverTime', { defaultMessage: 'KPI over time', @@ -211,3 +225,82 @@ export const UP_LABEL = i18n.translate('xpack.observability.expView.fieldLabels. export const DOWN_LABEL = i18n.translate('xpack.observability.expView.fieldLabels.downPings', { defaultMessage: 'Down Pings', }); + +export const CARRIER_NAME = i18n.translate('xpack.observability.expView.fieldLabels.carrierName', { + defaultMessage: 'Carrier Name', +}); + +export const REQUEST_METHOD = i18n.translate( + 'xpack.observability.expView.fieldLabels.requestMethod', + { + defaultMessage: 'Request Method', + } +); + +export const CONNECTION_TYPE = i18n.translate( + 'xpack.observability.expView.fieldLabels.connectionType', + { + defaultMessage: 'Connection Type', + } +); +export const HOST_OS = i18n.translate('xpack.observability.expView.fieldLabels.hostOS', { + defaultMessage: 'Host OS', +}); + +export const SERVICE_VERSION = i18n.translate( + 'xpack.observability.expView.fieldLabels.serviceVersion', + { + defaultMessage: 'Service Version', + } +); + +export const OS_PLATFORM = i18n.translate('xpack.observability.expView.fieldLabels.osPlatform', { + defaultMessage: 'OS Platform', +}); + +export const DEVICE_MODEL = i18n.translate('xpack.observability.expView.fieldLabels.deviceModel', { + defaultMessage: 'Device Model', +}); + +export const CARRIER_LOCATION = i18n.translate( + 'xpack.observability.expView.fieldLabels.carrierLocation', + { + defaultMessage: 'Carrier Location', + } +); + +export const RESPONSE_LATENCY = i18n.translate( + 'xpack.observability.expView.fieldLabels.responseLatency', + { + defaultMessage: 'Response latency', + } +); + +export const MOBILE_APP = i18n.translate('xpack.observability.expView.fieldLabels.mobileApp', { + defaultMessage: 'Mobile App', +}); + +export const MEMORY_USAGE = i18n.translate( + 'xpack.observability.expView.fieldLabels.mobile.memoryUsage', + { + defaultMessage: 'Memory Usage', + } +); + +export const CPU_USAGE = i18n.translate('xpack.observability.expView.fieldLabels.cpuUsage', { + defaultMessage: 'CPU Usage', +}); + +export const TRANSACTIONS_PER_MINUTE = i18n.translate( + 'xpack.observability.expView.fieldLabels.transactionPerMinute', + { + defaultMessage: 'Transactions per minute', + } +); + +export const NUMBER_OF_DEVICES = i18n.translate( + 'xpack.observability.expView.fieldLabels.numberOfDevices', + { + defaultMessage: 'Number of Devices', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts index 13a7900ef5764..07342d976cbea 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts @@ -12,6 +12,9 @@ import { getSyntheticsKPIConfig } from './synthetics/kpi_over_time_config'; import { getKPITrendsLensConfig } from './rum/kpi_over_time_config'; import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; import { getCoreWebVitalsConfig } from './rum/core_web_vitals_config'; +import { getMobileKPIConfig } from './mobile/kpi_over_time_config'; +import { getMobileKPIDistributionConfig } from './mobile/distribution_config'; +import { getMobileDeviceDistributionConfig } from './mobile/device_distribution_config'; interface Props { reportType: keyof typeof ReportViewTypes; @@ -34,7 +37,14 @@ export const getDefaultConfigs = ({ reportType, dataType, indexPattern }: Props) return getSyntheticsDistributionConfig({ indexPattern }); } return getSyntheticsKPIConfig({ indexPattern }); - + case 'mobile': + if (reportType === 'dist') { + return getMobileKPIDistributionConfig({ indexPattern }); + } + if (reportType === 'mdd') { + return getMobileDeviceDistributionConfig({ indexPattern }); + } + return getMobileKPIConfig({ indexPattern }); default: return getKPITrendsLensConfig({ indexPattern }); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index bc535e29ab435..22ad18c663b32 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -25,13 +25,14 @@ import { FieldBasedIndexPatternColumn, SumIndexPatternColumn, TermsIndexPatternColumn, + CardinalityIndexPatternColumn, } from '../../../../../../lens/public'; import { buildPhraseFilter, buildPhrasesFilter, IndexPattern, } from '../../../../../../../../src/plugins/data/common'; -import { FieldLabels, FILTER_RECORDS, USE_BREAK_DOWN_COLUMN } from './constants'; +import { FieldLabels, FILTER_RECORDS, USE_BREAK_DOWN_COLUMN, TERMS_COLUMN } from './constants'; import { ColumnFilter, DataSeries, UrlFilter, URLReportDefinition } from '../types'; function getLayerReferenceName(layerId: string) { @@ -55,6 +56,7 @@ export const parseCustomFieldName = ( let fieldName = sourceField; let columnType; let columnFilters; + let timeScale; let columnLabel; const rdf = reportViewConfig.reportDefinitions ?? []; @@ -70,17 +72,19 @@ export const parseCustomFieldName = ( ); columnType = currField?.columnType; columnFilters = currField?.columnFilters; + timeScale = currField?.timeScale; columnLabel = currField?.label; } } else if (customField.options?.[0].field || customField.options?.[0].id) { fieldName = customField.options?.[0].field || customField.options?.[0].id; columnType = customField.options?.[0].columnType; columnFilters = customField.options?.[0].columnFilters; + timeScale = customField.options?.[0].timeScale; columnLabel = customField.options?.[0].label; } } - return { fieldName, columnType, columnFilters, columnLabel }; + return { fieldName, columnType, columnFilters, timeScale, columnLabel }; }; export class LensAttributes { @@ -167,10 +171,10 @@ export class LensAttributes { this.visualization.layers[0].splitAccessor = undefined; } - getNumberRangeColumn(sourceField: string): RangeIndexPatternColumn { + getNumberRangeColumn(sourceField: string, label?: string): RangeIndexPatternColumn { return { sourceField, - label: this.reportViewConfig.labels[sourceField], + label: this.reportViewConfig.labels[sourceField] ?? label, dataType: 'number', operationType: 'range', isBucketed: true, @@ -183,6 +187,10 @@ export class LensAttributes { }; } + getCardinalityColumn(sourceField: string, label?: string) { + return this.getNumberOperationColumn(sourceField, 'unique_count', label); + } + getNumberColumn( sourceField: string, columnType?: string, @@ -190,21 +198,30 @@ export class LensAttributes { label?: string ) { if (columnType === 'operation' || operationType) { - if (operationType === 'median' || operationType === 'average' || operationType === 'sum') { + if ( + operationType === 'median' || + operationType === 'average' || + operationType === 'sum' || + operationType === 'unique_count' + ) { return this.getNumberOperationColumn(sourceField, operationType, label); } if (operationType?.includes('th')) { return this.getPercentileNumberColumn(sourceField, operationType); } } - return this.getNumberRangeColumn(sourceField); + return this.getNumberRangeColumn(sourceField, label); } getNumberOperationColumn( sourceField: string, - operationType: 'average' | 'median' | 'sum', + operationType: 'average' | 'median' | 'sum' | 'unique_count', label?: string - ): AvgIndexPatternColumn | MedianIndexPatternColumn | SumIndexPatternColumn { + ): + | AvgIndexPatternColumn + | MedianIndexPatternColumn + | SumIndexPatternColumn + | CardinalityIndexPatternColumn { return { ...buildNumberColumn(sourceField), label: @@ -247,6 +264,25 @@ export class LensAttributes { }; } + getTermsColumn(sourceField: string, label?: string): TermsIndexPatternColumn { + return { + operationType: 'terms', + sourceField, + label: label || 'Top values of ' + sourceField, + dataType: 'string', + isBucketed: true, + scale: 'ordinal', + params: { + size: 10, + orderBy: { + type: 'alphabetical', + fallback: false, + }, + orderDirection: 'desc', + }, + }; + } + getXAxis() { const { xAxisColumn } = this.reportViewConfig; @@ -263,15 +299,25 @@ export class LensAttributes { label?: string, colIndex?: number ) { - const { fieldMeta, columnType, fieldName, columnFilters, columnLabel } = this.getFieldMeta( - sourceField - ); + const { + fieldMeta, + columnType, + fieldName, + columnFilters, + timeScale, + columnLabel, + } = this.getFieldMeta(sourceField); const { type: fieldType } = fieldMeta ?? {}; + if (columnType === TERMS_COLUMN) { + return this.getTermsColumn(fieldName, columnLabel || label); + } + if (fieldName === 'Records' || columnType === FILTER_RECORDS) { return this.getRecordsColumn( columnLabel || label, - colIndex !== undefined ? columnFilters?.[colIndex] : undefined + colIndex !== undefined ? columnFilters?.[colIndex] : undefined, + timeScale ); } @@ -281,6 +327,9 @@ export class LensAttributes { if (fieldType === 'number') { return this.getNumberColumn(fieldName, columnType, operationType, columnLabel || label); } + if (operationType === 'unique_count') { + return this.getCardinalityColumn(fieldName, columnLabel || label); + } // FIXME review my approach again return this.getDateHistogramColumn(fieldName); @@ -291,13 +340,17 @@ export class LensAttributes { } getFieldMeta(sourceField: string) { - const { fieldName, columnType, columnFilters, columnLabel } = this.getCustomFieldName( - sourceField - ); + const { + fieldName, + columnType, + columnFilters, + timeScale, + columnLabel, + } = this.getCustomFieldName(sourceField); const fieldMeta = this.indexPattern.getFieldByName(fieldName); - return { fieldMeta, fieldName, columnType, columnFilters, columnLabel }; + return { fieldMeta, fieldName, columnType, columnFilters, timeScale, columnLabel }; } getMainYAxis() { @@ -330,7 +383,11 @@ export class LensAttributes { return lensColumns; } - getRecordsColumn(label?: string, columnFilter?: ColumnFilter): CountIndexPatternColumn { + getRecordsColumn( + label?: string, + columnFilter?: ColumnFilter, + timeScale?: string + ): CountIndexPatternColumn { return { dataType: 'number', isBucketed: false, @@ -339,6 +396,7 @@ export class LensAttributes { scale: 'ratio', sourceField: 'Records', filter: columnFilter, + timeScale, } as CountIndexPatternColumn; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts new file mode 100644 index 0000000000000..6f9806660e489 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConfigProps, DataSeries } from '../../types'; +import { FieldLabels, USE_BREAK_DOWN_COLUMN } from '../constants'; +import { buildPhraseFilter } from '../utils'; +import { SERVICE_NAME } from '../constants/elasticsearch_fieldnames'; +import { MOBILE_APP, NUMBER_OF_DEVICES } from '../constants/labels'; +import { MobileFields } from './mobile_fields'; + +export function getMobileDeviceDistributionConfig({ indexPattern }: ConfigProps): DataSeries { + return { + reportType: 'mobile-device-distribution', + defaultSeriesType: 'bar', + seriesTypes: ['bar', 'bar_horizontal'], + xAxisColumn: { + sourceField: USE_BREAK_DOWN_COLUMN, + }, + yAxisColumns: [ + { + sourceField: 'labels.device_id', + operationType: 'unique_count', + label: NUMBER_OF_DEVICES, + }, + ], + hasOperationType: false, + defaultFilters: Object.keys(MobileFields), + breakdowns: Object.keys(MobileFields), + filters: [ + ...buildPhraseFilter('agent.name', 'iOS/swift', indexPattern), + ...buildPhraseFilter('processor.event', 'transaction', indexPattern), + ], + labels: { + ...FieldLabels, + ...MobileFields, + [SERVICE_NAME]: MOBILE_APP, + }, + reportDefinitions: [ + { + field: SERVICE_NAME, + required: true, + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts new file mode 100644 index 0000000000000..62dd38e55a32a --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConfigProps, DataSeries } from '../../types'; +import { FieldLabels, OPERATION_COLUMN, RECORDS_FIELD } from '../constants'; +import { buildPhrasesFilter } from '../utils'; +import { + METRIC_SYSTEM_CPU_USAGE, + METRIC_SYSTEM_MEMORY_USAGE, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_DURATION, +} from '../constants/elasticsearch_fieldnames'; + +import { CPU_USAGE, MEMORY_USAGE, MOBILE_APP, RESPONSE_LATENCY } from '../constants/labels'; +import { MobileFields } from './mobile_fields'; + +export function getMobileKPIDistributionConfig({ indexPattern }: ConfigProps): DataSeries { + return { + reportType: 'data-distribution', + defaultSeriesType: 'bar', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: 'performance.metric', + }, + yAxisColumns: [ + { + sourceField: RECORDS_FIELD, + }, + ], + hasOperationType: false, + defaultFilters: Object.keys(MobileFields), + breakdowns: Object.keys(MobileFields), + filters: [ + ...buildPhrasesFilter('agent.name', ['iOS/swift', 'open-telemetry/swift'], indexPattern), + ], + labels: { + ...FieldLabels, + ...MobileFields, + [SERVICE_NAME]: MOBILE_APP, + }, + reportDefinitions: [ + { + field: SERVICE_NAME, + required: true, + }, + { + field: SERVICE_ENVIRONMENT, + required: true, + }, + { + field: 'performance.metric', + custom: true, + options: [ + { + label: RESPONSE_LATENCY, + field: TRANSACTION_DURATION, + id: TRANSACTION_DURATION, + columnType: OPERATION_COLUMN, + }, + { + label: MEMORY_USAGE, + field: METRIC_SYSTEM_MEMORY_USAGE, + id: METRIC_SYSTEM_MEMORY_USAGE, + columnType: OPERATION_COLUMN, + }, + { + label: CPU_USAGE, + field: METRIC_SYSTEM_CPU_USAGE, + id: METRIC_SYSTEM_CPU_USAGE, + columnType: OPERATION_COLUMN, + }, + ], + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts new file mode 100644 index 0000000000000..2ed4d95760db7 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConfigProps, DataSeries } from '../../types'; +import { FieldLabels, OPERATION_COLUMN, RECORDS_FIELD } from '../constants'; +import { buildPhrasesFilter } from '../utils'; +import { + METRIC_SYSTEM_CPU_USAGE, + METRIC_SYSTEM_MEMORY_USAGE, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_DURATION, +} from '../constants/elasticsearch_fieldnames'; +import { + CPU_USAGE, + MEMORY_USAGE, + MOBILE_APP, + RESPONSE_LATENCY, + TRANSACTIONS_PER_MINUTE, +} from '../constants/labels'; +import { MobileFields } from './mobile_fields'; + +export function getMobileKPIConfig({ indexPattern }: ConfigProps): DataSeries { + return { + reportType: 'kpi-over-time', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar', 'area'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumns: [ + { + sourceField: 'business.kpi', + operationType: 'median', + }, + ], + hasOperationType: true, + defaultFilters: Object.keys(MobileFields), + breakdowns: Object.keys(MobileFields), + filters: [ + ...buildPhrasesFilter('agent.name', ['iOS/swift', 'open-telemetry/swift'], indexPattern), + ], + labels: { + ...FieldLabels, + ...MobileFields, + [TRANSACTION_DURATION]: RESPONSE_LATENCY, + [SERVICE_NAME]: MOBILE_APP, + [METRIC_SYSTEM_MEMORY_USAGE]: MEMORY_USAGE, + [METRIC_SYSTEM_CPU_USAGE]: CPU_USAGE, + }, + reportDefinitions: [ + { + field: SERVICE_NAME, + required: true, + }, + { + field: SERVICE_ENVIRONMENT, + required: true, + }, + { + field: 'business.kpi', + custom: true, + options: [ + { + label: RESPONSE_LATENCY, + field: TRANSACTION_DURATION, + id: TRANSACTION_DURATION, + columnType: OPERATION_COLUMN, + }, + { + label: MEMORY_USAGE, + field: METRIC_SYSTEM_MEMORY_USAGE, + id: METRIC_SYSTEM_MEMORY_USAGE, + columnType: OPERATION_COLUMN, + }, + { + label: CPU_USAGE, + field: METRIC_SYSTEM_CPU_USAGE, + id: METRIC_SYSTEM_CPU_USAGE, + columnType: OPERATION_COLUMN, + }, + { + field: RECORDS_FIELD, + id: RECORDS_FIELD, + label: TRANSACTIONS_PER_MINUTE, + columnFilters: [ + { + language: 'kuery', + query: `processor.event: transaction`, + }, + ], + timeScale: 'm', + }, + ], + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_fields.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_fields.ts new file mode 100644 index 0000000000000..4ece4ff056a59 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/mobile_fields.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CARRIER_LOCATION, + CARRIER_NAME, + CONNECTION_TYPE, + DEVICE_MODEL, + HOST_OS, + OS_PLATFORM, + SERVICE_VERSION, +} from '../constants/labels'; + +export const MobileFields: Record = { + 'host.os.platform': OS_PLATFORM, + 'host.os.full': HOST_OS, + 'service.version': SERVICE_VERSION, + 'network.carrier.icc': CARRIER_LOCATION, + 'network.carrier.name': CARRIER_NAME, + 'network.connection_type': CONNECTION_TYPE, + 'labels.device_model': DEVICE_MODEL, +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx index 487ecdb2bafcc..779049601bd6d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -63,7 +63,7 @@ describe('ExploratoryView', () => { render(, { initSeries }); expect(await screen.findByText(/open in lens/i)).toBeInTheDocument(); - expect(await screen.findByText('Performance Distribution')).toBeInTheDocument(); + expect((await screen.findAllByText('Performance distribution'))[0]).toBeInTheDocument(); expect(await screen.findByText(/Lens Embeddable Component/i)).toBeInTheDocument(); await waitFor(() => { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx index 4f13cf6a1f9ca..4259bb778e511 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx @@ -12,7 +12,6 @@ import { useKibana } from '../../../../../../../../src/plugins/kibana_react/publ import { ObservabilityPublicPluginsStart } from '../../../../plugin'; import { ObservabilityIndexPatterns } from '../utils/observability_index_patterns'; import { getDataHandler } from '../../../../data_handler'; -import { HasDataResponse } from '../../../../typings/fetch_overview_data'; export interface IIndexPatternContext { loading: boolean; @@ -41,17 +40,13 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { synthetics: null, ux: null, apm: null, + mobile: null, } as HasAppDataState); const { services: { data }, } = useKibana(); - const checkIfAppHasData = async (dataType: AppDataType) => { - const handler = getDataHandler(dataType); - return handler?.hasData(); - }; - const loadIndexPattern: IIndexPatternContext['loadIndexPattern'] = useCallback( async ({ dataType }) => { setSelectedApp(dataType); @@ -59,15 +54,27 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { if (hasAppData[dataType] === null) { setLoading(true); try { - const hasDataResponse = (await checkIfAppHasData(dataType)) as HasDataResponse; - - const hasDataT = hasDataResponse.hasData; - + let hasDataT = false; + let indices: string | undefined = ''; + switch (dataType) { + case 'ux': + case 'synthetics': + const resultUx = await getDataHandler(dataType)?.hasData(); + hasDataT = Boolean(resultUx?.hasData); + indices = resultUx?.indices; + break; + case 'apm': + case 'mobile': + const resultApm = await getDataHandler('apm')?.hasData(); + hasDataT = Boolean(resultApm?.hasData); + indices = resultApm?.indices['apm_oss.transactionIndices']; + break; + } setHasAppData((prevState) => ({ ...prevState, [dataType]: hasDataT })); - if (hasDataT || hasAppData?.[dataType]) { + if (hasDataT && indices) { const obsvIndexP = new ObservabilityIndexPatterns(data); - const indPattern = await obsvIndexP.getIndexPattern(dataType, hasDataResponse.indices); + const indPattern = await obsvIndexP.getIndexPattern(dataType, indices); setIndexPatterns((prevState) => ({ ...prevState, [dataType]: indPattern })); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx index 3fe88de518f75..985afdf888868 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx @@ -15,6 +15,7 @@ import { useSeriesStorage } from '../../hooks/use_series_storage'; export const dataTypes: Array<{ id: AppDataType; label: string }> = [ { id: 'synthetics', label: 'Synthetic Monitoring' }, { id: 'ux', label: 'User Experience (RUM)' }, + { id: 'mobile', label: 'Mobile Experience' }, // { id: 'infra_logs', label: 'Logs' }, // { id: 'infra_metrics', label: 'Metrics' }, // { id: 'apm', label: 'APM' }, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx index 9687f1bea4ec9..4571ecfe252e9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx @@ -20,8 +20,10 @@ export function ReportFilters({ ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx index e24d246d60e58..9aef16931d7ec 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx @@ -18,16 +18,27 @@ import { ReportBreakdowns } from './columns/report_breakdowns'; import { NEW_SERIES_KEY, useSeriesStorage } from '../hooks/use_series_storage'; import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; import { getDefaultConfigs } from '../configurations/default_configs'; +import { + CORE_WEB_VITALS_LABEL, + DEVICE_DISTRIBUTION_LABEL, + KPI_OVER_TIME_LABEL, + PERF_DIST_LABEL, +} from '../configurations/constants/labels'; export const ReportTypes: Record> = { synthetics: [ - { id: 'kpi', label: 'KPI over time' }, - { id: 'dist', label: 'Performance distribution' }, + { id: 'kpi', label: KPI_OVER_TIME_LABEL }, + { id: 'dist', label: PERF_DIST_LABEL }, ], ux: [ - { id: 'kpi', label: 'KPI over time' }, - { id: 'dist', label: 'Performance distribution' }, - { id: 'cwv', label: 'Core Web Vitals' }, + { id: 'kpi', label: KPI_OVER_TIME_LABEL }, + { id: 'dist', label: PERF_DIST_LABEL }, + { id: 'cwv', label: CORE_WEB_VITALS_LABEL }, + ], + mobile: [ + { id: 'kpi', label: KPI_OVER_TIME_LABEL }, + { id: 'dist', label: PERF_DIST_LABEL }, + { id: 'mdd', label: DEVICE_DISTRIBUTION_LABEL }, ], apm: [], infra_logs: [], diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx index cfac838ba5aeb..2fadb0e56433e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx @@ -22,6 +22,7 @@ describe('FilterExpanded', function () { label={'Browser Family'} field={USER_AGENT_NAME} goBack={jest.fn()} + filters={[]} />, { initSeries } ); @@ -38,6 +39,7 @@ describe('FilterExpanded', function () { label={'Browser Family'} field={USER_AGENT_NAME} goBack={goBack} + filters={[]} />, { initSeries } ); @@ -64,6 +66,7 @@ describe('FilterExpanded', function () { label={'Browser Family'} field={USER_AGENT_NAME} goBack={goBack} + filters={[]} />, { initSeries } ); @@ -90,6 +93,7 @@ describe('FilterExpanded', function () { label={'Browser Family'} field={USER_AGENT_NAME} goBack={jest.fn()} + filters={[]} />, { initSeries } ); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx index 0d5b73f14671d..17d62b68c57e4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx @@ -6,17 +6,21 @@ */ import React, { useState, Fragment } from 'react'; -import { EuiFieldSearch, EuiSpacer, EuiButtonEmpty, EuiFilterGroup } from '@elastic/eui'; +import { EuiFieldSearch, EuiSpacer, EuiButtonEmpty, EuiFilterGroup, EuiText } from '@elastic/eui'; import styled from 'styled-components'; import { rgba } from 'polished'; import { i18n } from '@kbn/i18n'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types'; import { map } from 'lodash'; import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; import { useSeriesStorage } from '../../hooks/use_series_storage'; -import { UrlFilter } from '../../types'; +import { DataSeries, UrlFilter } from '../../types'; import { FilterValueButton } from './filter_value_btn'; import { useValuesList } from '../../../../../hooks/use_values_list'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; +import { ESFilter } from '../../../../../../../../../typings/elasticsearch'; +import { PersistableFilter } from '../../../../../../../lens/common'; +import { ExistsFilter } from '../../../../../../../../../src/plugins/data/common/es_query/filters'; interface Props { seriesId: string; @@ -25,9 +29,18 @@ interface Props { isNegated?: boolean; goBack: () => void; nestedField?: string; + filters: DataSeries['filters']; } -export function FilterExpanded({ seriesId, field, label, goBack, nestedField, isNegated }: Props) { +export function FilterExpanded({ + seriesId, + field, + label, + goBack, + nestedField, + isNegated, + filters: defaultFilters, +}: Props) { const { indexPattern } = useAppIndexPatternContext(); const [value, setValue] = useState(''); @@ -38,12 +51,25 @@ export function FilterExpanded({ seriesId, field, label, goBack, nestedField, is const series = getSeries(seriesId); + const queryFilters: ESFilter[] = []; + + defaultFilters?.forEach((qFilter: PersistableFilter | ExistsFilter) => { + if (qFilter.query) { + queryFilters.push(qFilter.query); + } + const asExistFilter = qFilter as ExistsFilter; + if (asExistFilter?.exists) { + queryFilters.push(asExistFilter.exists as QueryDslQueryContainer); + } + }); + const { values, loading } = useValuesList({ query: value, indexPatternTitle: indexPattern?.title, sourceField: field, time: series.time, keepHistory: true, + filters: queryFilters, }); const filters = series?.filters ?? []; @@ -73,6 +99,13 @@ export function FilterExpanded({ seriesId, field, label, goBack, nestedField, is /> + {displayValues.length === 0 && !loading && ( + + {i18n.translate('xpack.observability.filters.expanded.noFilter', { + defaultMessage: 'No filters found.', + })} + + )} {displayValues.map((opt) => ( diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx index 9e5770c2de8f9..b7e20b341b572 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx @@ -24,8 +24,10 @@ import { useSeriesStorage } from '../../hooks/use_series_storage'; interface Props { seriesId: string; defaultFilters: DataSeries['defaultFilters']; + filters: DataSeries['filters']; series: DataSeries; isNew?: boolean; + labels?: Record; } export interface Field { @@ -35,21 +37,28 @@ export interface Field { isNegated?: boolean; } -export function SeriesFilter({ series, isNew, seriesId, defaultFilters = [] }: Props) { +export function SeriesFilter({ + series, + isNew, + seriesId, + defaultFilters = [], + filters, + labels, +}: Props) { const [isPopoverVisible, setIsPopoverVisible] = useState(false); const [selectedField, setSelectedField] = useState(); const options: Field[] = defaultFilters.map((field) => { if (typeof field === 'string') { - return { label: FieldLabels[field], field }; + return { label: labels?.[field] ?? FieldLabels[field], field }; } return { field: field.field, nested: field.nested, isNegated: field.isNegated, - label: FieldLabels[field.field], + label: labels?.[field.field] ?? FieldLabels[field.field], }; }); @@ -102,6 +111,7 @@ export function SeriesFilter({ series, isNew, seriesId, defaultFilters = [] }: P goBack={() => { setSelectedField(undefined); }} + filters={filters} /> ) : null; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx index 79218aa111f16..17d4356dcf65b 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx @@ -49,7 +49,12 @@ export function SeriesEditor() { field: 'defaultFilters', width: '15%', render: (defaultFilters: string[], { id, seriesConfig }: EditItem) => ( - + ), }, { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index 98605dfdb4ca3..73b4d7794dd51 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -23,6 +23,7 @@ export const ReportViewTypes = { dist: 'data-distribution', kpi: 'kpi-over-time', cwv: 'core-web-vitals', + mdd: 'mobile-device-distribution', } as const; type ValueOf = T[keyof T]; @@ -45,8 +46,9 @@ export interface ReportDefinition { field?: string; label: string; description?: string; - columnType?: 'range' | 'operation' | 'FILTER_RECORDS'; + columnType?: 'range' | 'operation' | 'FILTER_RECORDS' | 'TERMS_COLUMN'; columnFilters?: ColumnFilter[]; + timeScale?: string; }>; } @@ -94,15 +96,15 @@ export interface ConfigProps { indexPattern: IIndexPattern; } -export type AppDataType = 'synthetics' | 'ux' | 'infra_logs' | 'infra_metrics' | 'apm'; +export type AppDataType = 'synthetics' | 'ux' | 'infra_logs' | 'infra_metrics' | 'apm' | 'mobile'; -type FormatType = 'duration' | 'number'; +type FormatType = 'duration' | 'number' | 'bytes' | 'percent'; type InputFormat = 'microseconds' | 'milliseconds' | 'seconds'; type OutputFormat = 'asSeconds' | 'asMilliseconds' | 'humanize' | 'humanizePrecise'; export interface FieldFormatParams { - inputFormat: InputFormat; - outputFormat: OutputFormat; + inputFormat?: InputFormat; + outputFormat?: OutputFormat; outputPrecision?: number; showSuffix?: boolean; useShortSuffix?: boolean; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts index 858eb52555da6..634408dd614da 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts @@ -23,6 +23,7 @@ const appFieldFormats: Record = { ux: rumFieldFormats, apm: apmFieldFormats, synthetics: syntheticsFieldFormats, + mobile: apmFieldFormats, }; function getFieldFormatsForApp(app: AppDataType) { @@ -35,6 +36,7 @@ export const indexPatternList: Record = { ux: 'rum_static_index_pattern_id', infra_logs: 'infra_logs_static_index_pattern_id', infra_metrics: 'infra_metrics_static_index_pattern_id', + mobile: 'mobile_static_index_pattern_id', }; const appToPatternMap: Record = { @@ -43,6 +45,7 @@ const appToPatternMap: Record = { ux: '(rum-data-view)*', infra_logs: '', infra_metrics: '', + mobile: '(mobile-data-view)*', }; const getAppIndicesWithPattern = (app: AppDataType, indices: string) => { @@ -124,6 +127,7 @@ export class ObservabilityIndexPatterns { if (!this.data) { throw new Error('data is not defined'); } + try { const indexPatternId = getAppIndexPatternId(app, indices); const indexPatternTitle = getAppIndicesWithPattern(app, indices); diff --git a/x-pack/plugins/observability/public/context/has_data_context.test.tsx b/x-pack/plugins/observability/public/context/has_data_context.test.tsx index b5a0806306461..f2f550e35ac6b 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.test.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.test.tsx @@ -5,7 +5,6 @@ * 2.0. */ -// import { act, getByText } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import { CoreStart } from 'kibana/public'; import React from 'react'; @@ -19,10 +18,17 @@ import * as pluginContext from '../hooks/use_plugin_context'; import { PluginContextValue } from './plugin_context'; import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; +import { ApmIndicesConfig } from '../../common/typings'; +import { act } from '@testing-library/react'; const relativeStart = '2020-10-08T06:00:00.000Z'; const relativeEnd = '2020-10-08T07:00:00.000Z'; +const sampleAPMIndices = { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'apm_oss.transactionIndices': 'apm-*', +} as ApmIndicesConfig; + function wrapper({ children }: { children: React.ReactElement }) { const history = createMemoryHistory(); return ( @@ -76,17 +82,18 @@ describe('HasDataContextProvider', () => { it('hasAnyData returns false and all apps return undefined', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toMatchObject({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: undefined, status: 'success' }, synthetics: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, @@ -105,16 +112,16 @@ describe('HasDataContextProvider', () => { describe('all apps return false', () => { beforeAll(() => { registerApps([ - { appName: 'apm', hasData: async () => false }, + { appName: 'apm', hasData: async () => ({ hasData: false }) }, { appName: 'infra_logs', hasData: async () => false }, { appName: 'infra_metrics', hasData: async () => false }, { appName: 'synthetics', - hasData: async () => ({ hasData: false, indices: 'heartbeat-*, synthetics-*' }), + hasData: async () => ({ hasData: false }), }, { appName: 'ux', - hasData: async () => ({ hasData: false, serviceName: undefined, indices: 'apm-*' }), + hasData: async () => ({ hasData: false }), }, ]); }); @@ -124,29 +131,28 @@ describe('HasDataContextProvider', () => { it('hasAnyData returns false and all apps return false', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: false, status: 'success' }, synthetics: { - hasData: { - hasData: false, - indices: 'heartbeat-*, synthetics-*', - }, + hasData: false, status: 'success', }, infra_logs: { hasData: false, status: 'success' }, infra_metrics: { hasData: false, status: 'success' }, ux: { - hasData: { hasData: false, serviceName: undefined, indices: 'apm-*' }, + hasData: false, status: 'success', }, alert: { hasData: [], status: 'success' }, @@ -162,7 +168,7 @@ describe('HasDataContextProvider', () => { describe('at least one app returns true', () => { beforeAll(() => { registerApps([ - { appName: 'apm', hasData: async () => true }, + { appName: 'apm', hasData: async () => ({ hasData: true }) }, { appName: 'infra_logs', hasData: async () => false }, { appName: 'infra_metrics', hasData: async () => false }, { @@ -181,29 +187,30 @@ describe('HasDataContextProvider', () => { it('hasAnyData returns true apm returns true and all other apps return false', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: true, status: 'success' }, synthetics: { - hasData: { - hasData: false, - indices: 'heartbeat-*, synthetics-*', - }, + hasData: false, + indices: 'heartbeat-*, synthetics-*', status: 'success', }, infra_logs: { hasData: false, status: 'success' }, infra_metrics: { hasData: false, status: 'success' }, ux: { - hasData: { hasData: false, serviceName: undefined, indices: 'apm-*' }, + hasData: false, + indices: 'apm-*', status: 'success', }, alert: { hasData: [], status: 'success' }, @@ -219,7 +226,7 @@ describe('HasDataContextProvider', () => { describe('all apps return true', () => { beforeAll(() => { registerApps([ - { appName: 'apm', hasData: async () => true }, + { appName: 'apm', hasData: async () => ({ hasData: true }) }, { appName: 'infra_logs', hasData: async () => true }, { appName: 'infra_metrics', hasData: async () => true }, { @@ -238,32 +245,34 @@ describe('HasDataContextProvider', () => { it('hasAnyData returns true and all apps return true', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: true, status: 'success', }, synthetics: { - hasData: { - hasData: true, - indices: 'heartbeat-*, synthetics-*', - }, + hasData: true, + indices: 'heartbeat-*, synthetics-*', status: 'success', }, infra_logs: { hasData: true, status: 'success' }, infra_metrics: { hasData: true, status: 'success' }, ux: { - hasData: { hasData: true, serviceName: 'ux', indices: 'apm-*' }, + hasData: true, + serviceName: 'ux', + indices: 'apm-*', status: 'success', }, alert: { hasData: [], status: 'success' }, @@ -279,7 +288,9 @@ describe('HasDataContextProvider', () => { describe('only apm is registered', () => { describe('when apm returns true', () => { beforeAll(() => { - registerApps([{ appName: 'apm', hasData: async () => true }]); + registerApps([ + { appName: 'apm', hasData: async () => ({ hasData: true, indices: sampleAPMIndices }) }, + ]); }); afterAll(unregisterAll); @@ -289,18 +300,20 @@ describe('HasDataContextProvider', () => { wrapper, }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { - apm: { hasData: true, status: 'success' }, + hasDataMap: { + apm: { hasData: true, indices: sampleAPMIndices, status: 'success' }, synthetics: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, infra_metrics: { hasData: undefined, status: 'success' }, @@ -317,7 +330,12 @@ describe('HasDataContextProvider', () => { describe('when apm returns false', () => { beforeAll(() => { - registerApps([{ appName: 'apm', hasData: async () => false }]); + registerApps([ + { + appName: 'apm', + hasData: async () => ({ indices: sampleAPMIndices, hasData: false }), + }, + ]); }); afterAll(unregisterAll); @@ -327,18 +345,24 @@ describe('HasDataContextProvider', () => { wrapper, }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { - apm: { hasData: false, status: 'success' }, + hasDataMap: { + apm: { + hasData: false, + indices: sampleAPMIndices, + status: 'success', + }, synthetics: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, infra_metrics: { hasData: undefined, status: 'success' }, @@ -381,29 +405,31 @@ describe('HasDataContextProvider', () => { it('hasAnyData returns true, apm is undefined and all other apps return true', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: undefined, status: 'failure' }, synthetics: { - hasData: { - hasData: true, - indices: 'heartbeat-*, synthetics-*', - }, + hasData: true, + indices: 'heartbeat-*, synthetics-*', status: 'success', }, infra_logs: { hasData: true, status: 'success' }, infra_metrics: { hasData: true, status: 'success' }, ux: { - hasData: { hasData: true, serviceName: 'ux', indices: 'apm-*' }, + hasData: true, + serviceName: 'ux', + indices: 'apm-*', status: 'success', }, alert: { hasData: [], status: 'success' }, @@ -457,17 +483,19 @@ describe('HasDataContextProvider', () => { it('hasAnyData returns false and all apps return undefined', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: undefined, status: 'failure' }, synthetics: { hasData: undefined, status: 'failure' }, infra_logs: { hasData: undefined, status: 'failure' }, @@ -505,17 +533,19 @@ describe('HasDataContextProvider', () => { it('returns all alerts available', async () => { const { result, waitForNextUpdate } = renderHook(() => useHasData(), { wrapper }); expect(result.current).toEqual({ - hasData: {}, + hasDataMap: {}, hasAnyData: false, isAllRequestsComplete: false, forceUpdate: expect.any(String), onRefreshTimeRange: expect.any(Function), }); - await waitForNextUpdate(); + await act(async () => { + await waitForNextUpdate(); + }); expect(result.current).toEqual({ - hasData: { + hasDataMap: { apm: { hasData: undefined, status: 'success' }, synthetics: { hasData: undefined, status: 'success' }, infra_logs: { hasData: undefined, status: 'success' }, diff --git a/x-pack/plugins/observability/public/context/has_data_context.tsx b/x-pack/plugins/observability/public/context/has_data_context.tsx index 97aa72f07b09c..047a596ea349e 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.tsx @@ -14,17 +14,23 @@ import { FETCH_STATUS } from '../hooks/use_fetcher'; import { usePluginContext } from '../hooks/use_plugin_context'; import { useTimeRange } from '../hooks/use_time_range'; import { getObservabilityAlerts } from '../services/get_observability_alerts'; -import { ObservabilityFetchDataPlugins, UXHasDataResponse } from '../typings/fetch_overview_data'; +import { ObservabilityFetchDataPlugins } from '../typings/fetch_overview_data'; +import { ApmIndicesConfig } from '../../common/typings'; type DataContextApps = ObservabilityFetchDataPlugins | 'alert'; export type HasDataMap = Record< DataContextApps, - { status: FETCH_STATUS; hasData?: boolean | UXHasDataResponse | Alert[] } + { + status: FETCH_STATUS; + hasData?: boolean | Alert[]; + indices?: string | ApmIndicesConfig; + serviceName?: string; + } >; export interface HasDataContextValue { - hasData: Partial; + hasDataMap: Partial; hasAnyData: boolean; isAllRequestsComplete: boolean; onRefreshTimeRange: () => void; @@ -40,7 +46,7 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode const [forceUpdate, setForceUpdate] = useState(''); const { absoluteStart, absoluteEnd } = useTimeRange(); - const [hasData, setHasData] = useState({}); + const [hasDataMap, setHasDataMap] = useState({}); const isExploratoryView = useRouteMatch('/exploratory-view'); @@ -49,23 +55,53 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode if (!isExploratoryView) apps.forEach(async (app) => { try { - if (app !== 'alert') { - const params = - app === 'ux' - ? { absoluteTime: { start: absoluteStart, end: absoluteEnd } } - : undefined; - - const result = await getDataHandler(app)?.hasData(params); - setHasData((prevState) => ({ + const updateState = ({ + hasData, + indices, + serviceName, + }: { + hasData?: boolean; + serviceName?: string; + indices?: string | ApmIndicesConfig; + }) => { + setHasDataMap((prevState) => ({ ...prevState, [app]: { - hasData: result, + hasData, + ...(serviceName ? { serviceName } : {}), + ...(indices ? { indices } : {}), status: FETCH_STATUS.SUCCESS, }, })); + }; + switch (app) { + case 'ux': + const params = { absoluteTime: { start: absoluteStart, end: absoluteEnd } }; + const resultUx = await getDataHandler(app)?.hasData(params); + updateState({ + hasData: resultUx?.hasData, + indices: resultUx?.indices, + serviceName: resultUx?.serviceName as string, + }); + break; + case 'synthetics': + const resultSy = await getDataHandler(app)?.hasData(); + updateState({ hasData: resultSy?.hasData, indices: resultSy?.indices }); + + break; + case 'apm': + const resultApm = await getDataHandler(app)?.hasData(); + updateState({ hasData: resultApm?.hasData, indices: resultApm?.indices }); + + break; + case 'infra_logs': + case 'infra_metrics': + const resultInfra = await getDataHandler(app)?.hasData(); + updateState({ hasData: resultInfra }); + break; } } catch (e) { - setHasData((prevState) => ({ + setHasDataMap((prevState) => ({ ...prevState, [app]: { hasData: undefined, @@ -83,7 +119,7 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode async function fetchAlerts() { try { const alerts = await getObservabilityAlerts({ core }); - setHasData((prevState) => ({ + setHasDataMap((prevState) => ({ ...prevState, alert: { hasData: alerts, @@ -91,7 +127,7 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode }, })); } catch (e) { - setHasData((prevState) => ({ + setHasDataMap((prevState) => ({ ...prevState, alert: { hasData: undefined, @@ -105,18 +141,18 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode }, [forceUpdate, core]); const isAllRequestsComplete = apps.every((app) => { - const appStatus = hasData[app]?.status; + const appStatus = hasDataMap[app]?.status; return appStatus !== undefined && appStatus !== FETCH_STATUS.LOADING; }); - const hasAnyData = (Object.keys(hasData) as ObservabilityFetchDataPlugins[]).some( - (app) => hasData[app]?.hasData === true + const hasAnyData = (Object.keys(hasDataMap) as ObservabilityFetchDataPlugins[]).some( + (app) => hasDataMap[app]?.hasData === true ); return ( { const originalConsole = global.console; beforeAll(() => { - // mocks console to avoid poluting the test output + // mocks console to avoid polluting the test output global.console = ({ error: jest.fn() } as unknown) as typeof console; }); @@ -58,7 +64,7 @@ describe('registerDataHandler', () => { }, }; }, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); it('registered data handler', () => { diff --git a/x-pack/plugins/observability/public/pages/home/index.test.tsx b/x-pack/plugins/observability/public/pages/home/index.test.tsx index a2c784cb4b2de..60b3e809e7de9 100644 --- a/x-pack/plugins/observability/public/pages/home/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/home/index.test.tsx @@ -24,32 +24,38 @@ describe('Home page', () => { }); it('renders loading component while requests are not returned', () => { - jest - .spyOn(hasData, 'useHasData') - .mockImplementation( - () => - ({ hasData: {}, hasAnyData: false, isAllRequestsComplete: false } as HasDataContextValue) - ); + jest.spyOn(hasData, 'useHasData').mockImplementation( + () => + ({ + hasDataMap: {}, + hasAnyData: false, + isAllRequestsComplete: false, + } as HasDataContextValue) + ); const { getByText } = render(); expect(getByText('Loading Observability')).toBeInTheDocument(); }); it('renders landing page', () => { - jest - .spyOn(hasData, 'useHasData') - .mockImplementation( - () => - ({ hasData: {}, hasAnyData: false, isAllRequestsComplete: true } as HasDataContextValue) - ); + jest.spyOn(hasData, 'useHasData').mockImplementation( + () => + ({ + hasDataMap: {}, + hasAnyData: false, + isAllRequestsComplete: true, + } as HasDataContextValue) + ); render(); expect(mockHistoryPush).toHaveBeenCalledWith({ pathname: '/landing' }); }); it('renders overview page', () => { - jest - .spyOn(hasData, 'useHasData') - .mockImplementation( - () => - ({ hasData: {}, hasAnyData: true, isAllRequestsComplete: false } as HasDataContextValue) - ); + jest.spyOn(hasData, 'useHasData').mockImplementation( + () => + ({ + hasDataMap: {}, + hasAnyData: true, + isAllRequestsComplete: false, + } as HasDataContextValue) + ); render(); expect(mockHistoryPush).toHaveBeenCalledWith({ pathname: '/overview' }); }); diff --git a/x-pack/plugins/observability/public/pages/overview/index.tsx b/x-pack/plugins/observability/public/pages/overview/index.tsx index 89398ad16f198..fdb52270befed 100644 --- a/x-pack/plugins/observability/public/pages/overview/index.tsx +++ b/x-pack/plugins/observability/public/pages/overview/index.tsx @@ -57,13 +57,13 @@ export function OverviewPage({ routeParams }: Props) { const { data: newsFeed } = useFetcher(() => getNewsFeed({ core }), [core]); - const { hasData, hasAnyData } = useHasData(); + const { hasDataMap, hasAnyData } = useHasData(); if (hasAnyData === undefined) { return ; } - const alerts = (hasData.alert?.hasData as Alert[]) || []; + const alerts = (hasDataMap.alert?.hasData as Alert[]) || []; const { refreshInterval = 10000, refreshPaused = true } = routeParams.query; diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index 2482ae7a8e7ab..dd424cf221d15 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -25,6 +25,7 @@ import { newsFeedFetchData } from './mock/news_feed.mock'; import { emptyResponse as emptyUptimeResponse, fetchUptimeData } from './mock/uptime.mock'; import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock'; import { KibanaPageTemplate } from '../../../../../../src/plugins/kibana_react/public'; +import { ApmIndicesConfig } from '../../../common/typings'; function unregisterAll() { unregisterDataHandler({ appName: 'apm' }); @@ -33,6 +34,11 @@ function unregisterAll() { unregisterDataHandler({ appName: 'synthetics' }); } +const sampleAPMIndices = { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'apm_oss.transactionIndices': 'apm-*', +} as ApmIndicesConfig; + const withCore = makeDecorator({ name: 'withCore', parameterName: 'core', @@ -177,7 +183,7 @@ storiesOf('app/Overview', module) registerDataHandler({ appName: 'apm', fetchData: fetchApmData, - hasData: async () => false, + hasData: async () => ({ hasData: false, indices: sampleAPMIndices }), }); registerDataHandler({ appName: 'infra_logs', @@ -272,7 +278,7 @@ storiesOf('app/Overview', module) registerDataHandler({ appName: 'apm', fetchData: fetchApmData, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); return ( @@ -289,7 +295,7 @@ storiesOf('app/Overview', module) registerDataHandler({ appName: 'apm', fetchData: fetchApmData, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); registerDataHandler({ appName: 'infra_logs', @@ -321,7 +327,7 @@ storiesOf('app/Overview', module) registerDataHandler({ appName: 'apm', fetchData: fetchApmData, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); registerDataHandler({ appName: 'infra_logs', @@ -355,7 +361,7 @@ storiesOf('app/Overview', module) registerDataHandler({ appName: 'apm', fetchData: fetchApmData, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); registerDataHandler({ appName: 'infra_logs', @@ -386,7 +392,7 @@ storiesOf('app/Overview', module) registerDataHandler({ appName: 'apm', fetchData: async () => emptyAPMResponse, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); registerDataHandler({ appName: 'infra_logs', @@ -420,7 +426,7 @@ storiesOf('app/Overview', module) fetchData: async () => { throw new Error('Error fetching APM data'); }, - hasData: async () => true, + hasData: async () => ({ hasData: true, indices: sampleAPMIndices }), }); registerDataHandler({ appName: 'infra_logs', diff --git a/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts index 6b69aa9888cf6..197a8c1060cdb 100644 --- a/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts +++ b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts @@ -7,6 +7,8 @@ import { ObservabilityApp } from '../../../typings/common'; import { UXMetrics } from '../../components/shared/core_web_vitals'; +import { ApmIndicesConfig } from '../../../common/typings'; + export interface Stat { type: 'number' | 'percent' | 'bytesPerSecond'; value: number; @@ -34,11 +36,20 @@ export interface HasDataParams { export interface HasDataResponse { hasData: boolean; - indices: string; } export interface UXHasDataResponse extends HasDataResponse { - serviceName: string | number | undefined; + serviceName?: string | number; + indices?: string; +} + +export interface SyntheticsHasDataResponse extends HasDataResponse { + indices: string; +} + +export interface APMHasDataResponse { + hasData: boolean; + indices: ApmIndicesConfig; } export type FetchData = ( @@ -134,9 +145,9 @@ export interface ObservabilityFetchDataResponse { } export interface ObservabilityHasDataResponse { - apm: boolean; + apm: APMHasDataResponse; infra_metrics: boolean; infra_logs: boolean; - synthetics: HasDataResponse; + synthetics: SyntheticsHasDataResponse; ux: UXHasDataResponse; } From 61b6496476a30d4e287fe674639241b07aad1a1d Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Fri, 18 Jun 2021 17:37:51 +0200 Subject: [PATCH 08/14] [Discover] Update kibana.json adding owner and description (#102292) --- src/plugins/discover/kibana.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 6ea22001f5d80..04469e0ef4276 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -16,5 +16,10 @@ "indexPatternFieldEditor" ], "optionalPlugins": ["home", "share", "usageCollection"], - "requiredBundles": ["kibanaUtils", "home", "kibanaReact"] + "requiredBundles": ["kibanaUtils", "home", "kibanaReact"], + "owner": { + "name": "Kibana App", + "githubTeam": "kibana-app" + }, + "description": "This plugin contains the Discover application and the saved search embeddable." } From a7444835dd396e2eebeb6bd876c251866e01305c Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 18 Jun 2021 21:25:11 +0300 Subject: [PATCH 09/14] =?UTF-8?q?[TSVB]=20Replaces=20EuiCodeEditor=20?= =?UTF-8?q?=F0=9F=91=89=20Monaco=20editor=20=20(#100684)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Сhanged EuiCodeEditor to CodeEditor (monaco) at markdown_editor.js * Added css lang support for monaco-editor. * Added .d.ts for css lang import directly from monaco. * Moved handlebars_url language to the code_editor. Moved handlebars_url language registration to the code_editor. Changed the way of registration of languages. * Added merge for markdown_handlebars lang. * Changed to simple markdown syntax. Handlebars syntax breaks highlighting of special chars in markdown syntax. * Removed useless mergeConfig function. * Removed legacy import. * Refactor export from monaco-editor. * Fixed 'Could not find a declaration file for module' * Fixed tests. * Fixed typings errors. * Added comment to typings. * Fixed clearMarkdown for Monaco editor. * Made changes based on suggestions. * Fixed types errors. * Fixed function tests types errors. * Fixes, based on nits. * Fixes based on nits. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-monaco/src/esql/index.ts | 3 ++- packages/kbn-monaco/src/helpers.ts | 21 +++++++++++++++++ packages/kbn-monaco/src/index.ts | 10 ++++++-- packages/kbn-monaco/src/painless/index.ts | 3 ++- packages/kbn-monaco/src/register_globals.ts | 13 ++++------- packages/kbn-monaco/src/types.ts | 22 ++++++++++++++++++ packages/kbn-monaco/src/xjson/index.ts | 3 ++- .../kibana_react/public/code_editor/index.tsx | 3 +++ .../languages/css}/constants.ts | 2 +- .../public/code_editor/languages/css/index.ts | 12 ++++++++++ .../code_editor/languages/css/language.ts | 12 ++++++++++ .../languages/handlebars/constants.ts | 9 ++++++++ .../code_editor/languages/handlebars/index.ts | 13 +++++++++++ .../languages/handlebars}/language.ts | 4 ++-- .../public/code_editor/languages/index.ts | 13 +++++++++++ .../languages/markdown/constants.ts | 9 ++++++++ .../code_editor/languages/markdown/index.ts | 12 ++++++++++ .../languages/markdown/language.ts | 12 ++++++++++ .../public/code_editor/register_languages.ts | 13 +++++++++++ .../url_template_editor.tsx | 14 +++-------- .../application/components/markdown_editor.js | 20 ++++++++-------- .../components/panel_config/markdown.tsx | 12 ++++------ .../page_objects/visual_builder_page.ts | 23 +++++++++---------- typings/index.d.ts | 4 ++++ 24 files changed, 205 insertions(+), 57 deletions(-) create mode 100644 packages/kbn-monaco/src/helpers.ts create mode 100644 packages/kbn-monaco/src/types.ts rename src/plugins/kibana_react/public/{url_template_editor => code_editor/languages/css}/constants.ts (90%) create mode 100644 src/plugins/kibana_react/public/code_editor/languages/css/index.ts create mode 100644 src/plugins/kibana_react/public/code_editor/languages/css/language.ts create mode 100644 src/plugins/kibana_react/public/code_editor/languages/handlebars/constants.ts create mode 100644 src/plugins/kibana_react/public/code_editor/languages/handlebars/index.ts rename src/plugins/kibana_react/public/{url_template_editor => code_editor/languages/handlebars}/language.ts (96%) create mode 100644 src/plugins/kibana_react/public/code_editor/languages/index.ts create mode 100644 src/plugins/kibana_react/public/code_editor/languages/markdown/constants.ts create mode 100644 src/plugins/kibana_react/public/code_editor/languages/markdown/index.ts create mode 100644 src/plugins/kibana_react/public/code_editor/languages/markdown/language.ts create mode 100644 src/plugins/kibana_react/public/code_editor/register_languages.ts diff --git a/packages/kbn-monaco/src/esql/index.ts b/packages/kbn-monaco/src/esql/index.ts index a3f9df00118b7..4b50a222ad2d6 100644 --- a/packages/kbn-monaco/src/esql/index.ts +++ b/packages/kbn-monaco/src/esql/index.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ +import { LangModule as LangModuleType } from '../types'; import { ID } from './constants'; import { lexerRules } from './lexer_rules'; -export const EsqlLang = { ID, lexerRules }; +export const EsqlLang: LangModuleType = { ID, lexerRules }; diff --git a/packages/kbn-monaco/src/helpers.ts b/packages/kbn-monaco/src/helpers.ts new file mode 100644 index 0000000000000..e525b8c132132 --- /dev/null +++ b/packages/kbn-monaco/src/helpers.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { monaco } from './monaco_imports'; +import { LangModule as LangModuleType } from './types'; + +function registerLanguage(language: LangModuleType) { + const { ID, lexerRules, languageConfiguration } = language; + + monaco.languages.register({ id: ID }); + monaco.languages.setMonarchTokensProvider(ID, lexerRules); + if (languageConfiguration) { + monaco.languages.setLanguageConfiguration(ID, languageConfiguration); + } +} + +export { registerLanguage }; diff --git a/packages/kbn-monaco/src/index.ts b/packages/kbn-monaco/src/index.ts index ce35d7c3572e6..85d3518461a49 100644 --- a/packages/kbn-monaco/src/index.ts +++ b/packages/kbn-monaco/src/index.ts @@ -12,7 +12,13 @@ import './register_globals'; export { monaco } from './monaco_imports'; export { XJsonLang } from './xjson'; export { PainlessLang, PainlessContext, PainlessAutocompleteField } from './painless'; - /* eslint-disable-next-line @kbn/eslint/module_migration */ import * as BarePluginApi from 'monaco-editor/esm/vs/editor/editor.api'; -export { BarePluginApi }; + +import { registerLanguage } from './helpers'; +import { + LangModule as LangModuleType, + CompleteLangModule as CompleteLangModuleType, +} from './types'; + +export { BarePluginApi, registerLanguage, LangModuleType, CompleteLangModuleType }; diff --git a/packages/kbn-monaco/src/painless/index.ts b/packages/kbn-monaco/src/painless/index.ts index 6858209756430..9863204117b12 100644 --- a/packages/kbn-monaco/src/painless/index.ts +++ b/packages/kbn-monaco/src/painless/index.ts @@ -9,8 +9,9 @@ import { ID } from './constants'; import { lexerRules, languageConfiguration } from './lexer_rules'; import { getSuggestionProvider, getSyntaxErrors } from './language'; +import { CompleteLangModule as CompleteLangModuleType } from '../types'; -export const PainlessLang = { +export const PainlessLang: CompleteLangModuleType = { ID, getSuggestionProvider, lexerRules, diff --git a/packages/kbn-monaco/src/register_globals.ts b/packages/kbn-monaco/src/register_globals.ts index 4047ddedeca42..c6eb68b89e718 100644 --- a/packages/kbn-monaco/src/register_globals.ts +++ b/packages/kbn-monaco/src/register_globals.ts @@ -10,6 +10,8 @@ import { XJsonLang } from './xjson'; import { PainlessLang } from './painless'; import { EsqlLang } from './esql'; import { monaco } from './monaco_imports'; +import { registerLanguage } from './helpers'; + // @ts-ignore import xJsonWorkerSrc from '!!raw-loader!../../target_web/xjson.editor.worker.js'; // @ts-ignore @@ -20,14 +22,9 @@ import painlessWorkerSrc from '!!raw-loader!../../target_web/painless.editor.wor /** * Register languages and lexer rules */ -monaco.languages.register({ id: XJsonLang.ID }); -monaco.languages.setMonarchTokensProvider(XJsonLang.ID, XJsonLang.lexerRules); -monaco.languages.setLanguageConfiguration(XJsonLang.ID, XJsonLang.languageConfiguration); -monaco.languages.register({ id: PainlessLang.ID }); -monaco.languages.setMonarchTokensProvider(PainlessLang.ID, PainlessLang.lexerRules); -monaco.languages.setLanguageConfiguration(PainlessLang.ID, PainlessLang.languageConfiguration); -monaco.languages.register({ id: EsqlLang.ID }); -monaco.languages.setMonarchTokensProvider(EsqlLang.ID, EsqlLang.lexerRules); +registerLanguage(XJsonLang); +registerLanguage(PainlessLang); +registerLanguage(EsqlLang); /** * Create web workers by language ID diff --git a/packages/kbn-monaco/src/types.ts b/packages/kbn-monaco/src/types.ts new file mode 100644 index 0000000000000..f977ada5b624b --- /dev/null +++ b/packages/kbn-monaco/src/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { monaco } from './monaco_imports'; + +export interface LangModule { + ID: string; + lexerRules: monaco.languages.IMonarchLanguage; + languageConfiguration?: monaco.languages.LanguageConfiguration; + getSuggestionProvider?: Function; + getSyntaxErrors?: Function; +} + +export interface CompleteLangModule extends LangModule { + languageConfiguration: monaco.languages.LanguageConfiguration; + getSuggestionProvider: Function; + getSyntaxErrors: Function; +} diff --git a/packages/kbn-monaco/src/xjson/index.ts b/packages/kbn-monaco/src/xjson/index.ts index 5e278795fef12..e9ece97ac0023 100644 --- a/packages/kbn-monaco/src/xjson/index.ts +++ b/packages/kbn-monaco/src/xjson/index.ts @@ -12,5 +12,6 @@ import './language'; import { ID } from './constants'; import { lexerRules, languageConfiguration } from './lexer_rules'; +import { LangModule as LangModuleType } from '../types'; -export const XJsonLang = { ID, lexerRules, languageConfiguration }; +export const XJsonLang: LangModuleType = { ID, lexerRules, languageConfiguration }; diff --git a/src/plugins/kibana_react/public/code_editor/index.tsx b/src/plugins/kibana_react/public/code_editor/index.tsx index 2440974c3b1d1..9e3824b784219 100644 --- a/src/plugins/kibana_react/public/code_editor/index.tsx +++ b/src/plugins/kibana_react/public/code_editor/index.tsx @@ -17,6 +17,9 @@ import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import { useUiSetting } from '../ui_settings'; import { Props } from './code_editor'; +import './register_languages'; + +export * from './languages'; const LazyBaseEditor = React.lazy(() => import('./code_editor')); diff --git a/src/plugins/kibana_react/public/url_template_editor/constants.ts b/src/plugins/kibana_react/public/code_editor/languages/css/constants.ts similarity index 90% rename from src/plugins/kibana_react/public/url_template_editor/constants.ts rename to src/plugins/kibana_react/public/code_editor/languages/css/constants.ts index 6c1a1dbce5d67..2f465775e2a1b 100644 --- a/src/plugins/kibana_react/public/url_template_editor/constants.ts +++ b/src/plugins/kibana_react/public/code_editor/languages/css/constants.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export const LANG = 'handlebars_url'; +export const LANG = 'css'; diff --git a/src/plugins/kibana_react/public/code_editor/languages/css/index.ts b/src/plugins/kibana_react/public/code_editor/languages/css/index.ts new file mode 100644 index 0000000000000..fa1cbf4808a4e --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/css/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { LangModuleType } from '@kbn/monaco'; +import { lexerRules, languageConfiguration } from './language'; +import { LANG } from './constants'; + +export const Lang: LangModuleType = { ID: LANG, lexerRules, languageConfiguration }; diff --git a/src/plugins/kibana_react/public/code_editor/languages/css/language.ts b/src/plugins/kibana_react/public/code_editor/languages/css/language.ts new file mode 100644 index 0000000000000..5bdd6c8eb8b1f --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/css/language.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @kbn/eslint/module_migration */ +import { conf, language } from 'monaco-editor/esm/vs/basic-languages/css/css'; + +export { conf as languageConfiguration, language as lexerRules }; diff --git a/src/plugins/kibana_react/public/code_editor/languages/handlebars/constants.ts b/src/plugins/kibana_react/public/code_editor/languages/handlebars/constants.ts new file mode 100644 index 0000000000000..1634c02429f59 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/handlebars/constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const LANG = 'handlebars'; diff --git a/src/plugins/kibana_react/public/code_editor/languages/handlebars/index.ts b/src/plugins/kibana_react/public/code_editor/languages/handlebars/index.ts new file mode 100644 index 0000000000000..ff3c08267da9b --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/handlebars/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { LangModuleType } from '@kbn/monaco'; +import { languageConfiguration, lexerRules } from './language'; +import { LANG } from './constants'; + +export const Lang: LangModuleType = { ID: LANG, languageConfiguration, lexerRules }; diff --git a/src/plugins/kibana_react/public/url_template_editor/language.ts b/src/plugins/kibana_react/public/code_editor/languages/handlebars/language.ts similarity index 96% rename from src/plugins/kibana_react/public/url_template_editor/language.ts rename to src/plugins/kibana_react/public/code_editor/languages/handlebars/language.ts index 278a7130ad1fa..7f760836088d6 100644 --- a/src/plugins/kibana_react/public/url_template_editor/language.ts +++ b/src/plugins/kibana_react/public/code_editor/languages/handlebars/language.ts @@ -13,7 +13,7 @@ import { monaco } from '@kbn/monaco'; -export const conf: monaco.languages.LanguageConfiguration = { +export const languageConfiguration: monaco.languages.LanguageConfiguration = { wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, comments: { @@ -42,7 +42,7 @@ export const conf: monaco.languages.LanguageConfiguration = { ], }; -export const language: monaco.languages.IMonarchLanguage = { +export const lexerRules: monaco.languages.IMonarchLanguage = { // Set defaultToken to invalid to see what you do not tokenize yet. defaultToken: 'invalid', tokenPostfix: '', diff --git a/src/plugins/kibana_react/public/code_editor/languages/index.ts b/src/plugins/kibana_react/public/code_editor/languages/index.ts new file mode 100644 index 0000000000000..ff7da1725fa7f --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Lang as CssLang } from './css'; +import { Lang as HandlebarsLang } from './handlebars'; +import { Lang as MarkdownLang } from './markdown'; + +export { CssLang, HandlebarsLang, MarkdownLang }; diff --git a/src/plugins/kibana_react/public/code_editor/languages/markdown/constants.ts b/src/plugins/kibana_react/public/code_editor/languages/markdown/constants.ts new file mode 100644 index 0000000000000..bd8aa23256637 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/markdown/constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const LANG = 'markdown'; diff --git a/src/plugins/kibana_react/public/code_editor/languages/markdown/index.ts b/src/plugins/kibana_react/public/code_editor/languages/markdown/index.ts new file mode 100644 index 0000000000000..f501de74debec --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/markdown/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { LangModuleType } from '@kbn/monaco'; +import { languageConfiguration, lexerRules } from './language'; +import { LANG } from './constants'; + +export const Lang: LangModuleType = { ID: LANG, languageConfiguration, lexerRules }; diff --git a/src/plugins/kibana_react/public/code_editor/languages/markdown/language.ts b/src/plugins/kibana_react/public/code_editor/languages/markdown/language.ts new file mode 100644 index 0000000000000..d8a1234fcf191 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/markdown/language.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @kbn/eslint/module_migration */ +import { conf, language } from 'monaco-editor/esm/vs/basic-languages/markdown/markdown'; + +export { conf as languageConfiguration, language as lexerRules }; diff --git a/src/plugins/kibana_react/public/code_editor/register_languages.ts b/src/plugins/kibana_react/public/code_editor/register_languages.ts new file mode 100644 index 0000000000000..b4a0f4d53cdf4 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/register_languages.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { registerLanguage } from '@kbn/monaco'; +import { CssLang, HandlebarsLang, MarkdownLang } from './languages'; + +registerLanguage(CssLang); +registerLanguage(HandlebarsLang); +registerLanguage(MarkdownLang); diff --git a/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx b/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx index f830b4012976a..0fed4d37e4f7f 100644 --- a/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx +++ b/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx @@ -9,18 +9,10 @@ import * as React from 'react'; import { monaco } from '@kbn/monaco'; import { Props as CodeEditorProps } from '../code_editor/code_editor'; -import { CodeEditor } from '../code_editor'; -import { LANG } from './constants'; -import { language, conf } from './language'; +import { CodeEditor, HandlebarsLang } from '../code_editor'; import './styles.scss'; -monaco.languages.register({ - id: LANG, -}); -monaco.languages.setMonarchTokensProvider(LANG, language); -monaco.languages.setLanguageConfiguration(LANG, conf); - export interface UrlTemplateEditorVariable { label: string; title?: string; @@ -74,7 +66,7 @@ export const UrlTemplateEditor: React.FC = ({ return; } - const { dispose } = monaco.languages.registerCompletionItemProvider(LANG, { + const { dispose } = monaco.languages.registerCompletionItemProvider(HandlebarsLang.ID, { triggerCharacters: ['{', '/', '?', '&', '='], provideCompletionItems(model, position, context, token) { const { lineNumber } = position; @@ -132,7 +124,7 @@ export const UrlTemplateEditor: React.FC = ({ return (
- diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx b/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx index c33b4df914a81..7f82f95d250ea 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx @@ -20,7 +20,6 @@ import { EuiSpacer, EuiTitle, EuiHorizontalRule, - EuiCodeEditor, } from '@elastic/eui'; // @ts-expect-error import less from 'less/lib/less-browser'; @@ -43,6 +42,7 @@ import { getDefaultQueryLanguage } from '../lib/get_default_query_language'; import { VisDataContext } from '../../contexts/vis_data_context'; import { PanelConfigProps, PANEL_CONFIG_TABS } from './types'; import { TimeseriesVisParams } from '../../../types'; +import { CodeEditor, CssLang } from '../../../../../kibana_react/public'; const lessC = less(window, { env: 'production' }); @@ -281,12 +281,10 @@ export class MarkdownPanelConfig extends Component< - diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index d796067372fa8..6e263dd1cdbbf 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -132,19 +132,18 @@ export class VisualBuilderPageObject extends FtrService { } public async clearMarkdown() { - // Since we use ACE editor and that isn't really storing its value inside - // a textarea we must really select all text and remove it, and cannot use - // clearValue(). await this.retry.waitForWithTimeout('text area is cleared', 20000, async () => { - const editor = await this.testSubjects.find('codeEditorContainer'); - const $ = await editor.parseDomContent(); - const value = $('.ace_line').text(); - if (value.length > 0) { - this.log.debug('Clearing text area input'); - this.waitForMarkdownTextAreaCleaned(); - } - - return value.length === 0; + const input = await this.find.byCssSelector('.tvbMarkdownEditor__editor textarea'); + await input.clickMouseButton(); + await input.clearValueWithKeyboard(); + + const linesContainer = await this.find.byCssSelector( + '.tvbMarkdownEditor__editor .view-lines' + ); + // lines of code in monaco-editor + // text is not present in textarea + const lines = await linesContainer.findAllByClassName('mtk1'); + return lines.length === 0; }); } diff --git a/typings/index.d.ts b/typings/index.d.ts index c7186a0e5795b..2a5c5e3fa430f 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -33,3 +33,7 @@ declare module 'axios/lib/adapters/xhr'; // See https://github.com/storybookjs/storybook/issues/11684 declare module 'react-syntax-highlighter/dist/cjs/create-element'; declare module 'react-syntax-highlighter/dist/cjs/prism-light'; + +// Monaco languages support +declare module 'monaco-editor/esm/vs/basic-languages/markdown/markdown'; +declare module 'monaco-editor/esm/vs/basic-languages/css/css'; From 32f3d0fda7d094f4e0a5c048d0b3d57d663941e4 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 18 Jun 2021 21:31:04 +0200 Subject: [PATCH 10/14] Do not double register dashboard url generator (#102599) Co-authored-by: Vadim Kibana --- src/plugins/dashboard/public/plugin.tsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index b73fe5f2ba410..b5d6eda71ca4a 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -205,19 +205,6 @@ export class DashboardPlugin }; }; - if (share) { - this.dashboardUrlGenerator = share.urlGenerators.registerUrlGenerator( - createDashboardUrlGenerator(async () => { - const [coreStart, , selfStart] = await core.getStartServices(); - return { - appBasePath: coreStart.application.getUrlForApp('dashboards'), - useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), - savedDashboardLoader: selfStart.getSavedDashboardLoader(), - }; - }) - ); - } - const { appMounted, appUnMounted, From 31aa1c8a59e00b32e2f82ead73edf7a01b600ef0 Mon Sep 17 00:00:00 2001 From: Kuldeep M Date: Fri, 18 Jun 2021 21:11:25 +0100 Subject: [PATCH 11/14] [Workplace Search] remove or replace xs props for text on source connect view (#102663) * remove xs props for text on source connect view * change more text sizes --- .../components/add_source/connect_instance.tsx | 8 +++----- .../components/add_source/source_features.tsx | 12 ++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx index fd45d779e6f2a..e3b34050593fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/connect_instance.tsx @@ -161,13 +161,13 @@ export const ConnectInstance: React.FC = ({ const permissionField = ( <> - +

{CONNECT_DOC_PERMISSIONS_TITLE}

- + {!needsPermissions && ( = ({ )} - {!indexPermissionsValue && ( <> - - +

{CONNECT_NOT_SYNCED_TEXT} {needsPermissions && whichDocsLink} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx index 7a66efe4ba5f4..0f170be8ba076 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/source_features.tsx @@ -63,20 +63,20 @@ export const SourceFeatures: React.FC = ({ features, objTy )} - + {title} - {children} + {children} ); }; const SyncFrequencyFeature = ( - +

= ({ features, objTy const SyncedItemsFeature = ( <> - +

{SOURCE_FEATURES_SEARCHABLE}

- +
    {objTypes!.map((objType, i) => (
  • {objType}
  • @@ -178,7 +178,7 @@ export const SourceFeatures: React.FC = ({ features, objTy return ( <> - +

    Included features

    From db312e540d3aba066416191c1e651124cdba0eb4 Mon Sep 17 00:00:00 2001 From: Nik Richers Date: Fri, 18 Jun 2021 13:45:36 -0700 Subject: [PATCH 12/14] Add email connector info for Elastic Cloud (#91363) * Move content into new location to fix merge conflict * Add whitelisting callout --- .../connectors/action-types/email.asciidoc | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/management/connectors/action-types/email.asciidoc b/docs/management/connectors/action-types/email.asciidoc index 719d00c16c932..bab04b8052674 100644 --- a/docs/management/connectors/action-types/email.asciidoc +++ b/docs/management/connectors/action-types/email.asciidoc @@ -95,6 +95,7 @@ The email connector can send email using many popular SMTP email services. For more information about configuring the email connector to work with different email systems, refer to: +* <> * <> * <> * <> @@ -102,6 +103,29 @@ For more information about configuring the email connector to work with differen For other email servers, you can check the list of well-known services that Nodemailer supports in the JSON file https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json[well-known/services.json]. The properties of the objects in those files — `host`, `port`, and `secure` — correspond to the same email connector configuration properties. A missing `secure` property in the "well-known/services.json" file is considered `false`. Typically, `port: 465` uses `secure: true`, and `port: 25` and `port: 587` use `secure: false`. +[float] +[[elasticcloud]] +==== Sending email from Elastic Cloud + +IMPORTANT: These instructions require you to link:{cloud}/ec-watcher.html#ec-watcher-whitelist[whitelist] the email addresses that notifications get sent first. + +Use the following connector settings to send email from Elastic Cloud: + +Sender:: +`noreply@watcheralert.found.io` + +Host:: +`dockerhost` + +Port:: +`10025` + +Secure:: +Toggle off + +Authentication:: +Toggle off + [float] [[gmail]] ==== Sending email from Gmail From 55b35fdf143da23cf3de1526450fb1e777a27584 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Fri, 18 Jun 2021 17:25:29 -0400 Subject: [PATCH 13/14] [Security Solution][Endpoint][Host Isolation] Isolation status badge from alert details (#102274) --- .../components/endpoint/agent_status.tsx | 30 ++++++++++ .../event_details/alert_summary_view.tsx | 27 ++++++++- .../components/event_details/translations.ts | 4 ++ .../containers/detection_engine/alerts/api.ts | 10 +--- .../alerts/use_host_isolation_status.tsx | 13 +++-- .../body/renderers/agent_statuses.tsx | 56 +++++++++++++++++++ .../timeline/body/renderers/constants.tsx | 1 + .../body/renderers/formatted_field.tsx | 13 +++++ 8 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx new file mode 100644 index 0000000000000..f93721349fdac --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiBadge } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { HostStatus } from '../../../../common/endpoint/types'; +import { HOST_STATUS_TO_BADGE_COLOR } from '../../../management/pages/endpoint_hosts/view/host_constants'; + +export const AgentStatus = React.memo(({ hostStatus }: { hostStatus: HostStatus }) => { + return ( + + + + ); +}); + +AgentStatus.displayName = 'AgentStatus'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index 5578264152c39..e229c0c6fae49 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -12,7 +12,7 @@ import { EuiDescriptionListTitle, EuiSpacer, } from '@elastic/eui'; -import { get, getOr } from 'lodash/fp'; +import { get, getOr, find } from 'lodash/fp'; import React, { useMemo } from 'react'; import styled from 'styled-components'; @@ -53,6 +53,7 @@ const fields = [ { id: 'signal.rule.severity', label: ALERTS_HEADERS_SEVERITY }, { id: 'signal.rule.risk_score', label: ALERTS_HEADERS_RISK_SCORE }, { id: 'host.name' }, + { id: 'host.status' }, { id: 'user.name' }, { id: SOURCE_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, { id: DESTINATION_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, @@ -177,6 +178,24 @@ const AlertSummaryViewComponent: React.FC<{ timelineId, ]); + const agentId = useMemo(() => { + const findAgentId = find({ category: 'agent', field: 'agent.id' }, data)?.values; + return findAgentId ? findAgentId[0] : ''; + }, [data]); + + const agentStatusRow = { + title: i18n.AGENT_STATUS, + description: { + contextId: timelineId, + eventId, + fieldName: 'host.status', + value: agentId, + linkValue: undefined, + }, + }; + + const summaryRowsWithAgentStatus = [...summaryRows, agentStatusRow]; + const ruleId = useMemo(() => { const item = data.find((d) => d.field === 'signal.rule.id'); return Array.isArray(item?.originalValue) @@ -188,7 +207,11 @@ const AlertSummaryViewComponent: React.FC<{ return ( <> - + {maybeRule?.note && ( {i18n.INVESTIGATION_GUIDE} diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts index 1ff88d9c2018b..a28d1976ca940 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts @@ -99,3 +99,7 @@ export const NESTED_COLUMN = (field: string) => defaultMessage: 'The {field} field is an object, and is broken down into nested fields which can be added as column', }); + +export const AGENT_STATUS = i18n.translate('xpack.securitySolution.detections.alerts.agentStatus', { + defaultMessage: 'Agent status', +}); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts index 71eac547dcc8e..05706981a681d 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts @@ -7,7 +7,7 @@ import { UpdateDocumentByQueryResponse } from 'elasticsearch'; import { getCasesFromAlertsUrl } from '../../../../../../cases/common'; -import { HostIsolationResponse, HostMetadataInfo } from '../../../../../common/endpoint/types'; +import { HostIsolationResponse, HostInfo } from '../../../../../common/endpoint/types'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL, DETECTION_ENGINE_SIGNALS_STATUS_URL, @@ -178,12 +178,8 @@ export const getCaseIdsFromAlertId = async ({ * * @param host id */ -export const getHostMetadata = async ({ - agentId, -}: { - agentId: string; -}): Promise => - KibanaServices.get().http.fetch( +export const getHostMetadata = async ({ agentId }: { agentId: string }): Promise => + KibanaServices.get().http.fetch( resolvePathVariables(HOST_METADATA_GET_ROUTE, { id: agentId }), { method: 'get' } ); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx index f7894d4764275..7419727fff6a2 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx @@ -7,25 +7,27 @@ import { isEmpty } from 'lodash'; import { useEffect, useState } from 'react'; -import { Maybe } from '../../../../../../observability/common/typings'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { getHostMetadata } from './api'; import { ISOLATION_STATUS_FAILURE } from './translations'; import { isEndpointHostIsolated } from '../../../../common/utils/validators'; +import { HostStatus } from '../../../../../common/endpoint/types'; interface HostIsolationStatusResponse { loading: boolean; - isIsolated: Maybe; + isIsolated: boolean; + agentStatus: HostStatus; } /* - * Retrieves the current isolation status of a host */ + * Retrieves the current isolation status of a host and the agent/host status */ export const useHostIsolationStatus = ({ agentId, }: { agentId: string; }): HostIsolationStatusResponse => { - const [isIsolated, setIsIsolated] = useState>(); + const [isIsolated, setIsIsolated] = useState(false); + const [agentStatus, setAgentStatus] = useState(HostStatus.UNHEALTHY); const [loading, setLoading] = useState(false); const { addError } = useAppToasts(); @@ -38,6 +40,7 @@ export const useHostIsolationStatus = ({ const metadataResponse = await getHostMetadata({ agentId }); if (isMounted) { setIsIsolated(isEndpointHostIsolated(metadataResponse.metadata)); + setAgentStatus(metadataResponse.host_status); } } catch (error) { addError(error.message, { title: ISOLATION_STATUS_FAILURE }); @@ -61,5 +64,5 @@ export const useHostIsolationStatus = ({ isMounted = false; }; }, [addError, agentId]); - return { loading, isIsolated }; + return { loading, isIsolated, agentStatus }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx new file mode 100644 index 0000000000000..16f11809dd72b --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { DefaultDraggable } from '../../../../../common/components/draggables'; +import { EndpointHostIsolationStatus } from '../../../../../common/components/endpoint/host_isolation'; +import { useHostIsolationStatus } from '../../../../../detections/containers/detection_engine/alerts/use_host_isolation_status'; +import { AgentStatus } from '../../../../../common/components/endpoint/agent_status'; + +export const AgentStatuses = React.memo( + ({ + fieldName, + contextId, + eventId, + value, + }: { + fieldName: string; + contextId: string; + eventId: string; + value: string; + }) => { + const { isIsolated, agentStatus } = useHostIsolationStatus({ agentId: value }); + const isolationFieldName = 'host.isolation'; + return ( + + + + + + + + + + + + + ); + } +); + +AgentStatuses.displayName = 'AgentStatuses'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx index b1ef634fe052f..761d82b482af2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx @@ -16,3 +16,4 @@ export const REFERENCE_URL_FIELD_NAME = 'reference.url'; export const EVENT_URL_FIELD_NAME = 'event.url'; export const SIGNAL_RULE_NAME_FIELD_NAME = 'signal.rule.name'; export const SIGNAL_STATUS_FIELD_NAME = 'signal.status'; +export const HOST_STATUS_FIELD_NAME = 'host.status'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index 12effcd3fa81f..efb51916e3765 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable complexity */ + import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { isNumber, isEmpty } from 'lodash/fp'; import React from 'react'; @@ -30,11 +32,13 @@ import { REFERENCE_URL_FIELD_NAME, EVENT_URL_FIELD_NAME, SIGNAL_STATUS_FIELD_NAME, + HOST_STATUS_FIELD_NAME, GEO_FIELD_TYPE, } from './constants'; import { RenderRuleName, renderEventModule, renderUrl } from './formatted_field_helpers'; import { RuleStatus } from './rule_status'; import { HostName } from './host_name'; +import { AgentStatuses } from './agent_statuses'; // simple black-list to prevent dragging and dropping fields such as message name const columnNamesNotDraggable = [MESSAGE_FIELD_NAME]; @@ -116,6 +120,15 @@ const FormattedFieldValueComponent: React.FC<{ return ( ); + } else if (fieldName === HOST_STATUS_FIELD_NAME) { + return ( + + ); } else if ( [ RULE_REFERENCE_FIELD_NAME, From 693823f8c59a19a08ffb1ae2fe0daae3bb195ecb Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 18 Jun 2021 23:35:30 +0100 Subject: [PATCH 14/14] skip flaky suite (#102366) --- .../functional/apps/dashboard_mode/dashboard_empty_screen.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js index 96c40bc4cf409..2adf13db26250 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js @@ -13,7 +13,8 @@ export default function ({ getPageObjects, getService }) { const dashboardPanelActions = getService('dashboardPanelActions'); const PageObjects = getPageObjects(['common', 'dashboard', 'visualize', 'lens']); - describe('empty dashboard', function () { + // FLAKY: https://github.com/elastic/kibana/issues/102366 + describe.skip('empty dashboard', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/lens/basic');