From a8020ddb89d53c6e8ab0ca9453bea87a1bc4d939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Fri, 18 Jun 2021 09:33:25 +0200 Subject: [PATCH 01/23] Fixes wrong list exception type when creating endpoint event filters list (#102522) --- .../exception_lists/create_endoint_event_filters_list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endoint_event_filters_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_endoint_event_filters_list.ts index fda8de5da8aae..94a049d10cc45 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_endoint_event_filters_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_endoint_event_filters_list.ts @@ -61,7 +61,7 @@ export const createEndpointEventFiltersList = async ({ os_types: [], tags: [], tie_breaker_id: tieBreaker ?? uuid.v4(), - type: 'endpoint', + type: 'endpoint_events', updated_by: user, version, }, From 7267f505a5414ccf9bf49dafb8f40033f75612d1 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 18 Jun 2021 10:57:17 +0300 Subject: [PATCH 02/23] [Cases] Route: Get all alerts attach to a case (#101878) Co-authored-by: Jonathan Buttner --- .../plugins/cases/common/api/cases/alerts.ts | 18 ++ .../plugins/cases/common/api/cases/index.ts | 1 + x-pack/plugins/cases/common/constants.ts | 1 + .../classes/client.casesclient.md | 28 +-- .../interfaces/attachments_add.addargs.md | 4 +- ...attachments_client.attachmentssubclient.md | 33 ++- .../attachments_delete.deleteallargs.md | 4 +- .../attachments_delete.deleteargs.md | 6 +- .../interfaces/attachments_get.findargs.md | 4 +- ...ttachments_get.getallalertsattachtocase.md | 21 ++ .../interfaces/attachments_get.getallargs.md | 6 +- .../interfaces/attachments_get.getargs.md | 4 +- .../attachments_update.updateargs.md | 6 +- .../interfaces/cases_client.casessubclient.md | 18 +- .../cases_get.caseidsbyalertidparams.md | 4 +- .../interfaces/cases_get.getparams.md | 6 +- .../interfaces/cases_push.pushparams.md | 4 +- .../configure_client.configuresubclient.md | 8 +- .../interfaces/stats_client.statssubclient.md | 2 +- .../sub_cases_client.subcasesclient.md | 8 +- .../user_actions_client.useractionget.md | 4 +- ...ser_actions_client.useractionssubclient.md | 2 +- .../cases_client/modules/attachments_get.md | 1 + .../docs/cases_client/modules/cases_get.md | 4 +- .../__snapshots__/audit_logger.test.ts.snap | 84 +++++++ .../cases/server/authorization/index.ts | 8 + .../cases/server/authorization/types.ts | 1 + .../cases/server/client/alerts/client.ts | 21 +- .../plugins/cases/server/client/alerts/get.ts | 9 +- .../cases/server/client/alerts/types.ts | 20 ++ .../server/client/alerts/update_status.ts | 2 +- .../cases/server/client/attachments/client.ts | 21 +- .../cases/server/client/attachments/get.ts | 71 +++++- .../cases/server/client/cases/update.ts | 2 +- x-pack/plugins/cases/server/client/client.ts | 2 +- x-pack/plugins/cases/server/client/mocks.ts | 1 + .../cases/server/client/sub_cases/update.ts | 2 +- x-pack/plugins/cases/server/common/utils.ts | 11 +- .../server/routes/api/comments/get_alerts.ts | 41 ++++ .../plugins/cases/server/routes/api/index.ts | 2 + .../cases/server/services/alerts/index.ts | 2 +- .../server/services/attachments/index.ts | 51 +++++ x-pack/plugins/cases/server/services/mocks.ts | 1 + .../case_api_integration/common/lib/utils.ts | 20 ++ .../alerts/get_alerts_attached_to_case.ts | 215 ++++++++++++++++++ .../security_and_spaces/tests/common/index.ts | 1 + 46 files changed, 674 insertions(+), 111 deletions(-) create mode 100644 x-pack/plugins/cases/common/api/cases/alerts.ts create mode 100644 x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md create mode 100644 x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts create mode 100644 x-pack/test/case_api_integration/security_and_spaces/tests/common/alerts/get_alerts_attached_to_case.ts diff --git a/x-pack/plugins/cases/common/api/cases/alerts.ts b/x-pack/plugins/cases/common/api/cases/alerts.ts new file mode 100644 index 0000000000000..1a1abb4cbb66a --- /dev/null +++ b/x-pack/plugins/cases/common/api/cases/alerts.ts @@ -0,0 +1,18 @@ +/* + * 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 * as rt from 'io-ts'; + +const AlertRt = rt.type({ + id: rt.string, + index: rt.string, + attached_at: rt.string, +}); + +export const AlertResponseRt = rt.array(AlertRt); + +export type AlertResponse = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/api/cases/index.ts b/x-pack/plugins/cases/common/api/cases/index.ts index 0f78ca9b35377..ba8ad15d02e27 100644 --- a/x-pack/plugins/cases/common/api/cases/index.ts +++ b/x-pack/plugins/cases/common/api/cases/index.ts @@ -12,3 +12,4 @@ export * from './status'; export * from './user_actions'; export * from './sub_case'; export * from './constants'; +export * from './alerts'; diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts index f0d3e8ccbcdea..317fe1d8ed144 100644 --- a/x-pack/plugins/cases/common/constants.ts +++ b/x-pack/plugins/cases/common/constants.ts @@ -51,6 +51,7 @@ export const CASE_TAGS_URL = `${CASES_URL}/tags`; export const CASE_USER_ACTIONS_URL = `${CASE_DETAILS_URL}/user_actions`; export const CASE_ALERTS_URL = `${CASES_URL}/alerts/{alert_id}`; +export const CASE_DETAILS_ALERTS_URL = `${CASE_DETAILS_URL}/alerts`; /** * Action routes diff --git a/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md b/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md index 98e2f284da4a6..a20f018cffeb8 100644 --- a/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md +++ b/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md @@ -45,7 +45,7 @@ Client wrapper that contains accessor methods for individual entities within the **Returns:** [*CasesClient*](client.casesclient.md) -Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L28) +Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L28) ## Properties @@ -53,7 +53,7 @@ Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/2085a • `Private` `Readonly` **\_attachments**: [*AttachmentsSubClient*](../interfaces/attachments_client.attachmentssubclient.md) -Defined in: [client.ts:24](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L24) +Defined in: [client.ts:24](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L24) ___ @@ -61,7 +61,7 @@ ___ • `Private` `Readonly` **\_cases**: [*CasesSubClient*](../interfaces/cases_client.casessubclient.md) -Defined in: [client.ts:23](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L23) +Defined in: [client.ts:23](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L23) ___ @@ -69,7 +69,7 @@ ___ • `Private` `Readonly` **\_casesClientInternal**: *CasesClientInternal* -Defined in: [client.ts:22](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L22) +Defined in: [client.ts:22](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L22) ___ @@ -77,7 +77,7 @@ ___ • `Private` `Readonly` **\_configure**: [*ConfigureSubClient*](../interfaces/configure_client.configuresubclient.md) -Defined in: [client.ts:27](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L27) +Defined in: [client.ts:27](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L27) ___ @@ -85,7 +85,7 @@ ___ • `Private` `Readonly` **\_stats**: [*StatsSubClient*](../interfaces/stats_client.statssubclient.md) -Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L28) +Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L28) ___ @@ -93,7 +93,7 @@ ___ • `Private` `Readonly` **\_subCases**: [*SubCasesClient*](../interfaces/sub_cases_client.subcasesclient.md) -Defined in: [client.ts:26](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L26) +Defined in: [client.ts:26](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L26) ___ @@ -101,7 +101,7 @@ ___ • `Private` `Readonly` **\_userActions**: [*UserActionsSubClient*](../interfaces/user_actions_client.useractionssubclient.md) -Defined in: [client.ts:25](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L25) +Defined in: [client.ts:25](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L25) ## Accessors @@ -113,7 +113,7 @@ Retrieves an interface for interacting with attachments (comments) entities. **Returns:** [*AttachmentsSubClient*](../interfaces/attachments_client.attachmentssubclient.md) -Defined in: [client.ts:50](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L50) +Defined in: [client.ts:50](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L50) ___ @@ -125,7 +125,7 @@ Retrieves an interface for interacting with cases entities. **Returns:** [*CasesSubClient*](../interfaces/cases_client.casessubclient.md) -Defined in: [client.ts:43](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L43) +Defined in: [client.ts:43](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L43) ___ @@ -137,7 +137,7 @@ Retrieves an interface for interacting with the configuration of external connec **Returns:** [*ConfigureSubClient*](../interfaces/configure_client.configuresubclient.md) -Defined in: [client.ts:76](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L76) +Defined in: [client.ts:76](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L76) ___ @@ -149,7 +149,7 @@ Retrieves an interface for retrieving statistics related to the cases entities. **Returns:** [*StatsSubClient*](../interfaces/stats_client.statssubclient.md) -Defined in: [client.ts:83](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L83) +Defined in: [client.ts:83](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L83) ___ @@ -163,7 +163,7 @@ Currently this functionality is disabled and will throw an error if this functio **Returns:** [*SubCasesClient*](../interfaces/sub_cases_client.subcasesclient.md) -Defined in: [client.ts:66](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L66) +Defined in: [client.ts:66](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L66) ___ @@ -175,4 +175,4 @@ Retrieves an interface for interacting with the user actions associated with the **Returns:** [*UserActionsSubClient*](../interfaces/user_actions_client.useractionssubclient.md) -Defined in: [client.ts:57](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/client.ts#L57) +Defined in: [client.ts:57](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/client.ts#L57) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md index 1bbca9167a5c2..d5233ab6d8cb4 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md @@ -21,7 +21,7 @@ The arguments needed for creating a new attachment to a case. The case ID that this attachment will be associated with -Defined in: [attachments/add.ts:308](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/add.ts#L308) +Defined in: [attachments/add.ts:305](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/add.ts#L305) ___ @@ -31,4 +31,4 @@ ___ The attachment values. -Defined in: [attachments/add.ts:312](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/add.ts#L312) +Defined in: [attachments/add.ts:309](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/add.ts#L309) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md index e9f65bcf9915a..1a9a687aa812b 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md @@ -16,6 +16,7 @@ API for interacting with the attachments to a case. - [find](attachments_client.attachmentssubclient.md#find) - [get](attachments_client.attachmentssubclient.md#get) - [getAll](attachments_client.attachmentssubclient.md#getall) +- [getAllAlertsAttachToCase](attachments_client.attachmentssubclient.md#getallalertsattachtocase) - [update](attachments_client.attachmentssubclient.md#update) ## Methods @@ -34,7 +35,7 @@ Adds an attachment to a case. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [attachments/client.ts:25](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L25) +Defined in: [attachments/client.ts:35](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L35) ___ @@ -52,7 +53,7 @@ Deletes a single attachment for a specific case. **Returns:** *Promise* -Defined in: [attachments/client.ts:33](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L33) +Defined in: [attachments/client.ts:43](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L43) ___ @@ -70,7 +71,7 @@ Deletes all attachments associated with a single case. **Returns:** *Promise* -Defined in: [attachments/client.ts:29](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L29) +Defined in: [attachments/client.ts:39](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L39) ___ @@ -88,7 +89,7 @@ Retrieves all comments matching the search criteria. **Returns:** *Promise*<[*ICommentsResponse*](typedoc_interfaces.icommentsresponse.md)\> -Defined in: [attachments/client.ts:37](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L37) +Defined in: [attachments/client.ts:47](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L47) ___ @@ -106,7 +107,7 @@ Retrieves a single attachment for a case. **Returns:** *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\> -Defined in: [attachments/client.ts:45](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L45) +Defined in: [attachments/client.ts:59](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L59) ___ @@ -124,7 +125,25 @@ Gets all attachments for a single case. **Returns:** *Promise*<[*IAllCommentsResponse*](typedoc_interfaces.iallcommentsresponse.md)\> -Defined in: [attachments/client.ts:41](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L41) +Defined in: [attachments/client.ts:55](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L55) + +___ + +### getAllAlertsAttachToCase + +▸ **getAllAlertsAttachToCase**(`params`: [*GetAllAlertsAttachToCase*](attachments_get.getallalertsattachtocase.md)): *Promise*<{ `attached_at`: *string* ; `id`: *string* ; `index`: *string* }[]\> + +Retrieves all alerts attach to a case given a single case ID + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [*GetAllAlertsAttachToCase*](attachments_get.getallalertsattachtocase.md) | + +**Returns:** *Promise*<{ `attached_at`: *string* ; `id`: *string* ; `index`: *string* }[]\> + +Defined in: [attachments/client.ts:51](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L51) ___ @@ -144,4 +163,4 @@ The request must include all fields for the attachment. Even the fields that are **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [attachments/client.ts:51](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/client.ts#L51) +Defined in: [attachments/client.ts:65](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/client.ts#L65) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md index 26b00ac6e037e..437758a0147f2 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md @@ -21,7 +21,7 @@ Parameters for deleting all comments of a case or sub case. The case ID to delete all attachments for -Defined in: [attachments/delete.ts:26](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/delete.ts#L26) +Defined in: [attachments/delete.ts:31](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/delete.ts#L31) ___ @@ -31,4 +31,4 @@ ___ If specified the caseID will be ignored and this value will be used to find a sub case for deleting all the attachments -Defined in: [attachments/delete.ts:30](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/delete.ts#L30) +Defined in: [attachments/delete.ts:35](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/delete.ts#L35) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md index f9d4038eb417a..1afa5679161d9 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md @@ -22,7 +22,7 @@ Parameters for deleting a single attachment of a case or sub case. The attachment ID to delete -Defined in: [attachments/delete.ts:44](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/delete.ts#L44) +Defined in: [attachments/delete.ts:49](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/delete.ts#L49) ___ @@ -32,7 +32,7 @@ ___ The case ID to delete an attachment from -Defined in: [attachments/delete.ts:40](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/delete.ts#L40) +Defined in: [attachments/delete.ts:45](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/delete.ts#L45) ___ @@ -42,4 +42,4 @@ ___ If specified the caseID will be ignored and this value will be used to find a sub case for deleting the attachment -Defined in: [attachments/delete.ts:48](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/delete.ts#L48) +Defined in: [attachments/delete.ts:53](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/delete.ts#L53) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md index dbbac0065be85..dc0da295b26d2 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md @@ -21,7 +21,7 @@ Parameters for finding attachments of a case The case ID for finding associated attachments -Defined in: [attachments/get.ts:48](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L48) +Defined in: [attachments/get.ts:47](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L47) ___ @@ -48,4 +48,4 @@ Optional parameters for filtering the returned attachments | `sortOrder` | *undefined* \| ``"desc"`` \| ``"asc"`` | | `subCaseId` | *undefined* \| *string* | -Defined in: [attachments/get.ts:52](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L52) +Defined in: [attachments/get.ts:51](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L51) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md new file mode 100644 index 0000000000000..541d1cf8f1d80 --- /dev/null +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md @@ -0,0 +1,21 @@ +[Cases Client API Interface](../cases_client_api.md) / [attachments/get](../modules/attachments_get.md) / GetAllAlertsAttachToCase + +# Interface: GetAllAlertsAttachToCase + +[attachments/get](../modules/attachments_get.md).GetAllAlertsAttachToCase + +## Table of contents + +### Properties + +- [caseId](attachments_get.getallalertsattachtocase.md#caseid) + +## Properties + +### caseId + +• **caseId**: *string* + +The ID of the case to retrieve the alerts from + +Defined in: [attachments/get.ts:87](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L87) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md index dbd66291e22de..ae67f85e96fc0 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md @@ -22,7 +22,7 @@ Parameters for retrieving all attachments of a case The case ID to retrieve all attachments for -Defined in: [attachments/get.ts:62](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L62) +Defined in: [attachments/get.ts:61](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L61) ___ @@ -32,7 +32,7 @@ ___ Optionally include the attachments associated with a sub case -Defined in: [attachments/get.ts:66](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L66) +Defined in: [attachments/get.ts:65](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L65) ___ @@ -42,4 +42,4 @@ ___ If included the case ID will be ignored and the attachments will be retrieved from the specified ID of the sub case -Defined in: [attachments/get.ts:70](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L70) +Defined in: [attachments/get.ts:69](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L69) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md index abfd4bb5958d3..2fc569985f980 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md @@ -19,7 +19,7 @@ The ID of the attachment to retrieve -Defined in: [attachments/get.ts:81](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L81) +Defined in: [attachments/get.ts:80](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L80) ___ @@ -29,4 +29,4 @@ ___ The ID of the case to retrieve an attachment from -Defined in: [attachments/get.ts:77](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/get.ts#L77) +Defined in: [attachments/get.ts:76](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/get.ts#L76) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md index b571067175f62..4b2dd7b404e7a 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md @@ -22,7 +22,7 @@ Parameters for updating a single attachment The ID of the case that is associated with this attachment -Defined in: [attachments/update.ts:29](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/update.ts#L29) +Defined in: [attachments/update.ts:32](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/update.ts#L32) ___ @@ -32,7 +32,7 @@ ___ The ID of a sub case, if specified a sub case will be searched for to perform the attachment update instead of on a case -Defined in: [attachments/update.ts:37](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/update.ts#L37) +Defined in: [attachments/update.ts:40](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/update.ts#L40) ___ @@ -42,4 +42,4 @@ ___ The full attachment request with the fields updated with appropriate values -Defined in: [attachments/update.ts:33](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/attachments/update.ts#L33) +Defined in: [attachments/update.ts:36](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/attachments/update.ts#L36) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md index e7d7dea34d0ad..d86308720cb95 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md @@ -36,7 +36,7 @@ Creates a case. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [cases/client.ts:48](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L48) +Defined in: [cases/client.ts:48](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L48) ___ @@ -56,7 +56,7 @@ Delete a case and all its comments. **Returns:** *Promise* -Defined in: [cases/client.ts:72](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L72) +Defined in: [cases/client.ts:72](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L72) ___ @@ -76,7 +76,7 @@ If the `owner` field is left empty then all the cases that the user has access t **Returns:** *Promise*<[*ICasesFindResponse*](typedoc_interfaces.icasesfindresponse.md)\> -Defined in: [cases/client.ts:54](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L54) +Defined in: [cases/client.ts:54](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L54) ___ @@ -94,7 +94,7 @@ Retrieves a single case with the specified ID. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [cases/client.ts:58](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L58) +Defined in: [cases/client.ts:58](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L58) ___ @@ -112,7 +112,7 @@ Retrieves the case IDs given a single alert ID **Returns:** *Promise* -Defined in: [cases/client.ts:84](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L84) +Defined in: [cases/client.ts:84](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L84) ___ @@ -131,7 +131,7 @@ Retrieves all the reporters across all accessible cases. **Returns:** *Promise*<{ `email`: *undefined* \| ``null`` \| *string* ; `full_name`: *undefined* \| ``null`` \| *string* ; `username`: *undefined* \| ``null`` \| *string* }[]\> -Defined in: [cases/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L80) +Defined in: [cases/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L80) ___ @@ -150,7 +150,7 @@ Retrieves all the tags across all cases the user making the request has access t **Returns:** *Promise* -Defined in: [cases/client.ts:76](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L76) +Defined in: [cases/client.ts:76](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L76) ___ @@ -168,7 +168,7 @@ Pushes a specific case to an external system. **Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\> -Defined in: [cases/client.ts:62](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L62) +Defined in: [cases/client.ts:62](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L62) ___ @@ -186,4 +186,4 @@ Update the specified cases with the passed in values. **Returns:** *Promise*<[*ICasesResponse*](typedoc_interfaces.icasesresponse.md)\> -Defined in: [cases/client.ts:66](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/client.ts#L66) +Defined in: [cases/client.ts:66](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/client.ts#L66) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.caseidsbyalertidparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.caseidsbyalertidparams.md index 1b8abba1a4071..274b7a8f2d431 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.caseidsbyalertidparams.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.caseidsbyalertidparams.md @@ -21,7 +21,7 @@ Parameters for finding cases IDs using an alert ID The alert ID to search for -Defined in: [cases/get.ts:47](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L47) +Defined in: [cases/get.ts:42](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L42) ___ @@ -37,4 +37,4 @@ The filtering options when searching for associated cases. | :------ | :------ | | `owner` | *undefined* \| *string* \| *string*[] | -Defined in: [cases/get.ts:51](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L51) +Defined in: [cases/get.ts:46](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L46) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md index 8c12b5533ac18..a528b7ce6256d 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md @@ -22,7 +22,7 @@ The parameters for retrieving a case Case ID -Defined in: [cases/get.ts:122](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L122) +Defined in: [cases/get.ts:110](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L110) ___ @@ -32,7 +32,7 @@ ___ Whether to include the attachments for a case in the response -Defined in: [cases/get.ts:126](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L126) +Defined in: [cases/get.ts:114](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L114) ___ @@ -42,4 +42,4 @@ ___ Whether to include the attachments for all children of a case in the response -Defined in: [cases/get.ts:130](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L130) +Defined in: [cases/get.ts:118](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L118) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md index 9f1810e4f0cc2..979e30cb31d3f 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md @@ -21,7 +21,7 @@ Parameters for pushing a case to an external system The ID of a case -Defined in: [cases/push.ts:53](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/push.ts#L53) +Defined in: [cases/push.ts:53](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/push.ts#L53) ___ @@ -31,4 +31,4 @@ ___ The ID of an external system to push to -Defined in: [cases/push.ts:57](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/push.ts#L57) +Defined in: [cases/push.ts:57](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/push.ts#L57) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md index 9b3827a57a9d3..cf69b101ce2bc 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md @@ -31,7 +31,7 @@ Creates a configuration if one does not already exist. If one exists it is delet **Returns:** *Promise*<[*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\> -Defined in: [configure/client.ts:102](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/configure/client.ts#L102) +Defined in: [configure/client.ts:98](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/configure/client.ts#L98) ___ @@ -50,7 +50,7 @@ Retrieves the external connector configuration for a particular case owner. **Returns:** *Promise*<{} \| [*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\> -Defined in: [configure/client.ts:84](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/configure/client.ts#L84) +Defined in: [configure/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/configure/client.ts#L80) ___ @@ -62,7 +62,7 @@ Retrieves the valid external connectors supported by the cases plugin. **Returns:** *Promise* -Defined in: [configure/client.ts:88](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/configure/client.ts#L88) +Defined in: [configure/client.ts:84](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/configure/client.ts#L84) ___ @@ -81,4 +81,4 @@ Updates a particular configuration with new values. **Returns:** *Promise*<[*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\> -Defined in: [configure/client.ts:95](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/configure/client.ts#L95) +Defined in: [configure/client.ts:91](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/configure/client.ts#L91) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md index 7e01205395277..761b34b5205ec 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md @@ -29,4 +29,4 @@ Retrieves the total number of open, closed, and in-progress cases. **Returns:** *Promise*<{ `count_closed_cases`: *number* ; `count_in_progress_cases`: *number* ; `count_open_cases`: *number* }\> -Defined in: [stats/client.ts:34](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/stats/client.ts#L34) +Defined in: [stats/client.ts:34](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/stats/client.ts#L34) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md index 76df26524b7b0..c83c68620e8ac 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md @@ -31,7 +31,7 @@ Deletes the specified entities and their attachments. **Returns:** *Promise* -Defined in: [sub_cases/client.ts:60](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/sub_cases/client.ts#L60) +Defined in: [sub_cases/client.ts:68](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/sub_cases/client.ts#L68) ___ @@ -49,7 +49,7 @@ Retrieves the sub cases matching the search criteria. **Returns:** *Promise*<[*ISubCasesFindResponse*](typedoc_interfaces.isubcasesfindresponse.md)\> -Defined in: [sub_cases/client.ts:64](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/sub_cases/client.ts#L64) +Defined in: [sub_cases/client.ts:72](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/sub_cases/client.ts#L72) ___ @@ -67,7 +67,7 @@ Retrieves a single sub case. **Returns:** *Promise*<[*ISubCaseResponse*](typedoc_interfaces.isubcaseresponse.md)\> -Defined in: [sub_cases/client.ts:68](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/sub_cases/client.ts#L68) +Defined in: [sub_cases/client.ts:76](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/sub_cases/client.ts#L76) ___ @@ -86,4 +86,4 @@ Updates the specified sub cases to the new values included in the request. **Returns:** *Promise*<[*ISubCasesResponse*](typedoc_interfaces.isubcasesresponse.md)\> -Defined in: [sub_cases/client.ts:72](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/sub_cases/client.ts#L72) +Defined in: [sub_cases/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/sub_cases/client.ts#L80) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md index 2c0c084ab9b30..f992a4116c800 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md @@ -21,7 +21,7 @@ Parameters for retrieving user actions for a particular case The ID of the case -Defined in: [user_actions/client.ts:19](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/user_actions/client.ts#L19) +Defined in: [user_actions/client.ts:19](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/user_actions/client.ts#L19) ___ @@ -31,4 +31,4 @@ ___ If specified then a sub case will be used for finding all the user actions -Defined in: [user_actions/client.ts:23](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/user_actions/client.ts#L23) +Defined in: [user_actions/client.ts:23](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/user_actions/client.ts#L23) diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md index f03667eccb858..e838a72159bef 100644 --- a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md +++ b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md @@ -28,4 +28,4 @@ Retrieves all user actions for a particular case. **Returns:** *Promise*<[*ICaseUserActionsResponse*](typedoc_interfaces.icaseuseractionsresponse.md)\> -Defined in: [user_actions/client.ts:33](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/user_actions/client.ts#L33) +Defined in: [user_actions/client.ts:33](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/user_actions/client.ts#L33) diff --git a/x-pack/plugins/cases/docs/cases_client/modules/attachments_get.md b/x-pack/plugins/cases/docs/cases_client/modules/attachments_get.md index 99358d6683256..6460511da79a1 100644 --- a/x-pack/plugins/cases/docs/cases_client/modules/attachments_get.md +++ b/x-pack/plugins/cases/docs/cases_client/modules/attachments_get.md @@ -7,5 +7,6 @@ ### Interfaces - [FindArgs](../interfaces/attachments_get.findargs.md) +- [GetAllAlertsAttachToCase](../interfaces/attachments_get.getallalertsattachtocase.md) - [GetAllArgs](../interfaces/attachments_get.getallargs.md) - [GetArgs](../interfaces/attachments_get.getargs.md) diff --git a/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md b/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md index 9e896881df17b..acfa0b918aa9a 100644 --- a/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md +++ b/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md @@ -31,7 +31,7 @@ Retrieves the reporters from all the cases. **Returns:** *Promise* -Defined in: [cases/get.ts:279](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L279) +Defined in: [cases/get.ts:255](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L255) ___ @@ -50,4 +50,4 @@ Retrieves the tags from all the cases. **Returns:** *Promise* -Defined in: [cases/get.ts:217](https://github.com/jonathan-buttner/kibana/blob/2085a3b4480/x-pack/plugins/cases/server/client/cases/get.ts#L217) +Defined in: [cases/get.ts:205](https://github.com/jonathan-buttner/kibana/blob/0e98e105663/x-pack/plugins/cases/server/client/cases/get.ts#L205) diff --git a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap index 7f5b8406b89f3..3ca77944776b3 100644 --- a/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap +++ b/x-pack/plugins/cases/server/authorization/__snapshots__/audit_logger.test.ts.snap @@ -756,6 +756,90 @@ Object { } `; +exports[`audit_logger log function event structure creates the correct audit event for operation: "getAlertsAttachedToCase" with an error and entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_comment_alerts_attach_to_case", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "1", + "type": "cases-comments", + }, + }, + "message": "Failed attempt to access cases-comments [id=1] as owner \\"awesome\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getAlertsAttachedToCase" with an error but no entity 1`] = ` +Object { + "error": Object { + "code": "Error", + "message": "an error", + }, + "event": Object { + "action": "case_comment_alerts_attach_to_case", + "category": Array [ + "database", + ], + "outcome": "failure", + "type": Array [ + "access", + ], + }, + "message": "Failed attempt to access a comments as any owners", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getAlertsAttachedToCase" without an error but with an entity 1`] = ` +Object { + "event": Object { + "action": "case_comment_alerts_attach_to_case", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "kibana": Object { + "saved_object": Object { + "id": "5", + "type": "cases-comments", + }, + }, + "message": "User has accessed cases-comments [id=5] as owner \\"super\\"", +} +`; + +exports[`audit_logger log function event structure creates the correct audit event for operation: "getAlertsAttachedToCase" without an error or entity 1`] = ` +Object { + "event": Object { + "action": "case_comment_alerts_attach_to_case", + "category": Array [ + "database", + ], + "outcome": "success", + "type": Array [ + "access", + ], + }, + "message": "User has accessed a comments as any owners", +} +`; + exports[`audit_logger log function event structure creates the correct audit event for operation: "getAllComments" with an error and entity 1`] = ` Object { "error": Object { diff --git a/x-pack/plugins/cases/server/authorization/index.ts b/x-pack/plugins/cases/server/authorization/index.ts index 87683c55f5da0..90b89c7f75766 100644 --- a/x-pack/plugins/cases/server/authorization/index.ts +++ b/x-pack/plugins/cases/server/authorization/index.ts @@ -184,6 +184,14 @@ export const Operations: Record; updateStatus(args: AlertUpdateStatus): Promise; diff --git a/x-pack/plugins/cases/server/client/alerts/get.ts b/x-pack/plugins/cases/server/client/alerts/get.ts index 186f914aa2cd7..2048ccae4fa60 100644 --- a/x-pack/plugins/cases/server/client/alerts/get.ts +++ b/x-pack/plugins/cases/server/client/alerts/get.ts @@ -5,16 +5,11 @@ * 2.0. */ -import { AlertInfo } from '../../common'; -import { CasesClientGetAlertsResponse } from './types'; +import { CasesClientGetAlertsResponse, AlertGet } from './types'; import { CasesClientArgs } from '..'; -interface GetParams { - alertsInfo: AlertInfo[]; -} - export const get = async ( - { alertsInfo }: GetParams, + { alertsInfo }: AlertGet, clientArgs: CasesClientArgs ): Promise => { const { alertsService, scopedClusterClient, logger } = clientArgs; diff --git a/x-pack/plugins/cases/server/client/alerts/types.ts b/x-pack/plugins/cases/server/client/alerts/types.ts index 26a582d92e54b..95cd9ae33bff9 100644 --- a/x-pack/plugins/cases/server/client/alerts/types.ts +++ b/x-pack/plugins/cases/server/client/alerts/types.ts @@ -5,6 +5,9 @@ * 2.0. */ +import { CaseStatuses } from '../../../common/api'; +import { AlertInfo } from '../../common'; + interface Alert { id: string; index: string; @@ -17,3 +20,20 @@ interface Alert { } export type CasesClientGetAlertsResponse = Alert[]; + +/** + * Defines the fields necessary to update an alert's status. + */ +export interface UpdateAlertRequest { + id: string; + index: string; + status: CaseStatuses; +} + +export interface AlertUpdateStatus { + alerts: UpdateAlertRequest[]; +} + +export interface AlertGet { + alertsInfo: AlertInfo[]; +} diff --git a/x-pack/plugins/cases/server/client/alerts/update_status.ts b/x-pack/plugins/cases/server/client/alerts/update_status.ts index 3c7f60ecae15d..a0684b59241b0 100644 --- a/x-pack/plugins/cases/server/client/alerts/update_status.ts +++ b/x-pack/plugins/cases/server/client/alerts/update_status.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { UpdateAlertRequest } from './client'; import { CasesClientArgs } from '..'; +import { UpdateAlertRequest } from './types'; interface UpdateAlertsStatusArgs { alerts: UpdateAlertRequest[]; diff --git a/x-pack/plugins/cases/server/client/attachments/client.ts b/x-pack/plugins/cases/server/client/attachments/client.ts index 6a0f2c71ddd91..a07633c0dd38c 100644 --- a/x-pack/plugins/cases/server/client/attachments/client.ts +++ b/x-pack/plugins/cases/server/client/attachments/client.ts @@ -5,14 +5,24 @@ * 2.0. */ -import { CommentResponse } from '../../../common'; +import { AlertResponse, CommentResponse } from '../../../common'; +import { CasesClient } from '../client'; import { CasesClientInternal } from '../client_internal'; import { IAllCommentsResponse, ICaseResponse, ICommentsResponse } from '../typedoc_interfaces'; import { CasesClientArgs } from '../types'; import { AddArgs, addComment } from './add'; import { DeleteAllArgs, deleteAll, DeleteArgs, deleteComment } from './delete'; -import { find, FindArgs, get, getAll, GetAllArgs, GetArgs } from './get'; +import { + find, + FindArgs, + get, + getAll, + getAllAlertsAttachToCase, + GetAllAlertsAttachToCase, + GetAllArgs, + GetArgs, +} from './get'; import { update, UpdateArgs } from './update'; /** @@ -35,6 +45,10 @@ export interface AttachmentsSubClient { * Retrieves all comments matching the search criteria. */ find(findArgs: FindArgs): Promise; + /** + * Retrieves all alerts attach to a case given a single case ID + */ + getAllAlertsAttachToCase(params: GetAllAlertsAttachToCase): Promise; /** * Gets all attachments for a single case. */ @@ -58,6 +72,7 @@ export interface AttachmentsSubClient { */ export const createAttachmentsSubClient = ( clientArgs: CasesClientArgs, + casesClient: CasesClient, casesClientInternal: CasesClientInternal ): AttachmentsSubClient => { const attachmentSubClient: AttachmentsSubClient = { @@ -65,6 +80,8 @@ export const createAttachmentsSubClient = ( deleteAll: (deleteAllArgs: DeleteAllArgs) => deleteAll(deleteAllArgs, clientArgs), delete: (deleteArgs: DeleteArgs) => deleteComment(deleteArgs, clientArgs), find: (findArgs: FindArgs) => find(findArgs, clientArgs), + getAllAlertsAttachToCase: (params: GetAllAlertsAttachToCase) => + getAllAlertsAttachToCase(params, clientArgs, casesClient), getAll: (getAllArgs: GetAllArgs) => getAll(getAllArgs, clientArgs), get: (getArgs: GetArgs) => get(getArgs, clientArgs), update: (updateArgs: UpdateArgs) => update(updateArgs, clientArgs), diff --git a/x-pack/plugins/cases/server/client/attachments/get.ts b/x-pack/plugins/cases/server/client/attachments/get.ts index 590038a200e48..4f4ade51f9a59 100644 --- a/x-pack/plugins/cases/server/client/attachments/get.ts +++ b/x-pack/plugins/cases/server/client/attachments/get.ts @@ -5,12 +5,14 @@ * 2.0. */ import Boom from '@hapi/boom'; -import { SavedObjectsFindResponse } from 'kibana/server'; +import { SavedObject, SavedObjectsFindResponse } from 'kibana/server'; import { + AlertResponse, AllCommentsResponse, AllCommentsResponseRt, AssociationType, + AttributesTypeAlerts, CommentAttributes, CommentResponse, CommentResponseRt, @@ -26,12 +28,14 @@ import { transformComments, flattenCommentSavedObject, flattenCommentSavedObjects, + getIDsAndIndicesAsArrays, } from '../../common'; import { defaultPage, defaultPerPage } from '../../routes/api'; import { CasesClientArgs } from '../types'; import { combineFilters, stringToKueryNode } from '../utils'; import { Operations } from '../../authorization'; import { includeFieldsRequiredForAuthentication } from '../../authorization/utils'; +import { CasesClient } from '../client'; /** * Parameters for finding attachments of a case @@ -76,6 +80,71 @@ export interface GetArgs { attachmentID: string; } +export interface GetAllAlertsAttachToCase { + /** + * The ID of the case to retrieve the alerts from + */ + caseId: string; +} + +const normalizeAlertResponse = (alerts: Array>): AlertResponse => + alerts.reduce((acc: AlertResponse, alert) => { + const { ids, indices } = getIDsAndIndicesAsArrays(alert.attributes); + + if (ids.length !== indices.length) { + return acc; + } + + return [ + ...acc, + ...ids.map((id, index) => ({ + id, + index: indices[index], + attached_at: alert.attributes.created_at, + })), + ]; + }, []); + +/** + * Retrieves all alerts attached to a specific case. + * + * @ignore + */ +export const getAllAlertsAttachToCase = async ( + { caseId }: GetAllAlertsAttachToCase, + clientArgs: CasesClientArgs, + casesClient: CasesClient +): Promise => { + const { unsecuredSavedObjectsClient, authorization, attachmentService } = clientArgs; + + // This will perform an authorization check to ensure the user has access to the parent case + const theCase = await casesClient.cases.get({ + id: caseId, + includeComments: false, + includeSubCaseComments: false, + }); + + const { + filter: authorizationFilter, + ensureSavedObjectsAreAuthorized, + } = await authorization.getAuthorizationFilter(Operations.getAlertsAttachedToCase); + + const alerts = await attachmentService.getAllAlertsAttachToCase({ + unsecuredSavedObjectsClient, + caseId: theCase.id, + filter: authorizationFilter, + }); + + ensureSavedObjectsAreAuthorized( + alerts.map((alert) => ({ + owner: alert.attributes.owner, + id: alert.id, + })) + ); + + return normalizeAlertResponse(alerts); +}; + /** * Retrieves the attachments for a case entity. This support pagination. * diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts index 5726cfe44f697..e5d9e1cddeee6 100644 --- a/x-pack/plugins/cases/server/client/cases/update.ts +++ b/x-pack/plugins/cases/server/client/cases/update.ts @@ -52,7 +52,7 @@ import { isCommentRequestTypeAlertOrGenAlert, transformCaseConnectorToEsConnector, } from '../../common'; -import { UpdateAlertRequest } from '../alerts/client'; +import { UpdateAlertRequest } from '../alerts/types'; import { CasesClientInternal } from '../client_internal'; import { CasesClientArgs } from '..'; import { Operations, OwnerEntity } from '../../authorization'; diff --git a/x-pack/plugins/cases/server/client/client.ts b/x-pack/plugins/cases/server/client/client.ts index b7b1dd46d003d..37e4cd52681e5 100644 --- a/x-pack/plugins/cases/server/client/client.ts +++ b/x-pack/plugins/cases/server/client/client.ts @@ -30,7 +30,7 @@ export class CasesClient { constructor(args: CasesClientArgs) { this._casesClientInternal = createCasesClientInternal(args); this._cases = createCasesSubClient(args, this, this._casesClientInternal); - this._attachments = createAttachmentsSubClient(args, this._casesClientInternal); + this._attachments = createAttachmentsSubClient(args, this, this._casesClientInternal); this._userActions = createUserActionsSubClient(args); this._subCases = createSubCasesClient(args, this._casesClientInternal); this._configure = createConfigurationSubClient(args, this._casesClientInternal); diff --git a/x-pack/plugins/cases/server/client/mocks.ts b/x-pack/plugins/cases/server/client/mocks.ts index 10b298995f87a..f6a36369c0b03 100644 --- a/x-pack/plugins/cases/server/client/mocks.ts +++ b/x-pack/plugins/cases/server/client/mocks.ts @@ -43,6 +43,7 @@ const createAttachmentsSubClientMock = (): AttachmentsSubClientMock => { getAll: jest.fn(), get: jest.fn(), update: jest.fn(), + getAllAlertsAttachToCase: jest.fn(), }; }; diff --git a/x-pack/plugins/cases/server/client/sub_cases/update.ts b/x-pack/plugins/cases/server/client/sub_cases/update.ts index c2c5f8719e64a..be671a8087f8e 100644 --- a/x-pack/plugins/cases/server/client/sub_cases/update.ts +++ b/x-pack/plugins/cases/server/client/sub_cases/update.ts @@ -44,7 +44,7 @@ import { isCommentRequestTypeAlertOrGenAlert, flattenSubCaseSavedObject, } from '../../common'; -import { UpdateAlertRequest } from '../../client/alerts/client'; +import { UpdateAlertRequest } from '../../client/alerts/types'; import { CasesClientArgs } from '../types'; import { CasesClientInternal } from '../client_internal'; diff --git a/x-pack/plugins/cases/server/common/utils.ts b/x-pack/plugins/cases/server/common/utils.ts index 54124260ae5d2..70ecc50df0f48 100644 --- a/x-pack/plugins/cases/server/common/utils.ts +++ b/x-pack/plugins/cases/server/common/utils.ts @@ -34,7 +34,7 @@ import { SubCasesFindResponse, User, } from '../../common'; -import { UpdateAlertRequest } from '../client/alerts/client'; +import { UpdateAlertRequest } from '../client/alerts/types'; /** * Default sort field for querying saved objects. @@ -271,17 +271,12 @@ const getAndValidateAlertInfoFromComment = (comment: CommentRequest): AlertInfo[ /** * Builds an AlertInfo object accumulating the alert IDs and indices for the passed in alerts. */ -export const getAlertInfoFromComments = (comments: CommentRequest[] | undefined): AlertInfo[] => { - if (comments === undefined) { - return []; - } - - return comments.reduce((acc: AlertInfo[], comment) => { +export const getAlertInfoFromComments = (comments: CommentRequest[] = []): AlertInfo[] => + comments.reduce((acc: AlertInfo[], comment) => { const alertInfo = getAndValidateAlertInfoFromComment(comment); acc.push(...alertInfo); return acc; }, []); -}; type NewCommentArgs = CommentRequest & { associationType: AssociationType; diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts new file mode 100644 index 0000000000000..9c0bfac4d9c6e --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.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 { schema } from '@kbn/config-schema'; + +import { RouteDeps } from '../types'; +import { wrapError } from '../utils'; +import { CASE_DETAILS_ALERTS_URL } from '../../../../common/constants'; + +export function initGetAllAlertsAttachToCaseApi({ router, logger }: RouteDeps) { + router.get( + { + path: CASE_DETAILS_ALERTS_URL, + validate: { + params: schema.object({ + case_id: schema.string({ minLength: 1 }), + }), + }, + }, + async (context, request, response) => { + try { + const caseId = request.params.case_id; + + const casesClient = await context.cases.getCasesClient(); + + return response.ok({ + body: await casesClient.attachments.getAllAlertsAttachToCase({ caseId }), + }); + } catch (error) { + logger.error( + `Failed to retrieve alert ids for this case id: ${request.params.case_id}: ${error}` + ); + return response.customError(wrapError(error)); + } + } + ); +} diff --git a/x-pack/plugins/cases/server/routes/api/index.ts b/x-pack/plugins/cases/server/routes/api/index.ts index 4f6db0c1081b1..011464a73396f 100644 --- a/x-pack/plugins/cases/server/routes/api/index.ts +++ b/x-pack/plugins/cases/server/routes/api/index.ts @@ -39,6 +39,7 @@ import { initFindSubCasesApi } from './sub_case/find_sub_cases'; import { initDeleteSubCasesApi } from './sub_case/delete_sub_cases'; import { ENABLE_CASE_CONNECTOR } from '../../../common'; import { initGetCaseIdsByAlertIdApi } from './cases/alerts/get_cases'; +import { initGetAllAlertsAttachToCaseApi } from './comments/get_alerts'; /** * Default page number when interacting with the saved objects API. @@ -89,4 +90,5 @@ export function initCaseApi(deps: RouteDeps) { initGetTagsApi(deps); // Alerts initGetCaseIdsByAlertIdApi(deps); + initGetAllAlertsAttachToCaseApi(deps); } diff --git a/x-pack/plugins/cases/server/services/alerts/index.ts b/x-pack/plugins/cases/server/services/alerts/index.ts index 4b2460d4b68cd..e33b0385bc123 100644 --- a/x-pack/plugins/cases/server/services/alerts/index.ts +++ b/x-pack/plugins/cases/server/services/alerts/index.ts @@ -12,7 +12,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { ElasticsearchClient, Logger } from 'kibana/server'; import { MAX_ALERTS_PER_SUB_CASE } from '../../../common'; import { AlertInfo, createCaseError } from '../../common'; -import { UpdateAlertRequest } from '../../client/alerts/client'; +import { UpdateAlertRequest } from '../../client/alerts/types'; export type AlertServiceContract = PublicMethodsOf; diff --git a/x-pack/plugins/cases/server/services/attachments/index.ts b/x-pack/plugins/cases/server/services/attachments/index.ts index 685b088701f68..c2d9b4826fc14 100644 --- a/x-pack/plugins/cases/server/services/attachments/index.ts +++ b/x-pack/plugins/cases/server/services/attachments/index.ts @@ -7,12 +7,23 @@ import { Logger, SavedObject, SavedObjectReference } from 'kibana/server'; +import { KueryNode } from '../../../../../../src/plugins/data/common'; import { + AttributesTypeAlerts, CASE_COMMENT_SAVED_OBJECT, CommentAttributes as AttachmentAttributes, CommentPatchAttributes as AttachmentPatchAttributes, + CASE_SAVED_OBJECT, + MAX_DOCS_PER_PAGE, + CommentType, } from '../../../common'; import { ClientArgs } from '..'; +import { buildFilter, combineFilters } from '../../client/utils'; + +interface GetAllAlertsAttachToCaseArgs extends ClientArgs { + caseId: string; + filter?: KueryNode; +} interface GetAttachmentArgs extends ClientArgs { attachmentId: string; @@ -39,6 +50,46 @@ interface BulkUpdateAttachmentArgs extends ClientArgs { export class AttachmentService { constructor(private readonly log: Logger) {} + /** + * Retrieves all the alerts attached to a case. + */ + public async getAllAlertsAttachToCase({ + unsecuredSavedObjectsClient, + caseId, + filter, + }: GetAllAlertsAttachToCaseArgs): Promise>> { + try { + this.log.debug(`Attempting to GET all alerts for case id ${caseId}`); + const alertsFilter = buildFilter({ + filters: [CommentType.alert, CommentType.generatedAlert], + field: 'type', + operator: 'or', + type: CASE_COMMENT_SAVED_OBJECT, + }); + + const combinedFilter = combineFilters([alertsFilter, filter]); + + const finder = unsecuredSavedObjectsClient.createPointInTimeFinder({ + type: CASE_COMMENT_SAVED_OBJECT, + hasReference: { type: CASE_SAVED_OBJECT, id: caseId }, + sortField: 'created_at', + sortOrder: 'asc', + filter: combinedFilter, + perPage: MAX_DOCS_PER_PAGE, + }); + + let result: Array> = []; + for await (const userActionSavedObject of finder.find()) { + result = result.concat(userActionSavedObject.saved_objects); + } + + return result; + } catch (error) { + this.log.error(`Error on GET all alerts for case id ${caseId}: ${error}`); + throw error; + } + } + public async get({ unsecuredSavedObjectsClient, attachmentId, diff --git a/x-pack/plugins/cases/server/services/mocks.ts b/x-pack/plugins/cases/server/services/mocks.ts index ce9aec942220a..c82624cd50ab6 100644 --- a/x-pack/plugins/cases/server/services/mocks.ts +++ b/x-pack/plugins/cases/server/services/mocks.ts @@ -104,6 +104,7 @@ export const createAttachmentServiceMock = (): AttachmentServiceMock => { create: jest.fn(), update: jest.fn(), bulkUpdate: jest.fn(), + getAllAlertsAttachToCase: jest.fn(), }; // the cast here is required because jest.Mocked tries to include private members and would throw an error diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index 9ed5d84e54621..63be1736405fc 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -45,6 +45,7 @@ import { CasesStatusResponse, CasesConfigurationsResponse, CaseUserActionsResponse, + AlertResponse, } from '../../../../plugins/cases/common/api'; import { getPostCaseRequest, postCollectionReq, postCommentGenAlertReq } from './mock'; import { getCaseUserActionUrl, getSubCasesUrl } from '../../../../plugins/cases/common/api/helpers'; @@ -1102,3 +1103,22 @@ export const pushCase = async ({ return res; }; + +export const getAlertsAttachedToCase = async ({ + supertest, + caseId, + expectedHttpCode = 200, + auth = { user: superUser, space: null }, +}: { + supertest: st.SuperTest; + caseId: string; + expectedHttpCode?: number; + auth?: { user: User; space: string | null }; +}): Promise => { + const { body: theCase } = await supertest + .get(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/alerts`) + .auth(auth.user.username, auth.user.password) + .expect(expectedHttpCode); + + return theCase; +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/alerts/get_alerts_attached_to_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/alerts/get_alerts_attached_to_case.ts new file mode 100644 index 0000000000000..d7b4e82c017db --- /dev/null +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/alerts/get_alerts_attached_to_case.ts @@ -0,0 +1,215 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +import { getPostCaseRequest, postCommentAlertReq } from '../../../../common/lib/mock'; +import { + createCase, + createComment, + deleteAllCaseItems, + getAlertsAttachedToCase, +} from '../../../../common/lib/utils'; +import { + globalRead, + noKibanaPrivileges, + obsOnly, + obsOnlyRead, + obsSec, + obsSecRead, + secOnly, + secOnlyRead, + superUser, +} from '../../../../common/lib/authentication/users'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const es = getService('es'); + + describe('get all alerts attach to a case', () => { + afterEach(async () => { + await deleteAllCaseItems(es); + }); + + it('should return all alerts for the specified case id', async () => { + const theCase = await createCase(supertest, getPostCaseRequest()); + + await createComment({ supertest, caseId: theCase.id, params: postCommentAlertReq }); + const updatedCase = await createComment({ + supertest, + caseId: theCase.id, + params: { ...postCommentAlertReq, alertId: 'test-id-2', index: 'test-index-2' }, + }); + + const alerts = await getAlertsAttachedToCase({ supertest, caseId: theCase.id }); + expect(alerts).to.eql([ + { + id: 'test-id', + index: 'test-index', + attached_at: updatedCase.comments![0].created_at, + }, + { + id: 'test-id-2', + index: 'test-index-2', + attached_at: updatedCase.comments![1].created_at, + }, + ]); + }); + + it('should return a 404 when case does not exist', async () => { + await getAlertsAttachedToCase({ supertest, caseId: 'not-exists', expectedHttpCode: 404 }); + }); + + it('should return a 404 when case id is empty', async () => { + await getAlertsAttachedToCase({ supertest, caseId: '', expectedHttpCode: 404 }); + }); + + describe('rbac', () => { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + it('should return the correct alert', async () => { + const secOnlyAuth = { user: secOnly, space: 'space1' }; + const obsOnlyAuth = { user: obsOnly, space: 'space1' }; + + const [case1, case2] = await Promise.all([ + createCase(supertestWithoutAuth, getPostCaseRequest(), 200, secOnlyAuth), + createCase( + supertestWithoutAuth, + getPostCaseRequest({ owner: 'observabilityFixture' }), + 200, + obsOnlyAuth + ), + ]); + + const [case2WithComments] = await Promise.all([ + createComment({ + supertest: supertestWithoutAuth, + caseId: case2.id, + params: { ...postCommentAlertReq, alertId: 'test-id-3', owner: 'observabilityFixture' }, + auth: obsOnlyAuth, + }), + createComment({ + supertest: supertestWithoutAuth, + caseId: case1.id, + params: postCommentAlertReq, + auth: secOnlyAuth, + }), + ]); + + // This call cannot be made inside the Promise.all call + // as there will be a race condition between the two calls + // and a 409 version conflict will be thrown + const case1WithComments = await createComment({ + supertest: supertestWithoutAuth, + caseId: case1.id, + params: { ...postCommentAlertReq, alertId: 'test-id-2' }, + auth: secOnlyAuth, + }); + + for (const scenario of [ + { + user: globalRead, + cases: [ + { theCase: case1WithComments, expectedAlerts: ['test-id', 'test-id-2'] }, + { theCase: case2WithComments, expectedAlerts: ['test-id-3'] }, + ], + }, + { + user: superUser, + cases: [ + { theCase: case1WithComments, expectedAlerts: ['test-id', 'test-id-2'] }, + { theCase: case2WithComments, expectedAlerts: ['test-id-3'] }, + ], + }, + { + user: secOnly, + cases: [{ theCase: case1WithComments, expectedAlerts: ['test-id', 'test-id-2'] }], + }, + { + user: secOnlyRead, + cases: [{ theCase: case1WithComments, expectedAlerts: ['test-id', 'test-id-2'] }], + }, + { + user: obsOnly, + cases: [{ theCase: case2WithComments, expectedAlerts: ['test-id-3'] }], + }, + { + user: obsOnlyRead, + cases: [{ theCase: case2WithComments, expectedAlerts: ['test-id-3'] }], + }, + { + user: obsSecRead, + cases: [ + { theCase: case1WithComments, expectedAlerts: ['test-id', 'test-id-2'] }, + { theCase: case2WithComments, expectedAlerts: ['test-id-3'] }, + ], + }, + { + user: obsSec, + cases: [ + { theCase: case1WithComments, expectedAlerts: ['test-id', 'test-id-2'] }, + { theCase: case2WithComments, expectedAlerts: ['test-id-3'] }, + ], + }, + ]) { + for (const theCase of scenario.cases) { + const res = await getAlertsAttachedToCase({ + supertest: supertestWithoutAuth, + caseId: theCase.theCase.id, + auth: { + user: scenario.user, + space: 'space1', + }, + }); + + expect(res.length).to.eql(theCase.expectedAlerts.length); + + for (const [index, alertId] of theCase.expectedAlerts.entries()) { + expect(res[index]).to.eql({ + id: alertId, + index: 'test-index', + attached_at: theCase.theCase.comments![index].created_at, + }); + } + } + } + }); + + for (const scenario of [ + { user: noKibanaPrivileges, space: 'space1' }, + { user: obsOnly, space: 'space1' }, + { user: obsOnlyRead, space: 'space1' }, + { user: secOnly, space: 'space2' }, + ]) { + it(`User ${scenario.user.username} with role(s) ${scenario.user.roles.join()} and space ${ + scenario.space + } - should not get alerts`, async () => { + const caseInfo = await createCase(supertest, getPostCaseRequest(), 200, { + user: superUser, + space: scenario.space, + }); + + await createComment({ + supertest: supertestWithoutAuth, + caseId: caseInfo.id, + params: postCommentAlertReq, + auth: { user: superUser, space: scenario.space }, + }); + + await getAlertsAttachedToCase({ + supertest: supertestWithoutAuth, + caseId: caseInfo.id, + auth: { user: scenario.user, space: scenario.space }, + expectedHttpCode: 403, + }); + }); + } + }); + }); +}; diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts index 9d35d5ec82fc5..9b24de26245f4 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/index.ts @@ -18,6 +18,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./comments/patch_comment')); loadTestFile(require.resolve('./comments/post_comment')); loadTestFile(require.resolve('./alerts/get_cases')); + loadTestFile(require.resolve('./alerts/get_alerts_attached_to_case')); loadTestFile(require.resolve('./cases/delete_cases')); loadTestFile(require.resolve('./cases/find_cases')); loadTestFile(require.resolve('./cases/get_case')); From 0ef1c3d735a83f1716d1f03793c00579886779f9 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 18 Jun 2021 09:31:29 +0100 Subject: [PATCH 03/23] [ML] Anomaly detection job custom_settings improvements (#102099) * [ML] Anomaly detection job custom_settings improvements * filter improvements * translations * fixing types * fixing tests * one more test fix * fixing bug with expanded row Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../anomaly_detection_jobs/summary_job.ts | 1 + .../job_details/extract_job_details.js | 28 ++++++++--- .../components/job_details/job_details.js | 6 ++- .../jobs_list_view/jobs_list_view.js | 50 +++++++++++++++---- .../jobs/jobs_list/components/utils.js | 39 ++++++++++----- .../ml/server/models/job_service/jobs.ts | 1 + .../hooks/use_installed_security_jobs.test.ts | 1 + .../common/components/ml_popover/api.mock.ts | 8 +++ .../hooks/use_security_jobs.test.ts | 1 + .../hooks/use_security_jobs_helpers.test.tsx | 1 + .../hooks/use_security_jobs_helpers.tsx | 1 + .../__snapshots__/jobs_table.test.tsx.snap | 3 ++ .../jobs_table_filters.test.tsx.snap | 3 ++ 13 files changed, 114 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts index 624056fdf3b82..e9e89a3c99771 100644 --- a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts +++ b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts @@ -36,6 +36,7 @@ export interface MlSummaryJob { earliestStartTimestampMs?: number; awaitingNodeAssignment: boolean; alertingRules?: MlAnomalyDetectionAlertRule[]; + jobTags: Record; } export interface AuditMessage { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js index 673484f08e196..dea8fdd30e372 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/extract_job_details.js @@ -26,19 +26,33 @@ export function extractJobDetails(job, basePath, refreshJobList) { items: filterObjects(job, true).map(formatValues), }; + const { job_tags: tags, custom_urls: urls, ...settings } = job.custom_settings ?? {}; const customUrl = { id: 'customUrl', title: i18n.translate('xpack.ml.jobsList.jobDetails.customUrlsTitle', { defaultMessage: 'Custom URLs', }), position: 'right', - items: [], + items: urls ? urls.map((cu) => [cu.url_name, cu.url_value, cu.time_range]) : [], + }; + + const customSettings = { + id: 'analysisConfig', + title: i18n.translate('xpack.ml.jobsList.jobDetails.customSettingsTitle', { + defaultMessage: 'Custom settings', + }), + position: 'right', + items: settings ? filterObjects(settings, true, true) : [], + }; + + const jobTags = { + id: 'analysisConfig', + title: i18n.translate('xpack.ml.jobsList.jobDetails.jobTagsTitle', { + defaultMessage: 'Job tags', + }), + position: 'right', + items: tags ? filterObjects(tags) : [], }; - if (job.custom_settings && job.custom_settings.custom_urls) { - customUrl.items.push( - ...job.custom_settings.custom_urls.map((cu) => [cu.url_name, cu.url_value, cu.time_range]) - ); - } const node = { id: 'node', @@ -213,6 +227,8 @@ export function extractJobDetails(job, basePath, refreshJobList) { analysisConfig, analysisLimits, dataDescription, + customSettings, + jobTags, datafeed, counts, modelSizeStats, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js index 812d156421c16..b514c8433daf4 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_details.js @@ -41,7 +41,7 @@ export class JobDetailsUI extends Component { } render() { - const { job } = this.state; + const job = this.state.job ?? this.props.job; const { services: { http: { basePath }, @@ -67,6 +67,8 @@ export class JobDetailsUI extends Component { analysisConfig, analysisLimits, dataDescription, + customSettings, + jobTags, datafeed, counts, modelSizeStats, @@ -85,7 +87,7 @@ export class JobDetailsUI extends Component { content: ( ), time: job.open_time, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js index 214b7616cf927..bf8db538bc8ae 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js @@ -18,7 +18,7 @@ import { EuiSpacer, EuiTitle, } from '@elastic/eui'; -import { isEqual } from 'lodash'; +import { isEqual, debounce } from 'lodash'; import { ml } from '../../../../services/ml_api_service'; import { checkForAutoStartDatafeed, filterJobs, loadFullJob } from '../utils'; @@ -43,6 +43,11 @@ import { JobListMlAnomalyAlertFlyout } from '../../../../../alerting/ml_alerting let deletingJobsRefreshTimeout = null; +const filterJobsDebounce = debounce((jobsSummaryList, filterClauses, callback) => { + const ss = filterJobs(jobsSummaryList, filterClauses); + callback(ss); +}, 500); + // 'isManagementTable' bool prop to determine when to configure table for use in Kibana management page export class JobsListView extends Component { constructor(props) { @@ -221,7 +226,7 @@ export class JobsListView extends Component { refreshSelectedJobs() { const selectedJobsIds = this.state.selectedJobs.map((j) => j.id); - const filteredJobIds = this.state.filteredJobsSummaryList.map((j) => j.id); + const filteredJobIds = (this.state.filteredJobsSummaryList ?? []).map((j) => j.id); // refresh the jobs stored as selected // only select those which are also in the filtered list @@ -232,9 +237,17 @@ export class JobsListView extends Component { this.setState({ selectedJobs }); } - setFilters = (query) => { - const filterClauses = (query && query.ast && query.ast.clauses) || []; - const filteredJobsSummaryList = filterJobs(this.state.jobsSummaryList, filterClauses); + setFilters = async (query) => { + if (query === null) { + this.setState( + { filteredJobsSummaryList: this.state.jobsSummaryList, filterClauses: [] }, + () => { + this.refreshSelectedJobs(); + } + ); + + return; + } this.props.onJobsViewStateUpdate( { @@ -244,11 +257,30 @@ export class JobsListView extends Component { this._isFiltersSet === false ); - this._isFiltersSet = true; + const filterClauses = (query && query.ast && query.ast.clauses) || []; - this.setState({ filteredJobsSummaryList, filterClauses }, () => { - this.refreshSelectedJobs(); - }); + if (filterClauses.length === 0) { + this.setState({ filteredJobsSummaryList: this.state.jobsSummaryList, filterClauses }, () => { + this.refreshSelectedJobs(); + }); + return; + } + + if (this._isFiltersSet === true) { + filterJobsDebounce(this.state.jobsSummaryList, filterClauses, (jobsSummaryList) => { + this.setState({ filteredJobsSummaryList: jobsSummaryList, filterClauses }, () => { + this.refreshSelectedJobs(); + }); + }); + } else { + // first use after page load, do not debounce. + const filteredJobsSummaryList = filterJobs(this.state.jobsSummaryList, filterClauses); + this.setState({ filteredJobsSummaryList, filterClauses }, () => { + this.refreshSelectedJobs(); + }); + } + + this._isFiltersSet = true; }; onRefreshClick = () => { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 5b8fa5c672c6e..f004fb6bad49d 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -347,12 +347,18 @@ export function filterJobs(jobs, clauses) { // if it's an array of job ids if (c.field === 'id') { js = jobs.filter((job) => c.value.indexOf(jobProperty(job, c.field)) >= 0); - } else { + } else if (c.field === 'groups') { // the groups value is an array of group ids js = jobs.filter((job) => jobProperty(job, c.field).some((g) => c.value.indexOf(g) >= 0)); + } else if (c.field === 'job_tags') { + js = jobTagFilter(jobs, c.value); } } else { - js = jobs.filter((job) => jobProperty(job, c.field) === c.value); + if (c.field === 'job_tags') { + js = js = jobTagFilter(jobs, [c.value]); + } else { + js = jobs.filter((job) => jobProperty(job, c.field) === c.value); + } } } @@ -369,6 +375,25 @@ export function filterJobs(jobs, clauses) { return filteredJobs; } +function jobProperty(job, prop) { + const propMap = { + job_state: 'jobState', + datafeed_state: 'datafeedState', + groups: 'groups', + id: 'id', + job_tags: 'jobTags', + }; + return job[propMap[prop]]; +} + +function jobTagFilter(jobs, value) { + return jobs.filter((job) => { + const tags = jobProperty(job, 'job_tags'); + return Object.entries(tags) + .map((t) => t.join(':')) + .find((t) => value.some((t1) => t1 === t)); + }); +} // check to see if a job has been stored in mlJobService.tempJobCloningObjects // if it has, return an object with the minimum properties needed for the // start datafeed modal. @@ -390,13 +415,3 @@ export function checkForAutoStartDatafeed() { }; } } - -function jobProperty(job, prop) { - const propMap = { - job_state: 'jobState', - datafeed_state: 'datafeedState', - groups: 'groups', - id: 'id', - }; - return job[propMap[prop]]; -} diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index b7f8ce569641e..22bac1cb08e19 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -218,6 +218,7 @@ export function jobsProvider( deleting: job.deleting || undefined, awaitingNodeAssignment: isJobAwaitingNodeAssignment(job), alertingRules: job.alerting_rules, + jobTags: job.custom_settings?.job_tags ?? {}, }; if (jobIds.find((j) => j === tempJob.id)) { tempJob.fullJob = job; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.test.ts b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.test.ts index a8f4d09cd7873..403b33d9c08f7 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.test.ts @@ -55,6 +55,7 @@ describe('useInstalledSecurityJobs', () => { id: 'siem-api-rare_process_linux_ecs', isSingleMetricViewerJob: true, jobState: 'closed', + jobTags: {}, latestTimestampMs: 1557434782207, memory_status: 'hard_limit', processed_record_count: 582251, diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/api.mock.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/api.mock.ts index ac057dff15621..28d0ae179508d 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/api.mock.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/api.mock.ts @@ -48,6 +48,7 @@ export const mockOpenedJob: MlSummaryJob = { nodeName: 'siem-es', processed_record_count: 3425264, awaitingNodeAssignment: false, + jobTags: {}, }; export const mockJobsSummaryResponse: MlSummaryJob[] = [ @@ -67,6 +68,7 @@ export const mockJobsSummaryResponse: MlSummaryJob[] = [ earliestTimestampMs: 1554327458406, isSingleMetricViewerJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, { id: 'siem-api-rare_process_linux_ecs', @@ -83,6 +85,7 @@ export const mockJobsSummaryResponse: MlSummaryJob[] = [ earliestTimestampMs: 1557353420495, isSingleMetricViewerJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, { id: 'siem-api-rare_process_windows_ecs', @@ -97,6 +100,7 @@ export const mockJobsSummaryResponse: MlSummaryJob[] = [ datafeedState: 'stopped', isSingleMetricViewerJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, { id: 'siem-api-suspicious_login_activity_ecs', @@ -111,6 +115,7 @@ export const mockJobsSummaryResponse: MlSummaryJob[] = [ datafeedState: 'stopped', isSingleMetricViewerJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, ]; @@ -520,6 +525,7 @@ export const mockSecurityJobs: SecurityJob[] = [ isInstalled: true, isElasticJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, { id: 'rare_process_by_host_linux_ecs', @@ -539,6 +545,7 @@ export const mockSecurityJobs: SecurityJob[] = [ isInstalled: true, isElasticJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, { datafeedId: '', @@ -558,5 +565,6 @@ export const mockSecurityJobs: SecurityJob[] = [ isInstalled: false, isElasticJob: true, awaitingNodeAssignment: false, + jobTags: {}, }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs.test.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs.test.ts index 3731bebd92624..3c91baa920da7 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs.test.ts @@ -62,6 +62,7 @@ describe('useSecurityJobs', () => { isInstalled: true, isSingleMetricViewerJob: true, jobState: 'closed', + jobTags: {}, latestTimestampMs: 1557434782207, memory_status: 'hard_limit', moduleId: '', diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.test.tsx index 8250807355648..7a488847cd583 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.test.tsx @@ -44,6 +44,7 @@ describe('useSecurityJobsHelpers', () => { isInstalled: false, isSingleMetricViewerJob: false, jobState: '', + jobTags: {}, memory_status: '', moduleId: 'siem_auditbeat', processed_record_count: 0, diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.tsx index 70a2a7c87225f..fe3803c88e4f7 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_security_jobs_helpers.tsx @@ -44,6 +44,7 @@ export const moduleToSecurityJob = ( isInstalled: false, isElasticJob: true, awaitingNodeAssignment: false, + jobTags: {}, }; }; diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap index d64fb474c1fb3..2d2525b92deb1 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap @@ -46,6 +46,7 @@ exports[`JobsTableComponent renders correctly against snapshot 1`] = ` "isInstalled": true, "isSingleMetricViewerJob": true, "jobState": "closed", + "jobTags": Object {}, "latestResultsTimestampMs": 1571022900000, "latestTimestampMs": 1571022859393, "memory_status": "ok", @@ -73,6 +74,7 @@ exports[`JobsTableComponent renders correctly against snapshot 1`] = ` "isInstalled": true, "isSingleMetricViewerJob": true, "jobState": "closed", + "jobTags": Object {}, "memory_status": "ok", "moduleId": "siem_auditbeat", "processed_record_count": 0, @@ -96,6 +98,7 @@ exports[`JobsTableComponent renders correctly against snapshot 1`] = ` "isInstalled": false, "isSingleMetricViewerJob": false, "jobState": "", + "jobTags": Object {}, "memory_status": "", "moduleId": "siem_winlogbeat", "processed_record_count": 0, diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/jobs_table_filters.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/jobs_table_filters.test.tsx.snap index 7367dbf7bdc0a..d66740ee5bb0e 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/jobs_table_filters.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/filters/__snapshots__/jobs_table_filters.test.tsx.snap @@ -49,6 +49,7 @@ exports[`JobsTableFilters renders correctly against snapshot 1`] = ` "isInstalled": true, "isSingleMetricViewerJob": true, "jobState": "closed", + "jobTags": Object {}, "latestResultsTimestampMs": 1571022900000, "latestTimestampMs": 1571022859393, "memory_status": "ok", @@ -76,6 +77,7 @@ exports[`JobsTableFilters renders correctly against snapshot 1`] = ` "isInstalled": true, "isSingleMetricViewerJob": true, "jobState": "closed", + "jobTags": Object {}, "memory_status": "ok", "moduleId": "siem_auditbeat", "processed_record_count": 0, @@ -99,6 +101,7 @@ exports[`JobsTableFilters renders correctly against snapshot 1`] = ` "isInstalled": false, "isSingleMetricViewerJob": false, "jobState": "", + "jobTags": Object {}, "memory_status": "", "moduleId": "siem_winlogbeat", "processed_record_count": 0, From ae99824880fd756e72345aebf75883250c575df0 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Fri, 18 Jun 2021 10:45:23 +0200 Subject: [PATCH 04/23] [Lens] Fix wrong error detection on transition to Top values operation (#102384) --- .../operations/definitions/terms/index.tsx | 8 +++++--- .../operations/definitions/terms/terms.test.tsx | 4 ++-- .../indexpattern_datasource/operations/layer_helpers.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 8248976ab8452..7551b88039182 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -194,14 +194,16 @@ export const termsOperation: OperationDefinition - [ + getErrorMessage: (layer, columnId, indexPattern) => { + const messages = [ ...(getInvalidFieldMessage( layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern ) || []), getDisallowedTermsMessage(layer, columnId, indexPattern) || '', - ].filter(Boolean), + ].filter(Boolean); + return messages.length ? messages : undefined; + }, isTransferable: (column, newIndexPattern) => { const newField = newIndexPattern.getFieldByName(column.sourceField); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index f5540732953ac..3b557461546ca 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -996,8 +996,8 @@ describe('terms', () => { indexPatternId: '', }; }); - it('returns empty array', () => { - expect(termsOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([]); + it('returns undefined for no errors found', () => { + expect(termsOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual(undefined); }); it('returns error message if the sourceField does not exist in index pattern', () => { layer = { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 4e3bcec4b6ca2..f0095b66e2ba6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -627,7 +627,7 @@ export function canTransition({ Boolean(newColumn) && !newLayer.incompleteColumns?.[columnId] && filterOperations(newColumn) && - !newDefinition.getErrorMessage?.(newLayer, columnId, indexPattern) + !newDefinition.getErrorMessage?.(newLayer, columnId, indexPattern)?.length ); } catch (e) { return false; From e51886522525c361fa10798b806b7d5ac5240b24 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Fri, 18 Jun 2021 10:49:28 +0100 Subject: [PATCH 05/23] [ML] Remove blank job definition as it is unused and out-of-sync with Elasticsearch (#102506) This a companion to elastic/elasticsearch#74188. This PR is functionally a no-op, as the removed method was not called anywhere. But it is sensible to remove it to prevent it being called in the future now that it references fields that don't exist in Elasticsearch. --- .../application/services/job_service.js | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index 3c93c8a1ae85a..8560cdd73153b 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -91,26 +91,6 @@ class JobService { }; } - getBlankJob() { - return { - job_id: '', - description: '', - groups: [], - analysis_config: { - bucket_span: '15m', - influencers: [], - detectors: [], - }, - data_description: { - time_field: '', - time_format: '', // 'epoch', - field_delimiter: '', - quote_character: '"', - format: 'delimited', - }, - }; - } - loadJobs() { return new Promise((resolve, reject) => { jobs = []; From ee1710cf395f9d9576c4db18dc9a9668bef1aaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Fri, 18 Jun 2021 14:50:58 +0200 Subject: [PATCH 06/23] [Logs UI] Add `event.original` fallback to message reconstruction rules (#102236) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../message/builtin_rules/generic.test.ts | 54 ++++++++++++++ .../message/builtin_rules/generic.ts | 73 ++++++++++--------- 2 files changed, 94 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts index ae5a45c61d3b5..ba8eab91e3456 100644 --- a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts +++ b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.test.ts @@ -186,4 +186,58 @@ describe('Generic Rules', () => { `); }); }); + + describe('event.original fallback', () => { + test('includes the event.dataset if present', () => { + const flattenedDocument = { + '@timestamp': ['2016-12-26T16:22:13.000Z'], + 'event.dataset': ['generic.test'], + 'event.original': ['TEST_MESSAGE'], + }; + + expect(format(flattenedDocument, {})).toMatchInlineSnapshot(` + Array [ + Object { + "constant": "[", + }, + Object { + "field": "event.dataset", + "highlights": Array [], + "value": Array [ + "generic.test", + ], + }, + Object { + "constant": "] ", + }, + Object { + "field": "event.original", + "highlights": Array [], + "value": Array [ + "TEST_MESSAGE", + ], + }, + ] + `); + }); + + test('includes the original message', () => { + const flattenedDocument = { + '@timestamp': ['2016-12-26T16:22:13.000Z'], + 'event.original': ['TEST_MESSAGE'], + }; + + expect(format(flattenedDocument, {})).toMatchInlineSnapshot(` + Array [ + Object { + "field": "event.original", + "highlights": Array [], + "value": Array [ + "TEST_MESSAGE", + ], + }, + ] + `); + }); + }); }); diff --git a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts index c16d65a75b3e0..07b6cf03e2c5d 100644 --- a/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts +++ b/x-pack/plugins/infra/server/services/log_entries/message/builtin_rules/generic.ts @@ -8,40 +8,15 @@ import { LogMessageFormattingRule } from '../rule_types'; const BUILTIN_GENERIC_MESSAGE_FIELDS = ['message', '@message']; +const BUILTIN_FALLBACK_MESSAGE_FIELDS = ['log.original', 'event.original']; -export const getGenericRules = (genericMessageFields: string[]) => [ - ...Array.from(new Set([...genericMessageFields, ...BUILTIN_GENERIC_MESSAGE_FIELDS])).reduce< - LogMessageFormattingRule[] - >((genericRules, fieldName) => [...genericRules, ...createGenericRulesForField(fieldName)], []), - { - when: { - exists: ['event.dataset', 'log.original'], - }, - format: [ - { - constant: '[', - }, - { - field: 'event.dataset', - }, - { - constant: '] ', - }, - { - field: 'log.original', - }, - ], - }, - { - when: { - exists: ['log.original'], - }, - format: [ - { - field: 'log.original', - }, - ], - }, +export const getGenericRules = (genericMessageFields: string[]): LogMessageFormattingRule[] => [ + ...Array.from(new Set([...genericMessageFields, ...BUILTIN_GENERIC_MESSAGE_FIELDS])).flatMap( + createGenericRulesForField + ), + ...BUILTIN_FALLBACK_MESSAGE_FIELDS.filter( + (fieldName) => !genericMessageFields.includes(fieldName) + ).flatMap(createFallbackRulesForField), ]; const createGenericRulesForField = (fieldName: string) => [ @@ -172,3 +147,35 @@ const createGenericRulesForField = (fieldName: string) => [ ], }, ]; + +const createFallbackRulesForField = (fieldName: string) => [ + { + when: { + exists: ['event.dataset', fieldName], + }, + format: [ + { + constant: '[', + }, + { + field: 'event.dataset', + }, + { + constant: '] ', + }, + { + field: fieldName, + }, + ], + }, + { + when: { + exists: [fieldName], + }, + format: [ + { + field: fieldName, + }, + ], + }, +]; From b9f64b78259f7dd6809a16597077015114b674c4 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 18 Jun 2021 15:57:33 +0300 Subject: [PATCH 07/23] TSVB visualizations with no timefield do not render after upgrading from 7.12.1 to 7.13.0 (#102494) * TSVB visualizations with no timefield do not render after upgrading from 7.12.1 to 7.13.0 Part of: #100778 * fix CI --- .../server/lib/get_vis_data.ts | 7 +- .../lib/cached_index_pattern_fetcher.ts | 13 ++- .../get_interval_and_timefield.test.ts | 4 +- .../vis_data/get_interval_and_timefield.ts | 9 +- .../server/lib/vis_data/get_table_data.ts | 16 ++- .../series/date_histogram.js | 6 +- .../series/date_histogram.test.js | 36 +++++-- .../series/positive_rate.js | 6 +- .../series/positive_rate.test.js | 10 +- .../request_processors/series/query.js | 18 +++- .../request_processors/series/query.test.js | 102 +++++++++++++++--- .../table/date_histogram.js | 13 ++- .../request_processors/table/positive_rate.js | 13 ++- .../request_processors/table/query.js | 15 ++- .../series/build_request_body.test.ts | 5 +- .../lib/vis_data/series/get_request_params.ts | 16 ++- 16 files changed, 226 insertions(+), 63 deletions(-) diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts index 5cdea62af9536..dd45812f4ebfc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts @@ -38,10 +38,9 @@ export async function getVisData( indexPatternsService, uiSettings, searchStrategyRegistry: framework.searchStrategyRegistry, - cachedIndexPatternFetcher: getCachedIndexPatternFetcher( - indexPatternsService, - Boolean(panel.use_kibana_indexes) - ), + cachedIndexPatternFetcher: getCachedIndexPatternFetcher(indexPatternsService, { + fetchKibanaIndexForStringIndexes: Boolean(panel.use_kibana_indexes), + }), }; return panel.type === PANEL_TYPES.TABLE diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts index 26ea191ab9217..5f989a50ca639 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts @@ -13,12 +13,19 @@ import type { IndexPatternValue, FetchedIndexPattern } from '../../../../common/ export const getCachedIndexPatternFetcher = ( indexPatternsService: IndexPatternsService, - fetchKibanaIndexForStringIndexes: boolean = false + globalOptions: { + fetchKibanaIndexForStringIndexes: boolean; + } = { + fetchKibanaIndexForStringIndexes: false, + } ) => { const cache = new Map(); - return async (indexPatternValue: IndexPatternValue): Promise => { - const key = getIndexPatternKey(indexPatternValue); + return async ( + indexPatternValue: IndexPatternValue, + fetchKibanaIndexForStringIndexes: boolean = globalOptions.fetchKibanaIndexForStringIndexes + ): Promise => { + const key = `${getIndexPatternKey(indexPatternValue)}:${fetchKibanaIndexForStringIndexes}`; if (cache.has(key)) { return cache.get(key); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts index 9520b876a5810..0d1ca9cba022a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts @@ -16,7 +16,7 @@ describe('getIntervalAndTimefield(panel, series)', () => { const panel = { time_field: '@timestamp', interval: 'auto' } as Panel; const series = {} as Series; - expect(getIntervalAndTimefield(panel, series, index)).toEqual({ + expect(getIntervalAndTimefield(panel, index, series)).toEqual({ timeField: '@timestamp', interval: 'auto', }); @@ -30,7 +30,7 @@ describe('getIntervalAndTimefield(panel, series)', () => { series_time_field: 'time', } as unknown) as Series; - expect(getIntervalAndTimefield(panel, series, index)).toEqual({ + expect(getIntervalAndTimefield(panel, index, series)).toEqual({ timeField: 'time', interval: '1m', }); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts index 90fb722c54f5b..0e90dfe77e814 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts @@ -7,12 +7,13 @@ */ import { AUTO_INTERVAL } from '../../../common/constants'; -import type { FetchedIndexPattern, Panel, Series } from '../../../common/types'; import { validateField } from '../../../common/fields_utils'; -export function getIntervalAndTimefield(panel: Panel, series: Series, index: FetchedIndexPattern) { +import type { FetchedIndexPattern, Panel, Series } from '../../../common/types'; + +export function getIntervalAndTimefield(panel: Panel, index: FetchedIndexPattern, series?: Series) { const timeField = - (series.override_index_pattern ? series.series_time_field : panel.time_field) || + (series?.override_index_pattern ? series.series_time_field : panel.time_field) || index.indexPattern?.timeFieldName; if (panel.use_kibana_indexes) { @@ -22,7 +23,7 @@ export function getIntervalAndTimefield(panel: Panel, series: Series, index: Fet let interval = panel.interval; let maxBars = panel.max_bars; - if (series.override_index_pattern) { + if (series?.override_index_pattern) { interval = series.series_interval || AUTO_INTERVAL; maxBars = series.series_max_bars; } diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts index 82411ed70d8ee..db2e027f7815c 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts @@ -25,6 +25,7 @@ import type { VisTypeTimeseriesVisDataRequest, } from '../../types'; import type { Panel } from '../../../common/types'; +import { getIntervalAndTimefield } from './get_interval_and_timefield'; export async function getTableData( requestContext: VisTypeTimeseriesRequestHandlerContext, @@ -66,6 +67,18 @@ export async function getTableData( return panel.pivot_id; }; + const buildSeriesMetaParams = async () => { + let index = panelIndex; + + /** This part of code is required to try to get the default timefield for string indices. + * The rest of the functionality available for Kibana indexes should not be active **/ + if (!panel.use_kibana_indexes && index.indexPatternString) { + index = await services.cachedIndexPatternFetcher(index.indexPatternString, true); + } + + return getIntervalAndTimefield(panel, index); + }; + const meta = { type: panel.type, uiRestrictions: capabilities.uiRestrictions, @@ -78,7 +91,8 @@ export async function getTableData( services.esQueryConfig, panelIndex, capabilities, - services.uiSettings + services.uiSettings, + buildSeriesMetaParams ); const [resp] = await searchStrategy.search(requestContext, req, [ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js index 253612c0274ad..cec3e82d5e37c 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js @@ -9,7 +9,6 @@ import { overwrite } from '../../helpers'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { offsetTime } from '../../offset_time'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { isLastValueTimerangeMode } from '../../helpers/get_timerange_mode'; import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; @@ -22,13 +21,14 @@ export function dateHistogram( esQueryConfig, seriesIndex, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams ) { return (next) => async (doc) => { const maxBarsUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS); const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { timeField, interval, maxBars } = getIntervalAndTimefield(panel, series, seriesIndex); + const { timeField, interval, maxBars } = await buildSeriesMetaParams(); const { from, to } = offsetTime(req, series.offset_time); let bucketInterval; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js index 2cd7a213b273e..08b9801254c2e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js @@ -8,6 +8,7 @@ import { DefaultSearchCapabilities } from '../../../search_strategies/capabilities/default_search_capabilities'; import { dateHistogram } from './date_histogram'; +import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { UI_SETTINGS } from '../../../../../../data/common'; describe('dateHistogram(req, panel, series)', () => { @@ -18,6 +19,7 @@ describe('dateHistogram(req, panel, series)', () => { let config; let indexPattern; let uiSettings; + let buildSeriesMetaParams; beforeEach(() => { req = { @@ -44,14 +46,24 @@ describe('dateHistogram(req, panel, series)', () => { uiSettings = { get: async (key) => (key === UI_SETTINGS.HISTOGRAM_MAX_BARS ? 100 : 50), }; + buildSeriesMetaParams = jest.fn(async () => { + return getIntervalAndTimefield(panel, indexPattern, series); + }); }); test('calls next when finished', async () => { const next = jest.fn(); - await dateHistogram(req, panel, series, config, indexPattern, capabilities, uiSettings)(next)( - {} - ); + await dateHistogram( + req, + panel, + series, + config, + indexPattern, + capabilities, + uiSettings, + buildSeriesMetaParams + )(next)({}); expect(next.mock.calls.length).toEqual(1); }); @@ -65,7 +77,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ @@ -106,7 +119,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ @@ -150,7 +164,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ @@ -197,7 +212,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc.aggs.test.aggs.timeseries.auto_date_histogram).toBeUndefined(); @@ -219,7 +235,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc.aggs.test.meta).toMatchInlineSnapshot(` @@ -242,7 +259,8 @@ describe('dateHistogram(req, panel, series)', () => { config, indexPattern, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js index 208321a98737e..91016384794c4 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js @@ -7,7 +7,6 @@ */ import { getBucketSize } from '../../helpers/get_bucket_size'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { bucketTransform } from '../../helpers/bucket_transform'; import { overwrite } from '../../helpers'; import { UI_SETTINGS } from '../../../../../../data/common'; @@ -58,12 +57,13 @@ export function positiveRate( esQueryConfig, seriesIndex, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams ) { return (next) => async (doc) => { const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { interval } = getIntervalAndTimefield(panel, series, seriesIndex); + const { interval } = await buildSeriesMetaParams(); const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings); if (series.metrics.some(filter)) { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js index b79e8de13062c..aac0063a54bef 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js @@ -12,6 +12,7 @@ describe('positiveRate(req, panel, series)', () => { let series; let req; let uiSettings; + let buildSeriesMetaParams; beforeEach(() => { panel = { @@ -42,6 +43,9 @@ describe('positiveRate(req, panel, series)', () => { uiSettings = { get: async () => 50, }; + buildSeriesMetaParams = jest.fn().mockResolvedValue({ + interval: 'auto', + }); }); test('calls next when finished', async () => { @@ -53,7 +57,8 @@ describe('positiveRate(req, panel, series)', () => { {}, {}, { maxBucketsLimit: 2000, getValidTimeInterval: jest.fn(() => '1d') }, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(next.mock.calls.length).toEqual(1); @@ -68,7 +73,8 @@ describe('positiveRate(req, panel, series)', () => { {}, {}, { maxBucketsLimit: 2000, getValidTimeInterval: jest.fn(() => '1d') }, - uiSettings + uiSettings, + buildSeriesMetaParams )(next)({}); expect(doc).toEqual({ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js index a5f4e17289e06..5031a0f2ec185 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js @@ -7,18 +7,28 @@ */ import { offsetTime } from '../../offset_time'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { esQuery } from '../../../../../../data/server'; -export function query(req, panel, series, esQueryConfig, seriesIndex) { - return (next) => (doc) => { - const { timeField } = getIntervalAndTimefield(panel, series, seriesIndex); +export function query( + req, + panel, + series, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { + return (next) => async (doc) => { + const { timeField } = await buildSeriesMetaParams(); const { from, to } = offsetTime(req, series.offset_time); doc.size = 0; + const ignoreGlobalFilter = panel.ignore_global_filter || series.ignore_global_filter; const queries = !ignoreGlobalFilter ? req.body.query : []; const filters = !ignoreGlobalFilter ? req.body.filters : []; + doc.query = esQuery.buildEsQuery(seriesIndex.indexPattern, queries, filters, esQueryConfig); const timerange = { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js index b3e88dbf1c6b9..849ae2e71bffc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js @@ -13,6 +13,7 @@ describe('query', () => { let series; let req; let seriesIndex; + let buildSeriesMetaParams; const config = { allowLeadingWildcards: true, @@ -35,17 +36,32 @@ describe('query', () => { }; series = { id: 'test' }; seriesIndex = {}; + buildSeriesMetaParams = jest.fn().mockResolvedValue({ + timeField: panel.time_field, + interval: panel.interval, + }); }); - test('calls next when finished', () => { + test('calls next when finished', async () => { const next = jest.fn(); - query(req, panel, series, config, seriesIndex)(next)({}); + await query(req, panel, series, config, seriesIndex, null, null, buildSeriesMetaParams)(next)( + {} + ); expect(next.mock.calls.length).toEqual(1); }); - test('returns doc with query for timerange', () => { + test('returns doc with query for timerange', async () => { const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -69,10 +85,19 @@ describe('query', () => { }); }); - test('returns doc with query for timerange (offset by 1h)', () => { + test('returns doc with query for timerange (offset by 1h)', async () => { series.offset_time = '1h'; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -96,7 +121,7 @@ describe('query', () => { }); }); - test('returns doc with global query', () => { + test('returns doc with global query', async () => { req.body.filters = [ { bool: { @@ -111,7 +136,16 @@ describe('query', () => { }, ]; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -147,10 +181,19 @@ describe('query', () => { }); }); - test('returns doc with series filter', () => { + test('returns doc with series filter', async () => { series.filter = { query: 'host:web-server', language: 'lucene' }; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -188,7 +231,7 @@ describe('query', () => { }, }); }); - test('returns doc with panel filter and global', () => { + test('returns doc with panel filter and global', async () => { req.body.filters = [ { bool: { @@ -204,7 +247,16 @@ describe('query', () => { ]; panel.filter = { query: 'host:web-server', language: 'lucene' }; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -255,7 +307,7 @@ describe('query', () => { }); }); - test('returns doc with panel filter (ignoring globals)', () => { + test('returns doc with panel filter (ignoring globals)', async () => { req.body.filters = [ { bool: { @@ -272,7 +324,16 @@ describe('query', () => { panel.filter = { query: 'host:web-server', language: 'lucene' }; panel.ignore_global_filter = true; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { @@ -311,7 +372,7 @@ describe('query', () => { }); }); - test('returns doc with panel filter (ignoring globals from series)', () => { + test('returns doc with panel filter (ignoring globals from series)', async () => { req.body.filters = [ { bool: { @@ -328,7 +389,16 @@ describe('query', () => { panel.filter = { query: 'host:web-server', language: 'lucene' }; series.ignore_global_filter = true; const next = (doc) => doc; - const doc = query(req, panel, series, config, seriesIndex)(next)({}); + const doc = await query( + req, + panel, + series, + config, + seriesIndex, + null, + null, + buildSeriesMetaParams + )(next)({}); expect(doc).toEqual({ size: 0, query: { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js index 92ac4078a3835..f37c27f74184f 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.js @@ -9,17 +9,24 @@ import { overwrite } from '../../helpers'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { isLastValueTimerangeMode } from '../../helpers/get_timerange_mode'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { getTimerange } from '../../helpers/get_timerange'; import { calculateAggRoot } from './calculate_agg_root'; import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; const { dateHistogramInterval } = search.aggs; -export function dateHistogram(req, panel, esQueryConfig, seriesIndex, capabilities, uiSettings) { +export function dateHistogram( + req, + panel, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { return (next) => async (doc) => { const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { timeField, interval } = getIntervalAndTimefield(panel, {}, seriesIndex); + const { timeField, interval } = await buildSeriesMetaParams(); const { from, to } = getTimerange(req); const meta = { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js index 3390362b56115..dafb2741d6ab4 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.js @@ -7,15 +7,22 @@ */ import { getBucketSize } from '../../helpers/get_bucket_size'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { calculateAggRoot } from './calculate_agg_root'; import { createPositiveRate, filter } from '../series/positive_rate'; import { UI_SETTINGS } from '../../../../../../data/common'; -export function positiveRate(req, panel, esQueryConfig, seriesIndex, capabilities, uiSettings) { +export function positiveRate( + req, + panel, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { return (next) => async (doc) => { const barTargetUiSettings = await uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); - const { interval } = getIntervalAndTimefield(panel, {}, seriesIndex); + const { interval } = await buildSeriesMetaParams(); const { intervalString } = getBucketSize(req, interval, capabilities, barTargetUiSettings); panel.series.forEach((column) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js index 66783e0cdfaef..7e555557ee1f7 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js @@ -7,12 +7,19 @@ */ import { getTimerange } from '../../helpers/get_timerange'; -import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; import { esQuery } from '../../../../../../data/server'; -export function query(req, panel, esQueryConfig, seriesIndex) { - return (next) => (doc) => { - const { timeField } = getIntervalAndTimefield(panel, {}, seriesIndex); +export function query( + req, + panel, + esQueryConfig, + seriesIndex, + capabilities, + uiSettings, + buildSeriesMetaParams +) { + return (next) => async (doc) => { + const { timeField } = await buildSeriesMetaParams(); const { from, to } = getTimerange(req); doc.size = 0; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts index 46acbb27e15e1..14b4fc89712e2 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts @@ -89,7 +89,10 @@ describe('buildRequestBody(req)', () => { capabilities, { get: async () => 50, - } + }, + jest.fn().mockResolvedValue({ + timeField: '@timestamp', + }) ); expect(doc).toEqual({ diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts index c565e2b86eaa3..a2248308dc571 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts @@ -7,6 +7,7 @@ */ import { buildRequestBody } from './build_request_body'; +import { getIntervalAndTimefield } from '../get_interval_and_timefield'; import type { FetchedIndexPattern, Panel, Series } from '../../../../common/types'; import type { @@ -34,6 +35,18 @@ export async function getSeriesRequestParams( seriesIndex = await cachedIndexPatternFetcher(series.series_index_pattern ?? ''); } + const buildSeriesMetaParams = async () => { + let index = seriesIndex; + + /** This part of code is required to try to get the default timefield for string indices. + * The rest of the functionality available for Kibana indexes should not be active **/ + if (!panel.use_kibana_indexes && index.indexPatternString) { + index = await cachedIndexPatternFetcher(index.indexPatternString, true); + } + + return getIntervalAndTimefield(panel, index, series); + }; + const request = await buildRequestBody( req, panel, @@ -41,7 +54,8 @@ export async function getSeriesRequestParams( esQueryConfig, seriesIndex, capabilities, - uiSettings + uiSettings, + buildSeriesMetaParams ); return { From d65416c60c803092eb17b80e77836e5ef772a429 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Fri, 18 Jun 2021 10:26:10 -0400 Subject: [PATCH 08/23] [Security Solution][Endpoint][Host Isolation] Fixes bug where host isolation/unisolation works from alert details (#102581) --- .../detections/components/host_isolation/index.tsx | 10 +++++----- .../detections/components/host_isolation/isolate.tsx | 6 +++--- .../components/host_isolation/unisolate.tsx | 6 +++--- .../containers/detection_engine/alerts/api.test.ts | 6 +++--- .../containers/detection_engine/alerts/api.ts | 12 ++++++------ .../detection_engine/alerts/use_host_isolation.tsx | 8 ++++---- .../detection_engine/alerts/use_host_unisolation.tsx | 8 ++++---- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx index 2ca8416841497..42d53f97d478b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx @@ -26,9 +26,9 @@ export const HostIsolationPanel = React.memo( cancelCallback: () => void; isolateAction: string; }) => { - const agentId = useMemo(() => { - const findAgentId = find({ category: 'agent', field: 'agent.id' }, details)?.values; - return findAgentId ? findAgentId[0] : ''; + const endpointId = useMemo(() => { + const findEndpointId = find({ category: 'agent', field: 'agent.id' }, details)?.values; + return findEndpointId ? findEndpointId[0] : ''; }, [details]); const hostName = useMemo(() => { @@ -87,7 +87,7 @@ export const HostIsolationPanel = React.memo( return isolateAction === 'isolateHost' ? ( ) : ( { const hostIsolated = await isolateHost(); diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx index e72a0d2de61bc..71f7cadda2f68 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx @@ -18,13 +18,13 @@ import { useHostUnisolation } from '../../containers/detection_engine/alerts/use export const UnisolateHost = React.memo( ({ - agentId, + endpointId, hostName, cases, caseIds, cancelCallback, }: { - agentId: string; + endpointId: string; hostName: string; cases: ReactNode; caseIds: string[]; @@ -33,7 +33,7 @@ export const UnisolateHost = React.memo( const [comment, setComment] = useState(''); const [isUnIsolated, setIsUnIsolated] = useState(false); - const { loading, unIsolateHost } = useHostUnisolation({ agentId, comment, caseIds }); + const { loading, unIsolateHost } = useHostUnisolation({ endpointId, comment, caseIds }); const confirmHostUnIsolation = useCallback(async () => { const hostIsolated = await unIsolateHost(); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts index 0c3159e0719e6..b944cb640b719 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts @@ -178,19 +178,19 @@ describe('Detections Alerts API', () => { test('check parameter url', async () => { await createHostIsolation({ - agentId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', + endpointId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', comment: 'commento', caseIds: ['88c04a90-b19c-11eb-b838-bf3c7840b969'], }); expect(postMock).toHaveBeenCalledWith('/api/endpoint/isolate', { body: - '{"agent_ids":["fd8a122b-4c54-4c05-b295-e5f8381fc59d"],"comment":"commento","case_ids":["88c04a90-b19c-11eb-b838-bf3c7840b969"]}', + '{"endpoint_ids":["fd8a122b-4c54-4c05-b295-e5f8381fc59d"],"comment":"commento","case_ids":["88c04a90-b19c-11eb-b838-bf3c7840b969"]}', }); }); test('happy path', async () => { const hostIsolationResponse = await createHostIsolation({ - agentId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', + endpointId: 'fd8a122b-4c54-4c05-b295-e5f8381fc59d', comment: 'commento', caseIds: ['88c04a90-b19c-11eb-b838-bf3c7840b969'], }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts index 3baa6580b36fb..71eac547dcc8e 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts @@ -118,16 +118,16 @@ export const createSignalIndex = async ({ signal }: BasicSignals): Promise => isolateHost({ - agent_ids: [agentId], + endpoint_ids: [endpointId], comment, case_ids: caseIds, }); @@ -142,16 +142,16 @@ export const createHostIsolation = async ({ * @throws An error if response is not OK */ export const createHostUnIsolation = async ({ - agentId, + endpointId, comment = '', caseIds, }: { - agentId: string; + endpointId: string; comment?: string; caseIds?: string[]; }): Promise => unIsolateHost({ - agent_ids: [agentId], + endpoint_ids: [endpointId], comment, case_ids: caseIds, }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx index ad3c6e91c03fe..12426e05ba528 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation.tsx @@ -16,13 +16,13 @@ interface HostIsolationStatus { } interface UseHostIsolationProps { - agentId: string; + endpointId: string; comment: string; caseIds?: string[]; } export const useHostIsolation = ({ - agentId, + endpointId, comment, caseIds, }: UseHostIsolationProps): HostIsolationStatus => { @@ -32,7 +32,7 @@ export const useHostIsolation = ({ const isolateHost = useCallback(async () => { try { setLoading(true); - const isolationStatus = await createHostIsolation({ agentId, comment, caseIds }); + const isolationStatus = await createHostIsolation({ endpointId, comment, caseIds }); setLoading(false); return isolationStatus.action ? true : false; } catch (error) { @@ -40,6 +40,6 @@ export const useHostIsolation = ({ addError(error.message, { title: HOST_ISOLATION_FAILURE }); return false; } - }, [agentId, comment, caseIds, addError]); + }, [endpointId, comment, caseIds, addError]); return { loading, isolateHost }; }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx index 1a0ecb0d15878..55119f7122e12 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_unisolation.tsx @@ -16,13 +16,13 @@ interface HostUnisolationStatus { } interface UseHostIsolationProps { - agentId: string; + endpointId: string; comment: string; caseIds?: string[]; } export const useHostUnisolation = ({ - agentId, + endpointId, comment, caseIds, }: UseHostIsolationProps): HostUnisolationStatus => { @@ -32,7 +32,7 @@ export const useHostUnisolation = ({ const unIsolateHost = useCallback(async () => { try { setLoading(true); - const isolationStatus = await createHostUnIsolation({ agentId, comment, caseIds }); + const isolationStatus = await createHostUnIsolation({ endpointId, comment, caseIds }); setLoading(false); return isolationStatus.action ? true : false; } catch (error) { @@ -40,6 +40,6 @@ export const useHostUnisolation = ({ addError(error.message, { title: HOST_ISOLATION_FAILURE }); return false; } - }, [agentId, comment, caseIds, addError]); + }, [endpointId, comment, caseIds, addError]); return { loading, unIsolateHost }; }; From 853de830c2c908d7c3601e76896c4edc0a9c2ff4 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Fri, 18 Jun 2021 17:32:17 +0300 Subject: [PATCH 09/23] [TSVB] Index pattern select field disappear in Annotation tab (#102314) * [TSVB] Index pattern select field disappear in Annotation tab * Refactor AnnotationsEditor and move renderRow logic into AnnotationRow * Remove duplicated license, add useCallback to functions in AnnotationRow, remove cross refecrence and update types * Refactor AnnotationEditor and AnnotationRow * refactoring * remove extra props * fix nits Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Alexey Antonov --- .../application/components/annotation_row.tsx | 280 +++++++++++++++ .../components/annotations_editor.js | 322 ------------------ .../components/annotations_editor.tsx | 113 ++++++ .../components/panel_config/timeseries.tsx | 4 +- .../public/application/components/yes_no.tsx | 8 +- 5 files changed, 398 insertions(+), 329 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx delete mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js create mode 100644 src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx new file mode 100644 index 0000000000000..715cf4d6709da --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx @@ -0,0 +1,280 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useEffect, useCallback, useMemo, ChangeEvent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiCode, + EuiComboBoxOptionOption, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiSpacer, + htmlIdGenerator, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { getDataStart } from '../../services'; +import { KBN_FIELD_TYPES, Query } from '../../../../../plugins/data/public'; + +import { AddDeleteButtons } from './add_delete_buttons'; +import { ColorPicker } from './color_picker'; +import { FieldSelect } from './aggs/field_select'; +import { IndexPatternSelect } from './lib/index_pattern_select'; +import { QueryBarWrapper } from './query_bar_wrapper'; +import { YesNo } from './yes_no'; +import { fetchIndexPattern } from '../../../common/index_patterns_utils'; +import { getDefaultQueryLanguage } from './lib/get_default_query_language'; + +// @ts-expect-error not typed yet +import { IconSelect } from './icon_select/icon_select'; + +import type { Annotation, FetchedIndexPattern, IndexPatternValue } from '../../../common/types'; +import type { VisFields } from '../lib/fetch_fields'; + +const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; + +const INDEX_PATTERN_KEY = 'index_pattern'; +const TIME_FIELD_KEY = 'time_field'; + +export interface AnnotationRowProps { + annotation: Annotation; + fields: VisFields; + onChange: (partialModel: Partial) => void; + handleAdd: () => void; + handleDelete: () => void; +} + +const getAnnotationDefaults = () => ({ + fields: '', + template: '', + index_pattern: '', + query_string: { query: '', language: getDefaultQueryLanguage() }, +}); + +export const AnnotationRow = ({ + annotation, + fields, + onChange, + handleAdd, + handleDelete, +}: AnnotationRowProps) => { + const model = useMemo(() => ({ ...getAnnotationDefaults(), ...annotation }), [annotation]); + const htmlId = htmlIdGenerator(model.id); + + const [fetchedIndex, setFetchedIndex] = useState(null); + + useEffect(() => { + const updateFetchedIndex = async (index: IndexPatternValue) => { + const { indexPatterns } = getDataStart(); + + setFetchedIndex( + index + ? await fetchIndexPattern(index, indexPatterns) + : { + indexPattern: undefined, + indexPatternString: undefined, + } + ); + }; + + updateFetchedIndex(model.index_pattern); + }, [model.index_pattern]); + + const togglePanelActivation = useCallback( + () => + onChange({ + hidden: !model.hidden, + }), + [model.hidden, onChange] + ); + + const handleChange = useCallback( + (name: string) => ( + event: Array> | ChangeEvent + ) => + onChange({ + [name]: Array.isArray(event) ? event?.[0]?.value : event.target.value, + }), + [onChange] + ); + + const handleQueryChange = useCallback( + (filter: Query) => + onChange({ + query_string: filter, + }), + [onChange] + ); + + return ( +
+ + + + + + + + + + + + + } + restrict={RESTRICT_FIELDS} + value={model.time_field} + onChange={handleChange(TIME_FIELD_KEY)} + indexPattern={model.index_pattern} + fields={fields} + /> + + + + + + + + + } + fullWidth + > + + + + + + + + + + + + + + + + + + + + + } + > + + + + + + } + fullWidth + > + + + + + + } + helpText={ + + {'{{field}}'} }} + /> + + } + fullWidth + > + + + + + + + + + + +
+ ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js b/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js deleted file mode 100644 index 09ce57639b952..0000000000000 --- a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.js +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import _ from 'lodash'; -import { collectionActions } from './lib/collection_actions'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; -import { AddDeleteButtons } from './add_delete_buttons'; -import { ColorPicker } from './color_picker'; -import { FieldSelect } from './aggs/field_select'; -import uuid from 'uuid'; -import { IconSelect } from './icon_select/icon_select'; -import { YesNo } from './yes_no'; -import { QueryBarWrapper } from './query_bar_wrapper'; -import { getDefaultQueryLanguage } from './lib/get_default_query_language'; -import { - htmlIdGenerator, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiSpacer, - EuiFieldText, - EuiTitle, - EuiButton, - EuiCode, - EuiText, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { IndexPatternSelect } from './lib/index_pattern_select'; - -function newAnnotation() { - return { - id: uuid.v1(), - color: '#F00', - index_pattern: '', - time_field: '', - icon: 'fa-tag', - ignore_global_filters: 1, - ignore_panel_filters: 1, - }; -} - -const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; - -export class AnnotationsEditor extends Component { - constructor(props) { - super(props); - this.renderRow = this.renderRow.bind(this); - } - - handleChange(item, name) { - return (e) => { - const handleChange = collectionActions.handleChange.bind(null, this.props); - const part = {}; - part[name] = _.get(e, '[0].value', _.get(e, 'target.value')); - handleChange(_.assign({}, item, part)); - }; - } - - handleQueryChange = (model, filter) => { - const part = { query_string: filter }; - collectionActions.handleChange(this.props, { - ...model, - ...part, - }); - }; - renderRow(row) { - const defaults = { - fields: '', - template: '', - index_pattern: '', - query_string: { query: '', language: getDefaultQueryLanguage() }, - }; - const model = { ...defaults, ...row }; - const handleChange = (part) => { - const fn = collectionActions.handleChange.bind(null, this.props); - fn(_.assign({}, model, part)); - }; - const togglePanelActivation = () => { - handleChange({ - hidden: !model.hidden, - }); - }; - const htmlId = htmlIdGenerator(model.id); - const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation); - const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); - - return ( -
- - - - - - - - - - - - - } - restrict={RESTRICT_FIELDS} - value={model.time_field} - onChange={this.handleChange(model, 'time_field')} - indexPattern={model.index_pattern} - fields={this.props.fields} - fullWidth - /> - - - - - - - - - } - fullWidth - > - this.handleQueryChange(model, query)} - indexPatterns={[model.index_pattern]} - /> - - - - - - - - - - - - - - - - - - - - } - > - - - - - - } - fullWidth - > - - - - - - } - helpText={ - - {'{{field}}'} }} - /> - - } - fullWidth - > - - - - - - - - - - -
- ); - } - - render() { - const { model } = this.props; - let content; - if (!model.annotations || !model.annotations.length) { - const handleAdd = collectionActions.handleAdd.bind(null, this.props, newAnnotation); - content = ( - -

- -

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

+ +

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

{CONNECT_DOC_PERMISSIONS_TITLE}

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

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

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

{SOURCE_FEATURES_SEARCHABLE}

- +