From 8c8323abfd5863825a001081ed090581b223d5b7 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 5 Apr 2021 14:01:24 +0200 Subject: [PATCH 01/10] [Search Sessions] fix updating deleting sessions from non-default space (#96123) * add spaces test * fix updating and deleting sessions in non-default space * revert back to batch update * Add space tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Liza K --- .../session/check_running_sessions.test.ts | 90 ++++++++++- .../search/session/check_running_sessions.ts | 43 ++++- .../api_integration/apis/search/session.ts | 153 ++++++++++++++++++ 3 files changed, 278 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.test.ts b/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.test.ts index f9c62069154b68..2611f6c9da19fa 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.test.ts @@ -13,9 +13,13 @@ import { EQL_SEARCH_STRATEGY, } from '../../../common'; import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks'; -import type { SavedObjectsClientContract } from 'kibana/server'; import { SearchSessionsConfig, SearchStatus } from './types'; import moment from 'moment'; +import { + SavedObjectsBulkUpdateObject, + SavedObjectsDeleteOptions, + SavedObjectsClientContract, +} from '../../../../../../src/core/server'; describe('getSearchStatus', () => { let mockClient: any; @@ -263,6 +267,45 @@ describe('getSearchStatus', () => { expect(savedObjectsClient.delete).not.toBeCalled(); }); + test('deletes in space', async () => { + savedObjectsClient.find.mockResolvedValue({ + saved_objects: [ + { + id: '123', + namespaces: ['awesome'], + attributes: { + persisted: false, + status: SearchSessionStatus.IN_PROGRESS, + created: moment().subtract(moment.duration(3, 'm')), + touched: moment().subtract(moment.duration(2, 'm')), + idMapping: { + 'map-key': { + strategy: ENHANCED_ES_SEARCH_STRATEGY, + id: 'async-id', + }, + }, + }, + }, + ], + total: 1, + } as any); + + await checkRunningSessions( + { + savedObjectsClient, + client: mockClient, + logger: mockLogger, + }, + config + ); + + expect(savedObjectsClient.delete).toBeCalled(); + + const [, id, opts] = savedObjectsClient.delete.mock.calls[0]; + expect(id).toBe('123'); + expect((opts as SavedObjectsDeleteOptions).namespace).toBe('awesome'); + }); + test('deletes a non persisted, abandoned session', async () => { savedObjectsClient.find.mockResolvedValue({ saved_objects: [ @@ -479,6 +522,50 @@ describe('getSearchStatus', () => { expect(savedObjectsClient.delete).not.toBeCalled(); }); + test('updates in space', async () => { + savedObjectsClient.bulkUpdate = jest.fn(); + const so = { + namespaces: ['awesome'], + attributes: { + status: SearchSessionStatus.IN_PROGRESS, + touched: '123', + idMapping: { + 'search-hash': { + id: 'search-id', + strategy: 'cool', + status: SearchStatus.IN_PROGRESS, + }, + }, + }, + }; + savedObjectsClient.find.mockResolvedValue({ + saved_objects: [so], + total: 1, + } as any); + + mockClient.asyncSearch.status.mockResolvedValue({ + body: { + is_partial: false, + is_running: false, + completion_status: 200, + }, + }); + + await checkRunningSessions( + { + savedObjectsClient, + client: mockClient, + logger: mockLogger, + }, + config + ); + + expect(mockClient.asyncSearch.status).toBeCalledWith({ id: 'search-id' }); + const [updateInput] = savedObjectsClient.bulkUpdate.mock.calls[0]; + const updatedAttributes = updateInput[0] as SavedObjectsBulkUpdateObject; + expect(updatedAttributes.namespace).toBe('awesome'); + }); + test('updates to complete if the search is done', async () => { savedObjectsClient.bulkUpdate = jest.fn(); const so = { @@ -563,7 +650,6 @@ describe('getSearchStatus', () => { config ); const [updateInput] = savedObjectsClient.bulkUpdate.mock.calls[0]; - const updatedAttributes = updateInput[0].attributes as SearchSessionSavedObjectAttributes; expect(updatedAttributes.status).toBe(SearchSessionStatus.ERROR); expect(updatedAttributes.idMapping['search-hash'].status).toBe(SearchStatus.ERROR); diff --git a/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.ts index e521c39d7cfd31..6e52b17f368030 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/check_running_sessions.ts @@ -10,6 +10,7 @@ import { Logger, SavedObjectsClientContract, SavedObjectsFindResult, + SavedObjectsUpdateResponse, } from 'kibana/server'; import moment from 'moment'; import { EMPTY, from } from 'rxjs'; @@ -169,12 +170,20 @@ export async function checkRunningSessions( if (!session.attributes.persisted) { if (isSessionStale(session, config, logger)) { - deleted = true; // delete saved object to free up memory // TODO: there's a potential rare edge case of deleting an object and then receiving a new trackId for that same session! // Maybe we want to change state to deleted and cleanup later? logger.debug(`Deleting stale session | ${session.id}`); - await savedObjectsClient.delete(SEARCH_SESSION_TYPE, session.id); + try { + await savedObjectsClient.delete(SEARCH_SESSION_TYPE, session.id, { + namespace: session.namespaces?.[0], + }); + deleted = true; + } catch (e) { + logger.error( + `Error while deleting stale search session ${session.id}: ${e.message}` + ); + } // Send a delete request for each async search to ES Object.keys(session.attributes.idMapping).map(async (searchKey: string) => { @@ -183,8 +192,8 @@ export async function checkRunningSessions( try { await client.asyncSearch.delete({ id: searchInfo.id }); } catch (e) { - logger.debug( - `Error ignored while deleting async_search ${searchInfo.id}: ${e.message}` + logger.error( + `Error while deleting async_search ${searchInfo.id}: ${e.message}` ); } } @@ -202,9 +211,31 @@ export async function checkRunningSessions( if (updatedSessions.length) { // If there's an error, we'll try again in the next iteration, so there's no need to check the output. const updatedResponse = await savedObjectsClient.bulkUpdate( - updatedSessions + updatedSessions.map((session) => ({ + ...session, + namespace: session.namespaces?.[0], + })) + ); + + const success: Array< + SavedObjectsUpdateResponse + > = []; + const fail: Array> = []; + + updatedResponse.saved_objects.forEach((savedObjectResponse) => { + if ('error' in savedObjectResponse) { + fail.push(savedObjectResponse); + logger.error( + `Error while updating search session ${savedObjectResponse?.id}: ${savedObjectResponse.error?.message}` + ); + } else { + success.push(savedObjectResponse); + } + }); + + logger.debug( + `Updating search sessions: success: ${success.length}, fail: ${fail.length}` ); - logger.debug(`Updated ${updatedResponse.saved_objects.length} search sessions`); } }) ) diff --git a/x-pack/test/api_integration/apis/search/session.ts b/x-pack/test/api_integration/apis/search/session.ts index 50bc85ed1e793e..63a6a842fd9f71 100644 --- a/x-pack/test/api_integration/apis/search/session.ts +++ b/x-pack/test/api_integration/apis/search/session.ts @@ -14,6 +14,7 @@ export default function ({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const security = getService('security'); const retry = getService('retry'); + const spacesService = getService('spaces'); describe('search session', () => { describe('session management', () => { @@ -596,5 +597,157 @@ export default function ({ getService }: FtrProviderContext) { .expect(403); }); }); + + describe('in non-default space', () => { + const spaceId = 'foo-space'; + before(async () => { + try { + await spacesService.create({ + id: spaceId, + name: 'Foo Space', + }); + } catch { + // might already be created + } + }); + + after(async () => { + await spacesService.delete(spaceId); + }); + + it('should complete and delete non-persistent sessions', async () => { + const sessionId = `my-session-${Math.random()}`; + + // run search + const searchRes = await supertest + .post(`/s/${spaceId}/internal/search/ese`) + .set('kbn-xsrf', 'foo') + .send({ + sessionId, + params: { + body: { + query: { + term: { + agent: '1', + }, + }, + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = searchRes.body; + + await retry.waitForWithTimeout('searches persisted into session', 5000, async () => { + const resp = await supertest + .get(`/s/${spaceId}/internal/session/${sessionId}`) + .set('kbn-xsrf', 'foo') + .expect(200); + + const { touched, created, persisted, idMapping } = resp.body.attributes; + expect(persisted).to.be(false); + expect(touched).not.to.be(undefined); + expect(created).not.to.be(undefined); + + const idMappings = Object.values(idMapping).map((value: any) => value.id); + expect(idMappings).to.contain(id); + return true; + }); + + // not touched timeout in tests is 15s, wait to give a chance for status to update + await new Promise((resolve) => + setTimeout(() => { + resolve(void 0); + }, 15_000) + ); + + await retry.waitForWithTimeout( + 'searches eventually complete and session gets into the complete state', + 30_000, + async () => { + await supertest + .get(`/s/${spaceId}/internal/session/${sessionId}`) + .set('kbn-xsrf', 'foo') + .expect(404); + + return true; + } + ); + }); + + it('should complete persisten session', async () => { + const sessionId = `my-session-${Math.random()}`; + + // run search + const searchRes = await supertest + .post(`/s/${spaceId}/internal/search/ese`) + .set('kbn-xsrf', 'foo') + .send({ + sessionId, + params: { + body: { + query: { + term: { + agent: '1', + }, + }, + }, + wait_for_completion_timeout: '1ms', + }, + }) + .expect(200); + + const { id } = searchRes.body; + + // persist session + await supertest + .post(`/s/${spaceId}/internal/session`) + .set('kbn-xsrf', 'foo') + .send({ + sessionId, + name: 'My Session', + appId: 'discover', + expires: '123', + urlGeneratorId: 'discover', + }) + .expect(200); + + await retry.waitForWithTimeout('searches persisted into session', 5000, async () => { + const resp = await supertest + .get(`/s/${spaceId}/internal/session/${sessionId}`) + .set('kbn-xsrf', 'foo') + .expect(200); + + const { touched, created, persisted, idMapping } = resp.body.attributes; + expect(persisted).to.be(true); + expect(touched).not.to.be(undefined); + expect(created).not.to.be(undefined); + + const idMappings = Object.values(idMapping).map((value: any) => value.id); + expect(idMappings).to.contain(id); + return true; + }); + + // session refresh interval is 5 seconds, wait to give a chance for status to update + await new Promise((resolve) => setTimeout(resolve, 5000)); + + await retry.waitForWithTimeout( + 'searches eventually complete and session gets into the complete state', + 5000, + async () => { + const resp = await supertest + .get(`/s/${spaceId}/internal/session/${sessionId}`) + .set('kbn-xsrf', 'foo') + .expect(200); + + const { status } = resp.body.attributes; + + expect(status).to.be(SearchSessionStatus.COMPLETE); + return true; + } + ); + }); + }); }); } From 413477b788eeaf83edf6036603c120ffb3b2f4b7 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 5 Apr 2021 13:36:28 +0100 Subject: [PATCH 02/10] skip flaky suite (#96113) --- test/functional/apps/discover/_huge_fields.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_huge_fields.ts b/test/functional/apps/discover/_huge_fields.ts index 8cb39feb2e6bba..b3e63e482e7341 100644 --- a/test/functional/apps/discover/_huge_fields.ts +++ b/test/functional/apps/discover/_huge_fields.ts @@ -15,7 +15,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); - describe('test large number of fields in sidebar', function () { + // FLAKY: https://github.com/elastic/kibana/issues/96113 + describe.skip('test large number of fields in sidebar', function () { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader'], false); await esArchiver.loadIfNeeded('large_fields'); From b670ef29ae80f77d3bc2fa7dcc652d173efa985f Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 5 Apr 2021 15:57:42 +0300 Subject: [PATCH 03/10] [TSVB] fix wrong field list on overriding index pattern for series (#96204) --- .../public/application/components/aggs/agg.tsx | 6 +++--- .../public/application/components/aggs/filter_ratio.js | 5 +++-- .../public/application/components/aggs/percentile.js | 5 +++-- .../public/application/components/aggs/positive_rate.js | 6 +++--- .../public/application/components/aggs/std_agg.js | 5 +++-- .../public/application/components/aggs/std_deviation.js | 5 +++-- .../public/application/components/aggs/top_hit.js | 5 +++-- .../public/application/components/series_config.js | 7 +++---- .../public/application/components/split.js | 5 +++-- .../application/components/vis_types/timeseries/config.js | 7 +++---- 10 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx index 25965d796e6512..d02565717b2477 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx @@ -48,9 +48,9 @@ export function Agg(props: AggProps) { ...props.style, }; - const indexPattern = - (props.series.override_index_pattern && props.series.series_index_pattern) || - props.panel.index_pattern; + const indexPattern = props.series.override_index_pattern + ? props.series.series_index_pattern + : props.panel.index_pattern; return (
diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js index 19d2b88c8d1233..7f93567980b2d3 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js @@ -51,8 +51,9 @@ export const FilterRatioAgg = (props) => { (query) => handleChange({ denominator: query }), [handleChange] ); - const indexPattern = - (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; + const indexPattern = series.override_index_pattern + ? series.series_index_pattern + : panel.index_pattern; const defaults = { numerator: getDataStart().query.queryString.getDefaultQuery(), diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js index 505a2ff4f3c782..77b2e2f020307c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js @@ -39,8 +39,9 @@ export function PercentileAgg(props) { const handleSelectChange = createSelectHandler(handleChange); const handleNumberChange = createNumberHandler(handleChange); - const indexPattern = - (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; + const indexPattern = series.override_index_pattern + ? series.series_index_pattern + : panel.index_pattern; useEffect(() => { if (!checkModel(model)) { diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js index a80194f72b7b24..4b1528ca270817 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js @@ -65,9 +65,9 @@ export const PositiveRateAgg = (props) => { const handleSelectChange = createSelectHandler(handleChange); const htmlId = htmlIdGenerator(); - const indexPattern = - (props.series.override_index_pattern && props.series.series_index_pattern) || - props.panel.index_pattern; + const indexPattern = props.series.override_index_pattern + ? props.series.series_index_pattern + : props.panel.index_pattern; const selectedUnitOptions = UNIT_OPTIONS.filter((o) => o.value === model.unit); diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js index 4a4114f70f06a7..74b441f446308a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js @@ -31,8 +31,9 @@ export function StandardAgg(props) { const handleSelectChange = createSelectHandler(handleChange); const restrictFields = getSupportedFieldsByMetricType(model.type); - const indexPattern = - (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; + const indexPattern = series.override_index_pattern + ? series.series_index_pattern + : panel.index_pattern; const htmlId = htmlIdGenerator(); return ( diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js index c28cb294c3308a..749a97fa79f28b 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js @@ -72,8 +72,9 @@ const StandardDeviationAggUi = (props) => { const handleSelectChange = createSelectHandler(handleChange); const handleTextChange = createTextHandler(handleChange); - const indexPattern = - (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; + const indexPattern = series.override_index_pattern + ? series.series_index_pattern + : panel.index_pattern; const htmlId = htmlIdGenerator(); const selectedModeOption = modeOptions.find((option) => { return model.mode === option.value; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js index 9bea32b7cbd5b9..92e754c1dcdafa 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js @@ -100,8 +100,9 @@ const TopHitAggUi = (props) => { order: 'desc', }; const model = { ...defaults, ...props.model }; - const indexPattern = - (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; + const indexPattern = series.override_index_pattern + ? series.series_index_pattern + : panel.index_pattern; const aggWithOptionsRestrictFields = [ PANEL_TYPES.TABLE, diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config.js b/src/plugins/vis_type_timeseries/public/application/components/series_config.js index 3185503acb5693..8f3893feb89bdf 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/series_config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/series_config.js @@ -33,10 +33,9 @@ export const SeriesConfig = (props) => { const handleSelectChange = createSelectHandler(props.onChange); const handleTextChange = createTextHandler(props.onChange); const htmlId = htmlIdGenerator(); - const seriesIndexPattern = - props.model.override_index_pattern && props.model.series_index_pattern - ? props.model.series_index_pattern - : props.indexPatternForQuery; + const seriesIndexPattern = props.model.override_index_pattern + ? props.model.series_index_pattern + : props.indexPatternForQuery; return (
diff --git a/src/plugins/vis_type_timeseries/public/application/components/split.js b/src/plugins/vis_type_timeseries/public/application/components/split.js index 63aa717174a04b..4990800acf6dbf 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/split.js +++ b/src/plugins/vis_type_timeseries/public/application/components/split.js @@ -63,8 +63,9 @@ export class Split extends Component { render() { const { model, panel, uiRestrictions, seriesQuantity } = this.props; - const indexPattern = - (model.override_index_pattern && model.series_index_pattern) || panel.index_pattern; + const indexPattern = model.override_index_pattern + ? model.series_index_pattern + : panel.index_pattern; const splitMode = get(this.props, 'model.split_mode', SPLIT_MODES.EVERYTHING); const Component = this.getComponent(splitMode, uiRestrictions); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js index 22bf2fa4ca7080..1c3a0411998b0f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js @@ -327,10 +327,9 @@ export const TimeseriesConfig = injectI18n(function (props) { const disableSeparateYaxis = model.separate_axis ? false : true; - const seriesIndexPattern = - props.model.override_index_pattern && props.model.series_index_pattern - ? props.model.series_index_pattern - : props.indexPatternForQuery; + const seriesIndexPattern = props.model.override_index_pattern + ? props.model.series_index_pattern + : props.indexPatternForQuery; const initialPalette = { ...model.palette, From 76acef0c2ceeeb38e76ac6979ee6bcbda099561c Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Mon, 5 Apr 2021 09:05:30 -0400 Subject: [PATCH 04/10] [KQL] Fixed styles of KQL textarea for the K8 theme (#96190) * Fixed style of KQL textarea for K8 theme and some general heights and borders * Fix popover paddings --- .../ui/filter_bar/filter_editor/index.tsx | 2 +- .../public/ui/filter_bar/filter_options.tsx | 2 +- .../ui/query_string_input/_query_bar.scss | 17 +++++++++++++++-- .../ui/query_string_input/language_switcher.tsx | 2 +- .../saved_query_management_component.tsx | 4 ++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx index 5639229e1ff311..d2f04228ed3962 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx @@ -85,7 +85,7 @@ class FilterEditorUI extends Component { public render() { return (
- + { panelPaddingSize="none" repositionOnScroll > - +
- +

- + {savedQueryPopoverTitleText} {savedQueries.length > 0 ? ( @@ -234,7 +234,7 @@ export function SavedQueryManagementComponent({ )} - + Date: Mon, 5 Apr 2021 15:26:48 +0200 Subject: [PATCH 05/10] [Observability] Exploratory View initial skeleton (#94426) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- tsconfig.json | 1 + x-pack/plugins/observability/kibana.json | 2 +- .../public/assets/kibana_dashboard_dark.svg | 116 ++++++ .../public/assets/kibana_dashboard_light.svg | 116 ++++++ .../public/components/app/header/index.tsx | 4 +- .../components/empty_view.tsx | 32 ++ .../components/filter_label.test.tsx | 138 +++++++ .../components/filter_label.tsx | 90 ++++ .../configurations/constants.ts | 73 ++++ .../configurations/cpu_usage_config.ts | 42 ++ .../data/elasticsearch_fieldnames.ts | 144 +++++++ .../configurations/data/sample_attribute.ts | 74 ++++ .../data/test_index_pattern.json | 11 + .../configurations/default_configs.ts | 52 +++ .../configurations/kpi_trends_config.ts | 73 ++++ .../configurations/lens_attributes.test.ts | 387 ++++++++++++++++++ .../configurations/lens_attributes.ts | 273 ++++++++++++ .../configurations/logs_frequency_config.ts | 39 ++ .../configurations/memory_usage_config.ts | 42 ++ .../configurations/monitor_duration_config.ts | 48 +++ .../configurations/monitor_pings_config.ts | 43 ++ .../configurations/network_activity_config.ts | 41 ++ .../configurations/performance_dist_config.ts | 86 ++++ .../configurations/service_latency_config.ts | 52 +++ .../service_throughput_config.ts | 55 +++ .../configurations/url_constants.ts | 15 + .../exploratory_view/configurations/utils.ts | 54 +++ .../exploratory_view.test.tsx | 93 +++++ .../exploratory_view/exploratory_view.tsx | 87 ++++ .../exploratory_view/header/header.test.tsx | 53 +++ .../shared/exploratory_view/header/header.tsx | 63 +++ .../hooks/use_default_index_pattern.tsx | 61 +++ .../hooks/use_init_exploratory_view.ts | 44 ++ .../hooks/use_lens_attributes.ts | 88 ++++ .../hooks/use_series_filters.ts | 100 +++++ .../hooks/use_url_strorage.tsx | 103 +++++ .../shared/exploratory_view/index.tsx | 64 +++ .../shared/exploratory_view/rtl_helpers.tsx | 318 ++++++++++++++ .../columns/data_types_col.test.tsx | 59 +++ .../series_builder/columns/data_types_col.tsx | 56 +++ .../columns/report_breakdowns.test.tsx | 75 ++++ .../columns/report_breakdowns.tsx | 15 + .../columns/report_definition_col.test.tsx | 75 ++++ .../columns/report_definition_col.tsx | 95 +++++ .../columns/report_filters.test.tsx | 28 ++ .../series_builder/columns/report_filters.tsx | 22 + .../columns/report_types_col.test.tsx | 65 +++ .../columns/report_types_col.tsx | 61 +++ .../series_builder/custom_report_field.tsx | 47 +++ .../series_builder/series_builder.tsx | 201 +++++++++ .../series_date_picker/index.tsx | 55 +++ .../series_date_picker.test.tsx | 76 ++++ .../series_editor/columns/actions_col.tsx | 31 ++ .../series_editor/columns/breakdowns.test.tsx | 49 +++ .../series_editor/columns/breakdowns.tsx | 65 +++ .../columns/chart_types.test.tsx | 56 +++ .../series_editor/columns/chart_types.tsx | 149 +++++++ .../series_editor/columns/date_picker_col.tsx | 20 + .../columns/filter_expanded.test.tsx | 93 +++++ .../series_editor/columns/filter_expanded.tsx | 100 +++++ .../columns/filter_value_btn.test.tsx | 238 +++++++++++ .../columns/filter_value_btn.tsx | 117 ++++++ .../columns/metric_selection.test.tsx | 112 +++++ .../columns/metric_selection.tsx | 86 ++++ .../series_editor/columns/remove_series.tsx | 35 ++ .../series_editor/columns/series_filter.tsx | 139 +++++++ .../series_editor/selected_filters.test.tsx | 33 ++ .../series_editor/selected_filters.tsx | 96 +++++ .../series_editor/series_editor.tsx | 139 +++++++ .../shared/exploratory_view/types.ts | 89 ++++ .../field_value_selection.tsx | 19 +- .../shared/field_value_suggestions/index.tsx | 24 +- .../public/context/has_data_context.test.tsx | 9 +- .../public/context/has_data_context.tsx | 46 ++- .../public/hooks/use_breadcrumbs.ts | 71 ++++ .../public/hooks/use_quick_time_ranges.tsx | 22 + .../public/hooks/use_values_list.ts | 46 ++- x-pack/plugins/observability/public/index.ts | 1 + .../observability/public/routes/index.tsx | 21 + .../utils/observability_index_patterns.ts | 64 +++ x-pack/plugins/observability/tsconfig.json | 9 +- x-pack/plugins/uptime/public/apps/plugin.ts | 27 +- 82 files changed, 6058 insertions(+), 55 deletions(-) create mode 100644 x-pack/plugins/observability/public/assets/kibana_dashboard_dark.svg create mode 100644 x-pack/plugins/observability/public/assets/kibana_dashboard_light.svg create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/cpu_usage_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/elasticsearch_fieldnames.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/sample_attribute.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/test_index_pattern.json create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/kpi_trends_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/logs_frequency_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/memory_usage_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_duration_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_pings_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/network_activity_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/performance_dist_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_latency_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_throughput_config.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/url_constants.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_default_index_pattern.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_init_exploratory_view.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_url_strorage.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/custom_report_field.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/actions_col.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/remove_series.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts create mode 100644 x-pack/plugins/observability/public/hooks/use_breadcrumbs.ts create mode 100644 x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx create mode 100644 x-pack/plugins/observability/public/utils/observability_index_patterns.ts diff --git a/tsconfig.json b/tsconfig.json index 03597114333cac..30944ac71fcc8a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -83,6 +83,7 @@ "x-pack/plugins/uptime/server/lib/requests/helper.ts", "x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx", "x-pack/plugins/uptime/public/lib/helper/enzyme_helpers.tsx", + "x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx", "x-pack/plugins/apm/server/utils/test_helpers.tsx", "x-pack/plugins/apm/public/utils/testHelpers.tsx", diff --git a/x-pack/plugins/observability/kibana.json b/x-pack/plugins/observability/kibana.json index 84aa1be9a8d870..5c47d0376581a0 100644 --- a/x-pack/plugins/observability/kibana.json +++ b/x-pack/plugins/observability/kibana.json @@ -3,7 +3,7 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "observability"], - "optionalPlugins": ["licensing", "home", "usageCollection"], + "optionalPlugins": ["licensing", "home", "usageCollection","lens"], "requiredPlugins": ["data"], "ui": true, "server": true, diff --git a/x-pack/plugins/observability/public/assets/kibana_dashboard_dark.svg b/x-pack/plugins/observability/public/assets/kibana_dashboard_dark.svg new file mode 100644 index 00000000000000..834dd98d60e4c6 --- /dev/null +++ b/x-pack/plugins/observability/public/assets/kibana_dashboard_dark.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/observability/public/assets/kibana_dashboard_light.svg b/x-pack/plugins/observability/public/assets/kibana_dashboard_light.svg new file mode 100644 index 00000000000000..958d25362c4393 --- /dev/null +++ b/x-pack/plugins/observability/public/assets/kibana_dashboard_light.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/observability/public/components/app/header/index.tsx b/x-pack/plugins/observability/public/components/app/header/index.tsx index a41e3364d22b6e..8b86e0b25379b8 100644 --- a/x-pack/plugins/observability/public/components/app/header/index.tsx +++ b/x-pack/plugins/observability/public/components/app/header/index.tsx @@ -59,13 +59,13 @@ export function Header({ color, datePicker = null, restrictWidth }: Props) { - + - +

{i18n.translate('xpack.observability.home.title', { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx new file mode 100644 index 00000000000000..17f1b039667d0b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx @@ -0,0 +1,32 @@ +/* + * 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 { EuiImage } from '@elastic/eui'; +import styled from 'styled-components'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; + +export function EmptyView() { + const { + services: { http }, + } = useKibana(); + + return ( + + + + ); +} + +const Wrapper = styled.div` + text-align: center; + opacity: 0.4; + height: 550px; +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx new file mode 100644 index 00000000000000..37597e0ce513f2 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx @@ -0,0 +1,138 @@ +/* + * 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 { fireEvent, screen, waitFor } from '@testing-library/react'; +import { mockIndexPattern, render } from '../rtl_helpers'; +import { buildFilterLabel, FilterLabel } from './filter_label'; +import * as useSeriesHook from '../hooks/use_series_filters'; + +describe('FilterLabel', function () { + const invertFilter = jest.fn(); + jest.spyOn(useSeriesHook, 'useSeriesFilters').mockReturnValue({ + invertFilter, + } as any); + + it('should render properly', async function () { + render( + + ); + + await waitFor(() => { + screen.getByText('elastic-co'); + screen.getByText(/web application:/i); + screen.getByTitle('Delete Web Application: elastic-co'); + screen.getByRole('button', { + name: /delete web application: elastic-co/i, + }); + }); + }); + + it.skip('should delete filter', async function () { + const removeFilter = jest.fn(); + render( + + ); + + await waitFor(() => { + fireEvent.click(screen.getByLabelText('Filter actions')); + }); + + fireEvent.click(screen.getByTestId('deleteFilter')); + expect(removeFilter).toHaveBeenCalledTimes(1); + expect(removeFilter).toHaveBeenCalledWith('service.name', 'elastic-co', false); + }); + + it.skip('should invert filter', async function () { + const removeFilter = jest.fn(); + render( + + ); + + await waitFor(() => { + fireEvent.click(screen.getByLabelText('Filter actions')); + }); + + fireEvent.click(screen.getByTestId('negateFilter')); + expect(invertFilter).toHaveBeenCalledTimes(1); + expect(invertFilter).toHaveBeenCalledWith({ + field: 'service.name', + negate: false, + value: 'elastic-co', + }); + }); + + it('should display invert filter', async function () { + render( + + ); + + await waitFor(() => { + screen.getByText('elastic-co'); + screen.getByText(/web application:/i); + screen.getByTitle('Delete NOT Web Application: elastic-co'); + screen.getByRole('button', { + name: /delete not web application: elastic-co/i, + }); + }); + }); + + it('should build filter meta', function () { + expect( + buildFilterLabel({ + field: 'user_agent.name', + label: 'Browser family', + indexPattern: mockIndexPattern, + value: 'Firefox', + negate: false, + }) + ).toEqual({ + meta: { + alias: null, + disabled: false, + index: 'apm-*', + key: 'Browser family', + negate: false, + type: 'phrase', + value: 'Firefox', + }, + query: { + match_phrase: { + 'user_agent.name': 'Firefox', + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx new file mode 100644 index 00000000000000..3d6dc5b3f2bf51 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx @@ -0,0 +1,90 @@ +/* + * 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 { injectI18n } from '@kbn/i18n/react'; +import { esFilters, Filter, IndexPattern } from '../../../../../../../../src/plugins/data/public'; +import { useIndexPatternContext } from '../hooks/use_default_index_pattern'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { useSeriesFilters } from '../hooks/use_series_filters'; + +interface Props { + field: string; + label: string; + value: string; + seriesId: string; + negate: boolean; + definitionFilter?: boolean; + removeFilter: (field: string, value: string, notVal: boolean) => void; +} +export function buildFilterLabel({ + field, + value, + label, + indexPattern, + negate, +}: { + label: string; + value: string; + negate: boolean; + field: string; + indexPattern: IndexPattern; +}) { + const indexField = indexPattern.getFieldByName(field)!; + + const filter = esFilters.buildPhraseFilter(indexField, value, indexPattern); + + filter.meta.value = value; + filter.meta.key = label; + filter.meta.alias = null; + filter.meta.negate = negate; + filter.meta.disabled = false; + filter.meta.type = 'phrase'; + + return filter; +} +export function FilterLabel({ + label, + seriesId, + field, + value, + negate, + removeFilter, + definitionFilter, +}: Props) { + const FilterItem = injectI18n(esFilters.FilterItem); + + const { indexPattern } = useIndexPatternContext(); + + const filter = buildFilterLabel({ field, value, label, indexPattern, negate }); + + const { invertFilter } = useSeriesFilters({ seriesId }); + + const { + services: { uiSettings }, + } = useKibana(); + + return indexPattern ? ( + { + removeFilter(field, value, false); + }} + onUpdate={(filterN: Filter) => { + if (definitionFilter) { + // FIXME handle this use case + } else if (filterN.meta.negate !== negate) { + invertFilter({ field, value, negate }); + } + }} + uiSettings={uiSettings!} + hiddenPanelOptions={['pinFilter', 'editFilter', 'disableFilter']} + /> + ) : null; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants.ts new file mode 100644 index 00000000000000..aa3ac2fa64317b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants.ts @@ -0,0 +1,73 @@ +/* + * 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 { AppDataType, ReportViewTypeId } from '../types'; +import { + CLS_FIELD, + FCP_FIELD, + FID_FIELD, + LCP_FIELD, + TBT_FIELD, +} from './data/elasticsearch_fieldnames'; + +export const FieldLabels: Record = { + 'user_agent.name': 'Browser family', + 'user_agent.version': 'Browser version', + 'user_agent.os.name': 'Operating system', + 'client.geo.country_name': 'Location', + 'user_agent.device.name': 'Device', + 'observer.geo.name': 'Observer location', + 'service.name': 'Service Name', + 'service.environment': 'Environment', + + [LCP_FIELD]: 'Largest contentful paint', + [FCP_FIELD]: 'First contentful paint', + [TBT_FIELD]: 'Total blocking time', + [FID_FIELD]: 'First input delay', + [CLS_FIELD]: 'Cumulative layout shift', + + 'monitor.id': 'Monitor Id', + 'monitor.status': 'Monitor Status', + + 'agent.hostname': 'Agent host', + 'host.hostname': 'Host name', + 'monitor.name': 'Monitor name', + 'monitor.type': 'Monitor Type', + 'url.port': 'Port', + tags: 'Tags', + + // custom + + 'performance.metric': 'Metric', + 'Business.KPI': 'KPI', +}; + +export const DataViewLabels: Record = { + pld: 'Performance Distribution', + upd: 'Uptime monitor duration', + upp: 'Uptime pings', + svl: 'APM Service latency', + kpi: 'KPI over time', + tpt: 'APM Service throughput', + cpu: 'System CPU Usage', + logs: 'Logs Frequency', + mem: 'System Memory Usage', + nwk: 'Network Activity', +}; + +export const ReportToDataTypeMap: Record = { + upd: 'synthetics', + upp: 'synthetics', + tpt: 'apm', + svl: 'apm', + kpi: 'rum', + pld: 'rum', + nwk: 'metrics', + mem: 'metrics', + logs: 'logs', + cpu: 'metrics', +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/cpu_usage_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/cpu_usage_config.ts new file mode 100644 index 00000000000000..5a4fb2aa3a6a59 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/cpu_usage_config.ts @@ -0,0 +1,42 @@ +/* + * 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 { DataSeries } from '../types'; +import { FieldLabels } from './constants'; +import { OperationType } from '../../../../../../lens/public'; + +interface Props { + seriesId: string; +} + +export function getCPUUsageLensConfig({ seriesId }: Props): DataSeries { + return { + id: seriesId, + reportType: 'cpu-usage', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'avg' as OperationType, + sourceField: 'system.cpu.user.pct', + label: 'CPU Usage %', + }, + hasMetricType: true, + defaultFilters: [], + breakdowns: ['host.hostname'], + filters: [], + labels: { ...FieldLabels, 'host.hostname': 'Host name' }, + reportDefinitions: [ + { + field: 'agent.hostname', + required: true, + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/elasticsearch_fieldnames.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/elasticsearch_fieldnames.ts new file mode 100644 index 00000000000000..3faf54fff31404 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/elasticsearch_fieldnames.ts @@ -0,0 +1,144 @@ +/* + * 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. + */ + +export const CLOUD = 'cloud'; +export const CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; +export const CLOUD_PROVIDER = 'cloud.provider'; +export const CLOUD_REGION = 'cloud.region'; +export const CLOUD_MACHINE_TYPE = 'cloud.machine.type'; + +export const SERVICE = 'service'; +export const SERVICE_NAME = 'service.name'; +export const SERVICE_ENVIRONMENT = 'service.environment'; +export const SERVICE_FRAMEWORK_NAME = 'service.framework.name'; +export const SERVICE_FRAMEWORK_VERSION = 'service.framework.version'; +export const SERVICE_LANGUAGE_NAME = 'service.language.name'; +export const SERVICE_LANGUAGE_VERSION = 'service.language.version'; +export const SERVICE_RUNTIME_NAME = 'service.runtime.name'; +export const SERVICE_RUNTIME_VERSION = 'service.runtime.version'; +export const SERVICE_NODE_NAME = 'service.node.name'; +export const SERVICE_VERSION = 'service.version'; + +export const AGENT = 'agent'; +export const AGENT_NAME = 'agent.name'; +export const AGENT_VERSION = 'agent.version'; + +export const URL_FULL = 'url.full'; +export const HTTP_REQUEST_METHOD = 'http.request.method'; +export const HTTP_RESPONSE_STATUS_CODE = 'http.response.status_code'; +export const USER_ID = 'user.id'; +export const USER_AGENT_ORIGINAL = 'user_agent.original'; +export const USER_AGENT_NAME = 'user_agent.name'; +export const USER_AGENT_VERSION = 'user_agent.version'; + +export const DESTINATION_ADDRESS = 'destination.address'; + +export const OBSERVER_HOSTNAME = 'observer.hostname'; +export const OBSERVER_VERSION_MAJOR = 'observer.version_major'; +export const OBSERVER_LISTENING = 'observer.listening'; +export const PROCESSOR_EVENT = 'processor.event'; + +export const TRANSACTION_DURATION = 'transaction.duration.us'; +export const TRANSACTION_DURATION_HISTOGRAM = 'transaction.duration.histogram'; +export const TRANSACTION_TYPE = 'transaction.type'; +export const TRANSACTION_RESULT = 'transaction.result'; +export const TRANSACTION_NAME = 'transaction.name'; +export const TRANSACTION_ID = 'transaction.id'; +export const TRANSACTION_SAMPLED = 'transaction.sampled'; +export const TRANSACTION_BREAKDOWN_COUNT = 'transaction.breakdown.count'; +export const TRANSACTION_PAGE_URL = 'transaction.page.url'; +// for transaction metrics +export const TRANSACTION_ROOT = 'transaction.root'; + +export const EVENT_OUTCOME = 'event.outcome'; + +export const TRACE_ID = 'trace.id'; + +export const SPAN_DURATION = 'span.duration.us'; +export const SPAN_TYPE = 'span.type'; +export const SPAN_SUBTYPE = 'span.subtype'; +export const SPAN_SELF_TIME_SUM = 'span.self_time.sum.us'; +export const SPAN_ACTION = 'span.action'; +export const SPAN_NAME = 'span.name'; +export const SPAN_ID = 'span.id'; +export const SPAN_DESTINATION_SERVICE_RESOURCE = 'span.destination.service.resource'; +export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT = + 'span.destination.service.response_time.count'; + +export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM = + 'span.destination.service.response_time.sum.us'; + +// Parent ID for a transaction or span +export const PARENT_ID = 'parent.id'; + +export const ERROR_GROUP_ID = 'error.grouping_key'; +export const ERROR_CULPRIT = 'error.culprit'; +export const ERROR_LOG_LEVEL = 'error.log.level'; +export const ERROR_LOG_MESSAGE = 'error.log.message'; +export const ERROR_EXC_MESSAGE = 'error.exception.message'; // only to be used in es queries, since error.exception is now an array +export const ERROR_EXC_HANDLED = 'error.exception.handled'; // only to be used in es queries, since error.exception is now an array +export const ERROR_EXC_TYPE = 'error.exception.type'; +export const ERROR_PAGE_URL = 'error.page.url'; + +// METRICS +export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; +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'; +export const METRIC_CGROUP_MEMORY_LIMIT_BYTES = 'system.process.cgroup.memory.mem.limit.bytes'; +export const METRIC_CGROUP_MEMORY_USAGE_BYTES = 'system.process.cgroup.memory.mem.usage.bytes'; + +export const METRIC_JAVA_HEAP_MEMORY_MAX = 'jvm.memory.heap.max'; +export const METRIC_JAVA_HEAP_MEMORY_COMMITTED = 'jvm.memory.heap.committed'; +export const METRIC_JAVA_HEAP_MEMORY_USED = 'jvm.memory.heap.used'; +export const METRIC_JAVA_NON_HEAP_MEMORY_MAX = 'jvm.memory.non_heap.max'; +export const METRIC_JAVA_NON_HEAP_MEMORY_COMMITTED = 'jvm.memory.non_heap.committed'; +export const METRIC_JAVA_NON_HEAP_MEMORY_USED = 'jvm.memory.non_heap.used'; +export const METRIC_JAVA_THREAD_COUNT = 'jvm.thread.count'; +export const METRIC_JAVA_GC_COUNT = 'jvm.gc.count'; +export const METRIC_JAVA_GC_TIME = 'jvm.gc.time'; + +export const LABEL_NAME = 'labels.name'; + +export const HOST = 'host'; +export const HOST_NAME = 'host.hostname'; +export const HOST_OS_PLATFORM = 'host.os.platform'; +export const CONTAINER_ID = 'container.id'; +export const KUBERNETES = 'kubernetes'; +export const POD_NAME = 'kubernetes.pod.name'; + +export const CLIENT_GEO_COUNTRY_ISO_CODE = 'client.geo.country_iso_code'; +export const CLIENT_GEO_COUNTRY_NAME = 'client.geo.country_name'; + +// RUM Labels +export const TRANSACTION_URL = 'url.full'; +export const CLIENT_GEO = 'client.geo'; +export const USER_AGENT_DEVICE = 'user_agent.device.name'; +export const USER_AGENT_OS = 'user_agent.os.name'; + +export const TRANSACTION_TIME_TO_FIRST_BYTE = 'transaction.marks.agent.timeToFirstByte'; +export const TRANSACTION_DOM_INTERACTIVE = 'transaction.marks.agent.domInteractive'; + +export const FCP_FIELD = 'transaction.marks.agent.firstContentfulPaint'; +export const LCP_FIELD = 'transaction.marks.agent.largestContentfulPaint'; +export const TBT_FIELD = 'transaction.experience.tbt'; +export const FID_FIELD = 'transaction.experience.fid'; +export const CLS_FIELD = 'transaction.experience.cls'; + +export const PROFILE_ID = 'profile.id'; +export const PROFILE_DURATION = 'profile.duration'; +export const PROFILE_TOP_ID = 'profile.top.id'; +export const PROFILE_STACK = 'profile.stack'; + +export const PROFILE_SAMPLES_COUNT = 'profile.samples.count'; +export const PROFILE_CPU_NS = 'profile.cpu.ns'; +export const PROFILE_WALL_US = 'profile.wall.us'; + +export const PROFILE_ALLOC_OBJECTS = 'profile.alloc_objects.count'; +export const PROFILE_ALLOC_SPACE = 'profile.alloc_space.bytes'; +export const PROFILE_INUSE_OBJECTS = 'profile.inuse_objects.count'; +export const PROFILE_INUSE_SPACE = 'profile.inuse_space.bytes'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/sample_attribute.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/sample_attribute.ts new file mode 100644 index 00000000000000..9b299e7d70bcc4 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/sample_attribute.ts @@ -0,0 +1,74 @@ +/* + * 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. + */ +export const sampleAttribute = { + title: 'Prefilled from exploratory view app', + description: '', + visualizationType: 'lnsXY', + references: [ + { id: 'apm-*', name: 'indexpattern-datasource-current-indexpattern', type: 'index-pattern' }, + { id: 'apm-*', name: 'indexpattern-datasource-layer-layer1', type: 'index-pattern' }, + ], + state: { + datasourceStates: { + indexpattern: { + layers: { + layer1: { + columnOrder: ['x-axis-column', 'y-axis-column'], + columns: { + 'x-axis-column': { + sourceField: 'transaction.duration.us', + label: 'Page load time', + dataType: 'number', + operationType: 'range', + isBucketed: true, + scale: 'interval', + params: { + type: 'histogram', + ranges: [{ from: 0, to: 1000, label: '' }], + maxBars: 'auto', + }, + }, + 'y-axis-column': { + dataType: 'number', + isBucketed: false, + label: 'Pages loaded', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + }, + incompleteColumns: {}, + }, + }, + }, + }, + visualization: { + legend: { isVisible: true, position: 'right' }, + valueLabels: 'hide', + fittingFunction: 'Linear', + curveType: 'CURVE_MONOTONE_X', + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + preferredSeriesType: 'line', + layers: [ + { + accessors: ['y-axis-column'], + layerId: 'layer1', + seriesType: 'line', + yConfig: [{ forAccessor: 'y-axis-column', color: 'green' }], + xAccessor: 'x-axis-column', + }, + ], + }, + query: { query: '', language: 'kuery' }, + filters: [ + { meta: { index: 'apm-*' }, query: { match_phrase: { 'transaction.type': 'page-load' } } }, + { meta: { index: 'apm-*' }, query: { match_phrase: { 'processor.event': 'transaction' } } }, + ], + }, +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/test_index_pattern.json b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/test_index_pattern.json new file mode 100644 index 00000000000000..31fec1fe8d4f46 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/data/test_index_pattern.json @@ -0,0 +1,11 @@ +{ + "attributes": { + "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"}}", + "fields": "[{\"name\":\"@timestamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.build.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.organization.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"as.organization.name\"}}},{\"name\":\"child.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.organization.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"client.as.organization.name\"}}},{\"name\":\"client.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.subdomain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.top_level_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.full_name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"client.user.full_name\"}}},{\"name\":\"client.user.group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"client.user.name\"}}},{\"name\":\"client.user.roles\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.account.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.account.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.availability_zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.image.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.instance.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.instance.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.machine.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.project.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.project.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.provider\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.region\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clr.gc.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clr.gc.gen0size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clr.gc.gen1size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clr.gc.gen2size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clr.gc.gen3size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"code_signature.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"code_signature.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"code_signature.subject_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"code_signature.trusted\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"code_signature.valid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.image.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.image.tag\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.runtime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.organization.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"destination.as.organization.name\"}}},{\"name\":\"destination.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.subdomain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.top_level_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.full_name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"destination.user.full_name\"}}},{\"name\":\"destination.user.group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"destination.user.name\"}}},{\"name\":\"destination.user.roles\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.subject_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.trusted\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.code_signature.valid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.company\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.file_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.imphash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.original_file_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dll.pe.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.data\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.header_flags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.op_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.subdomain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.top_level_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.resolved_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.response_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ecs.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.culprit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.handled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.exception.module\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.exception.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.grouping_key\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.log.level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.log.logger_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.log.message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.log.param_message\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.stack_trace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.stack_trace.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"error.stack_trace\"}}},{\"name\":\"error.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.category\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.dataset\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.end\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.ingested\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.module\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.outcome\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.provider\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.reason\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.risk_score\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.risk_score_norm\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.sequence\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.severity\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.timezone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.url\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.accessed\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.attributes\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.code_signature.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.code_signature.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.code_signature.subject_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.code_signature.trusted\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.code_signature.valid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.ctime\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.device\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.drive_letter\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.extension\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.gid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.group\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.inode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mime_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mtime\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.owner\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"file.path\"}}},{\"name\":\"file.pe.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.company\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.file_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.imphash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.original_file_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.pe.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"file.target_path\"}}},{\"name\":\"file.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.alternative_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.issuer.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.not_after\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.not_before\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.public_key_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.public_key_curve\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.public_key_exponent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.public_key_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.signature_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.subject.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.x509.version_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.goroutines\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.active\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.allocated\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.frees\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.idle\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.mallocs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.total\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.cpu_fraction\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.next_gc_limit\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.total_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.total_pause.ns\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.obtained\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.released\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.stack\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.total\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.containerized\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.build\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.codename\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"host.os.full\"}}},{\"name\":\"host.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"host.os.name\"}}},{\"name\":\"host.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.full_name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"host.user.full_name\"}}},{\"name\":\"host.user.group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"host.user.name\"}}},{\"name\":\"host.user.roles\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.content\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.content.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"http.request.body.content\"}}},{\"name\":\"http.request.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.mime_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.referrer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.content\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.content.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"http.response.body.content\"}}},{\"name\":\"http.response.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.finished\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.mime_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.status_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"interface.alias\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"interface.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"interface.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.gc.alloc\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.gc.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.gc.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.heap.committed\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.heap.max\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.heap.pool.committed\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.heap.pool.max\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.heap.pool.used\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.heap.used\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.non_heap.committed\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.non_heap.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.memory.non_heap.used\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jvm.thread.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.image\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.city\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.company\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.country_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.customer_email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.customer_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.customer_tier\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.env\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.events_encoded\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.events_failed\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.events_original\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.events_published\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.foo\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.git_rev\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.in_eu\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.ip\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.kibana_uuid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.lang\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.lorem\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.multi-line\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.plugin\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.productId\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.request_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.served_from_cache\",\"type\":\"conflict\",\"esTypes\":[\"boolean\",\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false,\"conflictDescriptions\":{\"boolean\":[\"apm-8.0.0-transaction-000001\"],\"keyword\":[\"apm-8.0.0-transaction-000002\"]}},{\"name\":\"labels.taskType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.this-is-a-very-long-tag-name-without-any-spaces\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.u\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"labels.worker\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.file.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.logger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.origin.file.line\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.origin.file.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.origin.function\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.syslog.facility.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.syslog.facility.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.syslog.priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.syslog.severity.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.syslog.severity.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"metricset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"metricset.period\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.application\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.community_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.direction\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.forwarded_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.iana_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.inner.vlan.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.inner.vlan.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.protocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.transport\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.vlan.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.vlan.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.eventloop.delay.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.eventloop.delay.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.handles.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.memory.arrayBuffers.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.memory.external.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.memory.heap.allocated.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.memory.heap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nodejs.requests.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.egress.interface.alias\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.egress.interface.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.egress.interface.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.egress.vlan.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.egress.vlan.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.egress.zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ingress.interface.alias\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ingress.interface.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ingress.interface.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ingress.vlan.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ingress.vlan.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ingress.zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.listening\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.full.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"observer.os.full\"}}},{\"name\":\"observer.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"observer.os.name\"}}},{\"name\":\"observer.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.version_major\",\"type\":\"number\",\"esTypes\":[\"byte\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"organization.name\"}}},{\"name\":\"os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.full.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"os.full\"}}},{\"name\":\"os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"os.name\"}}},{\"name\":\"os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.build_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.checksum\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.install_scope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.installed\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.license\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"package.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"parent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.company\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.file_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.imphash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.original_file_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pe.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.args\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.args_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.subject_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.trusted\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.code_signature.valid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.command_line\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.command_line.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.command_line\"}}},{\"name\":\"process.entity_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.executable\"}}},{\"name\":\"process.exit_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.name\"}}},{\"name\":\"process.parent.args\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.args_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.subject_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.trusted\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.code_signature.valid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.command_line\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.command_line.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.parent.command_line\"}}},{\"name\":\"process.parent.entity_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.executable\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.executable.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.parent.executable\"}}},{\"name\":\"process.parent.exit_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.parent.name\"}}},{\"name\":\"process.parent.pe.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.company\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.file_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.imphash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.original_file_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pe.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pgid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.ppid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.thread.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.thread.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.title\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.title.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.parent.title\"}}},{\"name\":\"process.parent.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.working_directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.parent.working_directory.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.parent.working_directory\"}}},{\"name\":\"process.pe.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.company\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.file_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.imphash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.original_file_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pe.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pgid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.ppid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.title\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"process.working_directory\"}}},{\"name\":\"processor.event\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"processor.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.alloc_objects.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.alloc_space.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.cpu.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.inuse_objects.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.inuse_space.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.samples.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.stack.filename\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.stack.function\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.stack.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.stack.line\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.top.filename\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.top.function\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.top.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"profile.top.line\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.data.bytes\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.data.strings\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.data.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.hive\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.key\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"registry.value\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"related.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"related.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"related.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"related.user\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ruby.gc.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ruby.heap.allocations.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ruby.heap.slots.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ruby.heap.slots.live\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ruby.threads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.author\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.category\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.license\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.ruleset\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.uuid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rule.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.organization.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"server.as.organization.name\"}}},{\"name\":\"server.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.subdomain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.top_level_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.full_name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"server.user.full_name\"}}},{\"name\":\"server.user.group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"server.user.name\"}}},{\"name\":\"server.user.roles\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.environment\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.framework.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.framework.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.language.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.language.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.runtime.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.runtime.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.organization.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"source.as.organization.name\"}}},{\"name\":\"source.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.subdomain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.top_level_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.full_name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"source.user.full_name\"}}},{\"name\":\"source.user.group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"source.user.name\"}}},{\"name\":\"source.user.roles\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sourcemap.bundle_filepath\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sourcemap.service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"sourcemap.service.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.db.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.db.rows_affected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.destination.service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.destination.service.resource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.destination.service.response_time.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.destination.service.response_time.sum.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.destination.service.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.duration.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.message.age.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.message.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.self_time.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.self_time.sum.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.start.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.subtype\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.sync\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"span.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.total.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.inactive_file.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.system.norm.pct\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.user.norm.pct\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.framework\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.tactic.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.tactic.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.tactic.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"threat.technique.name\"}}},{\"name\":\"threat.technique.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.subtechnique.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.subtechnique.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"threat.technique.subtechnique.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"threat.technique.subtechnique.name\"}}},{\"name\":\"threat.technique.subtechnique.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"timeseries.instance\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"timestamp.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.cipher\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.certificate\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.certificate_chain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.issuer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.ja3\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.not_after\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.not_before\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.server_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.subject\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.supported_ciphers\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.alternative_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.issuer.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.not_after\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.not_before\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.public_key_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.public_key_curve\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.public_key_exponent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.public_key_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.signature_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.subject.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.client.x509.version_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.curve\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.established\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.next_protocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.resumed\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.certificate\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.certificate_chain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.issuer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.ja3s\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.not_after\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.not_before\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.subject\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.alternative_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.issuer.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.not_after\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.not_before\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.public_key_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.public_key_curve\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.public_key_exponent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.public_key_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.signature_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.subject.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.server.x509.version_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tls.version_protocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"trace.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.breakdown.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.duration.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.duration.histogram\",\"type\":\"histogram\",\"esTypes\":[\"histogram\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.duration.sum.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.duration.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.experience.cls\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.experience.fid\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.experience.longtask.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.experience.longtask.max\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.experience.longtask.sum\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.experience.tbt\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.agent.domComplete\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.agent.domInteractive\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.agent.firstContentfulPaint\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.agent.largestContentfulPaint\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.agent.timeToFirstByte\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.connectEnd\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.connectStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domComplete\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domContentLoadedEventEnd\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domContentLoadedEventStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domInteractive\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domLoading\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domainLookupEnd\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.domainLookupStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.fetchStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.loadEventEnd\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.loadEventStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.requestStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.responseEnd\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.marks.navigationTiming.responseStart\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.message.age.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.message.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"transaction.name\"}}},{\"name\":\"transaction.result\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.root\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.sampled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.self_time.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.self_time.sum.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.span_count.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"transaction.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.extension\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.fragment\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.original.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"url.original\"}}},{\"name\":\"url.password\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.query\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.scheme\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.subdomain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.top_level_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.username\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.full_name.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"user.full_name\"}}},{\"name\":\"user.group.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.roles\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.device.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.original.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"user_agent.original\"}}},{\"name\":\"user_agent.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vlan.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vlan.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.category\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.classification\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.description\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.description.text\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"vulnerability.description\"}}},{\"name\":\"vulnerability.enumeration\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.reference\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.report_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.scanner.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.score.base\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.score.environmental\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.score.temporal\",\"type\":\"number\",\"esTypes\":[\"float\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.score.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vulnerability.severity\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.alternative_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.issuer.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.not_after\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.not_before\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.public_key_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.public_key_curve\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.public_key_exponent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":false,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.public_key_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.signature_algorithm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.common_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.country\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.distinguished_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.locality\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.organization\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.organizational_unit\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.subject.state_or_province\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"x509.version_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", + "timeFieldName": "@timestamp" + }, + "id": "apm-*", + "type": "index-pattern", + "version": "1" +} 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 new file mode 100644 index 00000000000000..85d48ef638d448 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts @@ -0,0 +1,52 @@ +/* + * 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 { ReportViewTypes } from '../types'; +import { getPerformanceDistLensConfig } from './performance_dist_config'; +import { getMonitorDurationConfig } from './monitor_duration_config'; +import { getServiceLatencyLensConfig } from './service_latency_config'; +import { getMonitorPingsConfig } from './monitor_pings_config'; +import { getServiceThroughputLensConfig } from './service_throughput_config'; +import { getKPITrendsLensConfig } from './kpi_trends_config'; +import { getCPUUsageLensConfig } from './cpu_usage_config'; +import { getMemoryUsageLensConfig } from './memory_usage_config'; +import { getNetworkActivityLensConfig } from './network_activity_config'; +import { getLogsFrequencyLensConfig } from './logs_frequency_config'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; + +interface Props { + reportType: keyof typeof ReportViewTypes; + seriesId: string; + indexPattern: IIndexPattern; +} + +export const getDefaultConfigs = ({ reportType, seriesId, indexPattern }: Props) => { + switch (ReportViewTypes[reportType]) { + case 'page-load-dist': + return getPerformanceDistLensConfig({ seriesId, indexPattern }); + case 'kpi-trends': + return getKPITrendsLensConfig({ seriesId, indexPattern }); + case 'uptime-duration': + return getMonitorDurationConfig({ seriesId }); + case 'uptime-pings': + return getMonitorPingsConfig({ seriesId }); + case 'service-latency': + return getServiceLatencyLensConfig({ seriesId, indexPattern }); + case 'service-throughput': + return getServiceThroughputLensConfig({ seriesId, indexPattern }); + case 'cpu-usage': + return getCPUUsageLensConfig({ seriesId }); + case 'memory-usage': + return getMemoryUsageLensConfig({ seriesId }); + case 'network-activity': + return getNetworkActivityLensConfig({ seriesId }); + case 'logs-frequency': + return getLogsFrequencyLensConfig({ seriesId }); + default: + return getKPITrendsLensConfig({ seriesId, indexPattern }); + } +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/kpi_trends_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/kpi_trends_config.ts new file mode 100644 index 00000000000000..a967a8824bca7b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/kpi_trends_config.ts @@ -0,0 +1,73 @@ +/* + * 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 } from './constants'; +import { buildPhraseFilter } from './utils'; +import { + CLIENT_GEO_COUNTRY_NAME, + PROCESSOR_EVENT, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_TYPE, + USER_AGENT_DEVICE, + USER_AGENT_NAME, + USER_AGENT_OS, + USER_AGENT_VERSION, +} from './data/elasticsearch_fieldnames'; + +export function getKPITrendsLensConfig({ seriesId, indexPattern }: ConfigProps): DataSeries { + return { + id: seriesId, + defaultSeriesType: 'bar_stacked', + reportType: 'kpi-trends', + seriesTypes: ['bar', 'bar_stacked'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'count', + label: 'Page views', + }, + hasMetricType: false, + defaultFilters: [ + USER_AGENT_OS, + CLIENT_GEO_COUNTRY_NAME, + USER_AGENT_DEVICE, + { + field: USER_AGENT_NAME, + nested: USER_AGENT_VERSION, + }, + ], + breakdowns: [USER_AGENT_NAME, USER_AGENT_OS, CLIENT_GEO_COUNTRY_NAME, USER_AGENT_DEVICE], + filters: [ + buildPhraseFilter(TRANSACTION_TYPE, 'page-load', indexPattern), + buildPhraseFilter(PROCESSOR_EVENT, 'transaction', indexPattern), + ], + labels: { ...FieldLabels, SERVICE_NAME: 'Web Application' }, + reportDefinitions: [ + { + field: SERVICE_NAME, + required: true, + }, + { + field: SERVICE_ENVIRONMENT, + }, + { + field: 'Business.KPI', + custom: true, + defaultValue: 'Records', + options: [ + { + field: 'Records', + label: 'Page views', + }, + ], + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts new file mode 100644 index 00000000000000..dcfaed938cc0f7 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts @@ -0,0 +1,387 @@ +/* + * 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 { LensAttributes } from './lens_attributes'; +import { mockIndexPattern } from '../rtl_helpers'; +import { getDefaultConfigs } from './default_configs'; +import { sampleAttribute } from './data/sample_attribute'; +import { LCP_FIELD, SERVICE_NAME } from './data/elasticsearch_fieldnames'; +import { USER_AGENT_NAME } from './data/elasticsearch_fieldnames'; + +describe('Lens Attribute', () => { + const reportViewConfig = getDefaultConfigs({ + reportType: 'pld', + indexPattern: mockIndexPattern, + seriesId: 'series-id', + }); + + let lnsAttr: LensAttributes; + + beforeEach(() => { + lnsAttr = new LensAttributes(mockIndexPattern, reportViewConfig, 'line', [], 'count', {}); + }); + + it('should return expected json', function () { + expect(lnsAttr.getJSON()).toEqual(sampleAttribute); + }); + + it('should return main y axis', function () { + expect(lnsAttr.getMainYAxis()).toEqual({ + dataType: 'number', + isBucketed: false, + label: 'Pages loaded', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }); + }); + + it('should return expected field type', function () { + expect(JSON.stringify(lnsAttr.getFieldMeta('transaction.type'))).toEqual( + JSON.stringify({ + count: 0, + name: 'transaction.type', + type: 'string', + esTypes: ['keyword'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }) + ); + }); + + it('should return expected field type for custom field with default value', function () { + expect(JSON.stringify(lnsAttr.getFieldMeta('performance.metric'))).toEqual( + JSON.stringify({ + count: 0, + name: 'transaction.duration.us', + type: 'number', + esTypes: ['long'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }) + ); + }); + + it('should return expected field type for custom field with passed value', function () { + lnsAttr = new LensAttributes(mockIndexPattern, reportViewConfig, 'line', [], 'count', { + 'performance.metric': LCP_FIELD, + }); + + expect(JSON.stringify(lnsAttr.getFieldMeta('performance.metric'))).toEqual( + JSON.stringify({ + count: 0, + name: LCP_FIELD, + type: 'number', + esTypes: ['scaled_float'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }) + ); + }); + + it('should return expected number column', function () { + expect(lnsAttr.getNumberColumn('transaction.duration.us')).toEqual({ + dataType: 'number', + isBucketed: true, + label: 'Page load time', + operationType: 'range', + params: { + maxBars: 'auto', + ranges: [ + { + from: 0, + label: '', + to: 1000, + }, + ], + type: 'histogram', + }, + scale: 'interval', + sourceField: 'transaction.duration.us', + }); + }); + + it('should return expected date histogram column', function () { + expect(lnsAttr.getDateHistogramColumn('@timestamp')).toEqual({ + dataType: 'date', + isBucketed: true, + label: '@timestamp', + operationType: 'date_histogram', + params: { + interval: 'auto', + }, + scale: 'interval', + sourceField: '@timestamp', + }); + }); + + it('should return main x axis', function () { + expect(lnsAttr.getXAxis()).toEqual({ + dataType: 'number', + isBucketed: true, + label: 'Page load time', + operationType: 'range', + params: { + maxBars: 'auto', + ranges: [ + { + from: 0, + label: '', + to: 1000, + }, + ], + type: 'histogram', + }, + scale: 'interval', + sourceField: 'transaction.duration.us', + }); + }); + + it('should return first layer', function () { + expect(lnsAttr.getLayer()).toEqual({ + columnOrder: ['x-axis-column', 'y-axis-column'], + columns: { + 'x-axis-column': { + dataType: 'number', + isBucketed: true, + label: 'Page load time', + operationType: 'range', + params: { + maxBars: 'auto', + ranges: [ + { + from: 0, + label: '', + to: 1000, + }, + ], + type: 'histogram', + }, + scale: 'interval', + sourceField: 'transaction.duration.us', + }, + 'y-axis-column': { + dataType: 'number', + isBucketed: false, + label: 'Pages loaded', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + }, + incompleteColumns: {}, + }); + }); + + it('should return expected XYState', function () { + expect(lnsAttr.getXyState()).toEqual({ + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + curveType: 'CURVE_MONOTONE_X', + fittingFunction: 'Linear', + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + layers: [ + { + accessors: ['y-axis-column'], + layerId: 'layer1', + palette: undefined, + seriesType: 'line', + xAccessor: 'x-axis-column', + yConfig: [{ color: 'green', forAccessor: 'y-axis-column' }], + }, + ], + legend: { isVisible: true, position: 'right' }, + preferredSeriesType: 'line', + tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true }, + valueLabels: 'hide', + }); + }); + + describe('ParseFilters function', function () { + it('should parse default filters', function () { + expect(lnsAttr.parseFilters()).toEqual([ + { meta: { index: 'apm-*' }, query: { match_phrase: { 'transaction.type': 'page-load' } } }, + { meta: { index: 'apm-*' }, query: { match_phrase: { 'processor.event': 'transaction' } } }, + ]); + }); + + it('should parse default and ui filters', function () { + lnsAttr = new LensAttributes( + mockIndexPattern, + reportViewConfig, + 'line', + [ + { field: SERVICE_NAME, values: ['elastic-co', 'kibana-front'] }, + { field: USER_AGENT_NAME, values: ['Firefox'], notValues: ['Chrome'] }, + ], + 'count', + {} + ); + + expect(lnsAttr.parseFilters()).toEqual([ + { meta: { index: 'apm-*' }, query: { match_phrase: { 'transaction.type': 'page-load' } } }, + { meta: { index: 'apm-*' }, query: { match_phrase: { 'processor.event': 'transaction' } } }, + { + meta: { + index: 'apm-*', + key: 'service.name', + params: ['elastic-co', 'kibana-front'], + type: 'phrases', + value: 'elastic-co, kibana-front', + }, + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + 'service.name': 'elastic-co', + }, + }, + { + match_phrase: { + 'service.name': 'kibana-front', + }, + }, + ], + }, + }, + }, + { + meta: { + index: 'apm-*', + }, + query: { + match_phrase: { + 'user_agent.name': 'Firefox', + }, + }, + }, + { + meta: { + index: 'apm-*', + negate: true, + }, + query: { + match_phrase: { + 'user_agent.name': 'Chrome', + }, + }, + }, + ]); + }); + }); + + describe('Layer breakdowns', function () { + it('should add breakdown column', function () { + lnsAttr.addBreakdown(USER_AGENT_NAME); + + expect(lnsAttr.visualization.layers).toEqual([ + { + accessors: ['y-axis-column'], + layerId: 'layer1', + palette: undefined, + seriesType: 'line', + splitAccessor: 'break-down-column', + xAccessor: 'x-axis-column', + yConfig: [{ color: 'green', forAccessor: 'y-axis-column' }], + }, + ]); + + expect(lnsAttr.layers.layer1).toEqual({ + columnOrder: ['x-axis-column', 'break-down-column', 'y-axis-column'], + columns: { + 'break-down-column': { + dataType: 'string', + isBucketed: true, + label: 'Top values of Browser family', + operationType: 'terms', + params: { + missingBucket: false, + orderBy: { columnId: 'y-axis-column', type: 'column' }, + orderDirection: 'desc', + otherBucket: true, + size: 3, + }, + scale: 'ordinal', + sourceField: 'user_agent.name', + }, + 'x-axis-column': { + dataType: 'number', + isBucketed: true, + label: 'Page load time', + operationType: 'range', + params: { + maxBars: 'auto', + ranges: [{ from: 0, label: '', to: 1000 }], + type: 'histogram', + }, + scale: 'interval', + sourceField: 'transaction.duration.us', + }, + 'y-axis-column': { + dataType: 'number', + isBucketed: false, + label: 'Pages loaded', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + }, + incompleteColumns: {}, + }); + }); + + it('should remove breakdown column', function () { + lnsAttr.addBreakdown(USER_AGENT_NAME); + + lnsAttr.removeBreakdown(); + + expect(lnsAttr.visualization.layers).toEqual([ + { + accessors: ['y-axis-column'], + layerId: 'layer1', + palette: undefined, + seriesType: 'line', + xAccessor: 'x-axis-column', + yConfig: [{ color: 'green', forAccessor: 'y-axis-column' }], + }, + ]); + + expect(lnsAttr.layers.layer1.columnOrder).toEqual(['x-axis-column', 'y-axis-column']); + + expect(lnsAttr.layers.layer1.columns).toEqual({ + 'x-axis-column': { + dataType: 'number', + isBucketed: true, + label: 'Page load time', + operationType: 'range', + params: { + maxBars: 'auto', + ranges: [{ from: 0, label: '', to: 1000 }], + type: 'histogram', + }, + scale: 'interval', + sourceField: 'transaction.duration.us', + }, + 'y-axis-column': { + dataType: 'number', + isBucketed: false, + label: 'Pages loaded', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + }, + }); + }); + }); +}); 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 new file mode 100644 index 00000000000000..589a93d1600686 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -0,0 +1,273 @@ +/* + * 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 { + CountIndexPatternColumn, + DateHistogramIndexPatternColumn, + LastValueIndexPatternColumn, + OperationType, + PersistedIndexPatternLayer, + RangeIndexPatternColumn, + SeriesType, + TypedLensByValueInput, + XYState, + XYCurveType, + DataType, +} from '../../../../../../lens/public'; +import { + buildPhraseFilter, + buildPhrasesFilter, + IndexPattern, +} from '../../../../../../../../src/plugins/data/common'; +import { FieldLabels } from './constants'; +import { DataSeries, UrlFilter } from '../types'; + +function getLayerReferenceName(layerId: string) { + return `indexpattern-datasource-layer-${layerId}`; +} + +export class LensAttributes { + indexPattern: IndexPattern; + layers: Record; + visualization: XYState; + filters: UrlFilter[]; + seriesType: SeriesType; + reportViewConfig: DataSeries; + reportDefinitions: Record; + + constructor( + indexPattern: IndexPattern, + reportViewConfig: DataSeries, + seriesType?: SeriesType, + filters?: UrlFilter[], + metricType?: OperationType, + reportDefinitions?: Record + ) { + this.indexPattern = indexPattern; + this.layers = {}; + this.filters = filters ?? []; + this.reportDefinitions = reportDefinitions ?? {}; + + if (typeof reportViewConfig.yAxisColumn.operationType !== undefined && metricType) { + reportViewConfig.yAxisColumn.operationType = metricType; + } + this.seriesType = seriesType ?? reportViewConfig.defaultSeriesType; + this.reportViewConfig = reportViewConfig; + this.layers.layer1 = this.getLayer(); + this.visualization = this.getXyState(); + } + + addBreakdown(sourceField: string) { + const fieldMeta = this.indexPattern.getFieldByName(sourceField); + + this.layers.layer1.columns['break-down-column'] = { + sourceField, + label: `Top values of ${FieldLabels[sourceField]}`, + dataType: fieldMeta?.type as DataType, + operationType: 'terms', + scale: 'ordinal', + isBucketed: true, + params: { + size: 3, + orderBy: { type: 'column', columnId: 'y-axis-column' }, + orderDirection: 'desc', + otherBucket: true, + missingBucket: false, + }, + }; + + this.layers.layer1.columnOrder = ['x-axis-column', 'break-down-column', 'y-axis-column']; + + this.visualization.layers[0].splitAccessor = 'break-down-column'; + } + + removeBreakdown() { + delete this.layers.layer1.columns['break-down-column']; + + this.layers.layer1.columnOrder = ['x-axis-column', 'y-axis-column']; + + this.visualization.layers[0].splitAccessor = undefined; + } + + getNumberColumn(sourceField: string): RangeIndexPatternColumn { + return { + sourceField, + label: this.reportViewConfig.labels[sourceField], + dataType: 'number', + operationType: 'range', + isBucketed: true, + scale: 'interval', + params: { + type: 'histogram', + ranges: [{ from: 0, to: 1000, label: '' }], + maxBars: 'auto', + }, + }; + } + + getDateHistogramColumn(sourceField: string): DateHistogramIndexPatternColumn { + return { + sourceField, + dataType: 'date', + isBucketed: true, + label: '@timestamp', + operationType: 'date_histogram', + params: { interval: 'auto' }, + scale: 'interval', + }; + } + + getXAxis(): + | LastValueIndexPatternColumn + | DateHistogramIndexPatternColumn + | RangeIndexPatternColumn { + const { xAxisColumn } = this.reportViewConfig; + + const { type: fieldType, name: fieldName } = this.getFieldMeta(xAxisColumn.sourceField)!; + + if (fieldType === 'date') { + return this.getDateHistogramColumn(fieldName); + } + if (fieldType === 'number') { + return this.getNumberColumn(fieldName); + } + + // FIXME review my approach again + return this.getDateHistogramColumn(fieldName); + } + + getFieldMeta(sourceField?: string) { + let xAxisField = sourceField; + + if (xAxisField) { + const rdf = this.reportViewConfig.reportDefinitions ?? []; + + const customField = rdf.find(({ field }) => field === xAxisField); + + if (customField) { + if (this.reportDefinitions[xAxisField]) { + xAxisField = this.reportDefinitions[xAxisField]; + } else if (customField.defaultValue) { + xAxisField = customField.defaultValue; + } else if (customField.options?.[0].field) { + xAxisField = customField.options?.[0].field; + } + } + + return this.indexPattern.getFieldByName(xAxisField); + } + } + + getMainYAxis() { + return { + dataType: 'number', + isBucketed: false, + label: 'Count of records', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', + ...this.reportViewConfig.yAxisColumn, + } as CountIndexPatternColumn; + } + + getLayer() { + return { + columnOrder: ['x-axis-column', 'y-axis-column'], + columns: { + 'x-axis-column': this.getXAxis(), + 'y-axis-column': this.getMainYAxis(), + }, + incompleteColumns: {}, + }; + } + + getXyState(): XYState { + return { + legend: { isVisible: true, position: 'right' }, + valueLabels: 'hide', + fittingFunction: 'Linear', + curveType: 'CURVE_MONOTONE_X' as XYCurveType, + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + preferredSeriesType: 'line', + layers: [ + { + accessors: ['y-axis-column'], + layerId: 'layer1', + seriesType: this.seriesType ?? 'line', + palette: this.reportViewConfig.palette, + yConfig: [{ forAccessor: 'y-axis-column', color: 'green' }], + xAccessor: 'x-axis-column', + }, + ], + }; + } + + parseFilters() { + const defaultFilters = this.reportViewConfig.filters ?? []; + const parsedFilters = this.reportViewConfig.filters ? [...defaultFilters] : []; + + this.filters.forEach(({ field, values = [], notValues = [] }) => { + const fieldMeta = this.indexPattern.fields.find((fieldT) => fieldT.name === field)!; + + if (values?.length > 0) { + if (values?.length > 1) { + const multiFilter = buildPhrasesFilter(fieldMeta, values, this.indexPattern); + parsedFilters.push(multiFilter); + } else { + const filter = buildPhraseFilter(fieldMeta, values[0], this.indexPattern); + parsedFilters.push(filter); + } + } + + if (notValues?.length > 0) { + if (notValues?.length > 1) { + const multiFilter = buildPhrasesFilter(fieldMeta, notValues, this.indexPattern); + multiFilter.meta.negate = true; + parsedFilters.push(multiFilter); + } else { + const filter = buildPhraseFilter(fieldMeta, notValues[0], this.indexPattern); + filter.meta.negate = true; + parsedFilters.push(filter); + } + } + }); + + return parsedFilters; + } + + getJSON(): TypedLensByValueInput['attributes'] { + return { + title: 'Prefilled from exploratory view app', + description: '', + visualizationType: 'lnsXY', + references: [ + { + id: this.indexPattern.id!, + name: 'indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: this.indexPattern.id!, + name: getLayerReferenceName('layer1'), + type: 'index-pattern', + }, + ], + state: { + datasourceStates: { + indexpattern: { + layers: this.layers, + }, + }, + visualization: this.visualization, + query: { query: '', language: 'kuery' }, + filters: this.parseFilters(), + }, + }; + } +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/logs_frequency_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/logs_frequency_config.ts new file mode 100644 index 00000000000000..68e5e697d2f9d1 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/logs_frequency_config.ts @@ -0,0 +1,39 @@ +/* + * 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 { DataSeries } from '../types'; +import { FieldLabels } from './constants'; + +interface Props { + seriesId: string; +} + +export function getLogsFrequencyLensConfig({ seriesId }: Props): DataSeries { + return { + id: seriesId, + reportType: 'logs-frequency', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'count', + }, + hasMetricType: false, + defaultFilters: [], + breakdowns: ['agent.hostname'], + filters: [], + labels: { ...FieldLabels }, + reportDefinitions: [ + { + field: 'agent.hostname', + required: true, + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/memory_usage_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/memory_usage_config.ts new file mode 100644 index 00000000000000..579372ed86fa74 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/memory_usage_config.ts @@ -0,0 +1,42 @@ +/* + * 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 { DataSeries } from '../types'; +import { FieldLabels } from './constants'; +import { OperationType } from '../../../../../../lens/public'; + +interface Props { + seriesId: string; +} + +export function getMemoryUsageLensConfig({ seriesId }: Props): DataSeries { + return { + id: seriesId, + reportType: 'memory-usage', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'avg' as OperationType, + sourceField: 'system.memory.used.pct', + label: 'Memory Usage %', + }, + hasMetricType: true, + defaultFilters: [], + breakdowns: ['host.hostname'], + filters: [], + labels: { ...FieldLabels, 'host.hostname': 'Host name' }, + reportDefinitions: [ + { + field: 'host.hostname', + required: true, + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_duration_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_duration_config.ts new file mode 100644 index 00000000000000..aa9b8b94c6d862 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_duration_config.ts @@ -0,0 +1,48 @@ +/* + * 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 { DataSeries } from '../types'; +import { FieldLabels } from './constants'; +import { OperationType } from '../../../../../../lens/public'; + +interface Props { + seriesId: string; +} + +export function getMonitorDurationConfig({ seriesId }: Props): DataSeries { + return { + id: seriesId, + reportType: 'uptime-duration', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar_stacked'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'avg' as OperationType, + sourceField: 'monitor.duration.us', + label: 'Monitor duration (ms)', + }, + hasMetricType: true, + defaultFilters: ['monitor.type', 'observer.geo.name', 'tags'], + breakdowns: [ + 'observer.geo.name', + 'monitor.name', + 'monitor.id', + 'monitor.type', + 'tags', + 'url.port', + ], + filters: [], + reportDefinitions: [ + { + field: 'monitor.id', + }, + ], + labels: { ...FieldLabels }, + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_pings_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_pings_config.ts new file mode 100644 index 00000000000000..72968626e934bf --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/monitor_pings_config.ts @@ -0,0 +1,43 @@ +/* + * 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 { DataSeries } from '../types'; +import { FieldLabels } from './constants'; + +interface Props { + seriesId: string; +} + +export function getMonitorPingsConfig({ seriesId }: Props): DataSeries { + return { + id: seriesId, + reportType: 'uptime-pings', + defaultSeriesType: 'bar_stacked', + seriesTypes: ['bar_stacked', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'count', + label: 'Monitor pings', + }, + hasMetricType: false, + defaultFilters: ['observer.geo.name'], + breakdowns: ['monitor.status', 'observer.geo.name', 'monitor.type'], + filters: [], + palette: { type: 'palette', name: 'status' }, + reportDefinitions: [ + { + field: 'monitor.id', + }, + { + field: 'url.full', + }, + ], + labels: { ...FieldLabels }, + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/network_activity_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/network_activity_config.ts new file mode 100644 index 00000000000000..63cdd0ec8bd605 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/network_activity_config.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataSeries } from '../types'; +import { FieldLabels } from './constants'; +import { OperationType } from '../../../../../../lens/public'; + +interface Props { + seriesId: string; +} + +export function getNetworkActivityLensConfig({ seriesId }: Props): DataSeries { + return { + id: seriesId, + reportType: 'network-activity', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'avg' as OperationType, + sourceField: 'system.memory.used.pct', + }, + hasMetricType: true, + defaultFilters: [], + breakdowns: ['host.hostname'], + filters: [], + labels: { ...FieldLabels, 'host.hostname': 'Host name' }, + reportDefinitions: [ + { + field: 'host.hostname', + required: true, + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/performance_dist_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/performance_dist_config.ts new file mode 100644 index 00000000000000..41617304c9f3df --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/performance_dist_config.ts @@ -0,0 +1,86 @@ +/* + * 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 } from './constants'; +import { buildPhraseFilter } from './utils'; +import { + CLIENT_GEO_COUNTRY_NAME, + CLS_FIELD, + FCP_FIELD, + FID_FIELD, + LCP_FIELD, + PROCESSOR_EVENT, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TBT_FIELD, + TRANSACTION_DURATION, + TRANSACTION_TYPE, + USER_AGENT_DEVICE, + USER_AGENT_NAME, + USER_AGENT_OS, + USER_AGENT_VERSION, +} from './data/elasticsearch_fieldnames'; + +export function getPerformanceDistLensConfig({ seriesId, indexPattern }: ConfigProps): DataSeries { + return { + id: seriesId ?? 'unique-key', + reportType: 'page-load-dist', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: 'performance.metric', + }, + yAxisColumn: { + operationType: 'count', + label: 'Pages loaded', + }, + hasMetricType: false, + defaultFilters: [ + USER_AGENT_OS, + CLIENT_GEO_COUNTRY_NAME, + USER_AGENT_DEVICE, + { + field: USER_AGENT_NAME, + nested: USER_AGENT_VERSION, + }, + ], + breakdowns: [USER_AGENT_NAME, USER_AGENT_OS, CLIENT_GEO_COUNTRY_NAME, USER_AGENT_DEVICE], + reportDefinitions: [ + { + field: SERVICE_NAME, + required: true, + }, + { + field: SERVICE_ENVIRONMENT, + }, + { + field: 'performance.metric', + custom: true, + defaultValue: TRANSACTION_DURATION, + options: [ + { label: 'Page load time', field: TRANSACTION_DURATION }, + { label: 'First contentful paint', field: FCP_FIELD }, + { label: 'Total blocking time', field: TBT_FIELD }, + // FIXME, review if we need these descriptions + { label: 'Largest contentful paint', field: LCP_FIELD, description: 'Core web vital' }, + { label: 'First input delay', field: FID_FIELD, description: 'Core web vital' }, + { label: 'Cumulative layout shift', field: CLS_FIELD, description: 'Core web vital' }, + ], + }, + ], + filters: [ + buildPhraseFilter(TRANSACTION_TYPE, 'page-load', indexPattern), + buildPhraseFilter(PROCESSOR_EVENT, 'transaction', indexPattern), + ], + labels: { + ...FieldLabels, + [SERVICE_NAME]: 'Web Application', + [TRANSACTION_DURATION]: 'Page load time', + }, + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_latency_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_latency_config.ts new file mode 100644 index 00000000000000..a31679c61a4aba --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_latency_config.ts @@ -0,0 +1,52 @@ +/* + * 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 } from './constants'; +import { buildPhraseFilter } from './utils'; +import { OperationType } from '../../../../../../lens/public'; + +export function getServiceLatencyLensConfig({ seriesId, indexPattern }: ConfigProps): DataSeries { + return { + id: seriesId, + reportType: 'service-latency', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'avg' as OperationType, + sourceField: 'transaction.duration.us', + label: 'Latency', + }, + hasMetricType: true, + defaultFilters: [ + 'user_agent.name', + 'user_agent.os.name', + 'client.geo.country_name', + 'user_agent.device.name', + ], + breakdowns: [ + 'user_agent.name', + 'user_agent.os.name', + 'client.geo.country_name', + 'user_agent.device.name', + ], + filters: [buildPhraseFilter('transaction.type', 'request', indexPattern)], + labels: { ...FieldLabels }, + reportDefinitions: [ + { + field: 'service.name', + required: true, + }, + { + field: 'service.environment', + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_throughput_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_throughput_config.ts new file mode 100644 index 00000000000000..32cae2167ddf02 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/service_throughput_config.ts @@ -0,0 +1,55 @@ +/* + * 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 } from './constants'; +import { buildPhraseFilter } from './utils'; +import { OperationType } from '../../../../../../lens/public'; + +export function getServiceThroughputLensConfig({ + seriesId, + indexPattern, +}: ConfigProps): DataSeries { + return { + id: seriesId, + reportType: 'service-latency', + defaultSeriesType: 'line', + seriesTypes: ['line', 'bar'], + xAxisColumn: { + sourceField: '@timestamp', + }, + yAxisColumn: { + operationType: 'avg' as OperationType, + sourceField: 'transaction.duration.us', + label: 'Throughput', + }, + hasMetricType: true, + defaultFilters: [ + 'user_agent.name', + 'user_agent.os.name', + 'client.geo.country_name', + 'user_agent.device.name', + ], + breakdowns: [ + 'user_agent.name', + 'user_agent.os.name', + 'client.geo.country_name', + 'user_agent.device.name', + ], + filters: [buildPhraseFilter('transaction.type', 'request', indexPattern)], + labels: { ...FieldLabels }, + reportDefinitions: [ + { + field: 'service.name', + required: true, + }, + { + field: 'service.environment', + }, + ], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/url_constants.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/url_constants.ts new file mode 100644 index 00000000000000..5b99c19dbabb7d --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/url_constants.ts @@ -0,0 +1,15 @@ +/* + * 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. + */ + +export enum URL_KEYS { + METRIC_TYPE = 'mt', + REPORT_TYPE = 'rt', + SERIES_TYPE = 'st', + BREAK_DOWN = 'bd', + FILTERS = 'ft', + REPORT_DEFINITIONS = 'rdf', +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts new file mode 100644 index 00000000000000..38b8ce81b2acd6 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts @@ -0,0 +1,54 @@ +/* + * 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 rison, { RisonValue } from 'rison-node'; +import type { AllSeries, AllShortSeries } from '../hooks/use_url_strorage'; +import type { SeriesUrl } from '../types'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; +import { esFilters } from '../../../../../../../../src/plugins/data/public'; +import { URL_KEYS } from './url_constants'; + +export function convertToShortUrl(series: SeriesUrl) { + const { + metric, + seriesType, + reportType, + breakdown, + filters, + reportDefinitions, + ...restSeries + } = series; + + return { + [URL_KEYS.METRIC_TYPE]: metric, + [URL_KEYS.REPORT_TYPE]: reportType, + [URL_KEYS.SERIES_TYPE]: seriesType, + [URL_KEYS.BREAK_DOWN]: breakdown, + [URL_KEYS.FILTERS]: filters, + [URL_KEYS.REPORT_DEFINITIONS]: reportDefinitions, + ...restSeries, + }; +} + +export function createExploratoryViewUrl(allSeries: AllSeries, baseHref = '') { + const allSeriesIds = Object.keys(allSeries); + + const allShortSeries: AllShortSeries = {}; + + allSeriesIds.forEach((seriesKey) => { + allShortSeries[seriesKey] = convertToShortUrl(allSeries[seriesKey]); + }); + + return ( + baseHref + + `/app/observability/exploratory-view#?sr=${rison.encode(allShortSeries as RisonValue)}` + ); +} + +export function buildPhraseFilter(field: string, value: any, indexPattern: IIndexPattern) { + const fieldMeta = indexPattern.fields.find((fieldT) => fieldT.name === field)!; + return esFilters.buildPhraseFilter(fieldMeta, value, indexPattern); +} 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 new file mode 100644 index 00000000000000..7e99874f557b30 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -0,0 +1,93 @@ +/* + * 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 { within } from '@testing-library/react'; +import { fireEvent, screen, waitFor } from '@testing-library/dom'; +import { render, mockUrlStorage, mockCore } from './rtl_helpers'; +import { ExploratoryView } from './exploratory_view'; +import { getStubIndexPattern } from '../../../../../../../src/plugins/data/public/test_utils'; +import * as obsvInd from '../../../utils/observability_index_patterns'; + +describe('ExploratoryView', () => { + beforeEach(() => { + const indexPattern = getStubIndexPattern( + 'apm-*', + () => {}, + '@timestamp', + [ + { + name: '@timestamp', + type: 'date', + esTypes: ['date'], + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + ], + mockCore() as any + ); + + jest.spyOn(obsvInd, 'ObservabilityIndexPatterns').mockReturnValue({ + getIndexPattern: jest.fn().mockReturnValue(indexPattern), + } as any); + }); + + it('renders exploratory view', async () => { + render(); + + await waitFor(() => { + screen.getByText(/open in lens/i); + screen.getByRole('heading', { name: /exploratory view/i }); + screen.getByRole('img', { name: /visulization/i }); + screen.getByText(/add series/i); + screen.getByText(/no series found, please add a series\./i); + }); + }); + + it('can add, cancel new series', async () => { + render(); + + await fireEvent.click(screen.getByText(/add series/i)); + + await waitFor(() => { + screen.getByText(/open in lens/i); + screen.getByText(/select a data type to start building a series\./i); + screen.getByRole('table', { name: /this table contains 1 rows\./i }); + const button = screen.getByRole('button', { name: /add/i }); + within(button).getByText(/add/i); + }); + + await fireEvent.click(screen.getByText(/cancel/i)); + + await waitFor(() => { + screen.getByText(/add series/i); + }); + }); + + it('renders lens component when there is series', async () => { + mockUrlStorage({ + data: { + 'uptime-pings-histogram': { + reportType: 'upp', + breakdown: 'monitor.status', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + render(); + + await waitFor(() => { + screen.getByText(/open in lens/i); + screen.getByRole('heading', { name: /uptime pings/i }); + screen.getByText(/uptime-pings-histogram/i); + screen.getByText(/Lens Embeddable Component/i); + screen.getByRole('table', { name: /this table contains 1 rows\./i }); + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx new file mode 100644 index 00000000000000..b3ad107bbe0e2f --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx @@ -0,0 +1,87 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import { EuiLoadingSpinner, EuiPanel, EuiTitle } from '@elastic/eui'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../plugin'; +import { ExploratoryViewHeader } from './header/header'; +import { SeriesEditor } from './series_editor/series_editor'; +import { useUrlStorage } from './hooks/use_url_strorage'; +import { useLensAttributes } from './hooks/use_lens_attributes'; +import { EmptyView } from './components/empty_view'; +import { useIndexPatternContext } from './hooks/use_default_index_pattern'; +import { TypedLensByValueInput } from '../../../../../lens/public'; + +export function ExploratoryView() { + const { + services: { lens }, + } = useKibana(); + + const [lensAttributes, setLensAttributes] = useState( + null + ); + + const { indexPattern } = useIndexPatternContext(); + + const LensComponent = lens?.EmbeddableComponent; + + const { firstSeriesId: seriesId, firstSeries: series } = useUrlStorage(); + + const lensAttributesT = useLensAttributes({ + seriesId, + indexPattern, + }); + + useEffect(() => { + setLensAttributes(lensAttributesT); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(lensAttributesT ?? {}), series?.reportType, series?.time?.from]); + + return ( + + {lens ? ( + <> + + {!indexPattern && ( + + + + )} + {lensAttributes && seriesId && series?.reportType && series?.time ? ( + + ) : ( + + )} + + + ) : ( + +

+ {i18n.translate('xpack.observability.overview.exploratoryView.lensDisabled', { + defaultMessage: + 'Lens app is not available, please enable Lens to use exploratory view.', + })} +

+
+ )} +
+ ); +} + +const SpinnerWrap = styled.div` + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx new file mode 100644 index 00000000000000..de6912f256be7c --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx @@ -0,0 +1,53 @@ +/* + * 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 { mockUrlStorage, render } from '../rtl_helpers'; +import { ExploratoryViewHeader } from './header'; +import { fireEvent } from '@testing-library/dom'; + +describe('ExploratoryViewHeader', function () { + it('should render properly', function () { + const { getByText } = render( + + ); + getByText('Open in Lens'); + }); + + it('should be able to click open in lens', function () { + mockUrlStorage({ + data: { + 'uptime-pings-histogram': { + reportType: 'upp', + breakdown: 'monitor.status', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + const { getByText, core } = render( + + ); + fireEvent.click(getByText('Open in Lens')); + + expect(core?.lens?.navigateToPrefilledEditor).toHaveBeenCalledTimes(1); + expect(core?.lens?.navigateToPrefilledEditor).toHaveBeenCalledWith({ + attributes: { title: 'Performance distribution' }, + id: '', + timeRange: { + from: 'now-15m', + to: 'now', + }, + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx new file mode 100644 index 00000000000000..bda3566c766021 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx @@ -0,0 +1,63 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { TypedLensByValueInput } from '../../../../../../lens/public'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../../plugin'; +import { DataViewLabels } from '../configurations/constants'; +import { useUrlStorage } from '../hooks/use_url_strorage'; + +interface Props { + seriesId: string; + lensAttributes: TypedLensByValueInput['attributes'] | null; +} + +export function ExploratoryViewHeader({ seriesId, lensAttributes }: Props) { + const { + services: { lens }, + } = useKibana(); + + const { series } = useUrlStorage(seriesId); + + return ( + + + +

+ {DataViewLabels[series.reportType] ?? + i18n.translate('xpack.observability.expView.heading.label', { + defaultMessage: 'Exploratory view', + })} +

+
+
+ + { + if (lensAttributes) { + lens.navigateToPrefilledEditor({ + id: '', + timeRange: series.time, + attributes: lensAttributes, + }); + } + }} + > + {i18n.translate('xpack.observability.expView.heading.openInLens', { + defaultMessage: 'Open in Lens', + })} + + +
+ ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_default_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_default_index_pattern.tsx new file mode 100644 index 00000000000000..04cbb4a4ddb18d --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_default_index_pattern.tsx @@ -0,0 +1,61 @@ +/* + * 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, { createContext, useContext, Context, useState, useEffect } from 'react'; +import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import { AppDataType } from '../types'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../../plugin'; +import { ObservabilityIndexPatterns } from '../../../../utils/observability_index_patterns'; + +export interface IIndexPatternContext { + indexPattern: IndexPattern; + loadIndexPattern: (dataType: AppDataType) => void; +} + +export const IndexPatternContext = createContext>({}); + +interface ProviderProps { + indexPattern?: IndexPattern; + children: JSX.Element; +} + +export function IndexPatternContextProvider({ + children, + indexPattern: initialIndexPattern, +}: ProviderProps) { + const [indexPattern, setIndexPattern] = useState(initialIndexPattern); + + useEffect(() => { + setIndexPattern(initialIndexPattern); + }, [initialIndexPattern]); + + const { + services: { data }, + } = useKibana(); + + const loadIndexPattern = async (dataType: AppDataType) => { + const obsvIndexP = new ObservabilityIndexPatterns(data); + const indPattern = await obsvIndexP.getIndexPattern(dataType); + setIndexPattern(indPattern!); + }; + + return ( + + {children} + + ); +} + +export const useIndexPatternContext = () => { + return useContext((IndexPatternContext as unknown) as Context); +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_init_exploratory_view.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_init_exploratory_view.ts new file mode 100644 index 00000000000000..9f462790e8d37f --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_init_exploratory_view.ts @@ -0,0 +1,44 @@ +/* + * 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 { useFetcher } from '../../../..'; +import { IKbnUrlStateStorage } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../../plugin'; +import { AllShortSeries } from './use_url_strorage'; +import { ReportToDataTypeMap } from '../configurations/constants'; +import { + DataType, + ObservabilityIndexPatterns, +} from '../../../../utils/observability_index_patterns'; + +export const useInitExploratoryView = (storage: IKbnUrlStateStorage) => { + const { + services: { data }, + } = useKibana(); + + const allSeriesKey = 'sr'; + + const allSeries = storage.get(allSeriesKey) ?? {}; + + const allSeriesIds = Object.keys(allSeries); + + const firstSeriesId = allSeriesIds?.[0]; + + const firstSeries = allSeries[firstSeriesId]; + + const { data: indexPattern } = useFetcher(() => { + const obsvIndexP = new ObservabilityIndexPatterns(data); + let reportType: DataType = 'apm'; + if (firstSeries?.rt) { + reportType = ReportToDataTypeMap[firstSeries?.rt]; + } + + return obsvIndexP.getIndexPattern(reportType); + }, [firstSeries?.rt, data]); + + return indexPattern; +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts new file mode 100644 index 00000000000000..1c735009f66f9d --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts @@ -0,0 +1,88 @@ +/* + * 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 { useMemo } from 'react'; +import { TypedLensByValueInput } from '../../../../../../lens/public'; +import { LensAttributes } from '../configurations/lens_attributes'; +import { useUrlStorage } from './use_url_strorage'; +import { getDefaultConfigs } from '../configurations/default_configs'; + +import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; +import { DataSeries, SeriesUrl, UrlFilter } from '../types'; + +interface Props { + seriesId: string; + indexPattern?: IndexPattern | null; +} + +export const getFiltersFromDefs = ( + reportDefinitions: SeriesUrl['reportDefinitions'], + dataViewConfig: DataSeries +) => { + const rdfFilters = Object.entries(reportDefinitions ?? {}).map(([field, value]) => { + return { + field, + values: [value], + }; + }) as UrlFilter[]; + + // let's filter out custom fields + return rdfFilters.filter(({ field }) => { + const rdf = dataViewConfig.reportDefinitions.find(({ field: fd }) => field === fd); + return !rdf?.custom; + }); +}; + +export const useLensAttributes = ({ + seriesId, + indexPattern, +}: Props): TypedLensByValueInput['attributes'] | null => { + const { series } = useUrlStorage(seriesId); + + const { breakdown, seriesType, metric: metricType, reportType, reportDefinitions = {} } = + series ?? {}; + + return useMemo(() => { + if (!indexPattern || !reportType) { + return null; + } + + const dataViewConfig = getDefaultConfigs({ + seriesId, + reportType, + indexPattern, + }); + + const filters: UrlFilter[] = (series.filters ?? []).concat( + getFiltersFromDefs(reportDefinitions, dataViewConfig) + ); + + const lensAttributes = new LensAttributes( + indexPattern, + dataViewConfig, + seriesType, + filters, + metricType, + reportDefinitions + ); + + if (breakdown) { + lensAttributes.addBreakdown(breakdown); + } + + return lensAttributes.getJSON(); + }, [ + indexPattern, + breakdown, + seriesType, + metricType, + reportType, + reportDefinitions, + seriesId, + series.filters, + ]); +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts new file mode 100644 index 00000000000000..35247180c2ee5b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts @@ -0,0 +1,100 @@ +/* + * 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 { useUrlStorage } from './use_url_strorage'; +import { UrlFilter } from '../types'; + +export interface UpdateFilter { + field: string; + value: string; + negate?: boolean; +} + +export const useSeriesFilters = ({ seriesId }: { seriesId: string }) => { + const { series, setSeries } = useUrlStorage(seriesId); + + const filters = series.filters ?? []; + + const removeFilter = ({ field, value, negate }: UpdateFilter) => { + const filtersN = filters.map((filter) => { + if (filter.field === field) { + if (negate) { + const notValuesN = filter.notValues?.filter((val) => val !== value); + return { ...filter, notValues: notValuesN }; + } else { + const valuesN = filter.values?.filter((val) => val !== value); + return { ...filter, values: valuesN }; + } + } + + return filter; + }); + setSeries(seriesId, { ...series, filters: filtersN }); + }; + + const addFilter = ({ field, value, negate }: UpdateFilter) => { + const currFilter: UrlFilter = { field }; + if (negate) { + currFilter.notValues = [value]; + } else { + currFilter.values = [value]; + } + if (filters.length === 0) { + setSeries(seriesId, { ...series, filters: [currFilter] }); + } else { + setSeries(seriesId, { + ...series, + filters: [currFilter, ...filters.filter((ft) => ft.field !== field)], + }); + } + }; + + const updateFilter = ({ field, value, negate }: UpdateFilter) => { + const currFilter: UrlFilter | undefined = filters.find(({ field: fd }) => field === fd) ?? { + field, + }; + + const currNotValues = currFilter.notValues ?? []; + const currValues = currFilter.values ?? []; + + const notValues = currNotValues.filter((val) => val !== value); + const values = currValues.filter((val) => val !== value); + + if (negate) { + notValues.push(value); + } else { + values.push(value); + } + + currFilter.notValues = notValues.length > 0 ? notValues : undefined; + currFilter.values = values.length > 0 ? values : undefined; + + const otherFilters = filters.filter(({ field: fd }) => fd !== field); + + if (notValues.length > 0 || values.length > 0) { + setSeries(seriesId, { ...series, filters: [...otherFilters, currFilter] }); + } else { + setSeries(seriesId, { ...series, filters: otherFilters }); + } + }; + + const setFilter = ({ field, value, negate }: UpdateFilter) => { + const currFilter: UrlFilter | undefined = filters.find(({ field: fd }) => field === fd); + + if (!currFilter) { + addFilter({ field, value, negate }); + } else { + updateFilter({ field, value, negate }); + } + }; + + const invertFilter = ({ field, value, negate }: UpdateFilter) => { + updateFilter({ field, value, negate: !negate }); + }; + + return { invertFilter, setFilter, removeFilter }; +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_url_strorage.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_url_strorage.tsx new file mode 100644 index 00000000000000..d38429703b7099 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_url_strorage.tsx @@ -0,0 +1,103 @@ +/* + * 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, { createContext, useContext, Context } from 'react'; +import { IKbnUrlStateStorage } from '../../../../../../../../src/plugins/kibana_utils/public'; +import type { AppDataType, ReportViewTypeId, SeriesUrl, UrlFilter } from '../types'; +import { convertToShortUrl } from '../configurations/utils'; +import { OperationType, SeriesType } from '../../../../../../lens/public'; +import { URL_KEYS } from '../configurations/url_constants'; + +export const UrlStorageContext = createContext(null); + +interface ProviderProps { + storage: IKbnUrlStateStorage; +} + +export function UrlStorageContextProvider({ + children, + storage, +}: ProviderProps & { children: JSX.Element }) { + return {children}; +} + +function convertFromShortUrl(newValue: ShortUrlSeries): SeriesUrl { + const { mt, st, rt, bd, ft, time, rdf, ...restSeries } = newValue; + return { + metric: mt, + reportType: rt!, + seriesType: st, + breakdown: bd, + filters: ft!, + time: time!, + reportDefinitions: rdf, + ...restSeries, + }; +} + +interface ShortUrlSeries { + [URL_KEYS.METRIC_TYPE]?: OperationType; + [URL_KEYS.REPORT_TYPE]?: ReportViewTypeId; + [URL_KEYS.SERIES_TYPE]?: SeriesType; + [URL_KEYS.BREAK_DOWN]?: string; + [URL_KEYS.FILTERS]?: UrlFilter[]; + [URL_KEYS.REPORT_DEFINITIONS]?: Record; + time?: { + to: string; + from: string; + }; + dataType?: AppDataType; +} + +export type AllShortSeries = Record; +export type AllSeries = Record; + +export const NEW_SERIES_KEY = 'newSeriesKey'; + +export function useUrlStorage(seriesId?: string) { + const allSeriesKey = 'sr'; + const storage = useContext((UrlStorageContext as unknown) as Context); + let series: SeriesUrl = {} as SeriesUrl; + const allShortSeries = storage.get(allSeriesKey) ?? {}; + + const allSeriesIds = Object.keys(allShortSeries); + + const allSeries: AllSeries = {}; + + allSeriesIds.forEach((seriesKey) => { + allSeries[seriesKey] = convertFromShortUrl(allShortSeries[seriesKey]); + }); + + if (seriesId) { + series = allSeries?.[seriesId] ?? ({} as SeriesUrl); + } + + const setSeries = async (seriesIdN: string, newValue: SeriesUrl) => { + allShortSeries[seriesIdN] = convertToShortUrl(newValue); + allSeries[seriesIdN] = newValue; + return storage.set(allSeriesKey, allShortSeries); + }; + + const removeSeries = (seriesIdN: string) => { + delete allShortSeries[seriesIdN]; + delete allSeries[seriesIdN]; + storage.set(allSeriesKey, allShortSeries); + }; + + const firstSeriesId = allSeriesIds?.[0]; + + return { + storage, + setSeries, + removeSeries, + series, + firstSeriesId, + allSeries, + allSeriesIds, + firstSeries: allSeries?.[firstSeriesId], + }; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx new file mode 100644 index 00000000000000..dc47a0f075fe69 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx @@ -0,0 +1,64 @@ +/* + * 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, { useContext } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useHistory } from 'react-router-dom'; +import { ThemeContext } from 'styled-components'; +import { ExploratoryView } from './exploratory_view'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../plugin'; +import { useBreadcrumbs } from '../../../hooks/use_breadcrumbs'; +import { IndexPatternContextProvider } from './hooks/use_default_index_pattern'; +import { + createKbnUrlStateStorage, + withNotifyOnErrors, +} from '../../../../../../../src/plugins/kibana_utils/public/'; +import { UrlStorageContextProvider } from './hooks/use_url_strorage'; +import { useInitExploratoryView } from './hooks/use_init_exploratory_view'; +import { WithHeaderLayout } from '../../app/layout/with_header'; + +export function ExploratoryViewPage() { + useBreadcrumbs([ + { + text: i18n.translate('xpack.observability.overview.exploratoryView', { + defaultMessage: 'Exploratory view', + }), + }, + ]); + + const theme = useContext(ThemeContext); + + const { + services: { uiSettings, notifications }, + } = useKibana(); + + const history = useHistory(); + + const kbnUrlStateStorage = createKbnUrlStateStorage({ + history, + useHash: uiSettings!.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(notifications!.toasts), + }); + + const indexPattern = useInitExploratoryView(kbnUrlStateStorage); + + return ( + + {indexPattern ? ( + + + + + + ) : null} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx new file mode 100644 index 00000000000000..112bfcc3ccb580 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx @@ -0,0 +1,318 @@ +/* + * 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 { of } from 'rxjs'; +import React, { ReactElement } from 'react'; +import { stringify } from 'query-string'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { render as reactTestLibRender, RenderOptions } from '@testing-library/react'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory, History } from 'history'; +import { CoreStart } from 'kibana/public'; +import { I18nProvider } from '@kbn/i18n/react'; +import { coreMock } from 'src/core/public/mocks'; +import { + KibanaServices, + KibanaContextProvider, +} from '../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../plugin'; +import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/common'; +import { lensPluginMock } from '../../../../../lens/public/mocks'; +import { IndexPatternContextProvider } from './hooks/use_default_index_pattern'; +import { AllSeries, UrlStorageContextProvider } from './hooks/use_url_strorage'; +import { + withNotifyOnErrors, + createKbnUrlStateStorage, +} from '../../../../../../../src/plugins/kibana_utils/public'; +import * as fetcherHook from '../../../hooks/use_fetcher'; +import * as useUrlHook from './hooks/use_url_strorage'; +import * as useSeriesFilterHook from './hooks/use_series_filters'; +import * as useHasDataHook from '../../../hooks/use_has_data'; +import * as useValuesListHook from '../../../hooks/use_values_list'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getStubIndexPattern } from '../../../../../../../src/plugins/data/public/index_patterns/index_pattern.stub'; +import indexPatternData from './configurations/data/test_index_pattern.json'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { setIndexPatterns } from '../../../../../../../src/plugins/data/public/services'; +import { IndexPatternsContract } from '../../../../../../../src/plugins/data/common/index_patterns/index_patterns'; +import { UrlFilter } from './types'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; + +interface KibanaProps { + services?: KibanaServices; +} + +export interface KibanaProviderOptions { + core?: ExtraCore & Partial; + kibanaProps?: KibanaProps; +} + +interface MockKibanaProviderProps> + extends KibanaProviderOptions { + children: ReactElement; + history: History; +} + +type MockRouterProps> = MockKibanaProviderProps; + +type Url = + | string + | { + path: string; + queryParams: Record; + }; + +interface RenderRouterOptions extends KibanaProviderOptions { + history?: History; + renderOptions?: Omit; + url?: Url; +} + +function getSetting(key: string): T { + if (key === 'timepicker:quickRanges') { + return ([ + { + display: 'Today', + from: 'now/d', + to: 'now/d', + }, + ] as unknown) as T; + } + return ('MMM D, YYYY @ HH:mm:ss.SSS' as unknown) as T; +} + +function setSetting$(key: string): T { + return (of('MMM D, YYYY @ HH:mm:ss.SSS') as unknown) as T; +} + +/* default mock core */ +const defaultCore = coreMock.createStart(); +export const mockCore: () => Partial = () => { + const core: Partial = { + ...defaultCore, + application: { + ...defaultCore.application, + getUrlForApp: () => '/app/observability', + navigateToUrl: jest.fn(), + capabilities: { + ...defaultCore.application.capabilities, + observability: { + 'alerting:save': true, + configureSettings: true, + save: true, + show: true, + }, + }, + }, + uiSettings: { + ...defaultCore.uiSettings, + get: getSetting, + get$: setSetting$, + }, + lens: lensPluginMock.createStartContract(), + data: dataPluginMock.createStartContract(), + }; + + return core; +}; + +/* Mock Provider Components */ +export function MockKibanaProvider>({ + children, + core, + history, + kibanaProps, +}: MockKibanaProviderProps) { + const { notifications } = core!; + + const kbnUrlStateStorage = createKbnUrlStateStorage({ + history, + useHash: false, + ...withNotifyOnErrors(notifications!.toasts), + }); + + const indexPattern = mockIndexPattern; + + setIndexPatterns(({ + ...[indexPattern], + get: async () => indexPattern, + } as unknown) as IndexPatternsContract); + + return ( + + + + + + {children} + + + + + + ); +} + +export function MockRouter({ + children, + core, + history = createMemoryHistory(), + kibanaProps, +}: MockRouterProps) { + return ( + + + {children} + + + ); +} + +/* Custom react testing library render */ +export function render( + ui: ReactElement, + { + history = createMemoryHistory(), + core: customCore, + kibanaProps, + renderOptions, + url, + }: RenderRouterOptions = {} +) { + if (url) { + history = getHistoryFromUrl(url); + } + + const core = { + ...mockCore(), + ...customCore, + }; + + return { + ...reactTestLibRender( + + {ui} + , + renderOptions + ), + history, + core, + }; +} + +const getHistoryFromUrl = (url: Url) => { + if (typeof url === 'string') { + return createMemoryHistory({ + initialEntries: [url], + }); + } + + return createMemoryHistory({ + initialEntries: [url.path + stringify(url.queryParams)], + }); +}; + +export const mockFetcher = (data: any) => { + return jest.spyOn(fetcherHook, 'useFetcher').mockReturnValue({ + data, + status: fetcherHook.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); +}; + +export const mockUseHasData = () => { + const onRefreshTimeRange = jest.fn(); + const spy = jest.spyOn(useHasDataHook, 'useHasData').mockReturnValue({ + onRefreshTimeRange, + } as any); + return { spy, onRefreshTimeRange }; +}; + +export const mockUseValuesList = (values?: string[]) => { + const onRefreshTimeRange = jest.fn(); + const spy = jest.spyOn(useValuesListHook, 'useValuesList').mockReturnValue({ + values: values ?? [], + } as any); + return { spy, onRefreshTimeRange }; +}; + +export const mockUrlStorage = ({ + data, + filters, + breakdown, +}: { + data?: AllSeries; + filters?: UrlFilter[]; + breakdown?: string; +}) => { + const mockDataSeries = data || { + 'performance-distribution': { + reportType: 'pld', + breakdown: breakdown || 'user_agent.name', + time: { from: 'now-15m', to: 'now' }, + ...(filters ? { filters } : {}), + }, + }; + const allSeriesIds = Object.keys(mockDataSeries); + const firstSeriesId = allSeriesIds?.[0]; + + const series = mockDataSeries[firstSeriesId]; + + const removeSeries = jest.fn(); + const setSeries = jest.fn(); + + const spy = jest.spyOn(useUrlHook, 'useUrlStorage').mockReturnValue({ + firstSeriesId, + allSeriesIds, + removeSeries, + setSeries, + series, + firstSeries: mockDataSeries[firstSeriesId], + allSeries: mockDataSeries, + } as any); + + return { spy, removeSeries, setSeries }; +}; + +export function mockUseSeriesFilter() { + const removeFilter = jest.fn(); + const invertFilter = jest.fn(); + const setFilter = jest.fn(); + const spy = jest.spyOn(useSeriesFilterHook, 'useSeriesFilters').mockReturnValue({ + removeFilter, + invertFilter, + setFilter, + }); + + return { + spy, + removeFilter, + invertFilter, + setFilter, + }; +} + +const hist = createMemoryHistory(); +export const mockHistory = { + ...hist, + createHref: jest.fn(({ pathname }) => `/observability${pathname}`), + push: jest.fn(), + location: { + ...hist.location, + pathname: '/current-path', + }, +}; + +export const mockIndexPattern = getStubIndexPattern( + 'apm-*', + () => {}, + '@timestamp', + JSON.parse(indexPatternData.attributes.fields), + mockCore() as any +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx new file mode 100644 index 00000000000000..d33d8515d3bee0 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { fireEvent, screen } from '@testing-library/react'; +import { mockUrlStorage, render } from '../../rtl_helpers'; +import { dataTypes, DataTypesCol } from './data_types_col'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; + +describe('DataTypesCol', function () { + it('should render properly', function () { + const { getByText } = render(); + + dataTypes.forEach(({ label }) => { + getByText(label); + }); + }); + + it('should set series on change', function () { + const { setSeries } = mockUrlStorage({}); + + render(); + + fireEvent.click(screen.getByText(/user experience\(rum\)/i)); + + expect(setSeries).toHaveBeenCalledTimes(1); + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { dataType: 'rum' }); + }); + + it('should set series on change on already selected', function () { + const { setSeries } = mockUrlStorage({ + data: { + [NEW_SERIES_KEY]: { + dataType: 'synthetics', + reportType: 'upp', + breakdown: 'monitor.status', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + render(); + + const button = screen.getByRole('button', { + name: /Synthetic Monitoring/i, + }); + + expect(button.classList).toContain('euiButton--fill'); + + fireEvent.click(button); + + // undefined on click selected + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { dataType: undefined }); + }); +}); 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 new file mode 100644 index 00000000000000..7ea44e66a721af --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.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 { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { AppDataType } from '../../types'; +import { useIndexPatternContext } from '../../hooks/use_default_index_pattern'; +import { NEW_SERIES_KEY, useUrlStorage } from '../../hooks/use_url_strorage'; + +export const dataTypes: Array<{ id: AppDataType; label: string }> = [ + { id: 'synthetics', label: 'Synthetic Monitoring' }, + { id: 'rum', label: 'User Experience(RUM)' }, + { id: 'logs', label: 'Logs' }, + { id: 'metrics', label: 'Metrics' }, + { id: 'apm', label: 'APM' }, +]; + +export function DataTypesCol() { + const { series, setSeries } = useUrlStorage(NEW_SERIES_KEY); + + const { loadIndexPattern } = useIndexPatternContext(); + + const onDataTypeChange = (dataType?: AppDataType) => { + if (dataType) { + loadIndexPattern(dataType); + } + setSeries(NEW_SERIES_KEY, { dataType } as any); + }; + + const selectedDataType = series.dataType; + + return ( + + {dataTypes.map(({ id: dataTypeId, label }) => ( + + { + onDataTypeChange(dataTypeId === selectedDataType ? undefined : dataTypeId); + }} + > + {label} + + + ))} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx new file mode 100644 index 00000000000000..dba660fff9c363 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx @@ -0,0 +1,75 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { render } from '../../../../../utils/test_helper'; +import { getDefaultConfigs } from '../../configurations/default_configs'; +import { mockIndexPattern, mockUrlStorage } from '../../rtl_helpers'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; +import { ReportBreakdowns } from './report_breakdowns'; +import { USER_AGENT_OS } from '../../configurations/data/elasticsearch_fieldnames'; + +describe('Series Builder ReportBreakdowns', function () { + const dataViewSeries = getDefaultConfigs({ + reportType: 'pld', + indexPattern: mockIndexPattern, + seriesId: NEW_SERIES_KEY, + }); + + it('should render properly', function () { + mockUrlStorage({}); + + render(); + + screen.getByText('Select an option: , is selected'); + screen.getAllByText('Browser family'); + }); + + it('should set new series breakdown on change', function () { + const { setSeries } = mockUrlStorage({}); + + render(); + + const btn = screen.getByRole('button', { + name: /select an option: Browser family , is selected/i, + hidden: true, + }); + + fireEvent.click(btn); + + fireEvent.click(screen.getByText(/operating system/i)); + + expect(setSeries).toHaveBeenCalledTimes(1); + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { + breakdown: USER_AGENT_OS, + reportType: 'pld', + time: { from: 'now-15m', to: 'now' }, + }); + }); + it('should set undefined on new series on no select breakdown', function () { + const { setSeries } = mockUrlStorage({}); + + render(); + + const btn = screen.getByRole('button', { + name: /select an option: Browser family , is selected/i, + hidden: true, + }); + + fireEvent.click(btn); + + fireEvent.click(screen.getByText(/no breakdown/i)); + + expect(setSeries).toHaveBeenCalledTimes(1); + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { + breakdown: undefined, + reportType: 'pld', + time: { from: 'now-15m', to: 'now' }, + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx new file mode 100644 index 00000000000000..7667cea417a52a --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx @@ -0,0 +1,15 @@ +/* + * 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 { Breakdowns } from '../../series_editor/columns/breakdowns'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; +import { DataSeries } from '../../types'; + +export function ReportBreakdowns({ dataViewSeries }: { dataViewSeries: DataSeries }) { + return ; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx new file mode 100644 index 00000000000000..2fda5811541660 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx @@ -0,0 +1,75 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { getDefaultConfigs } from '../../configurations/default_configs'; +import { mockIndexPattern, mockUrlStorage, mockUseValuesList, render } from '../../rtl_helpers'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; +import { ReportDefinitionCol } from './report_definition_col'; +import { SERVICE_NAME } from '../../configurations/data/elasticsearch_fieldnames'; + +describe('Series Builder ReportDefinitionCol', function () { + const dataViewSeries = getDefaultConfigs({ + reportType: 'pld', + indexPattern: mockIndexPattern, + seriesId: NEW_SERIES_KEY, + }); + + const { setSeries } = mockUrlStorage({ + data: { + 'performance-dist': { + dataType: 'rum', + reportType: 'pld', + time: { from: 'now-30d', to: 'now' }, + reportDefinitions: { [SERVICE_NAME]: 'elastic-co' }, + }, + }, + }); + + it('should render properly', async function () { + render(); + + screen.getByText('Web Application'); + screen.getByText('Environment'); + screen.getByText('Select an option: Page load time, is selected'); + screen.getByText('Page load time'); + }); + + it('should render selected report definitions', function () { + render(); + + screen.getByText('elastic-co'); + }); + + it('should be able to remove selected definition', function () { + render(); + + const removeBtn = screen.getByText(/elastic-co/i); + + fireEvent.click(removeBtn); + + expect(setSeries).toHaveBeenCalledTimes(1); + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { + dataType: 'rum', + reportDefinitions: {}, + reportType: 'pld', + time: { from: 'now-30d', to: 'now' }, + }); + }); + + it('should be able to unselected selected definition', async function () { + mockUseValuesList(['elastic-co']); + render(); + + const definitionBtn = screen.getByText(/web application/i); + + fireEvent.click(definitionBtn); + + screen.getByText('Apply'); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx new file mode 100644 index 00000000000000..ce11c869de0ab6 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx @@ -0,0 +1,95 @@ +/* + * 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, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useIndexPatternContext } from '../../hooks/use_default_index_pattern'; +import { NEW_SERIES_KEY, useUrlStorage } from '../../hooks/use_url_strorage'; +import { CustomReportField } from '../custom_report_field'; +import FieldValueSuggestions from '../../../field_value_suggestions'; +import { DataSeries } from '../../types'; + +export function ReportDefinitionCol({ dataViewSeries }: { dataViewSeries: DataSeries }) { + const { indexPattern } = useIndexPatternContext(); + + const { series, setSeries } = useUrlStorage(NEW_SERIES_KEY); + + const { reportDefinitions: rtd = {} } = series; + + const { reportDefinitions, labels, filters } = dataViewSeries; + + const onChange = (field: string, value?: string) => { + if (!value) { + delete rtd[field]; + setSeries(NEW_SERIES_KEY, { + ...series, + reportDefinitions: { ...rtd }, + }); + } else { + setSeries(NEW_SERIES_KEY, { + ...series, + reportDefinitions: { ...rtd, [field]: value }, + }); + } + }; + + const onRemove = (field: string) => { + delete rtd[field]; + setSeries(NEW_SERIES_KEY, { + ...series, + reportDefinitions: rtd, + }); + }; + + return ( + + {indexPattern && + reportDefinitions.map(({ field, custom, options, defaultValue }) => ( + + {!custom ? ( + + + onChange(field, val)} + filters={(filters ?? []).map(({ query }) => query)} + time={series.time} + width={200} + /> + + {rtd?.[field] && ( + + onRemove(field)} + iconOnClick={() => onRemove(field)} + iconOnClickAriaLabel={'Click to remove'} + onClickAriaLabel={'Click to remove'} + > + {rtd?.[field]} + + + )} + + ) : ( + + )} + + ))} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx new file mode 100644 index 00000000000000..674f5e6f49bded --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx @@ -0,0 +1,28 @@ +/* + * 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 { screen } from '@testing-library/react'; +import { render } from '../../../../../utils/test_helper'; +import { ReportFilters } from './report_filters'; +import { getDefaultConfigs } from '../../configurations/default_configs'; +import { mockIndexPattern, mockUrlStorage } from '../../rtl_helpers'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; + +describe('Series Builder ReportFilters', function () { + const dataViewSeries = getDefaultConfigs({ + reportType: 'pld', + indexPattern: mockIndexPattern, + seriesId: NEW_SERIES_KEY, + }); + mockUrlStorage({}); + it('should render properly', function () { + render(); + + screen.getByText('Add filter'); + }); +}); 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 new file mode 100644 index 00000000000000..903dda549aeee3 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { SeriesFilter } from '../../series_editor/columns/series_filter'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; +import { DataSeries } from '../../types'; + +export function ReportFilters({ dataViewSeries }: { dataViewSeries: DataSeries }) { + return ( + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx new file mode 100644 index 00000000000000..567e2654130e80 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx @@ -0,0 +1,65 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { mockUrlStorage, render } from '../../rtl_helpers'; +import { ReportTypesCol, SELECTED_DATA_TYPE_FOR_REPORT } from './report_types_col'; +import { ReportTypes } from '../series_builder'; + +describe('ReportTypesCol', function () { + it('should render properly', function () { + render(); + screen.getByText('Performance distribution'); + screen.getByText('KPI over time'); + }); + + it('should display empty message', function () { + render(); + screen.getByText(SELECTED_DATA_TYPE_FOR_REPORT); + }); + + it('should set series on change', function () { + const { setSeries } = mockUrlStorage({}); + render(); + + fireEvent.click(screen.getByText(/monitor duration/i)); + + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { + breakdown: 'user_agent.name', + reportDefinitions: {}, + reportType: 'upd', + time: { from: 'now-15m', to: 'now' }, + }); + expect(setSeries).toHaveBeenCalledTimes(1); + }); + + it('should set selected as filled', function () { + const { setSeries } = mockUrlStorage({ + data: { + newSeriesKey: { + dataType: 'synthetics', + reportType: 'upp', + breakdown: 'monitor.status', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + render(); + + const button = screen.getByRole('button', { + name: /pings histogram/i, + }); + + expect(button.classList).toContain('euiButton--fill'); + fireEvent.click(button); + + // undefined on click selected + expect(setSeries).toHaveBeenCalledWith('newSeriesKey', { dataType: 'synthetics' }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx new file mode 100644 index 00000000000000..5c94a5bca60f85 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx @@ -0,0 +1,61 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { ReportViewTypeId, SeriesUrl } from '../../types'; +import { NEW_SERIES_KEY, useUrlStorage } from '../../hooks/use_url_strorage'; + +interface Props { + reportTypes: Array<{ id: ReportViewTypeId; label: string }>; +} + +export function ReportTypesCol({ reportTypes }: Props) { + const { + series: { reportType: selectedReportType, ...restSeries }, + setSeries, + } = useUrlStorage(NEW_SERIES_KEY); + + return reportTypes?.length > 0 ? ( + + {reportTypes.map(({ id: reportType, label }) => ( + + { + if (reportType === selectedReportType) { + setSeries(NEW_SERIES_KEY, { + dataType: restSeries.dataType, + } as SeriesUrl); + } else { + setSeries(NEW_SERIES_KEY, { + ...restSeries, + reportType, + reportDefinitions: {}, + }); + } + }} + > + {label} + + + ))} + + ) : ( + {SELECTED_DATA_TYPE_FOR_REPORT} + ); +} + +export const SELECTED_DATA_TYPE_FOR_REPORT = i18n.translate( + 'xpack.observability.expView.reportType.noDataType', + { defaultMessage: 'Select a data type to start building a series.' } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/custom_report_field.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/custom_report_field.tsx new file mode 100644 index 00000000000000..6039fd4cba2804 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/custom_report_field.tsx @@ -0,0 +1,47 @@ +/* + * 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 { EuiSuperSelect } from '@elastic/eui'; +import { useUrlStorage } from '../hooks/use_url_strorage'; +import { ReportDefinition } from '../types'; + +interface Props { + field: string; + seriesId: string; + defaultValue?: string; + options: ReportDefinition['options']; +} + +export function CustomReportField({ field, seriesId, options: opts, defaultValue }: Props) { + const { series, setSeries } = useUrlStorage(seriesId); + + const { reportDefinitions: rtd = {} } = series; + + const onChange = (value: string) => { + setSeries(seriesId, { ...series, reportDefinitions: { ...rtd, [field]: value } }); + }; + + const { reportDefinitions } = series; + + const NO_SELECT = 'no_select'; + + const options = [{ label: 'Select metric', field: NO_SELECT }, ...(opts ?? [])]; + + return ( +
+ ({ + value: fd, + inputDisplay: label, + }))} + valueOfSelected={reportDefinitions?.[field] || defaultValue || NO_SELECT} + onChange={(value) => onChange(value)} + /> +
+ ); +} 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 new file mode 100644 index 00000000000000..983c18af031d09 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx @@ -0,0 +1,201 @@ +/* + * 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, { useState } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiBasicTable, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; +import { AppDataType, ReportViewTypeId, ReportViewTypes, SeriesUrl } from '../types'; +import { DataTypesCol } from './columns/data_types_col'; +import { ReportTypesCol } from './columns/report_types_col'; +import { ReportDefinitionCol } from './columns/report_definition_col'; +import { ReportFilters } from './columns/report_filters'; +import { ReportBreakdowns } from './columns/report_breakdowns'; +import { NEW_SERIES_KEY, useUrlStorage } from '../hooks/use_url_strorage'; +import { useIndexPatternContext } from '../hooks/use_default_index_pattern'; +import { getDefaultConfigs } from '../configurations/default_configs'; + +export const ReportTypes: Record> = { + synthetics: [ + { id: 'upd', label: 'Monitor duration' }, + { id: 'upp', label: 'Pings histogram' }, + ], + rum: [ + { id: 'pld', label: 'Performance distribution' }, + { id: 'kpi', label: 'KPI over time' }, + ], + apm: [ + { id: 'svl', label: 'Latency' }, + { id: 'tpt', label: 'Throughput' }, + ], + logs: [ + { + id: 'logs', + label: 'Logs Frequency', + }, + ], + metrics: [ + { id: 'cpu', label: 'CPU usage' }, + { id: 'mem', label: 'Memory usage' }, + { id: 'nwk', label: 'Network activity' }, + ], +}; + +export function SeriesBuilder() { + const { series, setSeries, allSeriesIds, removeSeries } = useUrlStorage(NEW_SERIES_KEY); + + const { dataType, reportType, reportDefinitions = {}, filters = [] } = series; + + const [isFlyoutVisible, setIsFlyoutVisible] = useState(!!series.dataType); + + const { indexPattern } = useIndexPatternContext(); + + const getDataViewSeries = () => { + return getDefaultConfigs({ + indexPattern, + reportType: reportType!, + seriesId: NEW_SERIES_KEY, + }); + }; + + const columns = [ + { + name: i18n.translate('xpack.observability.expView.seriesBuilder.dataType', { + defaultMessage: 'Data Type', + }), + width: '20%', + render: (val: string) => , + }, + { + name: i18n.translate('xpack.observability.expView.seriesBuilder.report', { + defaultMessage: 'Report', + }), + width: '20%', + render: (val: string) => ( + + ), + }, + { + name: i18n.translate('xpack.observability.expView.seriesBuilder.definition', { + defaultMessage: 'Definition', + }), + width: '30%', + render: (val: string) => + reportType && indexPattern ? ( + + ) : null, + }, + { + name: i18n.translate('xpack.observability.expView.seriesBuilder.filters', { + defaultMessage: 'Filters', + }), + width: '25%', + render: (val: string) => + reportType && indexPattern ? : null, + }, + { + name: i18n.translate('xpack.observability.expView.seriesBuilder.breakdown', { + defaultMessage: 'Breakdowns', + }), + width: '25%', + field: 'id', + render: (val: string) => + reportType && indexPattern ? ( + + ) : null, + }, + ]; + + const addSeries = () => { + if (reportType) { + const newSeriesId = `${ + reportDefinitions?.['service.name'] || + reportDefinitions?.['monitor.id'] || + ReportViewTypes[reportType] + }`; + + const newSeriesN = { + reportType, + time: { from: 'now-30m', to: 'now' }, + filters, + reportDefinitions, + } as SeriesUrl; + + setSeries(newSeriesId, newSeriesN).then(() => { + removeSeries(NEW_SERIES_KEY); + setIsFlyoutVisible(false); + }); + } + }; + + const items = [{ id: NEW_SERIES_KEY }]; + + let flyout; + + if (isFlyoutVisible) { + flyout = ( + + + + + + + {i18n.translate('xpack.observability.expView.seriesBuilder.add', { + defaultMessage: 'Add', + })} + + + + { + removeSeries(NEW_SERIES_KEY); + setIsFlyoutVisible(false); + }} + > + {i18n.translate('xpack.observability.expView.seriesBuilder.cancel', { + defaultMessage: 'Cancel', + })} + + + + + ); + } + + return ( +
+ {!isFlyoutVisible && ( + <> + setIsFlyoutVisible((prevState) => !prevState)} + disabled={allSeriesIds.length > 0} + > + {i18n.translate('xpack.observability.expView.seriesBuilder.addSeries', { + defaultMessage: 'Add series', + })} + + + + )} + {flyout} +
+ ); +} + +const BottomFlyout = styled.div` + height: 300px; +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx new file mode 100644 index 00000000000000..71e3317ad6db86 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx @@ -0,0 +1,55 @@ +/* + * 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 { EuiSuperDatePicker } from '@elastic/eui'; +import React, { useEffect } from 'react'; +import { useHasData } from '../../../../hooks/use_has_data'; +import { useUrlStorage } from '../hooks/use_url_strorage'; +import { useQuickTimeRanges } from '../../../../hooks/use_quick_time_ranges'; + +export interface TimePickerTime { + from: string; + to: string; +} + +export interface TimePickerQuickRange extends TimePickerTime { + display: string; +} + +interface Props { + seriesId: string; +} + +export function SeriesDatePicker({ seriesId }: Props) { + const { onRefreshTimeRange } = useHasData(); + + const commonlyUsedRanges = useQuickTimeRanges(); + + const { series, setSeries } = useUrlStorage(seriesId); + + function onTimeChange({ start, end }: { start: string; end: string }) { + onRefreshTimeRange(); + setSeries(seriesId, { ...series, time: { from: start, to: end } }); + } + + useEffect(() => { + if (!series || !series.time) { + setSeries(seriesId, { ...series, time: { from: 'now-5h', to: 'now' } }); + } + }, [seriesId, series, setSeries]); + + return ( + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx new file mode 100644 index 00000000000000..acc9ba9658a081 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx @@ -0,0 +1,76 @@ +/* + * 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 { mockUrlStorage, mockUseHasData, render } from '../rtl_helpers'; +import { fireEvent, waitFor } from '@testing-library/react'; +import { SeriesDatePicker } from './index'; + +describe('SeriesDatePicker', function () { + it('should render properly', function () { + mockUrlStorage({ + data: { + 'uptime-pings-histogram': { + reportType: 'upp', + breakdown: 'monitor.status', + time: { from: 'now-30m', to: 'now' }, + }, + }, + }); + const { getByText } = render(); + + getByText('Last 30 minutes'); + }); + + it('should set defaults', async function () { + const { setSeries: setSeries1 } = mockUrlStorage({ + data: { + 'uptime-pings-histogram': { + reportType: 'upp', + breakdown: 'monitor.status', + }, + }, + } as any); + render(); + expect(setSeries1).toHaveBeenCalledTimes(1); + expect(setSeries1).toHaveBeenCalledWith('uptime-pings-histogram', { + breakdown: 'monitor.status', + reportType: 'upp', + time: { from: 'now-5h', to: 'now' }, + }); + }); + + it('should set series data', async function () { + const { setSeries } = mockUrlStorage({ + data: { + 'uptime-pings-histogram': { + reportType: 'upp', + breakdown: 'monitor.status', + time: { from: 'now-30m', to: 'now' }, + }, + }, + }); + + const { onRefreshTimeRange } = mockUseHasData(); + const { getByTestId } = render(); + + await waitFor(function () { + fireEvent.click(getByTestId('superDatePickerToggleQuickMenuButton')); + }); + + fireEvent.click(getByTestId('superDatePickerCommonlyUsed_Today')); + + expect(onRefreshTimeRange).toHaveBeenCalledTimes(1); + + expect(setSeries).toHaveBeenCalledWith('series-id', { + breakdown: 'monitor.status', + reportType: 'upp', + time: { from: 'now/d', to: 'now/d' }, + }); + expect(setSeries).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/actions_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/actions_col.tsx new file mode 100644 index 00000000000000..c6209381a4da13 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/actions_col.tsx @@ -0,0 +1,31 @@ +/* + * 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 { DataSeries } from '../../types'; +import { SeriesChartTypes } from './chart_types'; +import { MetricSelection } from './metric_selection'; + +interface Props { + series: DataSeries; +} + +export function ActionsCol({ series }: Props) { + return ( + + + + + {series.hasMetricType && ( + + + + )} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx new file mode 100644 index 00000000000000..654a93a08a7c8d --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx @@ -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 React from 'react'; +import { fireEvent, screen } from '@testing-library/react'; +import { Breakdowns } from './breakdowns'; +import { mockIndexPattern, mockUrlStorage, render } from '../../rtl_helpers'; +import { NEW_SERIES_KEY } from '../../hooks/use_url_strorage'; +import { getDefaultConfigs } from '../../configurations/default_configs'; +import { USER_AGENT_OS } from '../../configurations/data/elasticsearch_fieldnames'; + +describe('Breakdowns', function () { + const dataViewSeries = getDefaultConfigs({ + reportType: 'pld', + indexPattern: mockIndexPattern, + seriesId: NEW_SERIES_KEY, + }); + + it('should render properly', async function () { + mockUrlStorage({}); + + render(); + + screen.getAllByText('Browser family'); + }); + + it('should call set series on change', function () { + const { setSeries } = mockUrlStorage({ breakdown: USER_AGENT_OS }); + + render(); + + screen.getAllByText('Operating system'); + + fireEvent.click(screen.getByTestId('seriesBreakdown')); + + fireEvent.click(screen.getByText('Browser family')); + + expect(setSeries).toHaveBeenCalledWith('series-id', { + breakdown: 'user_agent.name', + reportType: 'pld', + time: { from: 'now-15m', to: 'now' }, + }); + expect(setSeries).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx new file mode 100644 index 00000000000000..0d34d7245725ad --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx @@ -0,0 +1,65 @@ +/* + * 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 { EuiSuperSelect } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FieldLabels } from '../../configurations/constants'; +import { useUrlStorage } from '../../hooks/use_url_strorage'; + +interface Props { + seriesId: string; + breakdowns: string[]; +} + +export function Breakdowns({ seriesId, breakdowns = [] }: Props) { + const { setSeries, series } = useUrlStorage(seriesId); + + const selectedBreakdown = series.breakdown; + const NO_BREAKDOWN = 'no_breakdown'; + + const onOptionChange = (optionId: string) => { + if (optionId === NO_BREAKDOWN) { + setSeries(seriesId, { + ...series, + breakdown: undefined, + }); + } else { + setSeries(seriesId, { + ...series, + breakdown: selectedBreakdown === optionId ? undefined : optionId, + }); + } + }; + + const items = breakdowns.map((breakdown) => ({ id: breakdown, label: FieldLabels[breakdown] })); + items.push({ + id: NO_BREAKDOWN, + label: i18n.translate('xpack.observability.exp.breakDownFilter.noBreakdown', { + defaultMessage: 'No breakdown', + }), + }); + + const options = items.map(({ id, label }) => ({ + inputDisplay: id === NO_BREAKDOWN ? label : {label}, + value: id, + dropdownDisplay: label, + })); + + return ( +
+ onOptionChange(value)} + data-test-subj={'seriesBreakdown'} + /> +
+ ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.tsx new file mode 100644 index 00000000000000..f291d0de4dac0b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.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 { fireEvent, screen, waitFor } from '@testing-library/react'; +import { SeriesChartTypes, XYChartTypes } from './chart_types'; +import { mockUrlStorage, render } from '../../rtl_helpers'; + +describe.skip('SeriesChartTypes', function () { + it('should render properly', async function () { + mockUrlStorage({}); + + render(); + + await waitFor(() => { + screen.getByText(/chart type/i); + }); + }); + + it('should call set series on change', async function () { + const { setSeries } = mockUrlStorage({}); + + render(); + + await waitFor(() => { + screen.getByText(/chart type/i); + }); + + fireEvent.click(screen.getByText(/chart type/i)); + fireEvent.click(screen.getByTestId('lnsXY_seriesType-bar_stacked')); + + expect(setSeries).toHaveBeenNthCalledWith(1, 'performance-distribution', { + breakdown: 'user_agent.name', + reportType: 'pld', + seriesType: 'bar_stacked', + time: { from: 'now-15m', to: 'now' }, + }); + expect(setSeries).toHaveBeenCalledTimes(3); + }); + + describe('XYChartTypes', function () { + it('should render properly', async function () { + mockUrlStorage({}); + + render(); + + await waitFor(() => { + screen.getByText(/chart type/i); + }); + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx new file mode 100644 index 00000000000000..017655053eef2c --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx @@ -0,0 +1,149 @@ +/* + * 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, { useState } from 'react'; + +import { + EuiButton, + EuiButtonGroup, + EuiButtonIcon, + EuiLoadingSpinner, + EuiPopover, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import styled from 'styled-components'; +import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../../../plugin'; +import { useFetcher } from '../../../../..'; +import { useUrlStorage } from '../../hooks/use_url_strorage'; +import { SeriesType } from '../../../../../../../lens/public'; + +export function SeriesChartTypes({ + seriesId, + defaultChartType, +}: { + seriesId: string; + defaultChartType: SeriesType; +}) { + const { series, setSeries, allSeries } = useUrlStorage(seriesId); + + const seriesType = series?.seriesType ?? defaultChartType; + + const onChange = (value: SeriesType) => { + Object.keys(allSeries).forEach((seriesKey) => { + const seriesN = allSeries[seriesKey]; + + setSeries(seriesKey, { ...seriesN, seriesType: value }); + }); + }; + + return ( + + ); +} + +export interface XYChartTypesProps { + onChange: (value: SeriesType) => void; + value: SeriesType; + label?: string; + includeChartTypes?: string[]; + excludeChartTypes?: string[]; +} + +export function XYChartTypes({ + onChange, + value, + label, + includeChartTypes, + excludeChartTypes, +}: XYChartTypesProps) { + const [isOpen, setIsOpen] = useState(false); + + const { + services: { lens }, + } = useKibana(); + + const { data = [], loading } = useFetcher(() => lens.getXyVisTypes(), [lens]); + + let vizTypes = data ?? []; + + if ((excludeChartTypes ?? []).length > 0) { + vizTypes = vizTypes.filter(({ id }) => !excludeChartTypes?.includes(id)); + } + + if ((includeChartTypes ?? []).length > 0) { + vizTypes = vizTypes.filter(({ id }) => includeChartTypes?.includes(id)); + } + + return loading ? ( + + ) : ( + id === value)?.icon} + onClick={() => { + setIsOpen((prevState) => !prevState); + }} + > + {label} + + ) : ( + id === value)?.label} + iconType={vizTypes.find(({ id }) => id === value)?.icon!} + onClick={() => { + setIsOpen((prevState) => !prevState); + }} + /> + ) + } + closePopover={() => setIsOpen(false)} + > + ({ + id: t.id, + label: t.label, + title: t.label, + iconType: t.icon || 'empty', + 'data-test-subj': `lnsXY_seriesType-${t.id}`, + }))} + idSelected={value} + onChange={(valueN: string) => { + onChange(valueN as SeriesType); + }} + /> + + ); +} + +const ButtonGroup = styled(EuiButtonGroup)` + &&& { + .euiButtonGroupButton-isSelected { + background-color: #a5a9b1 !important; + } + } +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx new file mode 100644 index 00000000000000..8c99de51978a7c --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { SeriesDatePicker } from '../../series_date_picker'; + +interface Props { + seriesId: string; +} +export function DatePickerCol({ seriesId }: Props) { + return ( +
+ +
+ ); +} 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 new file mode 100644 index 00000000000000..edd5546f139409 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx @@ -0,0 +1,93 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { FilterExpanded } from './filter_expanded'; +import { mockUrlStorage, mockUseValuesList, render } from '../../rtl_helpers'; +import { USER_AGENT_NAME } from '../../configurations/data/elasticsearch_fieldnames'; + +describe('FilterExpanded', function () { + it('should render properly', async function () { + mockUrlStorage({ filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }); + + render( + + ); + + screen.getByText('Browser Family'); + }); + it('should call go back on click', async function () { + mockUrlStorage({ filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }); + const goBack = jest.fn(); + + render( + + ); + + fireEvent.click(screen.getByText('Browser Family')); + + expect(goBack).toHaveBeenCalledTimes(1); + expect(goBack).toHaveBeenCalledWith(); + }); + + it('should call useValuesList on load', async function () { + mockUrlStorage({ filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }); + + const { spy } = mockUseValuesList(['Chrome', 'Firefox']); + + const goBack = jest.fn(); + + render( + + ); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toBeCalledWith( + expect.objectContaining({ + time: { from: 'now-15m', to: 'now' }, + sourceField: USER_AGENT_NAME, + }) + ); + }); + it('should filter display values', async function () { + mockUrlStorage({ filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }); + + mockUseValuesList(['Chrome', 'Firefox']); + + render( + + ); + + expect(screen.queryByText('Firefox')).toBeTruthy(); + + fireEvent.input(screen.getByRole('searchbox'), { target: { value: 'ch' } }); + + expect(screen.queryByText('Firefox')).toBeFalsy(); + expect(screen.getByText('Chrome')).toBeTruthy(); + }); +}); 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 new file mode 100644 index 00000000000000..280912dd0902f7 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx @@ -0,0 +1,100 @@ +/* + * 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, { useState, Fragment } from 'react'; +import { + EuiFieldSearch, + EuiSpacer, + EuiButtonEmpty, + EuiLoadingSpinner, + EuiFilterGroup, +} from '@elastic/eui'; +import { useIndexPatternContext } from '../../hooks/use_default_index_pattern'; +import { useUrlStorage } from '../../hooks/use_url_strorage'; +import { UrlFilter } from '../../types'; +import { FilterValueButton } from './filter_value_btn'; +import { useValuesList } from '../../../../../hooks/use_values_list'; + +interface Props { + seriesId: string; + label: string; + field: string; + goBack: () => void; + nestedField?: string; +} + +export function FilterExpanded({ seriesId, field, label, goBack, nestedField }: Props) { + const { indexPattern } = useIndexPatternContext(); + + const [value, setValue] = useState(''); + + const [isOpen, setIsOpen] = useState({ value: '', negate: false }); + + const { series } = useUrlStorage(seriesId); + + const { values, loading } = useValuesList({ + sourceField: field, + time: series.time, + indexPattern, + }); + + const filters = series?.filters ?? []; + + const currFilter: UrlFilter | undefined = filters.find(({ field: fd }) => field === fd); + + const displayValues = (values || []).filter((opt) => + opt.toLowerCase().includes(value.toLowerCase()) + ); + + return ( + <> + goBack()}> + {label} + + { + setValue(evt.target.value); + }} + /> + + {loading && ( +
+ +
+ )} + {displayValues.map((opt) => ( + + + + + + + + ))} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx new file mode 100644 index 00000000000000..7f76c9ea999eed --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx @@ -0,0 +1,238 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { FilterValueButton } from './filter_value_btn'; +import { mockUrlStorage, mockUseSeriesFilter, mockUseValuesList, render } from '../../rtl_helpers'; +import { + USER_AGENT_NAME, + USER_AGENT_VERSION, +} from '../../configurations/data/elasticsearch_fieldnames'; + +describe('FilterValueButton', function () { + it('should render properly', async function () { + render( + + ); + + screen.getByText('Chrome'); + }); + + it('should render display negate state', async function () { + render( + + ); + + screen.getByText('Not Chrome'); + screen.getByTitle('Not Chrome'); + const btn = screen.getByRole('button'); + expect(btn.classList).toContain('euiButtonEmpty--danger'); + }); + + it('should call set filter on click', async function () { + const { setFilter, removeFilter } = mockUseSeriesFilter(); + + render( + + ); + + fireEvent.click(screen.getByText('Not Chrome')); + + expect(removeFilter).toHaveBeenCalledTimes(0); + expect(setFilter).toHaveBeenCalledTimes(1); + + expect(setFilter).toHaveBeenCalledWith({ + field: 'user_agent.name', + negate: true, + value: 'Chrome', + }); + }); + it('should remove filter on click if already selected', async function () { + mockUrlStorage({}); + const { removeFilter } = mockUseSeriesFilter(); + + render( + + ); + + fireEvent.click(screen.getByText('Chrome')); + + expect(removeFilter).toHaveBeenCalledWith({ + field: 'user_agent.name', + negate: false, + value: 'Chrome', + }); + }); + + it('should change filter on negated one', async function () { + const { removeFilter } = mockUseSeriesFilter(); + + render( + + ); + + fireEvent.click(screen.getByText('Not Chrome')); + + expect(removeFilter).toHaveBeenCalledWith({ + field: 'user_agent.name', + negate: true, + value: 'Chrome', + }); + }); + + it('should force open nested', async function () { + mockUseSeriesFilter(); + const { spy } = mockUseValuesList(); + + render( + + ); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toBeCalledWith( + expect.objectContaining({ + filters: [ + { + term: { + [USER_AGENT_NAME]: 'Chrome', + }, + }, + ], + sourceField: 'user_agent.version', + }) + ); + }); + it('should set isNestedOpen on click', async function () { + mockUseSeriesFilter(); + const { spy } = mockUseValuesList(); + + render( + + ); + + expect(spy).toHaveBeenCalledTimes(2); + expect(spy).toBeCalledWith( + expect.objectContaining({ + filters: [ + { + term: { + [USER_AGENT_NAME]: 'Chrome', + }, + }, + ], + sourceField: USER_AGENT_VERSION, + }) + ); + }); + + it('should set call setIsNestedOpen on click selected', async function () { + mockUseSeriesFilter(); + mockUseValuesList(); + + const setIsNestedOpen = jest.fn(); + + render( + + ); + + fireEvent.click(screen.getByText('Chrome')); + + expect(setIsNestedOpen).toHaveBeenCalledTimes(1); + expect(setIsNestedOpen).toHaveBeenCalledWith({ negate: false, value: '' }); + }); + + it('should set call setIsNestedOpen on click not selected', async function () { + mockUseSeriesFilter(); + mockUseValuesList(); + + const setIsNestedOpen = jest.fn(); + + render( + + ); + + fireEvent.click(screen.getByText('Not Chrome')); + + expect(setIsNestedOpen).toHaveBeenCalledTimes(1); + expect(setIsNestedOpen).toHaveBeenCalledWith({ negate: true, value: 'Chrome' }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx new file mode 100644 index 00000000000000..42cdfd595e66ba --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx @@ -0,0 +1,117 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { useMemo } from 'react'; +import { EuiFilterButton, hexToRgb } from '@elastic/eui'; +import { useIndexPatternContext } from '../../hooks/use_default_index_pattern'; +import { useUrlStorage } from '../../hooks/use_url_strorage'; +import { useSeriesFilters } from '../../hooks/use_series_filters'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; +import FieldValueSuggestions from '../../../field_value_suggestions'; + +interface Props { + value: string; + field: string; + allSelectedValues?: string[]; + negate: boolean; + nestedField?: string; + seriesId: string; + isNestedOpen: { + value: string; + negate: boolean; + }; + setIsNestedOpen: (val: { value: string; negate: boolean }) => void; +} + +export function FilterValueButton({ + isNestedOpen, + setIsNestedOpen, + value, + field, + negate, + seriesId, + nestedField, + allSelectedValues, +}: Props) { + const { series } = useUrlStorage(seriesId); + + const { indexPattern } = useIndexPatternContext(); + + const { setFilter, removeFilter } = useSeriesFilters({ seriesId }); + + const hasActiveFilters = (allSelectedValues ?? []).includes(value); + + const button = ( + { + if (hasActiveFilters) { + removeFilter({ field, value, negate }); + } else { + setFilter({ field, value, negate }); + } + if (!hasActiveFilters) { + setIsNestedOpen({ value, negate }); + } else { + setIsNestedOpen({ value: '', negate }); + } + }} + > + {negate + ? i18n.translate('xpack.observability.expView.filterValueButton.negate', { + defaultMessage: 'Not {value}', + values: { value }, + }) + : value} + + ); + + const onNestedChange = (val?: string) => { + setFilter({ field: nestedField!, value: val! }); + setIsNestedOpen({ value: '', negate }); + }; + + const forceOpenNested = isNestedOpen?.value === value && isNestedOpen.negate === negate; + + const filters = useMemo(() => { + return [ + { + term: { + [field]: value, + }, + }, + ]; + }, [field, value]); + + return nestedField && forceOpenNested ? ( + + ) : ( + button + ); +} + +const FilterButton = euiStyled(EuiFilterButton)` + background-color: rgba(${(props) => { + const color = props.hasActiveFilters + ? props.color === 'danger' + ? hexToRgb(props.theme.eui.euiColorDanger) + : hexToRgb(props.theme.eui.euiColorPrimary) + : 'initial'; + return `${color[0]}, ${color[1]}, ${color[2]}, 0.1`; + }}); +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.test.tsx new file mode 100644 index 00000000000000..ced04f0a59c8cb --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.test.tsx @@ -0,0 +1,112 @@ +/* + * 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 { fireEvent, screen } from '@testing-library/react'; +import { mockUrlStorage, render } from '../../rtl_helpers'; +import { MetricSelection } from './metric_selection'; + +describe('MetricSelection', function () { + it('should render properly', function () { + render(); + + screen.getByText('Average'); + }); + + it('should display selected value', function () { + mockUrlStorage({ + data: { + 'performance-distribution': { + reportType: 'kpi', + metric: 'median', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + render(); + + screen.getByText('Median'); + }); + + it('should be disabled on disabled state', function () { + render(); + + const btn = screen.getByRole('button'); + + expect(btn.classList).toContain('euiButton-isDisabled'); + }); + + it('should call set series on change', function () { + const { setSeries } = mockUrlStorage({ + data: { + 'performance-distribution': { + reportType: 'kpi', + metric: 'median', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + render(); + + fireEvent.click(screen.getByText('Median')); + + screen.getByText('Chart metric group'); + + fireEvent.click(screen.getByText('95th Percentile')); + + expect(setSeries).toHaveBeenNthCalledWith(1, 'performance-distribution', { + metric: '95th', + reportType: 'kpi', + time: { from: 'now-15m', to: 'now' }, + }); + // FIXME This is a bug in EUI EuiButtonGroup calls on change multiple times + // This should be one https://github.com/elastic/eui/issues/4629 + expect(setSeries).toHaveBeenCalledTimes(3); + }); + + it('should call set series on change for all series', function () { + const { setSeries } = mockUrlStorage({ + data: { + 'page-views': { + reportType: 'kpi', + metric: 'median', + time: { from: 'now-15m', to: 'now' }, + }, + 'performance-distribution': { + reportType: 'kpi', + metric: 'median', + time: { from: 'now-15m', to: 'now' }, + }, + }, + }); + + render(); + + fireEvent.click(screen.getByText('Median')); + + screen.getByText('Chart metric group'); + + fireEvent.click(screen.getByText('95th Percentile')); + + expect(setSeries).toHaveBeenNthCalledWith(1, 'page-views', { + metric: '95th', + reportType: 'kpi', + time: { from: 'now-15m', to: 'now' }, + }); + + expect(setSeries).toHaveBeenNthCalledWith(2, 'performance-distribution', { + metric: '95th', + reportType: 'kpi', + time: { from: 'now-15m', to: 'now' }, + }); + // FIXME This is a bug in EUI EuiButtonGroup calls on change multiple times + // This should be one https://github.com/elastic/eui/issues/4629 + expect(setSeries).toHaveBeenCalledTimes(6); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.tsx new file mode 100644 index 00000000000000..e01e371b5eeeb9 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/metric_selection.tsx @@ -0,0 +1,86 @@ +/* + * 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, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiButtonGroup, EuiPopover } from '@elastic/eui'; +import { useUrlStorage } from '../../hooks/use_url_strorage'; +import { OperationType } from '../../../../../../../lens/public'; + +const toggleButtons = [ + { + id: `avg`, + label: i18n.translate('xpack.observability.expView.metricsSelect.average', { + defaultMessage: 'Average', + }), + }, + { + id: `median`, + label: i18n.translate('xpack.observability.expView.metricsSelect.median', { + defaultMessage: 'Median', + }), + }, + { + id: `95th`, + label: i18n.translate('xpack.observability.expView.metricsSelect.9thPercentile', { + defaultMessage: '95th Percentile', + }), + }, + { + id: `99th`, + label: i18n.translate('xpack.observability.expView.metricsSelect.99thPercentile', { + defaultMessage: '99th Percentile', + }), + }, +]; + +export function MetricSelection({ + seriesId, + isDisabled, +}: { + seriesId: string; + isDisabled: boolean; +}) { + const { series, setSeries, allSeries } = useUrlStorage(seriesId); + + const [isOpen, setIsOpen] = useState(false); + + const [toggleIdSelected, setToggleIdSelected] = useState(series?.metric ?? 'avg'); + + const onChange = (optionId: OperationType) => { + setToggleIdSelected(optionId); + + Object.keys(allSeries).forEach((seriesKey) => { + const seriesN = allSeries[seriesKey]; + + setSeries(seriesKey, { ...seriesN, metric: optionId }); + }); + }; + const button = ( + setIsOpen((prevState) => !prevState)} + size="s" + color="text" + isDisabled={isDisabled} + > + {toggleButtons.find(({ id }) => id === toggleIdSelected)!.label} + + ); + + return ( + setIsOpen(false)}> + onChange(id as OperationType)} + /> + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/remove_series.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/remove_series.tsx new file mode 100644 index 00000000000000..67aebed9433269 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/remove_series.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { EuiButtonIcon } from '@elastic/eui'; +import { DataSeries } from '../../types'; +import { useUrlStorage } from '../../hooks/use_url_strorage'; + +interface Props { + series: DataSeries; +} + +export function RemoveSeries({ series }: Props) { + const { removeSeries } = useUrlStorage(); + + const onClick = () => { + removeSeries(series.id); + }; + return ( + + ); +} 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 new file mode 100644 index 00000000000000..24b65d2adb38e3 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx @@ -0,0 +1,139 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { useState, Fragment } from 'react'; +import { + EuiButton, + EuiPopover, + EuiSpacer, + EuiButtonEmpty, + EuiFlexItem, + EuiFlexGroup, +} from '@elastic/eui'; +import { FilterExpanded } from './filter_expanded'; +import { DataSeries } from '../../types'; +import { FieldLabels } from '../../configurations/constants'; +import { SelectedFilters } from '../selected_filters'; +import { NEW_SERIES_KEY, useUrlStorage } from '../../hooks/use_url_strorage'; + +interface Props { + seriesId: string; + defaultFilters: DataSeries['defaultFilters']; + series: DataSeries; + isNew?: boolean; +} + +export interface Field { + label: string; + field: string; + nested?: string; +} + +export function SeriesFilter({ series, isNew, seriesId, defaultFilters = [] }: Props) { + const [isPopoverVisible, setIsPopoverVisible] = useState(false); + + const [selectedField, setSelectedField] = useState(); + + const options = defaultFilters.map((field) => { + if (typeof field === 'string') { + return { label: FieldLabels[field], field }; + } + return { label: FieldLabels[field.field], field: field.field, nested: field.nested }; + }); + const disabled = seriesId === NEW_SERIES_KEY && !isNew; + + const { setSeries, series: urlSeries } = useUrlStorage(seriesId); + + const button = ( + { + setIsPopoverVisible(true); + }} + isDisabled={disabled} + size="s" + > + {i18n.translate('xpack.observability.expView.seriesEditor.addFilter', { + defaultMessage: 'Add filter', + })} + + ); + + const mainPanel = ( + <> + + {options.map((opt) => ( + + { + setSelectedField(opt); + }} + > + {opt.label} + + + + ))} + + ); + + const childPanel = selectedField ? ( + { + setSelectedField(undefined); + }} + /> + ) : null; + + const closePopover = () => { + setIsPopoverVisible(false); + setSelectedField(undefined); + }; + + return ( + + {!disabled && } + + + {!selectedField ? mainPanel : childPanel} + + + {(urlSeries.filters ?? []).length > 0 && ( + + { + setSeries(seriesId, { ...urlSeries, filters: undefined }); + }} + isDisabled={disabled} + size="s" + > + {i18n.translate('xpack.observability.expView.seriesEditor.clearFilter', { + defaultMessage: 'Clear filters', + })} + + + )} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx new file mode 100644 index 00000000000000..5770a7e209f068 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx @@ -0,0 +1,33 @@ +/* + * 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 { screen, waitFor } from '@testing-library/react'; +import { mockIndexPattern, mockUrlStorage, render } from '../rtl_helpers'; +import { SelectedFilters } from './selected_filters'; +import { getDefaultConfigs } from '../configurations/default_configs'; +import { NEW_SERIES_KEY } from '../hooks/use_url_strorage'; +import { USER_AGENT_NAME } from '../configurations/data/elasticsearch_fieldnames'; + +describe('SelectedFilters', function () { + const dataViewSeries = getDefaultConfigs({ + reportType: 'pld', + indexPattern: mockIndexPattern, + seriesId: NEW_SERIES_KEY, + }); + + it('should render properly', async function () { + mockUrlStorage({ filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }); + + render(); + + await waitFor(() => { + screen.getByText('Chrome'); + screen.getByTitle('Filter: Browser family: Chrome. Select for more filter actions.'); + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx new file mode 100644 index 00000000000000..be8b1feb4d7236 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx @@ -0,0 +1,96 @@ +/* + * 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, { Fragment } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { NEW_SERIES_KEY, useUrlStorage } from '../hooks/use_url_strorage'; +import { FilterLabel } from '../components/filter_label'; +import { DataSeries, UrlFilter } from '../types'; +import { useIndexPatternContext } from '../hooks/use_default_index_pattern'; +import { useSeriesFilters } from '../hooks/use_series_filters'; +import { getFiltersFromDefs } from '../hooks/use_lens_attributes'; + +interface Props { + seriesId: string; + series: DataSeries; + isNew?: boolean; +} +export function SelectedFilters({ seriesId, isNew, series: dataSeries }: Props) { + const { series } = useUrlStorage(seriesId); + + const { reportDefinitions = {} } = series; + + const { labels } = dataSeries; + + const filters: UrlFilter[] = series.filters ?? []; + + let definitionFilters: UrlFilter[] = getFiltersFromDefs(reportDefinitions, dataSeries); + + // we don't want to display report definition filters in new series view + if (seriesId === NEW_SERIES_KEY && isNew) { + definitionFilters = []; + } + + const { removeFilter } = useSeriesFilters({ seriesId }); + + const { indexPattern } = useIndexPatternContext(); + + return (filters.length > 0 || definitionFilters.length > 0) && indexPattern ? ( + + + {filters.map(({ field, values, notValues }) => ( + + {(values ?? []).map((val) => ( + + removeFilter({ field, value: val, negate: false })} + negate={false} + /> + + ))} + {(notValues ?? []).map((val) => ( + + removeFilter({ field, value: val, negate: true })} + /> + + ))} + + ))} + + {definitionFilters.map(({ field, values }) => ( + + {(values ?? []).map((val) => ( + + { + // FIXME handle this use case + }} + negate={false} + definitionFilter={true} + /> + + ))} + + ))} + + + ) : 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 new file mode 100644 index 00000000000000..2d423c9aee3fcd --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx @@ -0,0 +1,139 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiBasicTable, EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; +import { SeriesFilter } from './columns/series_filter'; +import { ActionsCol } from './columns/actions_col'; +import { Breakdowns } from './columns/breakdowns'; +import { DataSeries } from '../types'; +import { SeriesBuilder } from '../series_builder/series_builder'; +import { NEW_SERIES_KEY, useUrlStorage } from '../hooks/use_url_strorage'; +import { getDefaultConfigs } from '../configurations/default_configs'; +import { DatePickerCol } from './columns/date_picker_col'; +import { RemoveSeries } from './columns/remove_series'; +import { useIndexPatternContext } from '../hooks/use_default_index_pattern'; + +export function SeriesEditor() { + const { allSeries, firstSeriesId } = useUrlStorage(); + + const columns = [ + { + name: i18n.translate('xpack.observability.expView.seriesEditor.name', { + defaultMessage: 'Name', + }), + field: 'id', + width: '15%', + render: (val: string) => ( + + {' '} + {val === NEW_SERIES_KEY ? 'new-series-preview' : val} + + ), + }, + ...(firstSeriesId !== NEW_SERIES_KEY + ? [ + { + name: i18n.translate('xpack.observability.expView.seriesEditor.filters', { + defaultMessage: 'Filters', + }), + field: 'defaultFilters', + width: '25%', + render: (defaultFilters: string[], series: DataSeries) => ( + + ), + }, + { + name: i18n.translate('xpack.observability.expView.seriesEditor.breakdowns', { + defaultMessage: 'Breakdowns', + }), + field: 'breakdowns', + width: '15%', + render: (val: string[], item: DataSeries) => ( + + ), + }, + { + name: '', + align: 'center' as const, + width: '15%', + field: 'id', + render: (val: string, item: DataSeries) => , + }, + ] + : []), + { + name: ( +
+ {i18n.translate('xpack.observability.expView.seriesEditor.time', { + defaultMessage: 'Time', + })} +
+ ), + width: '20%', + field: 'id', + align: 'right' as const, + render: (val: string, item: DataSeries) => , + }, + + ...(firstSeriesId !== NEW_SERIES_KEY + ? [ + { + name: i18n.translate('xpack.observability.expView.seriesEditor.actions', { + defaultMessage: 'Actions', + }), + align: 'center' as const, + width: '5%', + field: 'id', + render: (val: string, item: DataSeries) => , + }, + ] + : []), + ]; + + const allSeriesKeys = Object.keys(allSeries); + + const items: DataSeries[] = []; + + const { indexPattern } = useIndexPatternContext(); + + allSeriesKeys.forEach((seriesKey) => { + const series = allSeries[seriesKey]; + if (series.reportType && indexPattern) { + items.push( + getDefaultConfigs({ + indexPattern, + reportType: series.reportType, + seriesId: seriesKey, + }) + ); + } + }); + + return ( + <> + + (firstSeriesId === NEW_SERIES_KEY ? {} : { height: 100 })} + noItemsMessage={i18n.translate('xpack.observability.expView.seriesEditor.notFound', { + defaultMessage: 'No series found, please add a series.', + })} + cellProps={{ + style: { + verticalAlign: 'top', + }, + }} + /> + + + + ); +} 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 new file mode 100644 index 00000000000000..444e0ddaecb4a1 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -0,0 +1,89 @@ +/* + * 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 { PaletteOutput } from 'src/plugins/charts/public'; +import { + LastValueIndexPatternColumn, + DateHistogramIndexPatternColumn, + SeriesType, + OperationType, + IndexPatternColumn, +} from '../../../../../lens/public'; + +import { PersistableFilter } from '../../../../../lens/common'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; + +export const ReportViewTypes = { + pld: 'page-load-dist', + kpi: 'kpi-trends', + upd: 'uptime-duration', + upp: 'uptime-pings', + svl: 'service-latency', + tpt: 'service-throughput', + logs: 'logs-frequency', + cpu: 'cpu-usage', + mem: 'memory-usage', + nwk: 'network-activity', +} as const; + +type ValueOf = T[keyof T]; + +export type ReportViewTypeId = keyof typeof ReportViewTypes; + +export type ReportViewType = ValueOf; + +export interface ReportDefinition { + field: string; + required?: boolean; + custom?: boolean; + defaultValue?: string; + options?: Array<{ field: string; label: string; description?: string }>; +} + +export interface DataSeries { + reportType: ReportViewType; + id: string; + xAxisColumn: Partial | Partial; + yAxisColumn: Partial; + + breakdowns: string[]; + defaultSeriesType: SeriesType; + defaultFilters: Array; + seriesTypes: SeriesType[]; + filters?: PersistableFilter[]; + reportDefinitions: ReportDefinition[]; + labels: Record; + hasMetricType: boolean; + palette?: PaletteOutput; +} + +export interface SeriesUrl { + time: { + to: string; + from: string; + }; + breakdown?: string; + filters?: UrlFilter[]; + seriesType?: SeriesType; + reportType: ReportViewTypeId; + metric?: OperationType; + dataType?: AppDataType; + reportDefinitions?: Record; +} + +export interface UrlFilter { + field: string; + values?: string[]; + notValues?: string[]; +} + +export interface ConfigProps { + seriesId: string; + indexPattern: IIndexPattern; +} + +export type AppDataType = 'synthetics' | 'rum' | 'logs' | 'metrics' | 'apm'; diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx index b2c682dc58937f..a44aab2da85be7 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx @@ -15,14 +15,19 @@ import { EuiSelectableOption, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { PopoverAnchorPosition } from '@elastic/eui/src/components/popover/popover'; export interface FieldValueSelectionProps { value?: string; label: string; - loading: boolean; + loading?: boolean; onChange: (val?: string) => void; values?: string[]; setQuery: Dispatch>; + anchorPosition?: PopoverAnchorPosition; + forceOpen?: boolean; + button?: JSX.Element; + width?: number; } const formatOptions = (values?: string[], value?: string): EuiSelectableOption[] => { @@ -38,6 +43,10 @@ export function FieldValueSelection({ loading, values, setQuery, + button, + width, + forceOpen, + anchorPosition, onChange: onSelectionChange, }: FieldValueSelectionProps) { const [options, setOptions] = useState(formatOptions(values, value)); @@ -63,8 +72,9 @@ export function FieldValueSelection({ setQuery((evt.target as HTMLInputElement).value); }; - const button = ( + const anchorButton = ( void; + filters: ESFilter[]; + anchorPosition?: PopoverAnchorPosition; + time?: { from: string; to: string }; + forceOpen?: boolean; + button?: JSX.Element; + width?: number; } export function FieldValueSuggestions({ @@ -25,12 +33,18 @@ export function FieldValueSuggestions({ label, indexPattern, value, + filters, + button, + time, + width, + forceOpen, + anchorPosition, onChange: onSelectionChange, }: FieldValueSuggestionsProps) { const [query, setQuery] = useState(''); const [debouncedValue, setDebouncedValue] = useState(''); - const { values, loading } = useValuesList({ indexPattern, query, sourceField }); + const { values, loading } = useValuesList({ indexPattern, query, sourceField, filters, time }); useDebounce( () => { @@ -48,6 +62,10 @@ export function FieldValueSuggestions({ setQuery={setDebouncedValue} loading={loading} value={value} + button={button} + forceOpen={forceOpen} + anchorPosition={anchorPosition} + width={width} /> ); } 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 5e48860a9b0492..01655c0d7b2d77 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 @@ -17,12 +17,19 @@ import { HasData, ObservabilityFetchDataPlugins } from '../typings/fetch_overvie import { HasDataContextProvider } from './has_data_context'; import * as pluginContext from '../hooks/use_plugin_context'; import { PluginContextValue } from './plugin_context'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; const relativeStart = '2020-10-08T06:00:00.000Z'; const relativeEnd = '2020-10-08T07:00:00.000Z'; function wrapper({ children }: { children: React.ReactElement }) { - return {children}; + const history = createMemoryHistory(); + return ( + + {children} + + ); } function unregisterAll() { 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 085b7fd7ba028e..a2628d37828a44 100644 --- a/x-pack/plugins/observability/public/context/has_data_context.tsx +++ b/x-pack/plugins/observability/public/context/has_data_context.tsx @@ -7,6 +7,7 @@ import { uniqueId } from 'lodash'; import React, { createContext, useEffect, useState } from 'react'; +import { useRouteMatch } from 'react-router-dom'; import { Alert } from '../../../alerting/common'; import { getDataHandler } from '../data_handler'; import { FETCH_STATUS } from '../hooks/use_fetcher'; @@ -41,35 +42,38 @@ export function HasDataContextProvider({ children }: { children: React.ReactNode const [hasData, setHasData] = useState({}); + const isExploratoryView = useRouteMatch('/exploratory-view'); + useEffect( () => { - 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); + 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) => ({ + ...prevState, + [app]: { + hasData: result, + status: FETCH_STATUS.SUCCESS, + }, + })); + } + } catch (e) { setHasData((prevState) => ({ ...prevState, [app]: { - hasData: result, - status: FETCH_STATUS.SUCCESS, + hasData: undefined, + status: FETCH_STATUS.FAILURE, }, })); } - } catch (e) { - setHasData((prevState) => ({ - ...prevState, - [app]: { - hasData: undefined, - status: FETCH_STATUS.FAILURE, - }, - })); - } - }); + }); }, // eslint-disable-next-line react-hooks/exhaustive-deps [] diff --git a/x-pack/plugins/observability/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/observability/public/hooks/use_breadcrumbs.ts new file mode 100644 index 00000000000000..a354ac8a07f050 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_breadcrumbs.ts @@ -0,0 +1,71 @@ +/* + * 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 { ChromeBreadcrumb } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { MouseEvent, useEffect } from 'react'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { stringify } from 'query-string'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { useQueryParams } from './use_query_params'; + +const EMPTY_QUERY = '?'; + +function handleBreadcrumbClick( + breadcrumbs: ChromeBreadcrumb[], + navigateToHref?: (url: string) => Promise +) { + return breadcrumbs.map((bc) => ({ + ...bc, + ...(bc.href + ? { + onClick: (event: MouseEvent) => { + if (navigateToHref && bc.href) { + event.preventDefault(); + navigateToHref(bc.href); + } + }, + } + : {}), + })); +} + +export const makeBaseBreadcrumb = (href: string, params?: any): EuiBreadcrumb => { + if (params) { + const crumbParams = { ...params }; + + delete crumbParams.statusFilter; + const query = stringify(crumbParams, { skipEmptyString: true, skipNull: true }); + href += query === EMPTY_QUERY ? '' : query; + } + return { + text: i18n.translate('xpack.observability.breadcrumbs.observability', { + defaultMessage: 'Observability', + }), + href, + }; +}; + +export const useBreadcrumbs = (extraCrumbs: ChromeBreadcrumb[]) => { + const params = useQueryParams(); + + const { + services: { chrome, application }, + } = useKibana(); + + const setBreadcrumbs = chrome?.setBreadcrumbs; + const appPath = application?.getUrlForApp('observability-overview') ?? ''; + const navigate = application?.navigateToUrl; + + useEffect(() => { + if (setBreadcrumbs) { + setBreadcrumbs( + handleBreadcrumbClick([makeBaseBreadcrumb(appPath, params)].concat(extraCrumbs), navigate) + ); + } + }, [appPath, extraCrumbs, navigate, params, setBreadcrumbs]); +}; diff --git a/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx b/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx new file mode 100644 index 00000000000000..82a0fc39b8519c --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useUiSetting } from '../../../../../src/plugins/kibana_react/public'; +import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; +import { TimePickerQuickRange } from '../components/shared/exploratory_view/series_date_picker'; + +export function useQuickTimeRanges() { + const timePickerQuickRanges = useUiSetting( + UI_SETTINGS.TIMEPICKER_QUICK_RANGES + ); + + return timePickerQuickRanges.map(({ from, to, display }) => ({ + start: from, + end: to, + label: display, + })); +} diff --git a/x-pack/plugins/observability/public/hooks/use_values_list.ts b/x-pack/plugins/observability/public/hooks/use_values_list.ts index 25a12ab4a9ebd1..e17f515ed6cb9e 100644 --- a/x-pack/plugins/observability/public/hooks/use_values_list.ts +++ b/x-pack/plugins/observability/public/hooks/use_values_list.ts @@ -5,32 +5,58 @@ * 2.0. */ -import { IIndexPattern } from '../../../../../src/plugins/data/common'; +import { IndexPattern } from '../../../../../src/plugins/data/common'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; import { useFetcher } from './use_fetcher'; import { ESFilter } from '../../../../../typings/elasticsearch'; -import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; -interface Props { +export interface Props { sourceField: string; query?: string; - indexPattern: IIndexPattern; + indexPattern: IndexPattern; filters?: ESFilter[]; + time?: { from: string; to: string }; } -export const useValuesList = ({ sourceField, indexPattern, query, filters }: Props) => { +export const useValuesList = ({ + sourceField, + indexPattern, + query = '', + filters, + time, +}: Props): { values: string[]; loading?: boolean } => { const { services: { data }, } = useKibana<{ data: DataPublicPluginStart }>(); - const { data: values, status } = useFetcher(() => { + const { from, to } = time ?? {}; + + const { data: values, loading } = useFetcher(() => { + if (!sourceField || !indexPattern) { + return []; + } return data.autocomplete.getValueSuggestions({ indexPattern, query: query || '', - field: indexPattern.fields.find(({ name }) => name === sourceField)!, - boolFilter: filters ?? [], + useTimeRange: !(from && to), + field: indexPattern.getFieldByName(sourceField)!, + boolFilter: + from && to + ? [ + ...(filters || []), + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + ] + : filters || [], }); - }, [sourceField, query, data.autocomplete, indexPattern, filters]); + }, [query, sourceField, data.autocomplete, indexPattern, from, to, filters]); - return { values, loading: status === 'loading' || status === 'pending' }; + return { values: values as string[], loading }; }; diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 35443ca090077f..837404d273ee43 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -55,3 +55,4 @@ export * from './typings'; export { useChartTheme } from './hooks/use_chart_theme'; export { useTheme } from './hooks/use_theme'; export { getApmTraceUrl } from './utils/get_apm_trace_url'; +export { createExploratoryViewUrl } from './components/shared/exploratory_view/configurations/utils'; diff --git a/x-pack/plugins/observability/public/routes/index.tsx b/x-pack/plugins/observability/public/routes/index.tsx index 20817901dab82d..49cc55832dcf27 100644 --- a/x-pack/plugins/observability/public/routes/index.tsx +++ b/x-pack/plugins/observability/public/routes/index.tsx @@ -14,6 +14,7 @@ import { OverviewPage } from '../pages/overview'; import { jsonRt } from './json_rt'; import { AlertsPage } from '../pages/alerts'; import { CasesPage } from '../pages/cases'; +import { ExploratoryViewPage } from '../components/shared/exploratory_view'; export type RouteParams = DecodeParams; @@ -115,4 +116,24 @@ export const routes = { }, ], }, + '/exploratory-view': { + handler: () => { + return ; + }, + params: { + query: t.partial({ + rangeFrom: t.string, + rangeTo: t.string, + refreshPaused: jsonRt.pipe(t.boolean), + refreshInterval: jsonRt.pipe(t.number), + }), + }, + breadcrumb: [ + { + text: i18n.translate('xpack.observability.overview.exploratoryView', { + defaultMessage: 'Exploratory view', + }), + }, + ], + }, }; diff --git a/x-pack/plugins/observability/public/utils/observability_index_patterns.ts b/x-pack/plugins/observability/public/utils/observability_index_patterns.ts new file mode 100644 index 00000000000000..b23a2461055442 --- /dev/null +++ b/x-pack/plugins/observability/public/utils/observability_index_patterns.ts @@ -0,0 +1,64 @@ +/* + * 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 { DataPublicPluginStart, IndexPattern } from '../../../../../src/plugins/data/public'; + +export type DataType = 'synthetics' | 'apm' | 'logs' | 'metrics' | 'rum'; + +const indexPatternList: Record = { + synthetics: 'synthetics_static_index_pattern_id', + apm: 'apm_static_index_pattern_id', + rum: 'apm_static_index_pattern_id', + logs: 'logs_static_index_pattern_id', + metrics: 'metrics_static_index_pattern_id', +}; + +const appToPatternMap: Record = { + synthetics: 'heartbeat-*', + apm: 'apm-*', + rum: 'apm-*', + logs: 'logs-*,filebeat-*', + metrics: 'metrics-*,metricbeat-*', +}; + +export class ObservabilityIndexPatterns { + data?: DataPublicPluginStart; + + constructor(data: DataPublicPluginStart) { + this.data = data; + } + + async createIndexPattern(app: DataType) { + if (!this.data) { + throw new Error('data is not defined'); + } + + const pattern = appToPatternMap[app]; + + const fields = await this.data.indexPatterns.getFieldsForWildcard({ + pattern, + }); + + return await this.data.indexPatterns.createAndSave({ + fields, + title: pattern, + id: indexPatternList[app], + timeFieldName: '@timestamp', + }); + } + + async getIndexPattern(app: DataType): Promise { + if (!this.data) { + throw new Error('data is not defined'); + } + try { + return await this.data?.indexPatterns.get(indexPatternList[app]); + } catch (e) { + return await this.createIndexPattern(app || 'apm'); + } + } +} diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index 083c35a26c20bf..cc6e298795e4a3 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -7,7 +7,14 @@ "declaration": true, "declarationMap": true }, - "include": ["common/**/*", "public/**/*", "server/**/*", "typings/**/*"], + "include": [ + "common/**/*", + "public/**/*", + "public/**/*.json", + "server/**/*", + "typings/**/*", + "../../../typings/**/*" + ], "references": [ { "path": "../../../src/core/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, diff --git a/x-pack/plugins/uptime/public/apps/plugin.ts b/x-pack/plugins/uptime/public/apps/plugin.ts index e3457884594a98..7ea6b72547386b 100644 --- a/x-pack/plugins/uptime/public/apps/plugin.ts +++ b/x-pack/plugins/uptime/public/apps/plugin.ts @@ -68,18 +68,21 @@ export class UptimePlugin return UptimeDataHelper(coreStart); }; - plugins.observability.dashboard.register({ - appName: 'uptime', - hasData: async () => { - const dataHelper = await getUptimeDataHelper(); - const status = await dataHelper.indexStatus(); - return status.docCount > 0; - }, - fetchData: async (params: FetchDataParams) => { - const dataHelper = await getUptimeDataHelper(); - return await dataHelper.overviewData(params); - }, - }); + + if (plugins.observability) { + plugins.observability.dashboard.register({ + appName: 'uptime', + hasData: async () => { + const dataHelper = await getUptimeDataHelper(); + const status = await dataHelper.indexStatus(); + return status.docCount > 0; + }, + fetchData: async (params: FetchDataParams) => { + const dataHelper = await getUptimeDataHelper(); + return await dataHelper.overviewData(params); + }, + }); + } core.application.register({ id: PLUGIN.ID, From 9cebff12980d7afe2e4b13fcb15c26c627f38498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 5 Apr 2021 10:51:56 -0400 Subject: [PATCH 06/10] [OBS]home page is showing incorrect value of APM throughput (tpm) (#95991) * fixing obs transaction per minute value * addressing PR comments * fixing unit test * addressing PR comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...pm_observability_overview_fetchers.test.ts | 20 ++-- .../apm_observability_overview_fetchers.ts | 12 +-- .../get_transaction_coordinates.ts | 64 ------------- .../get_transactions_per_minute.ts | 95 +++++++++++++++++++ .../server/routes/observability_overview.ts | 8 +- .../components/app/section/apm/index.test.tsx | 26 +++++ .../components/app/section/apm/index.tsx | 15 ++- .../typings/fetch_overview_data/index.ts | 2 +- .../observability_overview.ts | 19 ++-- 9 files changed, 166 insertions(+), 95 deletions(-) delete mode 100644 x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts create mode 100644 x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts diff --git a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.test.ts b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.test.ts index 1821e92ee5a78f..29fabc51fd5827 100644 --- a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.test.ts +++ b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.test.ts @@ -46,11 +46,14 @@ describe('Observability dashboard data', () => { callApmApiMock.mockImplementation(() => Promise.resolve({ serviceCount: 10, - transactionCoordinates: [ - { x: 1, y: 1 }, - { x: 2, y: 2 }, - { x: 3, y: 3 }, - ], + transactionPerMinute: { + value: 2, + timeseries: [ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 }, + ], + }, }) ); const response = await fetchObservabilityOverviewPageData(params); @@ -81,7 +84,7 @@ describe('Observability dashboard data', () => { callApmApiMock.mockImplementation(() => Promise.resolve({ serviceCount: 0, - transactionCoordinates: [], + transactionPerMinute: { value: null, timeseries: [] }, }) ); const response = await fetchObservabilityOverviewPageData(params); @@ -108,7 +111,10 @@ describe('Observability dashboard data', () => { callApmApiMock.mockImplementation(() => Promise.resolve({ serviceCount: 0, - transactionCoordinates: [{ x: 1 }, { x: 2 }, { x: 3 }], + transactionPerMinute: { + value: 0, + timeseries: [{ x: 1 }, { x: 2 }, { x: 3 }], + }, }) ); const response = await fetchObservabilityOverviewPageData(params); 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 55ead8d942aca3..3a02efd05e5a5d 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 @@ -5,7 +5,6 @@ * 2.0. */ -import { mean } from 'lodash'; import { ApmFetchDataResponse, FetchDataParams, @@ -31,7 +30,7 @@ export const fetchObservabilityOverviewPageData = async ({ }, }); - const { serviceCount, transactionCoordinates } = data; + const { serviceCount, transactionPerMinute } = data; return { appLink: `/app/apm/services?rangeFrom=${relativeTime.start}&rangeTo=${relativeTime.end}`, @@ -42,17 +41,12 @@ export const fetchObservabilityOverviewPageData = async ({ }, transactions: { type: 'number', - value: - mean( - transactionCoordinates - .map(({ y }) => y) - .filter((y) => y && isFinite(y)) - ) || 0, + value: transactionPerMinute.value || 0, }, }, series: { transactions: { - coordinates: transactionCoordinates, + coordinates: transactionPerMinute.timeseries, }, }, }; diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts deleted file mode 100644 index aac18e2bdfe4c7..00000000000000 --- a/x-pack/plugins/apm/server/lib/observability_overview/get_transaction_coordinates.ts +++ /dev/null @@ -1,64 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { rangeQuery } from '../../../server/utils/queries'; -import { Coordinates } from '../../../../observability/typings/common'; -import { Setup, SetupTimeRange } from '../helpers/setup_request'; -import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; -import { calculateThroughput } from '../helpers/calculate_throughput'; -import { withApmSpan } from '../../utils/with_apm_span'; - -export function getTransactionCoordinates({ - setup, - bucketSize, - searchAggregatedTransactions, -}: { - setup: Setup & SetupTimeRange; - bucketSize: string; - searchAggregatedTransactions: boolean; -}): Promise { - return withApmSpan( - 'observability_overview_get_transaction_distribution', - async () => { - const { apmEventClient, start, end } = setup; - - const { aggregations } = await apmEventClient.search({ - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: rangeQuery(start, end), - }, - }, - aggs: { - distribution: { - date_histogram: { - field: '@timestamp', - fixed_interval: bucketSize, - min_doc_count: 0, - }, - }, - }, - }, - }); - - return ( - aggregations?.distribution.buckets.map((bucket) => ({ - x: bucket.key, - y: calculateThroughput({ start, end, value: bucket.doc_count }), - })) || [] - ); - } - ); -} diff --git a/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts b/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts new file mode 100644 index 00000000000000..da8ac7c50b5947 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/observability_overview/get_transactions_per_minute.ts @@ -0,0 +1,95 @@ +/* + * 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 { + TRANSACTION_PAGE_LOAD, + TRANSACTION_REQUEST, +} from '../../../common/transaction_types'; +import { TRANSACTION_TYPE } from '../../../common/elasticsearch_fieldnames'; +import { rangeQuery } from '../../../server/utils/queries'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; +import { getProcessorEventForAggregatedTransactions } from '../helpers/aggregated_transactions'; +import { calculateThroughput } from '../helpers/calculate_throughput'; +import { withApmSpan } from '../../utils/with_apm_span'; + +export function getTransactionsPerMinute({ + setup, + bucketSize, + searchAggregatedTransactions, +}: { + setup: Setup & SetupTimeRange; + bucketSize: string; + searchAggregatedTransactions: boolean; +}) { + return withApmSpan( + 'observability_overview_get_transactions_per_minute', + async () => { + const { apmEventClient, start, end } = setup; + + const { aggregations } = await apmEventClient.search({ + apm: { + events: [ + getProcessorEventForAggregatedTransactions( + searchAggregatedTransactions + ), + ], + }, + body: { + size: 0, + query: { + bool: { + filter: rangeQuery(start, end), + }, + }, + aggs: { + transactionType: { + terms: { + field: TRANSACTION_TYPE, + }, + aggs: { + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: bucketSize, + min_doc_count: 0, + }, + aggs: { + throughput: { rate: { unit: 'minute' as const } }, + }, + }, + }, + }, + }, + }, + }); + + if (!aggregations || !aggregations.transactionType.buckets) { + return { value: undefined, timeseries: [] }; + } + + const topTransactionTypeBucket = + aggregations.transactionType.buckets.find( + ({ key: transactionType }) => + transactionType === TRANSACTION_REQUEST || + transactionType === TRANSACTION_PAGE_LOAD + ) || aggregations.transactionType.buckets[0]; + + return { + value: calculateThroughput({ + start, + end, + value: topTransactionTypeBucket?.doc_count || 0, + }), + timeseries: + topTransactionTypeBucket?.timeseries.buckets.map((bucket) => ({ + x: bucket.key, + y: bucket.throughput.value, + })) || [], + }; + } + ); +} diff --git a/x-pack/plugins/apm/server/routes/observability_overview.ts b/x-pack/plugins/apm/server/routes/observability_overview.ts index b9c0a76b6fb90d..1aac2c09d01c5f 100644 --- a/x-pack/plugins/apm/server/routes/observability_overview.ts +++ b/x-pack/plugins/apm/server/routes/observability_overview.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; import { setupRequest } from '../lib/helpers/setup_request'; import { getServiceCount } from '../lib/observability_overview/get_service_count'; -import { getTransactionCoordinates } from '../lib/observability_overview/get_transaction_coordinates'; +import { getTransactionsPerMinute } from '../lib/observability_overview/get_transactions_per_minute'; import { getHasData } from '../lib/observability_overview/has_data'; import { createRoute } from './create_route'; import { rangeRt } from './default_api_types'; @@ -39,18 +39,18 @@ export const observabilityOverviewRoute = createRoute({ ); return withApmSpan('observability_overview', async () => { - const [serviceCount, transactionCoordinates] = await Promise.all([ + const [serviceCount, transactionPerMinute] = await Promise.all([ getServiceCount({ setup, searchAggregatedTransactions, }), - getTransactionCoordinates({ + getTransactionsPerMinute({ setup, bucketSize, searchAggregatedTransactions, }), ]); - return { serviceCount, transactionCoordinates }; + return { serviceCount, transactionPerMinute }; }); }, }); 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 e5f100be285e13..d29481a39eb726 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 @@ -56,6 +56,32 @@ describe('APMSection', () => { } as unknown) as ObservabilityPublicPluginsStart, })); }); + + it('renders transaction stat less then 1k', () => { + const resp = { + appLink: '/app/apm', + stats: { + services: { value: 11, type: 'number' }, + transactions: { value: 900, type: 'number' }, + }, + series: { + transactions: { coordinates: [] }, + }, + }; + jest.spyOn(fetcherHook, 'useFetcher').mockReturnValue({ + data: resp, + status: fetcherHook.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const { getByText, queryAllByTestId } = render(); + + expect(getByText('APM')).toBeInTheDocument(); + expect(getByText('View in app')).toBeInTheDocument(); + expect(getByText('Services 11')).toBeInTheDocument(); + expect(getByText('Throughput 900.0 tpm')).toBeInTheDocument(); + expect(queryAllByTestId('loading')).toEqual([]); + }); + it('renders with transaction series and stats', () => { jest.spyOn(fetcherHook, 'useFetcher').mockReturnValue({ data: response, 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 91a536840ecbd8..e71468d3b028c6 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 @@ -31,6 +31,19 @@ function formatTpm(value?: number) { return numeral(value).format('0.00a'); } +function formatTpmStat(value?: number) { + if (!value || value === 0) { + return '0'; + } + if (value <= 0.1) { + return '< 0.1'; + } + if (value > 1000) { + return numeral(value).format('0.00a'); + } + return numeral(value).format('0,0.0'); +} + export function APMSection({ bucketSize }: Props) { const theme = useContext(ThemeContext); const chartTheme = useChartTheme(); @@ -93,7 +106,7 @@ export function APMSection({ bucketSize }: Props) { ({ x: new Date(x).toISOString(), @@ -67,23 +68,23 @@ export default function ApiTest({ getService }: FtrProviderContext) { Array [ Object { "x": "2020-12-08T13:57:00.000Z", - "y": 0.166666666666667, + "y": 2, }, Object { "x": "2020-12-08T13:58:00.000Z", - "y": 5.23333333333333, + "y": 61, }, Object { "x": "2020-12-08T13:59:00.000Z", - "y": 4.4, + "y": 36, }, Object { "x": "2020-12-08T14:00:00.000Z", - "y": 5.73333333333333, + "y": 75, }, Object { "x": "2020-12-08T14:01:00.000Z", - "y": 4.33333333333333, + "y": 36, }, ] `); From 123f3400a82f89a7be5a6c2d9526e62102530c47 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Mon, 5 Apr 2021 10:19:32 -0500 Subject: [PATCH 07/10] [Workplace Search] Add sub nav and fix rendering bugs in Personal dashboard (#96100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix route for private deferated source summary * Make schema types nullable Federated sources don’t have counts and the server returns null so our routes have to expect that sometimes these values will be null * Add SourceSubNav to Personal dashboard We are able to leverage the existing component with a couple a small change; the existing componet is a subnav in the larger Enterprise Search shared navigation component and does not include its styles. This caused the list items to render with bullet points next to them. Adding this class and displaying the nav items as block elements fixes this issue. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../views/content_sources/components/source_sub_nav.tsx | 4 ++-- .../views/content_sources/private_sources_layout.test.tsx | 3 +++ .../views/content_sources/private_sources_layout.tsx | 3 +++ .../views/content_sources/source_logic.test.ts | 2 +- .../workplace_search/views/content_sources/source_logic.ts | 2 +- .../workplace_search/views/content_sources/sources.scss | 6 ++++++ .../server/routes/workplace_search/sources.ts | 6 +++--- 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx index 99cebd5ded585a..bf0c5471f7b574 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.tsx @@ -33,7 +33,7 @@ export const SourceSubNav: React.FC = () => { const isCustom = serviceType === CUSTOM_SERVICE_TYPE; return ( - <> +
{NAV.OVERVIEW} @@ -53,6 +53,6 @@ export const SourceSubNav: React.FC = () => { {NAV.SETTINGS} - +
); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx index 488eb4b49853bd..9e3b50ea083eb9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.test.tsx @@ -17,6 +17,8 @@ import { EuiCallOut } from '@elastic/eui'; import { ViewContentHeader } from '../../components/shared/view_content_header'; +import { SourceSubNav } from './components/source_sub_nav'; + import { PRIVATE_CAN_CREATE_PAGE_TITLE, PRIVATE_VIEW_ONLY_PAGE_TITLE, @@ -40,6 +42,7 @@ describe('PrivateSourcesLayout', () => { const wrapper = shallow({children}); expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1); + expect(wrapper.find(SourceSubNav)).toHaveLength(1); }); it('uses correct title and description when private sources are enabled', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx index bdc2421432c8a3..2a6281075dc400 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources_layout.tsx @@ -14,6 +14,8 @@ import { EuiPage, EuiPageSideBar, EuiPageBody, EuiCallOut } from '@elastic/eui'; import { AppLogic } from '../../app_logic'; import { ViewContentHeader } from '../../components/shared/view_content_header'; +import { SourceSubNav } from './components/source_sub_nav'; + import { PRIVATE_DASHBOARD_READ_ONLY_MODE_WARNING, PRIVATE_CAN_CREATE_PAGE_TITLE, @@ -49,6 +51,7 @@ export const PrivateSourcesLayout: React.FC = ({ + {readOnlyMode && ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts index d20d0576d11ce4..a9712cc4e1dc09 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts @@ -214,7 +214,7 @@ describe('SourceLogic', () => { SourceLogic.actions.initializeFederatedSummary(contentSource.id); expect(http.get).toHaveBeenCalledWith( - '/api/workplace_search/org/sources/123/federated_summary' + '/api/workplace_search/account/sources/123/federated_summary' ); await promise; expect(onUpdateSummarySpy).toHaveBeenCalledWith(contentSource.summary); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts index 72700ce42c75d9..3da90c4fc77392 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts @@ -156,7 +156,7 @@ export const SourceLogic = kea>({ } }, initializeFederatedSummary: async ({ sourceId }) => { - const route = `/api/workplace_search/org/sources/${sourceId}/federated_summary`; + const route = `/api/workplace_search/account/sources/${sourceId}/federated_summary`; try { const response = await HttpLogic.values.http.get(route); actions.onUpdateSummary(response.summary); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss index f142567fb621f0..abab139e32369a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources.scss @@ -30,3 +30,9 @@ margin-left: -$sideBarWidth; } } + +.sourcesSubNav { + li { + display: block; + } +} diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts index 8257dd0dc52b09..1dd6d859d88ade 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts @@ -22,9 +22,9 @@ const schemaValuesSchema = schema.recordOf( ); const pageSchema = schema.object({ - current: schema.number(), - size: schema.number(), - total_pages: schema.number(), + current: schema.nullable(schema.number()), + size: schema.nullable(schema.number()), + total_pages: schema.nullable(schema.number()), total_results: schema.number(), }); From ea03eb1bab086b6e6e526d8c1eb11ffa877d7d52 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Mon, 5 Apr 2021 10:27:05 -0500 Subject: [PATCH 08/10] [Enterprise Search] Expose core.chrome.setIsVisible for use in Workplace Search (#95984) * Hide chrome for Workplace Search by default The Workplace Search Personal dashboard needs the chrome hidden. We hide it globally here first to prevent a flash of chrome on the Personal dashboard and unhide it for admin routes, which will be in a future commit * Add core.chrome.setIsVisible to KibanaLogic * Toggle chrome visibility for Workplace Search * Add test * Refactor to set context and chrome when pathname changes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../applications/__mocks__/kibana_logic.mock.ts | 1 + .../enterprise_search/public/applications/index.tsx | 1 + .../applications/shared/kibana/kibana_logic.ts | 2 ++ .../applications/workplace_search/index.test.tsx | 4 +++- .../public/applications/workplace_search/index.tsx | 12 +++++++----- x-pack/plugins/enterprise_search/public/plugin.ts | 3 +++ 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts index 133f704fd59a9b..2325ddcf2b2704 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts @@ -19,6 +19,7 @@ export const mockKibanaValues = { history: mockHistory, navigateToUrl: jest.fn(), setBreadcrumbs: jest.fn(), + setChromeIsVisible: jest.fn(), setDocTitle: jest.fn(), renderHeaderActions: jest.fn(), }; diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 155ff5b92ba277..c2bf77751528ae 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -49,6 +49,7 @@ export const renderApp = ( history: params.history, navigateToUrl: core.application.navigateToUrl, setBreadcrumbs: core.chrome.setBreadcrumbs, + setChromeIsVisible: core.chrome.setIsVisible, setDocTitle: core.chrome.docTitle.change, renderHeaderActions: (HeaderActions) => params.setHeaderActionMenu((el) => renderHeaderActions(HeaderActions, store, el)), diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index 8015d22f7c44ab..2bef7d373f1606 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -24,6 +24,7 @@ interface KibanaLogicProps { charts: ChartsPluginStart; navigateToUrl: ApplicationStart['navigateToUrl']; setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void; + setChromeIsVisible(isVisible: boolean): void; setDocTitle(title: string): void; renderHeaderActions(HeaderActions: FC): void; } @@ -47,6 +48,7 @@ export const KibanaLogic = kea>({ {}, ], setBreadcrumbs: [props.setBreadcrumbs, {}], + setChromeIsVisible: [props.setChromeIsVisible, {}], setDocTitle: [props.setDocTitle, {}], renderHeaderActions: [props.renderHeaderActions, {}], }), diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx index 48bdcd6551b650..a2c0ec18def4b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx @@ -57,11 +57,13 @@ describe('WorkplaceSearchConfigured', () => { setMockActions({ initializeAppData, setContext }); }); - it('renders layout and header actions', () => { + it('renders layout, chrome, and header actions', () => { const wrapper = shallow(); expect(wrapper.find(Layout).first().prop('readOnlyMode')).toBeFalsy(); expect(wrapper.find(OverviewMVP)).toHaveLength(1); + + expect(mockKibanaValues.setChromeIsVisible).toHaveBeenCalledWith(true); expect(mockKibanaValues.renderHeaderActions).toHaveBeenCalledWith(WorkplaceSearchHeaderActions); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index c269a987dc0927..7a76de43be41ba 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -53,7 +53,7 @@ export const WorkplaceSearch: React.FC = (props) => { export const WorkplaceSearchConfigured: React.FC = (props) => { const { hasInitialized } = useValues(AppLogic); const { initializeAppData, setContext } = useActions(AppLogic); - const { renderHeaderActions } = useValues(KibanaLogic); + const { renderHeaderActions, setChromeIsVisible } = useValues(KibanaLogic); const { errorConnecting, readOnlyMode } = useValues(HttpLogic); const { pathname } = useLocation(); @@ -66,11 +66,13 @@ export const WorkplaceSearchConfigured: React.FC = (props) => { * Personal dashboard urls begin with /p/ * EX: http://localhost:5601/app/enterprise_search/workplace_search/p/sources */ - const personalSourceUrlRegex = /^\/p\//g; // matches '/p/*' + useEffect(() => { + const personalSourceUrlRegex = /^\/p\//g; // matches '/p/*' + const isOrganization = !pathname.match(personalSourceUrlRegex); // TODO: Once auth is figured out, we need to have a check for the equivilent of `isAdmin`. - // TODO: Once auth is figured out, we need to have a check for the equivilent of `isAdmin`. - const isOrganization = !pathname.match(personalSourceUrlRegex); - setContext(isOrganization); + setContext(isOrganization); + setChromeIsVisible(isOrganization); + }, [pathname]); useEffect(() => { if (!hasInitialized) { diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index f00e81a5accf72..dd1a62d243d030 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -114,6 +114,9 @@ export class EnterpriseSearchPlugin implements Plugin { const { chrome, http } = kibanaDeps.core; chrome.docTitle.change(WORKPLACE_SEARCH_PLUGIN.NAME); + // The Workplace Search Personal dashboard needs the chrome hidden. We hide it globally + // here first to prevent a flash of chrome on the Personal dashboard and unhide it for admin routes. + chrome.setIsVisible(false); await this.getInitialData(http); const pluginData = this.getPluginData(); From 95e45ddebc6e86bb3d63f04bd7c4e56ad8ef55bb Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 5 Apr 2021 18:00:13 +0200 Subject: [PATCH 09/10] Use plugin version in its publicPath (#95945) * Use plugin version in its publicPath * remove useless comment * fix types * update generated doc --- .../register_bundle_routes.test.ts | 15 +++++++------ .../bundle_routes/register_bundle_routes.ts | 8 +++---- src/core/server/legacy/legacy_service.test.ts | 1 + .../server/plugins/plugins_service.test.ts | 14 ++++++++++--- src/core/server/plugins/plugins_service.ts | 1 + src/core/server/plugins/plugins_system.ts | 1 + src/core/server/plugins/types.ts | 13 +++++++++--- .../bootstrap/get_plugin_bundle_paths.test.ts | 21 ++++++++++++------- .../bootstrap/get_plugin_bundle_paths.ts | 10 +++++++-- src/core/server/server.api.md | 8 +++---- 10 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts b/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts index d51c3691469575..830f4a9a943645 100644 --- a/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts +++ b/src/core/server/core_app/bundle_routes/register_bundle_routes.test.ts @@ -10,7 +10,7 @@ import { registerRouteForBundleMock } from './register_bundle_routes.test.mocks' import { PackageInfo } from '@kbn/config'; import { httpServiceMock } from '../../http/http_service.mock'; -import { UiPlugins } from '../../plugins'; +import { InternalPluginInfo, UiPlugins } from '../../plugins'; import { registerBundleRoutes } from './register_bundle_routes'; import { FileHashCache } from './file_hash_cache'; @@ -29,9 +29,12 @@ const createUiPlugins = (...ids: string[]): UiPlugins => ({ internal: ids.reduce((map, id) => { map.set(id, { publicTargetDir: `/plugins/${id}/public-target-dir`, + publicAssetsDir: `/plugins/${id}/public-assets-dir`, + version: '8.0.0', + requiredBundles: [], }); return map; - }, new Map()), + }, new Map()), }); describe('registerBundleRoutes', () => { @@ -86,16 +89,16 @@ describe('registerBundleRoutes', () => { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: '/plugins/plugin-a/public-target-dir', - publicPath: '/server-base-path/42/bundles/plugin/plugin-a/', - routePath: '/42/bundles/plugin/plugin-a/', + publicPath: '/server-base-path/42/bundles/plugin/plugin-a/8.0.0/', + routePath: '/42/bundles/plugin/plugin-a/8.0.0/', }); expect(registerRouteForBundleMock).toHaveBeenCalledWith(router, { fileHashCache: expect.any(FileHashCache), isDist: true, bundlesPath: '/plugins/plugin-b/public-target-dir', - publicPath: '/server-base-path/42/bundles/plugin/plugin-b/', - routePath: '/42/bundles/plugin/plugin-b/', + publicPath: '/server-base-path/42/bundles/plugin/plugin-b/8.0.0/', + routePath: '/42/bundles/plugin/plugin-b/8.0.0/', }); }); }); diff --git a/src/core/server/core_app/bundle_routes/register_bundle_routes.ts b/src/core/server/core_app/bundle_routes/register_bundle_routes.ts index ee54f8ef34622e..df46753747f5b8 100644 --- a/src/core/server/core_app/bundle_routes/register_bundle_routes.ts +++ b/src/core/server/core_app/bundle_routes/register_bundle_routes.ts @@ -27,7 +27,7 @@ import { registerRouteForBundle } from './bundles_route'; */ export function registerBundleRoutes({ router, - serverBasePath, // serverBasePath + serverBasePath, uiPlugins, packageInfo, }: { @@ -57,10 +57,10 @@ export function registerBundleRoutes({ isDist, }); - [...uiPlugins.internal.entries()].forEach(([id, { publicTargetDir }]) => { + [...uiPlugins.internal.entries()].forEach(([id, { publicTargetDir, version }]) => { registerRouteForBundle(router, { - publicPath: `${serverBasePath}/${buildNum}/bundles/plugin/${id}/`, - routePath: `/${buildNum}/bundles/plugin/${id}/`, + publicPath: `${serverBasePath}/${buildNum}/bundles/plugin/${id}/${version}/`, + routePath: `/${buildNum}/bundles/plugin/${id}/${version}/`, bundlesPath: publicTargetDir, fileHashCache, isDist, diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index d0a02b9859960b..67b5393f0b8381 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -91,6 +91,7 @@ beforeEach(() => { 'plugin-id', { requiredBundles: [], + version: '8.0.0', publicTargetDir: 'path/to/target/public', publicAssetsDir: '/plugins/name/assets/', }, diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 2d54648d229502..6bf7a1fadb4d3c 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -562,12 +562,12 @@ describe('PluginsService', () => { plugin$: from([ createPlugin('plugin-1', { path: 'path-1', - version: 'some-version', + version: 'version-1', configPath: 'plugin1', }), createPlugin('plugin-2', { path: 'path-2', - version: 'some-version', + version: 'version-2', configPath: 'plugin2', }), ]), @@ -577,7 +577,7 @@ describe('PluginsService', () => { }); describe('uiPlugins.internal', () => { - it('includes disabled plugins', async () => { + it('contains internal properties for plugins', async () => { config$.next({ plugins: { initialize: true }, plugin1: { enabled: false } }); const { uiPlugins } = await pluginsService.discover({ environment: environmentSetup }); expect(uiPlugins.internal).toMatchInlineSnapshot(` @@ -586,15 +586,23 @@ describe('PluginsService', () => { "publicAssetsDir": /path-1/public/assets, "publicTargetDir": /path-1/target/public, "requiredBundles": Array [], + "version": "version-1", }, "plugin-2" => Object { "publicAssetsDir": /path-2/public/assets, "publicTargetDir": /path-2/target/public, "requiredBundles": Array [], + "version": "version-2", }, } `); }); + + it('includes disabled plugins', async () => { + config$.next({ plugins: { initialize: true }, plugin1: { enabled: false } }); + const { uiPlugins } = await pluginsService.discover({ environment: environmentSetup }); + expect([...uiPlugins.internal.keys()].sort()).toEqual(['plugin-1', 'plugin-2']); + }); }); describe('plugin initialization', () => { diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 8b33e2cf4cc6be..09be40ecaf2a2c 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -222,6 +222,7 @@ export class PluginsService implements CoreService(); diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index a6086bd6f17e8e..3a01049c5e1fe3 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -224,12 +224,15 @@ export interface DiscoveredPlugin { */ export interface InternalPluginInfo { /** - * Bundles that must be loaded for this plugoin + * Version of the plugin + */ + readonly version: string; + /** + * Bundles that must be loaded for this plugin */ readonly requiredBundles: readonly string[]; /** - * Path to the target/public directory of the plugin which should be - * served + * Path to the target/public directory of the plugin which should be served */ readonly publicTargetDir: string; /** @@ -250,7 +253,9 @@ export interface Plugin< TPluginsStart extends object = object > { setup(core: CoreSetup, plugins: TPluginsSetup): TSetup; + start(core: CoreStart, plugins: TPluginsStart): TStart; + stop?(): void; } @@ -267,7 +272,9 @@ export interface AsyncPlugin< TPluginsStart extends object = object > { setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; + start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; + stop?(): void; } diff --git a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts b/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts index ea3843884df317..0abd8fd5a00576 100644 --- a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts +++ b/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { UiPlugins } from '../../plugins'; +import { InternalPluginInfo, UiPlugins } from '../../plugins'; import { getPluginsBundlePaths } from './get_plugin_bundle_paths'; const createUiPlugins = (pluginDeps: Record) => { @@ -16,12 +16,13 @@ const createUiPlugins = (pluginDeps: Record) => { browserConfigs: new Map(), }; - Object.entries(pluginDeps).forEach(([pluginId, deps]) => { + const addPlugin = (pluginId: string, deps: string[]) => { uiPlugins.internal.set(pluginId, { requiredBundles: deps, + version: '8.0.0', publicTargetDir: '', publicAssetsDir: '', - } as any); + } as InternalPluginInfo); uiPlugins.public.set(pluginId, { id: pluginId, configPath: 'config-path', @@ -29,6 +30,12 @@ const createUiPlugins = (pluginDeps: Record) => { requiredPlugins: [], requiredBundles: deps, }); + + deps.forEach((dep) => addPlugin(dep, [])); + }; + + Object.entries(pluginDeps).forEach(([pluginId, deps]) => { + addPlugin(pluginId, deps); }); return uiPlugins; @@ -56,13 +63,13 @@ describe('getPluginsBundlePaths', () => { }); expect(pluginBundlePaths.get('a')).toEqual({ - bundlePath: '/regular-bundle-path/plugin/a/a.plugin.js', - publicPath: '/regular-bundle-path/plugin/a/', + bundlePath: '/regular-bundle-path/plugin/a/8.0.0/a.plugin.js', + publicPath: '/regular-bundle-path/plugin/a/8.0.0/', }); expect(pluginBundlePaths.get('b')).toEqual({ - bundlePath: '/regular-bundle-path/plugin/b/b.plugin.js', - publicPath: '/regular-bundle-path/plugin/b/', + bundlePath: '/regular-bundle-path/plugin/b/8.0.0/b.plugin.js', + publicPath: '/regular-bundle-path/plugin/b/8.0.0/', }); }); }); diff --git a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts b/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts index c8291b2720a92c..86ffdcf835f7b5 100644 --- a/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts +++ b/src/core/server/rendering/bootstrap/get_plugin_bundle_paths.ts @@ -25,9 +25,15 @@ export const getPluginsBundlePaths = ({ while (pluginsToProcess.length > 0) { const pluginId = pluginsToProcess.pop() as string; + const plugin = uiPlugins.internal.get(pluginId); + if (!plugin) { + continue; + } + const { version } = plugin; + pluginBundlePaths.set(pluginId, { - publicPath: `${regularBundlePath}/plugin/${pluginId}/`, - bundlePath: `${regularBundlePath}/plugin/${pluginId}/${pluginId}.plugin.js`, + publicPath: `${regularBundlePath}/plugin/${pluginId}/${version}/`, + bundlePath: `${regularBundlePath}/plugin/${pluginId}/${version}/${pluginId}.plugin.js`, }); const pluginBundleIds = uiPlugins.internal.get(pluginId)?.requiredBundles ?? []; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index de96c5ccfb81ed..fb5fe3efd3e062 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -3259,9 +3259,9 @@ export const validBodyOutput: readonly ["data", "stream"]; // // src/core/server/elasticsearch/client/types.ts:94:7 - (ae-forgotten-export) The symbol "Explanation" needs to be exported by the entry point index.d.ts // src/core/server/http/router/response.ts:297:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:286:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:286:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:289:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:394:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "create" +// src/core/server/plugins/types.ts:293:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:293:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:296:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:401:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "create" ``` From 8e11e2598e874d603e99bcfac407ef9e09784102 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Mon, 5 Apr 2021 12:04:20 -0400 Subject: [PATCH 10/10] [Maps] Enable all zoom levels for all users (#96093) --- .github/CODEOWNERS | 1 - docs/developer/plugin-list.asciidoc | 4 - packages/kbn-optimizer/limits.yml | 1 - .../service_settings/service_settings.test.js | 50 ++------- .../service_settings/service_settings.ts | 17 +-- .../service_settings_types.ts | 2 - src/plugins/maps_legacy/kibana.json | 2 +- .../public/map/base_maps_visualization.js | 3 +- .../maps_legacy/public/map/kibana_map.js | 23 ---- .../maps_legacy/public/map/map_messages.js | 105 ------------------ test/functional/apps/visualize/_tile_map.ts | 59 ---------- tsconfig.json | 1 - tsconfig.refs.json | 1 - .../plugins/maps_legacy_licensing/README.md | 4 - .../plugins/maps_legacy_licensing/kibana.json | 8 -- .../maps_legacy_licensing/public/index.ts | 12 -- .../maps_legacy_licensing/public/plugin.ts | 48 -------- .../maps_legacy_licensing/tsconfig.json | 15 --- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 20 files changed, 12 insertions(+), 346 deletions(-) delete mode 100644 src/plugins/maps_legacy/public/map/map_messages.js delete mode 100644 x-pack/plugins/maps_legacy_licensing/README.md delete mode 100644 x-pack/plugins/maps_legacy_licensing/kibana.json delete mode 100644 x-pack/plugins/maps_legacy_licensing/public/index.ts delete mode 100644 x-pack/plugins/maps_legacy_licensing/public/plugin.ts delete mode 100644 x-pack/plugins/maps_legacy_licensing/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d14556ea1dabf5..b9afc197bac9c1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -146,7 +146,6 @@ /x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis #CC# /src/plugins/maps_legacy/ @elastic/kibana-gis #CC# /x-pack/plugins/file_upload @elastic/kibana-gis -#CC# /x-pack/plugins/maps_legacy_licensing @elastic/kibana-gis /src/plugins/tile_map/ @elastic/kibana-gis /src/plugins/region_map/ @elastic/kibana-gis diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index bcf74936077ece..691d7fb82f3bc4 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -452,10 +452,6 @@ using the CURL scripts in the scripts folder. |Visualize geo data from Elasticsearch or 3rd party geo-services. -|{kib-repo}blob/{branch}/x-pack/plugins/maps_legacy_licensing/README.md[mapsLegacyLicensing] -|This plugin provides access to the detailed tile map services from Elastic. - - |{kib-repo}blob/{branch}/x-pack/plugins/ml/readme.md[ml] |This plugin provides access to the machine learning features provided by Elastic. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 3c9fd4f59a406e..a027768ad66a0a 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -51,7 +51,6 @@ pageLoadAssetSize: management: 46112 maps: 80000 mapsLegacy: 87859 - mapsLegacyLicensing: 20214 ml: 82187 monitoring: 80000 navigation: 37269 diff --git a/src/plugins/maps_ems/public/service_settings/service_settings.test.js b/src/plugins/maps_ems/public/service_settings/service_settings.test.js index 5bd371aace79bb..eb67997c253b97 100644 --- a/src/plugins/maps_ems/public/service_settings/service_settings.test.js +++ b/src/plugins/maps_ems/public/service_settings/service_settings.test.js @@ -103,43 +103,8 @@ describe('service_settings (FKA tile_map test)', function () { expect(tmsService.attribution.includes('OpenStreetMap')).toEqual(true); }); - describe('modify - url', function () { - let tilemapServices; - + describe('tms mods', function () { let serviceSettings; - async function assertQuery(expected) { - const attrs = await serviceSettings.getAttributesForTMSLayer(tilemapServices[0]); - const urlObject = url.parse(attrs.url, true); - Object.keys(expected).forEach((key) => { - expect(urlObject.query[key]).toEqual(expected[key]); - }); - } - - it('accepts an object', async () => { - serviceSettings = makeServiceSettings(); - serviceSettings.setQueryParams({ foo: 'bar' }); - tilemapServices = await serviceSettings.getTMSServices(); - await assertQuery({ foo: 'bar' }); - }); - - it('merged additions with previous values', async () => { - // ensure that changes are always additive - serviceSettings = makeServiceSettings(); - serviceSettings.setQueryParams({ foo: 'bar' }); - serviceSettings.setQueryParams({ bar: 'stool' }); - tilemapServices = await serviceSettings.getTMSServices(); - await assertQuery({ foo: 'bar', bar: 'stool' }); - }); - - it('overwrites conflicting previous values', async () => { - serviceSettings = makeServiceSettings(); - // ensure that conflicts are overwritten - serviceSettings.setQueryParams({ foo: 'bar' }); - serviceSettings.setQueryParams({ bar: 'stool' }); - serviceSettings.setQueryParams({ foo: 'tstool' }); - tilemapServices = await serviceSettings.getTMSServices(); - await assertQuery({ foo: 'tstool', bar: 'stool' }); - }); it('should merge in tilemap url', async () => { serviceSettings = makeServiceSettings( @@ -161,7 +126,7 @@ describe('service_settings (FKA tile_map test)', function () { id: 'road_map', name: 'Road Map - Bright', url: - 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3', + 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl', minZoom: 0, maxZoom: 10, attribution: @@ -208,19 +173,19 @@ describe('service_settings (FKA tile_map test)', function () { ); expect(desaturationFalse.url).toEqual( - 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3' + 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl' ); expect(desaturationFalse.maxZoom).toEqual(10); expect(desaturationTrue.url).toEqual( - 'https://tiles.foobar/raster/styles/osm-bright-desaturated/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3' + 'https://tiles.foobar/raster/styles/osm-bright-desaturated/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl' ); expect(desaturationTrue.maxZoom).toEqual(18); expect(darkThemeDesaturationFalse.url).toEqual( - 'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3' + 'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl' ); expect(darkThemeDesaturationFalse.maxZoom).toEqual(22); expect(darkThemeDesaturationTrue.url).toEqual( - 'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3' + 'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl' ); expect(darkThemeDesaturationTrue.maxZoom).toEqual(22); }); @@ -264,14 +229,13 @@ describe('service_settings (FKA tile_map test)', function () { describe('File layers', function () { it('should load manifest (all props)', async function () { const serviceSettings = makeServiceSettings(); - serviceSettings.setQueryParams({ foo: 'bar' }); const fileLayers = await serviceSettings.getFileLayers(); expect(fileLayers.length).toEqual(19); const assertions = fileLayers.map(async function (fileLayer) { expect(fileLayer.origin).toEqual(ORIGIN.EMS); const fileUrl = await serviceSettings.getUrlForRegionLayer(fileLayer); const urlObject = url.parse(fileUrl, true); - Object.keys({ foo: 'bar', elastic_tile_service_tos: 'agree' }).forEach((key) => { + Object.keys({ elastic_tile_service_tos: 'agree' }).forEach((key) => { expect(typeof urlObject.query[key]).toEqual('string'); }); }); diff --git a/src/plugins/maps_ems/public/service_settings/service_settings.ts b/src/plugins/maps_ems/public/service_settings/service_settings.ts index f7c735b6c3037d..412db42a1570c2 100644 --- a/src/plugins/maps_ems/public/service_settings/service_settings.ts +++ b/src/plugins/maps_ems/public/service_settings/service_settings.ts @@ -22,7 +22,6 @@ export class ServiceSettings implements IServiceSettings { private readonly _mapConfig: MapsEmsConfig; private readonly _tilemapsConfig: TileMapConfig; private readonly _hasTmsConfigured: boolean; - private _showZoomMessage: boolean; private readonly _emsClient: EMSClient; private readonly tmsOptionsFromConfig: any; @@ -31,7 +30,6 @@ export class ServiceSettings implements IServiceSettings { this._tilemapsConfig = tilemapsConfig; this._hasTmsConfigured = typeof tilemapsConfig.url === 'string' && tilemapsConfig.url !== ''; - this._showZoomMessage = true; this._emsClient = new EMSClient({ language: i18n.getLocale(), appVersion: getKibanaVersion(), @@ -45,6 +43,9 @@ export class ServiceSettings implements IServiceSettings { return fetch(...args); }, }); + // any kibana user, regardless of distribution, should get all zoom levels + // use `sspl` license to indicate this + this._emsClient.addQueryParams({ license: 'sspl' }); const markdownIt = new MarkdownIt({ html: false, @@ -58,18 +59,6 @@ export class ServiceSettings implements IServiceSettings { }); } - shouldShowZoomMessage({ origin }: { origin: string }): boolean { - return origin === ORIGIN.EMS && this._showZoomMessage; - } - - enableZoomMessage(): void { - this._showZoomMessage = true; - } - - disableZoomMessage(): void { - this._showZoomMessage = false; - } - __debugStubManifestCalls(manifestRetrieval: () => Promise): { removeStub: () => void } { const oldGetManifest = this._emsClient.getManifest; diff --git a/src/plugins/maps_ems/public/service_settings/service_settings_types.ts b/src/plugins/maps_ems/public/service_settings/service_settings_types.ts index 80a9aae8358441..6b04bd200eba85 100644 --- a/src/plugins/maps_ems/public/service_settings/service_settings_types.ts +++ b/src/plugins/maps_ems/public/service_settings/service_settings_types.ts @@ -46,8 +46,6 @@ export interface IServiceSettings { getFileLayers(): Promise; getUrlForRegionLayer(layer: FileLayer): Promise; setQueryParams(params: { [p: string]: string }): void; - enableZoomMessage(): void; - disableZoomMessage(): void; getAttributesForTMSLayer( tmsServiceConfig: TmsLayer, isDesaturated: boolean, diff --git a/src/plugins/maps_legacy/kibana.json b/src/plugins/maps_legacy/kibana.json index 8e283288e34b2b..f321274791a3b4 100644 --- a/src/plugins/maps_legacy/kibana.json +++ b/src/plugins/maps_legacy/kibana.json @@ -5,5 +5,5 @@ "ui": true, "server": true, "requiredPlugins": ["mapsEms"], - "requiredBundles": ["kibanaReact", "visDefaultEditor", "mapsEms"] + "requiredBundles": ["visDefaultEditor", "mapsEms"] } diff --git a/src/plugins/maps_legacy/public/map/base_maps_visualization.js b/src/plugins/maps_legacy/public/map/base_maps_visualization.js index 9cd574c5246e86..a261bcf6edd809 100644 --- a/src/plugins/maps_legacy/public/map/base_maps_visualization.js +++ b/src/plugins/maps_legacy/public/map/base_maps_visualization.js @@ -193,13 +193,12 @@ export function BaseMapsVisualizationProvider() { isDesaturated, isDarkMode ); - const showZoomMessage = serviceSettings.shouldShowZoomMessage(tmsLayer); const options = { ...tmsLayer }; delete options.id; delete options.subdomains; this._kibanaMap.setBaseLayer({ baseLayerType: 'tms', - options: { ...options, showZoomMessage, ...meta }, + options: { ...options, ...meta }, }); } diff --git a/src/plugins/maps_legacy/public/map/kibana_map.js b/src/plugins/maps_legacy/public/map/kibana_map.js index eea83154192897..62dbbda2588a50 100644 --- a/src/plugins/maps_legacy/public/map/kibana_map.js +++ b/src/plugins/maps_legacy/public/map/kibana_map.js @@ -7,13 +7,11 @@ */ import { EventEmitter } from 'events'; -import { createZoomWarningMsg } from './map_messages'; import $ from 'jquery'; import { get, isEqual, escape } from 'lodash'; import { zoomToPrecision } from './zoom_to_precision'; import { i18n } from '@kbn/i18n'; import { ORIGIN } from '../../../maps_ems/common'; -import { getToasts } from '../kibana_services'; import { L } from '../leaflet'; function makeFitControl(fitContainer, kibanaMap) { @@ -479,22 +477,6 @@ export class KibanaMap extends EventEmitter { this._updateLegend(); } - _addMaxZoomMessage = (layer) => { - const zoomWarningMsg = createZoomWarningMsg( - getToasts(), - this.getZoomLevel, - this.getMaxZoomLevel - ); - - this._leafletMap.on('zoomend', zoomWarningMsg); - this._containerNode.setAttribute('data-test-subj', 'zoomWarningEnabled'); - - layer.on('remove', () => { - this._leafletMap.off('zoomend', zoomWarningMsg); - this._containerNode.removeAttribute('data-test-subj'); - }); - }; - setLegendPosition(position) { if (this._legendPosition === position) { if (!this._leafletLegendControl) { @@ -572,11 +554,6 @@ export class KibanaMap extends EventEmitter { }); this._leafletBaseLayer = baseLayer; - if (settings.options.showZoomMessage) { - baseLayer.on('add', () => { - this._addMaxZoomMessage(baseLayer); - }); - } this._leafletBaseLayer.addTo(this._leafletMap); this._leafletBaseLayer.bringToBack(); if (settings.options.minZoom > this._leafletMap.getZoom()) { diff --git a/src/plugins/maps_legacy/public/map/map_messages.js b/src/plugins/maps_legacy/public/map/map_messages.js deleted file mode 100644 index f60d819f0b3909..00000000000000 --- a/src/plugins/maps_legacy/public/map/map_messages.js +++ /dev/null @@ -1,105 +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 React from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; -import { toMountPoint } from '../../../kibana_react/public'; - -export const createZoomWarningMsg = (function () { - let disableZoomMsg = false; - const setZoomMsg = (boolDisableMsg) => (disableZoomMsg = boolDisableMsg); - - class ZoomWarning extends React.Component { - constructor(props) { - super(props); - this.state = { - disabled: false, - }; - } - - render() { - return ( -
-

- - {`default distribution `} - - ), - ems: ( - - {`Elastic Maps Service`} - - ), - wms: ( - - {`Custom WMS Configuration`} - - ), - configSettings: ( - - {`Custom TMS Using Config Settings`} - - ), - }} - /> -

- - { - this.setState( - { - disabled: true, - }, - () => this.props.onChange(this.state.disabled) - ); - }} - data-test-subj="suppressZoomWarnings" - > - {`Don't show again`} - -
- ); - } - } - - const zoomToast = { - title: 'No additional zoom levels', - text: toMountPoint(), - 'data-test-subj': 'maxZoomWarning', - }; - - return (toastService, getZoomLevel, getMaxZoomLevel) => { - return () => { - const zoomLevel = getZoomLevel(); - const maxMapZoom = getMaxZoomLevel(); - if (!disableZoomMsg && zoomLevel === maxMapZoom) { - toastService.addDanger(zoomToast); - } - }; - }; -})(); diff --git a/test/functional/apps/visualize/_tile_map.ts b/test/functional/apps/visualize/_tile_map.ts index 668aec6ac5783d..3af467affa1fbe 100644 --- a/test/functional/apps/visualize/_tile_map.ts +++ b/test/functional/apps/visualize/_tile_map.ts @@ -15,7 +15,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const inspector = getService('inspector'); const filterBar = getService('filterBar'); - const testSubjects = getService('testSubjects'); const browser = getService('browser'); const PageObjects = getPageObjects([ 'common', @@ -221,63 +220,5 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); - - describe('zoom warning behavior', function describeIndexTests() { - // Zoom warning is only applicable to OSS - this.tags(['skipCloud', 'skipFirefox']); - - const waitForLoading = false; - let zoomWarningEnabled; - let last = false; - const toastDefaultLife = 6000; - - before(async function () { - await browser.setWindowSize(1280, 1000); - - log.debug('navigateToApp visualize'); - await PageObjects.visualize.navigateToNewAggBasedVisualization(); - log.debug('clickTileMap'); - await PageObjects.visualize.clickTileMap(); - await PageObjects.visualize.clickNewSearch(); - - zoomWarningEnabled = await testSubjects.exists('zoomWarningEnabled'); - log.debug(`Zoom warning enabled: ${zoomWarningEnabled}`); - - const zoomLevel = 9; - for (let i = 0; i < zoomLevel; i++) { - await PageObjects.tileMap.clickMapZoomIn(); - } - }); - - beforeEach(async function () { - await PageObjects.tileMap.clickMapZoomIn(waitForLoading); - }); - - afterEach(async function () { - if (!last) { - await PageObjects.common.sleep(toastDefaultLife); - await PageObjects.tileMap.clickMapZoomOut(waitForLoading); - } - }); - - it('should show warning at zoom 10', async () => { - await testSubjects.existOrFail('maxZoomWarning'); - }); - - it('should continue providing zoom warning if left alone', async () => { - await testSubjects.existOrFail('maxZoomWarning'); - }); - - it('should suppress zoom warning if suppress warnings button clicked', async () => { - last = true; - await PageObjects.visChart.waitForVisualization(); - await testSubjects.click('suppressZoomWarnings'); - await PageObjects.tileMap.clickMapZoomOut(waitForLoading); - await testSubjects.waitForDeleted('suppressZoomWarnings'); - await PageObjects.tileMap.clickMapZoomIn(waitForLoading); - - await testSubjects.missingOrFail('maxZoomWarning'); - }); - }); }); } diff --git a/tsconfig.json b/tsconfig.json index 30944ac71fcc8a..7c06e808586401 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -181,7 +181,6 @@ { "path": "./x-pack/plugins/license_management/tsconfig.json" }, { "path": "./x-pack/plugins/licensing/tsconfig.json" }, { "path": "./x-pack/plugins/logstash/tsconfig.json" }, - { "path": "./x-pack/plugins/maps_legacy_licensing/tsconfig.json" }, { "path": "./x-pack/plugins/maps/tsconfig.json" }, { "path": "./x-pack/plugins/ml/tsconfig.json" }, { "path": "./x-pack/plugins/monitoring/tsconfig.json" }, diff --git a/tsconfig.refs.json b/tsconfig.refs.json index 2d9ddc1b9e5681..f13455a14b4df7 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -85,7 +85,6 @@ { "path": "./x-pack/plugins/license_management/tsconfig.json" }, { "path": "./x-pack/plugins/licensing/tsconfig.json" }, { "path": "./x-pack/plugins/logstash/tsconfig.json" }, - { "path": "./x-pack/plugins/maps_legacy_licensing/tsconfig.json" }, { "path": "./x-pack/plugins/maps/tsconfig.json" }, { "path": "./x-pack/plugins/ml/tsconfig.json" }, { "path": "./x-pack/plugins/monitoring/tsconfig.json" }, diff --git a/x-pack/plugins/maps_legacy_licensing/README.md b/x-pack/plugins/maps_legacy_licensing/README.md deleted file mode 100644 index 7c2ce84d848d43..00000000000000 --- a/x-pack/plugins/maps_legacy_licensing/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Tile Map Plugin - -This plugin provides access to the detailed tile map services from Elastic. - diff --git a/x-pack/plugins/maps_legacy_licensing/kibana.json b/x-pack/plugins/maps_legacy_licensing/kibana.json deleted file mode 100644 index 7a49e0aaa7be17..00000000000000 --- a/x-pack/plugins/maps_legacy_licensing/kibana.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "id": "mapsLegacyLicensing", - "version": "8.0.0", - "kibanaVersion": "kibana", - "server": false, - "ui": true, - "requiredPlugins": ["licensing", "mapsEms"] -} diff --git a/x-pack/plugins/maps_legacy_licensing/public/index.ts b/x-pack/plugins/maps_legacy_licensing/public/index.ts deleted file mode 100644 index 9105919eaa6353..00000000000000 --- a/x-pack/plugins/maps_legacy_licensing/public/index.ts +++ /dev/null @@ -1,12 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { MapsLegacyLicensing } from './plugin'; - -export function plugin() { - return new MapsLegacyLicensing(); -} diff --git a/x-pack/plugins/maps_legacy_licensing/public/plugin.ts b/x-pack/plugins/maps_legacy_licensing/public/plugin.ts deleted file mode 100644 index f8118575cd6a2a..00000000000000 --- a/x-pack/plugins/maps_legacy_licensing/public/plugin.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { LicensingPluginSetup, ILicense } from '../../licensing/public'; -import { IServiceSettings, MapsEmsPluginSetup } from '../../../../src/plugins/maps_ems/public'; - -/** - * These are the interfaces with your public contracts. You should export these - * for other plugins to use in _their_ `SetupDeps`/`StartDeps` interfaces. - * @public - */ - -export interface MapsLegacyLicensingSetupDependencies { - licensing: LicensingPluginSetup; - mapsEms: MapsEmsPluginSetup; -} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface MapsLegacyLicensingStartDependencies {} - -export type MapsLegacyLicensingSetup = ReturnType; -export type MapsLegacyLicensingStart = ReturnType; - -export class MapsLegacyLicensing - implements Plugin { - public setup(core: CoreSetup, plugins: MapsLegacyLicensingSetupDependencies) { - const { licensing, mapsEms } = plugins; - if (licensing) { - licensing.license$.subscribe(async (license: ILicense) => { - const serviceSettings: IServiceSettings = await mapsEms.getServiceSettings(); - const { uid, isActive } = license; - if (isActive && license.hasAtLeast('basic')) { - serviceSettings.setQueryParams({ license: uid || '' }); - serviceSettings.disableZoomMessage(); - } else { - serviceSettings.setQueryParams({ license: '' }); - serviceSettings.enableZoomMessage(); - } - }); - } - } - - public start(core: CoreStart, plugins: MapsLegacyLicensingStartDependencies) {} -} diff --git a/x-pack/plugins/maps_legacy_licensing/tsconfig.json b/x-pack/plugins/maps_legacy_licensing/tsconfig.json deleted file mode 100644 index 3b8102b5205a88..00000000000000 --- a/x-pack/plugins/maps_legacy_licensing/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "../../../tsconfig.project.json", - "compilerOptions": { - "composite": true, - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": ["public/**/*"], - "references": [ - { "path": "../licensing/tsconfig.json" }, - { "path": "../../../src/plugins/maps_ems/tsconfig.json" } - ] -} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 133b4d0b6aaa85..6dc490b4ffc530 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3080,7 +3080,6 @@ "maps_legacy.baseMapsVisualization.childShouldImplementMethodErrorMessage": "子はdata-updateに対応できるようこのメソッドを導入する必要があります", "maps_legacy.defaultDistributionMessage": "Mapsを入手するには、ElasticsearchとKibanaの{defaultDistribution}にアップグレードしてください。", "maps_legacy.kibanaMap.leaflet.fitDataBoundsAriaLabel": "データバウンドを合わせる", - "maps_legacy.kibanaMap.zoomWarning": "ズームレベルが最大に達しました。完全にズームインするには、ElasticsearchとKibanaの{defaultDistribution}にアップグレードしてください。{ems}ではより多くのズームレベルを無料で利用できます。または、独自のマップサーバーを構成できます。詳細は、{ wms }または{ configSettings}をご覧ください。", "maps_legacy.legacyMapDeprecationMessage": "Mapsを使用すると、複数のレイヤーとインデックスを追加する、個別のドキュメントをプロットする、データ値から特徴を表現する、ヒートマップ、グリッド、クラスターを追加するなど、さまざまなことが可能です。{getMapsMessage}", "maps_legacy.legacyMapDeprecationTitle": "{label}は8.0でMapsに移行されます。", "maps_legacy.openInMapsButtonLabel": "Mapsで表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0f9d8b90a2578b..32574690b13f24 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3101,7 +3101,6 @@ "maps_legacy.baseMapsVisualization.childShouldImplementMethodErrorMessage": "子对象应实现此方法以响应数据更新", "maps_legacy.defaultDistributionMessage": "要获取 Maps,请升级到 {defaultDistribution} 版的 Elasticsearch 和 Kibana。", "maps_legacy.kibanaMap.leaflet.fitDataBoundsAriaLabel": "适应数据边界", - "maps_legacy.kibanaMap.zoomWarning": "已达到缩放级别数目上限。要一直放大,请升级到 Elasticsearch 和 Kibana 的{defaultDistribution}。您可以通过 {ems} 免费使用其他缩放级别。或者,您可以配置自己的地图服务器。请前往 { wms } 或 { configSettings} 以获取详细信息。", "maps_legacy.legacyMapDeprecationMessage": "使用 Maps,可以添加多个图层和索引,绘制单个文档,使用数据值表示特征,添加热图、网格和集群,等等。{getMapsMessage}", "maps_legacy.legacyMapDeprecationTitle": "在 8.0 中,{label} 将迁移到 Maps。", "maps_legacy.openInMapsButtonLabel": "在 Maps 中查看",