From b39c46eeac36d65eaaf18bdbaa29ba86d9cea26c Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 20 Jul 2020 09:30:06 +0100 Subject: [PATCH 01/26] [ML] Disabling secondary auth headers when security is disabled (#72371) Co-authored-by: Elastic Machine --- x-pack/plugins/ml/server/lib/request_authorization.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/server/lib/request_authorization.ts b/x-pack/plugins/ml/server/lib/request_authorization.ts index 01df0900b96f4..d40909a3a3a4c 100644 --- a/x-pack/plugins/ml/server/lib/request_authorization.ts +++ b/x-pack/plugins/ml/server/lib/request_authorization.ts @@ -7,7 +7,9 @@ import { KibanaRequest } from 'kibana/server'; export function getAuthorizationHeader(request: KibanaRequest) { - return { - headers: { 'es-secondary-authorization': request.headers.authorization }, - }; + return request.headers.authorization === undefined + ? {} + : { + headers: { 'es-secondary-authorization': request.headers.authorization }, + }; } From ec4f9d50ba311122cf5f6583e790a86db13e462a Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 20 Jul 2020 10:43:12 +0200 Subject: [PATCH 02/26] Migrate and cleanup legacy scss (#69369) --- src/core/public/index.scss | 3 +- .../public/styles}/_ace_overrides.scss | 5 +- src/core/public/styles/_base.scss | 58 +++ src/core/public/styles/_index.scss | 2 + src/legacy/ui/public/_index.scss | 3 - .../ui/public/styles/_legacy/_base.scss | 153 -------- .../ui/public/styles/_legacy/_index.scss | 7 - .../styles/_legacy/components/_config.scss | 11 - .../_legacy/components/_control_group.scss | 67 ---- .../styles/_legacy/components/_hintbox.scss | 59 --- .../styles/_legacy/components/_index.scss | 13 - .../styles/_legacy/components/_input.scss | 12 - .../styles/_legacy/components/_kui_forms.scss | 29 -- .../styles/_legacy/components/_navbar.scss | 86 ----- .../_legacy/components/_pagination.scss | 56 --- .../styles/_legacy/components/_spinner.scss | 6 - .../styles/_legacy/components/_table.scss | 46 --- .../styles/_legacy/components/_truncate.scss | 3 - .../styles/_legacy/components/_ui_select.scss | 357 ------------------ src/legacy/ui/public/styles/_mixins.scss | 2 - .../public/application/_discover.scss | 12 + .../discover/public/application/_hacks.scss | 4 - .../discover/public/application/_mixins.scss | 27 -- .../angular/doc_table/_doc_table.scss | 77 +++- .../application/angular/doc_table/index.scss | 2 - .../discover/public/application/index.scss | 13 +- .../public/paginate/_paginate.scss | 57 +++ .../kibana_legacy/public/paginate/paginate.js | 1 + src/plugins/timelion/public/_base.scss | 19 + .../timelion/public/directives/_form.scss} | 40 +- .../timelion/public/directives/_index.scss | 3 + .../directives/_saved_object_finder.scss} | 6 +- src/plugins/timelion/public/index.scss | 1 + .../public/agg_table/_agg_table.scss | 4 + .../plugins/ml/public/application/_hacks.scss | 36 -- .../server/lib/layouts/preserve_layout.css | 1 - .../reporting/server/lib/layouts/print.css | 1 - 37 files changed, 251 insertions(+), 1031 deletions(-) rename src/{legacy/ui/public/styles/_legacy/components => core/public/styles}/_ace_overrides.scss (96%) create mode 100644 src/core/public/styles/_base.scss create mode 100644 src/core/public/styles/_index.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/_base.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/_index.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_config.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_control_group.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_hintbox.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_index.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_input.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_kui_forms.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_navbar.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_pagination.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_spinner.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_table.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_truncate.scss delete mode 100644 src/legacy/ui/public/styles/_legacy/components/_ui_select.scss delete mode 100644 src/plugins/discover/public/application/_hacks.scss delete mode 100644 src/plugins/discover/public/application/_mixins.scss create mode 100644 src/plugins/kibana_legacy/public/paginate/_paginate.scss create mode 100644 src/plugins/timelion/public/_base.scss rename src/{legacy/ui/public/styles/_legacy/_mixins.scss => plugins/timelion/public/directives/_form.scss} (58%) rename src/{legacy/ui/public/styles/_legacy/components/_list_group_menu.scss => plugins/timelion/public/directives/_saved_object_finder.scss} (85%) diff --git a/src/core/public/index.scss b/src/core/public/index.scss index 87825350b4e98..c2ad2841d5a77 100644 --- a/src/core/public/index.scss +++ b/src/core/public/index.scss @@ -2,6 +2,5 @@ @import './chrome/index'; @import './overlays/index'; @import './rendering/index'; +@import './styles/index'; -// Global styles need to be migrated -@import '../../legacy/ui/public/styles/_legacy/_index'; diff --git a/src/legacy/ui/public/styles/_legacy/components/_ace_overrides.scss b/src/core/public/styles/_ace_overrides.scss similarity index 96% rename from src/legacy/ui/public/styles/_legacy/components/_ace_overrides.scss rename to src/core/public/styles/_ace_overrides.scss index 2f0bc011f6a5c..30acdbbc80975 100644 --- a/src/legacy/ui/public/styles/_legacy/components/_ace_overrides.scss +++ b/src/core/public/styles/_ace_overrides.scss @@ -1,6 +1,3 @@ -@import '@elastic/eui/src/components/call_out/variables'; -@import '@elastic/eui/src/components/call_out/mixins'; - // SASSTODO: Replace with an EUI editor // Intentionally not using the EuiCodeBlock colors here because they actually change // hue from light to dark theme. So some colors would change while others wouldn't. @@ -181,7 +178,7 @@ } &.ace_multiselect .ace_selection.ace_start { - box-shadow: 0 0 3px 0px $euiColorEmptyShade; + box-shadow: 0 0 3px 0 $euiColorEmptyShade; } .ace_marker-layer .ace_step { diff --git a/src/core/public/styles/_base.scss b/src/core/public/styles/_base.scss new file mode 100644 index 0000000000000..9b06b526fc7dd --- /dev/null +++ b/src/core/public/styles/_base.scss @@ -0,0 +1,58 @@ +@import '@elastic/eui/src/components/collapsible_nav/variables'; +// Application Layout + +// chrome-context +// TODO #64541 +// Delete this block +.chrHeaderWrapper:not(.headerWrapper) .content { + display: flex; + flex-flow: row nowrap; + width: 100%; + height: 100%; + overflow: hidden; +} + +.application, +.app-container { + > * { + position: relative; + } +} + +.application { + position: relative; + z-index: 0; + display: flex; + flex-grow: 1; + flex-shrink: 0; + flex-basis: auto; + flex-direction: column; + + > * { + flex-shrink: 0; + } +} + +// We apply brute force focus states to anything not coming from Eui +// which has focus states designed at the component level. +// You can also use "kbn-resetFocusState" to not apply the default focus +// state. This is useful when you've already hand crafted your own +// focus states in Kibana. +:focus { + &:not([class^='eui']):not(.kbn-resetFocusState) { + @include euiFocusRing; + } +} + +// A necessary hack so that the above focus policy doesn't pollute some EUI +// entrenched inputs. +.euiComboBox { + // :not() specificity needed to override the above + input:not([class^='eui']):focus { + animation: none !important; + } +} + +.euiBody--collapsibleNavIsDocked .euiBottomBar { + margin-left: $euiCollapsibleNavWidth; +} diff --git a/src/core/public/styles/_index.scss b/src/core/public/styles/_index.scss new file mode 100644 index 0000000000000..600414402c278 --- /dev/null +++ b/src/core/public/styles/_index.scss @@ -0,0 +1,2 @@ +@import './base'; +@import './ace_overrides'; diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index 323de2ea7d263..a441b773d4a4e 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -1,6 +1,3 @@ -// Legacy styles to come before all -@import './styles/_legacy/index'; - // Prefix all styles with "kbn" to avoid conflicts. // Examples // kbnChart diff --git a/src/legacy/ui/public/styles/_legacy/_base.scss b/src/legacy/ui/public/styles/_legacy/_base.scss deleted file mode 100644 index 877ae033ae584..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/_base.scss +++ /dev/null @@ -1,153 +0,0 @@ -@import '@elastic/eui/src/components/collapsible_nav/variables'; - -// Forms - -// Angular form states -input.ng-invalid, -textarea.ng-invalid, -select.ng-invalid { - &.ng-dirty, - &.ng-touched { - border-color: $euiColorDanger !important; - } -} - -input[type='radio'], -input[type='checkbox'], -.radio, -.radio-inline, -.checkbox, -.checkbox-inline { - &[disabled], - fieldset[disabled] & { - cursor: default; - opacity: 0.8; - } -} - -.checkbox label { - display: flex; - align-items: center; - padding-left: 0 !important; - - input[type='checkbox'] { - float: none; - margin: 0 $euiSizeXS; - position: static; - } -} - -// Application Layout - -// chrome-context -// TODO #64541 -// Delete this block -.chrHeaderWrapper:not(.headerWrapper) .content { - display: flex; - flex-flow: row nowrap; - width: 100%; - height: 100%; - overflow: hidden; -} - -.application, -.app-container { - > * { - position: relative; - } - - > config { - z-index: 1; - } - - > navbar { - padding-bottom: $euiSizeS; - } - - > nav, - > navbar { - z-index: 2 !important; - } -} - -.application { - position: relative; - z-index: 0; - display: flex; - flex-grow: 1; - flex-shrink: 0; - flex-basis: auto; - flex-direction: column; - - > * { - flex-shrink: 0; - } -} - -[fixed-scroll] { - overflow-x: auto; - padding-bottom: 0; - - + .fixed-scroll-scroller { - position: fixed; - bottom: 0; - overflow-x: auto; - overflow-y: hidden; - } -} - -// Too overused in many places to be moved elsewhere - -.page-row { - padding: 0 $euiSize; - margin: $euiSize 0; -} - -.page-row-text { - color: $euiColorDarkShade; - font-size: $euiFontSizeS; -} - -// We apply brute force focus states to anything not coming from Eui -// which has focus states designed at the component level. -// You can also use "kbn-resetFocusState" to not apply the default focus -// state. This is useful when you've already hand crafted your own -// focus states in Kibana. -:focus { - &:not([class^='eui']):not(.kbn-resetFocusState) { - @include euiFocusRing; - } -} - -// A necessary hack so that the above focus policy doesn't pollute some EUI -// entrenched inputs. -.euiComboBox { - // :not() specificity needed to override the above - input:not([class^='eui']):focus { - animation: none !important; - } -} - -.euiBody--collapsibleNavIsDocked .euiBottomBar { - margin-left: $euiCollapsibleNavWidth; -} - -// Utility classes - -.fullWidth { - width: 100% !important; -} - -.small { - font-size: 0.9em !important; -} -.smaller { - font-size: 0.8em !important; -} -.smallest { - font-size: 0.7em !important; -} - -.text-monospace { - font-family: $euiCodeFontFamily; -} diff --git a/src/legacy/ui/public/styles/_legacy/_index.scss b/src/legacy/ui/public/styles/_legacy/_index.scss deleted file mode 100644 index a0b1a98b09b7d..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/_index.scss +++ /dev/null @@ -1,7 +0,0 @@ -// // -// // KIBANA THEME -@import './base'; -@import './mixins'; - -// // Components -@import './components/index'; diff --git a/src/legacy/ui/public/styles/_legacy/components/_config.scss b/src/legacy/ui/public/styles/_legacy/components/_config.scss deleted file mode 100644 index b56826f1e7088..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_config.scss +++ /dev/null @@ -1,11 +0,0 @@ -// SASSTODO: Selector is so generic it's hard to find if it's actually used -.config { - @extend .navbar !optional; - @extend .navbar-default !optional; - border-bottom: 1px solid transparent; - - .container-fluid { - background-color: $euiPageBackgroundColor; - padding: $euiSizeS; - } -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_control_group.scss b/src/legacy/ui/public/styles/_legacy/components/_control_group.scss deleted file mode 100644 index ce958a9aae77f..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_control_group.scss +++ /dev/null @@ -1,67 +0,0 @@ -.control-group { - display: flex; - flex: 0 0 auto; - flex-direction: row; - flex-wrap: wrap; - align-items: stretch; - padding: $euiSizeXS $euiSize; - - > * { - padding-right: $euiSize; - flex: 0 0 auto; - - &:last-child { - padding-right: 0; - } - } - - // horizontal group of buttons/form elements - .inline-form .input-group { - margin-bottom: 0; - display: flex; - - > * { - border-radius: 0; - } - - > :first-child { - border-bottom-left-radius: $euiBorderRadius; - border-top-left-radius: $euiBorderRadius; - } - - > :last-child { - border-bottom-right-radius: $euiBorderRadius; - border-top-right-radius: $euiBorderRadius; - } - } - - .inline-form { - @include flex-parent(0, 0, auto); - display: flex; - - > .typeahead { - @include flex-parent(); - - > .input-group { - display: flex; - flex: 1 0 auto; - - > * { - float: none; - height: auto; - width: auto; - flex: 0 0 auto; - } - - input[type="text"] { - flex: 1 1 100%; - } - } - } - } - - // the element should take up an even share of available space - > .fill { - flex: 1 1 1%; - } -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_hintbox.scss b/src/legacy/ui/public/styles/_legacy/components/_hintbox.scss deleted file mode 100644 index 0c447031636ac..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_hintbox.scss +++ /dev/null @@ -1,59 +0,0 @@ -.hintbox { - padding: $euiSizeS $euiSizeM; - border-radius: $euiBorderRadius; - margin-bottom: $euiSizeS; - background-color: $euiColorLightShade; - line-height: $euiLineHeight; - - a { - color: $euiLinkColor !important; - - &:hover { - color: darken($euiLinkColor, 10%) !important; - } - } - - pre { - background-color: $euiColorEmptyShade; - } - - ul, ol { - padding-left: $euiSizeL; - } - - // inspired by Bootstrap alerts component - // https://github.com/twbs/bootstrap/blob/063c1b0780ea0240e4adce4c88d57fc23e099475/less/alerts.less#L27-L35 - > * { - margin: 0; - } - - > * + * { - margin-top: $euiSizeS; - } - - // https://github.com/twbs/bootstrap/blob/2aa102bfd40859d15790febed1939e0111a6fb1a/less/tables.less#L88-L106 - .table-bordered { - border: $euiBorderThin; - > thead, - > tbody, - > tfoot { - > tr { - > th, - > td { - border: $euiBorderThin; - } - } - } - > thead > tr { - > th, - > td { - border-bottom-width: 2px; - } - } - } -} - - .hintbox-label, - .hintbox-label[ng-click] { - cursor: help; - } diff --git a/src/legacy/ui/public/styles/_legacy/components/_index.scss b/src/legacy/ui/public/styles/_legacy/components/_index.scss deleted file mode 100644 index cfae0700bb71e..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_index.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import './ace_overrides'; -@import './control_group'; -@import './hintbox'; -@import './input'; -@import './kui_forms'; -@import './list_group_menu'; -@import './navbar'; -@import './config'; -@import './pagination'; -@import './spinner'; -@import './table'; -@import './truncate'; -@import './ui_select'; diff --git a/src/legacy/ui/public/styles/_legacy/components/_input.scss b/src/legacy/ui/public/styles/_legacy/components/_input.scss deleted file mode 100644 index 13efc9646e820..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_input.scss +++ /dev/null @@ -1,12 +0,0 @@ -i.input-error { - position: absolute; - margin-left: -$euiSizeL; - color: $euiColorDanger; - margin-top: $euiSizeS; - z-index: 5; -} - -select { - color: $euiTextColor; - background-color: $euiColorEmptyShade; -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_kui_forms.scss b/src/legacy/ui/public/styles/_legacy/components/_kui_forms.scss deleted file mode 100644 index 2e1b814d647e3..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_kui_forms.scss +++ /dev/null @@ -1,29 +0,0 @@ -.form-control { - @include __legacyInputStyles__bad; -} - -select.form-control { - @include __legacySelectStyles__bad; -} - -.kuiFormSection { - margin-bottom: $euiSize; -} - -.kuiFormLabel { - @include __legacyLabelStyles__bad; - display: block; - margin-bottom: 5px; -} - -.kuiInputNote { - margin: $euiSizeXS 0 $euiSizeS; -} - -.kuiInputNote--danger { - color: $euiColorDanger; -} - -.kuiInputNote--warning { - color: $euiColorWarning; -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_navbar.scss b/src/legacy/ui/public/styles/_legacy/components/_navbar.scss deleted file mode 100644 index b06c655789a50..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_navbar.scss +++ /dev/null @@ -1,86 +0,0 @@ -navbar { - @extend .control-group; - - max-height: 340px; - margin-bottom: 0; - padding: 0 $euiSizeS $euiSizeXS; - color: $euiColorDarkShade; - background-color: $euiColorLightShade; - border: none; - z-index: $euiZLevel1; - - > * { - padding-right: $euiSizeS; - } - - .navbar-text { - margin-top: $euiSizeXS; - margin-bottom: $euiSizeXS; - } - - // the "brand" that is displayed, usually on the left of the navbar - > .name { - @include euiTextTruncate; - - align-self: center; - font-size: $euiFontSizeL; - } - - button { - color: $euiColorDarkShade; - background-color: transparent; - - &:hover, - &:focus { - color: $euiColorDarkShade; - background-color: transparent; - } - - &:active, &.active { - color: $euiColorDarkestShade; - background-color: transparent; - box-shadow: none; - - &:focus { - outline: none; - } - } - - &[disabled] { - color: $euiColorMediumShade; - background-color: transparent; - } - } - - .inline-form .input-group { - button { - color: $euiColorEmptyShade; - background-color: $euiColorDarkShade; - border: none; - } - } - - // responsive modifications - - // desktop - @include euiBreakpoint('l', 'xl') { - > .name { - // 500px is sort of arbitrary, not sure how to deal with lots of buttons - max-width: 500px; - } - } - - // tablets/phones - @include euiBreakpoint('xs', 's', 'm') { - > .fill { - flex: 1 1 map-get($euiBreakpoints, 'l'); - } - } - - // phones - @include euiBreakpoint('xs', 's') { - > .name { - max-width: 100%; - } - } -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_pagination.scss b/src/legacy/ui/public/styles/_legacy/components/_pagination.scss deleted file mode 100644 index cf68f2ac8253f..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_pagination.scss +++ /dev/null @@ -1,56 +0,0 @@ -paginate { - display: block; - - paginate-controls { - display: flex; - align-items: center; - padding: $euiSizeXS $euiSizeXS $euiSizeS; - text-align: center; - - .pagination-other-pages { - flex: 1 0 auto; - display: flex; - justify-content: center; - } - - .pagination-other-pages-list { - flex: 0 0 auto; - display: flex; - justify-content: center; - padding: 0; - margin: 0; - list-style: none; - - > li { - flex: 0 0 auto; - user-select: none; - - a { - text-decoration: none; - background-color: $euiColorLightestShade; - margin-left: $euiSizeXS / 2; - padding: $euiSizeS $euiSizeM; - } - - a:hover { - text-decoration: underline; - } - - &.active a { - text-decoration: none !important; - font-weight: $euiFontWeightBold; - color: $euiColorDarkShade; - cursor: default; - } - } - } - - .pagination-size { - flex: 0 0 auto; - - input[type=number] { - width: 3em; - } - } - } -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_spinner.scss b/src/legacy/ui/public/styles/_legacy/components/_spinner.scss deleted file mode 100644 index 7b3b1bd615ae0..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_spinner.scss +++ /dev/null @@ -1,6 +0,0 @@ -.spinner.ng-hide { - visibility: hidden; - display: block !important; - opacity: 0; - transition-delay: 0.25s; -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_table.scss b/src/legacy/ui/public/styles/_legacy/components/_table.scss deleted file mode 100644 index d0ac9d6f79862..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_table.scss +++ /dev/null @@ -1,46 +0,0 @@ -@import '../../../../../../plugins/discover/public/application/mixins'; - -.table { - // Nesting - .table { - background-color: $euiColorEmptyShade; - } -} - -kbn-table, .kbn-table, tbody[kbn-rows] { - @include dscDocSourceStyle; - // sub tables should not have a leading border - .table .table { - margin-bottom: 0; - - tr:first-child > td { - border-top: none; - } - - td.field-name { - font-weight: $euiFontWeightBold; - } - } -} - -table { - th { - i.fa-sort { - color: $euiColorLightShade; - } - - button.fa-sort-asc, - button.fa-sort-down, - i.fa-sort-asc, - i.fa-sort-down { - color: $euiColorPrimary; - } - - button.fa-sort-desc, - button.fa-sort-up, - i.fa-sort-desc, - i.fa-sort-up { - color: $euiColorPrimary; - } - } -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_truncate.scss b/src/legacy/ui/public/styles/_legacy/components/_truncate.scss deleted file mode 100644 index 30ba5fea2a4ea..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_truncate.scss +++ /dev/null @@ -1,3 +0,0 @@ -.truncate-by-height { - overflow: hidden; -} diff --git a/src/legacy/ui/public/styles/_legacy/components/_ui_select.scss b/src/legacy/ui/public/styles/_legacy/components/_ui_select.scss deleted file mode 100644 index 691ec17b5b967..0000000000000 --- a/src/legacy/ui/public/styles/_legacy/components/_ui_select.scss +++ /dev/null @@ -1,357 +0,0 @@ -/*! - * ui-select - * http://github.com/angular-ui/ui-select - * Version: 0.19.5 - 2016-10-24T23:13:59.551Z - * License: MIT - */ - -/* Style when highlighting a search. */ -.ui-select-highlight { - font-weight: bold; -} - -.ui-select-offscreen { - clip: rect(0 0 0 0) !important; - width: 1px !important; - height: 1px !important; - border: 0 !important; - margin: 0 !important; - padding: 0 !important; - overflow: hidden !important; - position: absolute !important; - outline: 0 !important; - left: 0px !important; - top: 0px !important; -} - -.ui-select-choices-row:hover { - background-color: $euiColorLightestShade; -} - -/* Select2 theme */ - -/* Mark invalid Select2 */ -.ng-dirty.ng-invalid > a.select2-choice { - border-color: $euiColorDanger; -} - -.select2-result-single { - padding-left: 0; -} - -.select2-locked > .select2-search-choice-close { - display: none; -} - -.select-locked > .ui-select-match-close { - display: none; -} - -body > .select2-container.open { - z-index: 9999; /* The z-index Select2 applies to the select2-drop */ -} - -/* Handle up direction Select2 */ -.ui-select-container[theme='select2'].direction-up .ui-select-match, -.ui-select-container.select2.direction-up .ui-select-match { - border-radius: 4px; /* FIXME hardcoded value :-/ */ - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.ui-select-container[theme='select2'].direction-up .ui-select-dropdown, -.ui-select-container.select2.direction-up .ui-select-dropdown { - border-radius: 4px; /* FIXME hardcoded value :-/ */ - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - - border-top-width: 1px; /* FIXME hardcoded value :-/ */ - border-top-style: solid; - - box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); - - margin-top: -4px; /* FIXME hardcoded value :-/ */ -} -.ui-select-container[theme='select2'].direction-up .ui-select-dropdown .select2-search, -.ui-select-container.select2.direction-up .ui-select-dropdown .select2-search { - margin-top: 4px; /* FIXME hardcoded value :-/ */ -} -.ui-select-container[theme='select2'].direction-up.select2-dropdown-open .ui-select-match, -.ui-select-container.select2.direction-up.select2-dropdown-open .ui-select-match { - border-bottom-color: $euiColorPrimary; -} - -.ui-select-container[theme='select2'] .ui-select-dropdown .ui-select-search-hidden, -.ui-select-container[theme='select2'] .ui-select-dropdown .ui-select-search-hidden input { - opacity: 0; - height: 0; - min-height: 0; - padding: 0; - margin: 0; - border: 0; -} - -/* Bootstrap theme */ - -/* Helper class to show styles when focus */ -.btn-default-focus { - color: $euiTextColor; - background-color: $euiColorEmptyShade; - border-color: $euiColorPrimary; - text-decoration: none; - outline: none; - box-shadow: none; -} - -.ui-select-bootstrap .ui-select-toggle { - @include __legacyInputStyles__bad; - @include __legacySelectStyles__bad; -} - -.ui-select-bootstrap .ui-select-toggle > .caret { - display: none; -} - -/* Fix Bootstrap dropdown position when inside a input-group */ -.input-group > .ui-select-bootstrap.dropdown { - /* Instead of relative */ - position: static; -} - -.input-group > .ui-select-bootstrap > input.ui-select-search.form-control { - border-radius: 4px; /* FIXME hardcoded value :-/ */ - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group > .ui-select-bootstrap > input.ui-select-search.form-control.direction-up { - border-radius: 4px !important; /* FIXME hardcoded value :-/ */ - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; -} - -.ui-select-bootstrap .ui-select-search-hidden { - opacity: 0; - height: 0; - min-height: 0; - padding: 0; - margin: 0; - border: 0; -} - -.ui-select-bootstrap > .ui-select-match > .btn { - @include __legacyInputStyles__bad; - @include __legacySelectStyles__bad; - text-align: left !important; // Instead of center because of .btn - - .ui-select-placeholder { - color: $euiColorMediumShade; - } - - &:focus, - &:active { - background-color: $euiColorEmptyShade; - color: $euiTextColor; - outline: none; - } -} - -.ui-select-bootstrap > .ui-select-match > .caret { - display: none; -} - -/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */ -.ui-select-bootstrap > .ui-select-choices, -.ui-select-bootstrap > .ui-select-no-choice { - width: 100%; - height: auto; - max-height: $euiSize * 14; - overflow-x: hidden; -} - -body > .ui-select-bootstrap.open { - z-index: $euiZContentMenu; -} - -.ui-select-multiple.ui-select-bootstrap { - height: auto; - padding: 3px 5px 2px; - border: $euiBorderThin; - background-color: $euiFormBackgroundColor; - - &.kuiInputError { - border-color: $euiColorDanger; - } -} - -.ui-select-multiple.ui-select-bootstrap input.ui-select-search { - background-color: transparent !important; /* To prevent double background when disabled */ - border: none; - outline: none; - height: 1.666666em; - margin-bottom: 3px; -} - -.ui-select-multiple.ui-select-bootstrap .ui-select-match .close { - font-size: 1.6em; - line-height: 0.75; -} - -.ui-select-multiple.ui-select-bootstrap .ui-select-match-item { - outline: 0; - margin: 0 3px 3px 0; -} - -.ui-select-multiple .ui-select-match-item { - position: relative; -} - -.ui-select-multiple .ui-select-match-item.dropping .ui-select-match-close { - pointer-events: none; -} - -.ui-select-multiple:hover .ui-select-match-item.dropping-before:before { - content: ''; - position: absolute; - top: 0; - right: 100%; - height: 100%; - margin-right: 2px; - border-left: 1px solid $euiColorPrimary; -} - -.ui-select-multiple:hover .ui-select-match-item.dropping-after:after { - content: ''; - position: absolute; - top: 0; - left: 100%; - height: 100%; - margin-left: 2px; - border-right: 1px solid $euiColorPrimary; -} - -.ui-select-bootstrap .ui-select-choices-row > span { - @include euiFontSizeS; - @include euiTextTruncate; - font-weight: inherit; - cursor: pointer; - display: block; - padding: $euiSizeXS $euiSize; - clear: both; - color: $euiTextColor; - white-space: nowrap; - - &:hover, - &:focus { - text-decoration: none; - color: $euiTextColor; - background-color: $euiFocusBackgroundColor; - } -} - -.ui-select-bootstrap .ui-select-choices-row.active > span { - color: $euiTextColor; - text-decoration: none; - outline: 0; - background-color: $euiFocusBackgroundColor; -} - -.ui-select-bootstrap .ui-select-choices-row.disabled > span, -.ui-select-bootstrap .ui-select-choices-row.active.disabled > span { - color: $euiButtonColorDisabled; - cursor: not-allowed; - background-color: transparent; -} - -/* fix hide/show angular animation */ -.ui-select-match.ng-hide-add, -.ui-select-search.ng-hide-add { - display: none !important; -} - -/* Mark invalid Bootstrap */ -.ui-select-bootstrap.ng-dirty.ng-invalid > button.btn.ui-select-match { - border-color: $euiColorDanger; -} - -/* Handle up direction Bootstrap */ -.ui-select-container[theme='bootstrap'].direction-up .ui-select-dropdown { - @include euiBottomShadowMedium; -} - -.ui-select-bootstrap .ui-select-match-text { - width: 100%; - padding-right: 1em; -} -.ui-select-bootstrap .ui-select-match-text span { - display: inline-block; - width: 100%; - overflow: hidden; -} -.ui-select-bootstrap .ui-select-toggle > a.btn { - position: absolute; - height: 10px; - right: 10px; - margin-top: -2px; -} - -/* Spinner */ -.ui-select-refreshing { - position: absolute; - right: 0; - padding: 8px 27px; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; -} - -@-webkit-keyframes ui-select-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@keyframes ui-select-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} - -.ui-select-spin { - -webkit-animation: ui-select-spin 2s infinite linear; - animation: ui-select-spin 2s infinite linear; -} - -.ui-select-refreshing.ng-animate { - -webkit-animation: none 0s; -} - -// Other Custom - -/** - * 1. Fix appearance of ui-select in the Filtering UI. - */ -.btn-default .ui-select-placeholder { - color: $euiColorMediumShade; /* 1 */ -} - -.uiSelectMatch--ellipsis { - @include euiTextTruncate; -} - -.ui-select-choices-group-label { - @include euiTitle('xxxs'); - @include euiTextTruncate; - padding: $euiSizeXS; -} diff --git a/src/legacy/ui/public/styles/_mixins.scss b/src/legacy/ui/public/styles/_mixins.scss index c0dc456000dcc..e335ef88a01b5 100644 --- a/src/legacy/ui/public/styles/_mixins.scss +++ b/src/legacy/ui/public/styles/_mixins.scss @@ -105,12 +105,10 @@ @keyframes kibanaFullScreenGraphics_FadeIn { from { opacity: 0; - transform: translateY(200px), scale(.75); } to { opacity: 1; - transform: translateY(0), scale(1); } } } diff --git a/src/plugins/discover/public/application/_discover.scss b/src/plugins/discover/public/application/_discover.scss index 1aaa0a24357ed..69df2a75b8d75 100644 --- a/src/plugins/discover/public/application/_discover.scss +++ b/src/plugins/discover/public/application/_discover.scss @@ -103,3 +103,15 @@ discover-app { right: $euiSizeM; top: $euiSizeXS; } + +[fixed-scroll] { + overflow-x: auto; + padding-bottom: 0; + + + .fixed-scroll-scroller { + position: fixed; + bottom: 0; + overflow-x: auto; + overflow-y: hidden; + } +} diff --git a/src/plugins/discover/public/application/_hacks.scss b/src/plugins/discover/public/application/_hacks.scss deleted file mode 100644 index 9bbe9cd14fd91..0000000000000 --- a/src/plugins/discover/public/application/_hacks.scss +++ /dev/null @@ -1,4 +0,0 @@ -// SASSTODO: the classname is dynamically generated with ng-class -.tab-discover { - overflow: hidden; -} diff --git a/src/plugins/discover/public/application/_mixins.scss b/src/plugins/discover/public/application/_mixins.scss deleted file mode 100644 index 100f81ae92bf0..0000000000000 --- a/src/plugins/discover/public/application/_mixins.scss +++ /dev/null @@ -1,27 +0,0 @@ -/** -* Style ES document _source in table view
key:
value
-* Use alpha so this will stand out against non-white backgrounds, e.g. the highlighted -* row in the Context Log. -*/ -@mixin dscDocSourceStyle { - dl.source { - margin-bottom: 0; - line-height:2em; - word-break: break-word; - - dt, dd { - display: inline; - } - - dt { - background-color: transparentize(shade($euiColorPrimary, 20%), .9); - color: $euiTextColor; - padding: ($euiSizeXS / 2) $euiSizeXS; - margin-right: $euiSizeXS; - word-break: normal; - border-radius: $euiBorderRadius; - } - } -} - - diff --git a/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss index 3e30214acd2a9..7d05171622e7b 100644 --- a/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss +++ b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss @@ -27,7 +27,6 @@ doc-table { } .kbnDocTable { - @include dscDocSourceStyle; font-size: $euiFontSizeXS; th { @@ -40,6 +39,35 @@ doc-table { } } +.kbn-table, +.kbnDocTable { + /** + * Style ES document _source in table view
key:
value
+ * Use alpha so this will stand out against non-white backgrounds, e.g. the highlighted + * row in the Context Log. + */ + + dl.source { + margin-bottom: 0; + line-height: 2em; + word-break: break-word; + + dt, + dd { + display: inline; + } + + dt { + background-color: transparentize(shade($euiColorPrimary, 20%), 0.9); + color: $euiTextColor; + padding: ($euiSizeXS / 2) $euiSizeXS; + margin-right: $euiSizeXS; + word-break: normal; + border-radius: $euiBorderRadius; + } + } +} + .kbnDocTable__row { td { position: relative; @@ -80,3 +108,50 @@ doc-table { text-align: center; } +.truncate-by-height { + overflow: hidden; +} + +.table { + // Nesting + .table { + background-color: $euiColorEmptyShade; + } +} + +.kbn-table { + // sub tables should not have a leading border + .table .table { + margin-bottom: 0; + + tr:first-child > td { + border-top: none; + } + + td.field-name { + font-weight: $euiFontWeightBold; + } + } +} + +table { + th { + i.fa-sort { + color: $euiColorLightShade; + } + + button.fa-sort-asc, + button.fa-sort-down, + i.fa-sort-asc, + i.fa-sort-down { + color: $euiColorPrimary; + } + + button.fa-sort-desc, + button.fa-sort-up, + i.fa-sort-desc, + i.fa-sort-up { + color: $euiColorPrimary; + } + } +} diff --git a/src/plugins/discover/public/application/angular/doc_table/index.scss b/src/plugins/discover/public/application/angular/doc_table/index.scss index 4e6cb83c5fe5a..3663d807851c4 100644 --- a/src/plugins/discover/public/application/angular/doc_table/index.scss +++ b/src/plugins/discover/public/application/angular/doc_table/index.scss @@ -1,4 +1,2 @@ -@import '../../mixins'; - @import 'doc_table'; @import 'components/index'; diff --git a/src/plugins/discover/public/application/index.scss b/src/plugins/discover/public/application/index.scss index aaec7ab387e96..5aa353828274c 100644 --- a/src/plugins/discover/public/application/index.scss +++ b/src/plugins/discover/public/application/index.scss @@ -1,13 +1,2 @@ -// Discover plugin styles -@import 'mixins'; -@import 'discover'; -@import 'hacks'; - -// Prefix all styles with "dsc" to avoid conflicts. -// Examples -// dscTable -// dscTable__footer -// monChart__legend--small -// monChart__legend-isLoading - @import 'angular/index'; +@import 'discover'; diff --git a/src/plugins/kibana_legacy/public/paginate/_paginate.scss b/src/plugins/kibana_legacy/public/paginate/_paginate.scss new file mode 100644 index 0000000000000..e9c1acaf9ee0d --- /dev/null +++ b/src/plugins/kibana_legacy/public/paginate/_paginate.scss @@ -0,0 +1,57 @@ +paginate { + display: block; + + paginate-controls { + display: flex; + align-items: center; + padding: $euiSizeXS $euiSizeXS $euiSizeS; + text-align: center; + + .pagination-other-pages { + flex: 1 0 auto; + display: flex; + justify-content: center; + } + + .pagination-other-pages-list { + flex: 0 0 auto; + display: flex; + justify-content: center; + padding: 0; + margin: 0; + list-style: none; + + > li { + flex: 0 0 auto; + user-select: none; + + a { + text-decoration: none; + background-color: $euiColorLightestShade; + margin-left: $euiSizeXS / 2; + padding: $euiSizeS $euiSizeM; + } + + a:hover { + text-decoration: underline; + } + + &.active a { + text-decoration: none !important; + font-weight: $euiFontWeightBold; + color: $euiColorDarkShade; + cursor: default; + } + } + } + + .pagination-size { + flex: 0 0 auto; + + input[type=number] { + width: 3em; + } + } + } +} + diff --git a/src/plugins/kibana_legacy/public/paginate/paginate.js b/src/plugins/kibana_legacy/public/paginate/paginate.js index ea93a969d08c7..f424c33ba7b02 100644 --- a/src/plugins/kibana_legacy/public/paginate/paginate.js +++ b/src/plugins/kibana_legacy/public/paginate/paginate.js @@ -19,6 +19,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import './_paginate.scss'; import paginateControlsTemplate from './paginate_controls.html'; export function PaginateDirectiveProvider($parse, $compile) { diff --git a/src/plugins/timelion/public/_base.scss b/src/plugins/timelion/public/_base.scss new file mode 100644 index 0000000000000..616ac9b3486e7 --- /dev/null +++ b/src/plugins/timelion/public/_base.scss @@ -0,0 +1,19 @@ +// Angular form states +.ng-invalid { + &.ng-dirty, + &.ng-touched { + border-color: $euiColorDanger; + } +} + +input[type='radio'], +input[type='checkbox'], +.radio, +.checkbox { + &[disabled], + fieldset[disabled] & { + cursor: default; + opacity: .8; + } +} + diff --git a/src/legacy/ui/public/styles/_legacy/_mixins.scss b/src/plugins/timelion/public/directives/_form.scss similarity index 58% rename from src/legacy/ui/public/styles/_legacy/_mixins.scss rename to src/plugins/timelion/public/directives/_form.scss index 2834f60555070..3fcf70700a864 100644 --- a/src/legacy/ui/public/styles/_legacy/_mixins.scss +++ b/src/plugins/timelion/public/directives/_form.scss @@ -1,23 +1,19 @@ -// These mixins are temporary helpers to consolidate styles of elements that -// are not yet converted to use EUI. - -// DO NOT CONTINUE TO USE THESE MIXINS - -@mixin __legacyInputStyles__bad { - &:not([type='range']) { - appearance: none; - } +.form-control { + @include euiFontSizeS; display: block; width: 100%; height: $euiFormControlCompressedHeight; padding: $euiSizeXS $euiSizeM; - @include euiFontSizeS; border: $euiBorderThin; background-color: $euiFormBackgroundColor; color: $euiTextColor; border-radius: $euiBorderRadius; cursor: pointer; + &:not([type='range']) { + appearance: none; + } + &:focus { border-color: $euiColorPrimary; outline: none; @@ -25,30 +21,16 @@ } } -@mixin __legacySelectStyles__bad { +// sass-lint:disable-block no-qualifying-elements +select.form-control { // Makes the select arrow similar to EUI's arrowDown icon - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill='#{hexToRGB($euiTextColor)}' d='M13.0688508,5.15725038 L8.38423975,9.76827428 C8.17054415,9.97861308 7.82999214,9.97914095 7.61576025,9.76827428 L2.93114915,5.15725038 C2.7181359,4.94758321 2.37277319,4.94758321 2.15975994,5.15725038 C1.94674669,5.36691756 1.94674669,5.70685522 2.15975994,5.9165224 L6.84437104,10.5275463 C7.48517424,11.1582836 8.51644979,11.1566851 9.15562896,10.5275463 L13.8402401,5.9165224 C14.0532533,5.70685522 14.0532533,5.36691756 13.8402401,5.15725038 C13.6272268,4.94758321 13.2818641,4.94758321 13.0688508,5.15725038 Z'/%3E%3C/svg%3E"); + background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"%3E%3Cpath fill="#{hexToRGB($euiTextColor)}" d="M13.0688508,5.15725038 L8.38423975,9.76827428 C8.17054415,9.97861308 7.82999214,9.97914095 7.61576025,9.76827428 L2.93114915,5.15725038 C2.7181359,4.94758321 2.37277319,4.94758321 2.15975994,5.15725038 C1.94674669,5.36691756 1.94674669,5.70685522 2.15975994,5.9165224 L6.84437104,10.5275463 C7.48517424,11.1582836 8.51644979,11.1566851 9.15562896,10.5275463 L13.8402401,5.9165224 C14.0532533,5.70685522 14.0532533,5.36691756 13.8402401,5.15725038 C13.6272268,4.94758321 13.2818641,4.94758321 13.0688508,5.15725038 Z"/%3E%3C/svg%3E'); background-size: $euiSize; background-repeat: no-repeat; background-position: calc(100% - #{$euiSizeS}); padding-right: $euiSizeXL; - - &::-ms-expand { - display: none; - } - - &:focus::-ms-value { - color: $euiTextColor; - background: transparent; - } } -@mixin __legacyLabelStyles__bad { - font-size: $euiFontSizeXS; - font-weight: $euiFontWeightSemiBold; - color: $euiTextColor; - - &[for] { - cursor: pointer; - } +.fullWidth { + width: 100%; } diff --git a/src/plugins/timelion/public/directives/_index.scss b/src/plugins/timelion/public/directives/_index.scss index cd46a1a0a369e..a407c1dfabdeb 100644 --- a/src/plugins/timelion/public/directives/_index.scss +++ b/src/plugins/timelion/public/directives/_index.scss @@ -3,3 +3,6 @@ @import './timelion_expression_suggestions/index'; @import './timelion_help/index'; @import './timelion_interval/index'; +@import './saved_object_finder'; +@import './form'; + diff --git a/src/legacy/ui/public/styles/_legacy/components/_list_group_menu.scss b/src/plugins/timelion/public/directives/_saved_object_finder.scss similarity index 85% rename from src/legacy/ui/public/styles/_legacy/components/_list_group_menu.scss rename to src/plugins/timelion/public/directives/_saved_object_finder.scss index 3bac75cb19d9d..b97dace5e9e00 100644 --- a/src/legacy/ui/public/styles/_legacy/components/_list_group_menu.scss +++ b/src/plugins/timelion/public/directives/_saved_object_finder.scss @@ -1,7 +1,7 @@ .list-group-menu { &.select-mode a { - outline: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); + outline: none; + color: tintOrShade($euiColorPrimary, 10%, 10%); } .list-group-menu-item { @@ -12,9 +12,11 @@ font-weight: bold; background-color: $euiColorLightShade; } + &:hover { background-color: tintOrShade($euiColorPrimary, 90%, 90%); } + li { list-style: none; color: tintOrShade($euiColorPrimary, 10%, 10%); diff --git a/src/plugins/timelion/public/index.scss b/src/plugins/timelion/public/index.scss index cf2a7859a505d..6bf7133287c51 100644 --- a/src/plugins/timelion/public/index.scss +++ b/src/plugins/timelion/public/index.scss @@ -8,4 +8,5 @@ // timChart__legend-isLoading @import './app'; +@import './base'; @import './directives/index'; diff --git a/src/plugins/vis_type_table/public/agg_table/_agg_table.scss b/src/plugins/vis_type_table/public/agg_table/_agg_table.scss index 0fffb21eab0fb..4bbc4eb034f8d 100644 --- a/src/plugins/vis_type_table/public/agg_table/_agg_table.scss +++ b/src/plugins/vis_type_table/public/agg_table/_agg_table.scss @@ -36,3 +36,7 @@ kbn-agg-table-group { padding: 0; } } + +.small { + font-size: 0.9em !important; +} diff --git a/x-pack/plugins/ml/public/application/_hacks.scss b/x-pack/plugins/ml/public/application/_hacks.scss index 39740360d8a84..63fec4e74b796 100644 --- a/x-pack/plugins/ml/public/application/_hacks.scss +++ b/x-pack/plugins/ml/public/application/_hacks.scss @@ -18,42 +18,6 @@ cursor: not-allowed; } -// ML bootstrap-select hacks that sit on top of Kibana hacks that often fight with KUI -// Should go away when EUI is fully adopted -.ui-select-match { - .btn-default[disabled], - .btn-default[disabled]:hover, - .btn-default[disabled]:focus { - background-color: $euiColorLightShade; - border-color: $euiColorLightShade; - opacity: 1; - - .ui-select-placeholder { - color: $euiColorDarkShade; - } - } - - .btn { - border: 1px solid $euiColorLightShade; - background-color: $euiColorEmptyShade; - color: $euiColorDarkestShade; - } - } - -.ui-select-container input[type="search"]::placeholder { - color: $euiColorDarkShade; -} - -.ui-select-container input[type="search"]:focus { - box-shadow: none; -} - -.ui-select-multiple.ui-select-bootstrap input.ui-select-search { - font-size: $euiFontSizeS; - padding: 5px 10px; // Matches current padding hacks from other parts of Kibana -} - - // SASSTODO: Remove all the floats .clear, .clearfix { clear: both; diff --git a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.css b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.css index 0ea9f3079de82..12ac5b27c7a4a 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.css +++ b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.css @@ -37,7 +37,6 @@ filter-bar, discover-app .dscTimechart, discover-app .dscSidebar__container, discover-app .kbnCollapsibleSidebar__collapseButton, -discover-app navbar[name=discover-search], discover-app .discover-table-footer { display: none; } diff --git a/x-pack/plugins/reporting/server/lib/layouts/print.css b/x-pack/plugins/reporting/server/lib/layouts/print.css index 4f1e3f4e5abd0..9b07e3c923138 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/print.css +++ b/x-pack/plugins/reporting/server/lib/layouts/print.css @@ -36,7 +36,6 @@ filter-bar, discover-app .dscTimechart, discover-app .dscSidebar__container, discover-app .kbnCollapsibleSidebar__collapseButton, -discover-app navbar[name="discover-search"], discover-app .discover-table-footer { display: none; } From b29e8ee9c746dd2eff95d950ae4dbca564b11441 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 20 Jul 2020 10:55:44 +0200 Subject: [PATCH 03/26] migrate retryCallCluster for new ES client (#71412) * adapt retryCallCluster for new ES client * review comments * retry on 408 ResponseError * use error name instead of instanceof base check * use error name instead of instanceof base check bis * use mockImplementationOnce chaining Co-authored-by: restrry --- src/core/server/elasticsearch/client/index.ts | 1 + .../client/retry_call_cluster.test.ts | 283 ++++++++++++++++++ .../client/retry_call_cluster.ts | 103 +++++++ .../legacy/retry_call_cluster.ts | 4 +- 4 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 src/core/server/elasticsearch/client/retry_call_cluster.test.ts create mode 100644 src/core/server/elasticsearch/client/retry_call_cluster.ts diff --git a/src/core/server/elasticsearch/client/index.ts b/src/core/server/elasticsearch/client/index.ts index 18e84482024ca..b8125de2ee498 100644 --- a/src/core/server/elasticsearch/client/index.ts +++ b/src/core/server/elasticsearch/client/index.ts @@ -22,3 +22,4 @@ export { IScopedClusterClient, ScopedClusterClient } from './scoped_cluster_clie export { ElasticsearchClientConfig } from './client_config'; export { IClusterClient, ICustomClusterClient, ClusterClient } from './cluster_client'; export { configureClient } from './configure_client'; +export { retryCallCluster, migrationRetryCallCluster } from './retry_call_cluster'; diff --git a/src/core/server/elasticsearch/client/retry_call_cluster.test.ts b/src/core/server/elasticsearch/client/retry_call_cluster.test.ts new file mode 100644 index 0000000000000..a7177c0b29047 --- /dev/null +++ b/src/core/server/elasticsearch/client/retry_call_cluster.test.ts @@ -0,0 +1,283 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { errors } from '@elastic/elasticsearch'; +import { elasticsearchClientMock } from './mocks'; +import { loggingSystemMock } from '../../logging/logging_system.mock'; +import { retryCallCluster, migrationRetryCallCluster } from './retry_call_cluster'; + +const dummyBody = { foo: 'bar' }; +const createErrorReturn = (err: any) => elasticsearchClientMock.createClientError(err); + +describe('retryCallCluster', () => { + let client: ReturnType; + + beforeEach(() => { + client = elasticsearchClientMock.createElasticSearchClient(); + }); + + it('returns response from ES API call in case of success', async () => { + const successReturn = elasticsearchClientMock.createClientResponse({ ...dummyBody }); + + client.asyncSearch.get.mockReturnValue(successReturn); + + const result = await retryCallCluster(() => client.asyncSearch.get()); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with `NoLivingConnectionsError`', async () => { + const successReturn = elasticsearchClientMock.createClientResponse({ ...dummyBody }); + + client.asyncSearch.get + .mockImplementationOnce(() => + createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any)) + ) + .mockImplementationOnce(() => successReturn); + + const result = await retryCallCluster(() => client.asyncSearch.get()); + expect(result.body).toEqual(dummyBody); + }); + + it('rejects when ES API calls reject with other errors', async () => { + client.ping + .mockImplementationOnce(() => createErrorReturn(new Error('unknown error'))) + .mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody })); + + await expect(retryCallCluster(() => client.ping())).rejects.toMatchInlineSnapshot( + `[Error: unknown error]` + ); + }); + + it('stops retrying when ES API calls reject with other errors', async () => { + client.ping + .mockImplementationOnce(() => + createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any)) + ) + .mockImplementationOnce(() => + createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any)) + ) + .mockImplementationOnce(() => createErrorReturn(new Error('unknown error'))) + .mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody })); + + await expect(retryCallCluster(() => client.ping())).rejects.toMatchInlineSnapshot( + `[Error: unknown error]` + ); + }); +}); + +describe('migrationRetryCallCluster', () => { + let client: ReturnType; + let logger: ReturnType; + + beforeEach(() => { + client = elasticsearchClientMock.createElasticSearchClient(); + logger = loggingSystemMock.createLogger(); + }); + + const mockClientPingWithErrorBeforeSuccess = (error: any) => { + client.ping + .mockImplementationOnce(() => createErrorReturn(error)) + .mockImplementationOnce(() => createErrorReturn(error)) + .mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody })); + }; + + it('retries ES API calls that rejects with `NoLivingConnectionsError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.NoLivingConnectionsError('no living connections', {} as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with `ConnectionError`', async () => { + mockClientPingWithErrorBeforeSuccess(new errors.ConnectionError('connection error', {} as any)); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with `TimeoutError`', async () => { + mockClientPingWithErrorBeforeSuccess(new errors.TimeoutError('timeout error', {} as any)); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with 503 `ResponseError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.ResponseError({ + statusCode: 503, + } as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects 401 `ResponseError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.ResponseError({ + statusCode: 401, + } as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with 403 `ResponseError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.ResponseError({ + statusCode: 403, + } as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with 408 `ResponseError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.ResponseError({ + statusCode: 408, + } as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with 410 `ResponseError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.ResponseError({ + statusCode: 410, + } as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('retries ES API calls that rejects with `snapshot_in_progress_exception` `ResponseError`', async () => { + mockClientPingWithErrorBeforeSuccess( + new errors.ResponseError({ + statusCode: 500, + body: { + error: { + type: 'snapshot_in_progress_exception', + }, + }, + } as any) + ); + + const result = await migrationRetryCallCluster(() => client.ping(), logger, 1); + expect(result.body).toEqual(dummyBody); + }); + + it('logs only once for each unique error message', async () => { + client.ping + .mockImplementationOnce(() => + createErrorReturn( + new errors.ResponseError({ + statusCode: 503, + } as any) + ) + ) + .mockImplementationOnce(() => + createErrorReturn(new errors.ConnectionError('connection error', {} as any)) + ) + .mockImplementationOnce(() => + createErrorReturn( + new errors.ResponseError({ + statusCode: 503, + } as any) + ) + ) + .mockImplementationOnce(() => + createErrorReturn(new errors.ConnectionError('connection error', {} as any)) + ) + .mockImplementationOnce(() => + createErrorReturn( + new errors.ResponseError({ + statusCode: 500, + body: { + error: { + type: 'snapshot_in_progress_exception', + }, + }, + } as any) + ) + ) + .mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody })); + + await migrationRetryCallCluster(() => client.ping(), logger, 1); + + expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Unable to connect to Elasticsearch. Error: Response Error", + ], + Array [ + "Unable to connect to Elasticsearch. Error: connection error", + ], + Array [ + "Unable to connect to Elasticsearch. Error: snapshot_in_progress_exception", + ], + ] + `); + }); + + it('rejects when ES API calls reject with other errors', async () => { + client.ping + .mockImplementationOnce(() => + createErrorReturn( + new errors.ResponseError({ + statusCode: 418, + body: { + error: { + type: `I'm a teapot`, + }, + }, + } as any) + ) + ) + .mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody })); + + await expect( + migrationRetryCallCluster(() => client.ping(), logger, 1) + ).rejects.toMatchInlineSnapshot(`[ResponseError: I'm a teapot]`); + }); + + it('stops retrying when ES API calls reject with other errors', async () => { + client.ping + .mockImplementationOnce(() => + createErrorReturn(new errors.TimeoutError('timeout error', {} as any)) + ) + .mockImplementationOnce(() => + createErrorReturn(new errors.TimeoutError('timeout error', {} as any)) + ) + .mockImplementationOnce(() => createErrorReturn(new Error('unknown error'))) + .mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody })); + + await expect( + migrationRetryCallCluster(() => client.ping(), logger, 1) + ).rejects.toMatchInlineSnapshot(`[Error: unknown error]`); + }); +}); diff --git a/src/core/server/elasticsearch/client/retry_call_cluster.ts b/src/core/server/elasticsearch/client/retry_call_cluster.ts new file mode 100644 index 0000000000000..1ad039e512215 --- /dev/null +++ b/src/core/server/elasticsearch/client/retry_call_cluster.ts @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { defer, throwError, iif, timer } from 'rxjs'; +import { concatMap, retryWhen } from 'rxjs/operators'; +import { Logger } from '../../logging'; + +const retryResponseStatuses = [ + 503, // ServiceUnavailable + 401, // AuthorizationException + 403, // AuthenticationException + 408, // RequestTimeout + 410, // Gone +]; + +/** + * Retries the provided Elasticsearch API call when a `NoLivingConnectionsError` error is + * encountered. The API call will be retried once a second, indefinitely, until + * a successful response or a different error is received. + * + * @example + * ```ts + * const response = await retryCallCluster(() => client.ping()); + * ``` + * + * @internal + */ +export const retryCallCluster = >(apiCaller: () => T): T => { + return defer(() => apiCaller()) + .pipe( + retryWhen((errors) => + errors.pipe( + concatMap((error) => + iif(() => error.name === 'NoLivingConnectionsError', timer(1000), throwError(error)) + ) + ) + ) + ) + .toPromise() as T; +}; + +/** + * Retries the provided Elasticsearch API call when an error such as + * `AuthenticationException` `NoConnections`, `ConnectionFault`, + * `ServiceUnavailable` or `RequestTimeout` are encountered. The API call will + * be retried once a second, indefinitely, until a successful response or a + * different error is received. + * + * @example + * ```ts + * const response = await migrationRetryCallCluster(() => client.ping(), logger); + * ``` + * + * @internal + */ +export const migrationRetryCallCluster = >( + apiCaller: () => T, + log: Logger, + delay: number = 2500 +): T => { + const previousErrors: string[] = []; + return defer(() => apiCaller()) + .pipe( + retryWhen((errors) => + errors.pipe( + concatMap((error) => { + if (!previousErrors.includes(error.message)) { + log.warn(`Unable to connect to Elasticsearch. Error: ${error.message}`); + previousErrors.push(error.message); + } + return iif( + () => + error.name === 'NoLivingConnectionsError' || + error.name === 'ConnectionError' || + error.name === 'TimeoutError' || + (error.name === 'ResponseError' && + retryResponseStatuses.includes(error.statusCode)) || + error?.body?.error?.type === 'snapshot_in_progress_exception', + timer(delay), + throwError(error) + ); + }) + ) + ) + ) + .toPromise() as T; +}; diff --git a/src/core/server/elasticsearch/legacy/retry_call_cluster.ts b/src/core/server/elasticsearch/legacy/retry_call_cluster.ts index 475a76d406017..1b05cb2bf13cd 100644 --- a/src/core/server/elasticsearch/legacy/retry_call_cluster.ts +++ b/src/core/server/elasticsearch/legacy/retry_call_cluster.ts @@ -53,7 +53,7 @@ export function migrationsRetryCallCluster( .pipe( retryWhen((error$) => error$.pipe( - concatMap((error, i) => { + concatMap((error) => { if (!previousErrors.includes(error.message)) { log.warn(`Unable to connect to Elasticsearch. Error: ${error.message}`); previousErrors.push(error.message); @@ -100,7 +100,7 @@ export function retryCallCluster(apiCaller: LegacyAPICaller) { .pipe( retryWhen((errors) => errors.pipe( - concatMap((error, i) => + concatMap((error) => iif( () => error instanceof legacyElasticsearch.errors.NoConnections, timer(1000), From ade93f0780c3a7fabb836c00021b7cc32a8a6b4d Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Mon, 20 Jul 2020 11:40:39 +0200 Subject: [PATCH 04/26] Disable indexing of unnecessary Saved Object fields (#70409) * Disable indexing of unnecessary SO fields * Add doc_values * Add no doc_values to discover saved object Co-authored-by: Elastic Machine --- .../server/saved_objects/dashboard.ts | 24 ++++++++++--------- .../discover/server/saved_objects/search.ts | 8 +++---- .../server/saved_objects/visualization.ts | 10 ++++---- x-pack/plugins/lens/server/saved_objects.ts | 1 + 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/plugins/dashboard/server/saved_objects/dashboard.ts b/src/plugins/dashboard/server/saved_objects/dashboard.ts index 14d2c822a421e..850b2470dd475 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard.ts @@ -44,21 +44,23 @@ export const dashboardSavedObjectType: SavedObjectsType = { mappings: { properties: { description: { type: 'text' }, - hits: { type: 'integer' }, - kibanaSavedObjectMeta: { properties: { searchSourceJSON: { type: 'text' } } }, - optionsJSON: { type: 'text' }, - panelsJSON: { type: 'text' }, + hits: { type: 'integer', index: false, doc_values: false }, + kibanaSavedObjectMeta: { + properties: { searchSourceJSON: { type: 'text', index: false, doc_values: false } }, + }, + optionsJSON: { type: 'text', index: false, doc_values: false }, + panelsJSON: { type: 'text', index: false, doc_values: false }, refreshInterval: { properties: { - display: { type: 'keyword' }, - pause: { type: 'boolean' }, - section: { type: 'integer' }, - value: { type: 'integer' }, + display: { type: 'keyword', index: false, doc_values: false }, + pause: { type: 'boolean', index: false, doc_values: false }, + section: { type: 'integer', index: false, doc_values: false }, + value: { type: 'integer', index: false, doc_values: false }, }, }, - timeFrom: { type: 'keyword' }, - timeRestore: { type: 'boolean' }, - timeTo: { type: 'keyword' }, + timeFrom: { type: 'keyword', index: false, doc_values: false }, + timeRestore: { type: 'boolean', index: false, doc_values: false }, + timeTo: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, version: { type: 'integer' }, }, diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index 2348d89c4f4dd..c13550e543ab6 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -43,15 +43,15 @@ export const searchSavedObjectType: SavedObjectsType = { }, mappings: { properties: { - columns: { type: 'keyword', index: false }, + columns: { type: 'keyword', index: false, doc_values: false }, description: { type: 'text' }, - hits: { type: 'integer', index: false }, + hits: { type: 'integer', index: false, doc_values: false }, kibanaSavedObjectMeta: { properties: { - searchSourceJSON: { type: 'text', index: false }, + searchSourceJSON: { type: 'text', index: false, doc_values: false }, }, }, - sort: { type: 'keyword', index: false }, + sort: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, version: { type: 'integer' }, }, diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts index c4756de0a8386..ad7618a8640ba 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -44,12 +44,14 @@ export const visualizationSavedObjectType: SavedObjectsType = { mappings: { properties: { description: { type: 'text' }, - kibanaSavedObjectMeta: { properties: { searchSourceJSON: { type: 'text' } } }, - savedSearchRefName: { type: 'keyword' }, + kibanaSavedObjectMeta: { + properties: { searchSourceJSON: { type: 'text', index: false, doc_values: false } }, + }, + savedSearchRefName: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, - uiStateJSON: { type: 'text' }, + uiStateJSON: { type: 'text', index: false, doc_values: false }, version: { type: 'integer' }, - visState: { type: 'text' }, + visState: { type: 'text', index: false, doc_values: false }, }, }, migrations: visualizationSavedObjectTypeMigrations, diff --git a/x-pack/plugins/lens/server/saved_objects.ts b/x-pack/plugins/lens/server/saved_objects.ts index a16cc3dab7967..82ee490bb22af 100644 --- a/x-pack/plugins/lens/server/saved_objects.ts +++ b/x-pack/plugins/lens/server/saved_objects.ts @@ -40,6 +40,7 @@ export function setupSavedObjects(core: CoreSetup) { }, expression: { index: false, + doc_values: false, type: 'keyword', }, }, From 3442451aacb0d5e469019c470d5cf494276eabae Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 20 Jul 2020 13:04:42 +0300 Subject: [PATCH 05/26] [Security Solution][Case] IBM Resilient content fixes (#72271) --- .../public/common/lib/connectors/resilient/index.tsx | 2 +- .../common/lib/connectors/resilient/translations.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx index d3daf195582a8..ba4879e87a1f6 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx @@ -30,7 +30,7 @@ const validateConnector = (action: ResilientActionConnector): ValidationResult = }; if (!action.config.orgId) { - errors.orgId = [...errors.orgId, i18n.RESILIENT_PROJECT_KEY_LABEL]; + errors.orgId = [...errors.orgId, i18n.RESILIENT_PROJECT_KEY_REQUIRED]; } if (!action.secrets.apiKeyId) { diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.ts index f8aec2eea3d4b..2ff97ad354095 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.ts +++ b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.ts @@ -11,7 +11,7 @@ export * from '../translations'; export const RESILIENT_DESC = i18n.translate( 'xpack.securitySolution.case.connectors.resilient.selectMessageText', { - defaultMessage: 'Push or update SIEM case data to a new issue in resilient', + defaultMessage: 'Push or update Security case data to a new issue in Resilient', } ); @@ -25,28 +25,28 @@ export const RESILIENT_TITLE = i18n.translate( export const RESILIENT_PROJECT_KEY_LABEL = i18n.translate( 'xpack.securitySolution.case.connectors.resilient.orgId', { - defaultMessage: 'Organization Id', + defaultMessage: 'Organization ID', } ); export const RESILIENT_PROJECT_KEY_REQUIRED = i18n.translate( 'xpack.securitySolution.case.connectors.resilient.requiredOrgIdTextField', { - defaultMessage: 'Organization Id', + defaultMessage: 'Organization ID is required', } ); export const RESILIENT_API_KEY_ID_LABEL = i18n.translate( 'xpack.securitySolution.case.connectors.resilient.apiKeyId', { - defaultMessage: 'API key id', + defaultMessage: 'API key ID', } ); export const RESILIENT_API_KEY_ID_REQUIRED = i18n.translate( 'xpack.securitySolution.case.connectors.resilient.requiredApiKeyIdTextField', { - defaultMessage: 'API key id is required', + defaultMessage: 'API key ID is required', } ); From 4acdf278dc75d67041bbdbb8154df06301ba2f04 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 20 Jul 2020 12:25:13 +0200 Subject: [PATCH 06/26] [ML] fix charts container width init (#72389) --- .../explorer/actions/load_explorer_data.ts | 2 ++ .../explorer_charts_container_service.d.ts | 1 + .../explorer_charts_container_service.js | 10 +++++++--- .../explorer_charts_container_service.test.js | 14 ++++---------- .../ml/public/application/explorer/legacy_utils.ts | 13 ------------- 5 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/explorer/legacy_utils.ts diff --git a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts index 3fcb032bd3ce1..9e53820fc00c5 100644 --- a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts +++ b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts @@ -200,6 +200,7 @@ const loadExplorerDataProvider = (anomalyTimelineService: AnomalyTimelineService if (selectedCells !== undefined && Array.isArray(anomalyChartRecords)) { memoizedAnomalyDataChange( lastRefresh, + swimlaneContainerWidth, anomalyChartRecords, timerange.earliestMs, timerange.latestMs, @@ -208,6 +209,7 @@ const loadExplorerDataProvider = (anomalyTimelineService: AnomalyTimelineService } else { memoizedAnomalyDataChange( lastRefresh, + swimlaneContainerWidth, [], timerange.earliestMs, timerange.latestMs, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.d.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.d.ts index 962072b974867..008f47d5b42ac 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.d.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.d.ts @@ -14,6 +14,7 @@ export declare interface ExplorerChartsData { export declare const getDefaultChartsData: () => ExplorerChartsData; export declare const anomalyDataChange: ( + chartsContainerWidth: number, anomalyRecords: any[], earliestMs: number, latestMs: number, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js index 898e29a303881..1b83a4ed30560 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js @@ -24,7 +24,6 @@ import { } from '../../../../common/util/job_utils'; import { mlResultsService } from '../../services/results_service'; import { mlJobService } from '../../services/job_service'; -import { getChartContainerWidth } from '../legacy_utils'; import { explorerService } from '../explorer_dashboard_service'; import { CHART_TYPE } from '../explorer_constants'; @@ -48,7 +47,13 @@ const MAX_CHARTS_PER_ROW = 4; // callback(getDefaultChartsData()); -export const anomalyDataChange = function (anomalyRecords, earliestMs, latestMs, severity = 0) { +export const anomalyDataChange = function ( + chartsContainerWidth, + anomalyRecords, + earliestMs, + latestMs, + severity = 0 +) { const data = getDefaultChartsData(); const filteredRecords = anomalyRecords.filter((record) => { @@ -56,7 +61,6 @@ export const anomalyDataChange = function (anomalyRecords, earliestMs, latestMs, }); const allSeriesRecords = processRecordsForDisplay(filteredRecords); // Calculate the number of charts per row, depending on the width available, to a max of 4. - const chartsContainerWidth = getChartContainerWidth(); let chartsPerRow = Math.min( Math.max(Math.floor(chartsContainerWidth / 550), 1), MAX_CHARTS_PER_ROW diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js index 6a9fd19180a4e..433aa65cc5dd4 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js @@ -88,12 +88,6 @@ jest.mock('../../util/string_utils', () => ({ }, })); -jest.mock('../legacy_utils', () => ({ - getChartContainerWidth() { - return 1140; - }, -})); - jest.mock('../explorer_dashboard_service', () => ({ explorerService: { setCharts: jest.fn(), @@ -109,7 +103,7 @@ describe('explorerChartsContainerService', () => { }); test('call anomalyChangeListener with empty series config', (done) => { - anomalyDataChange([], 1486656000000, 1486670399999); + anomalyDataChange(1140, [], 1486656000000, 1486670399999); setImmediate(() => { expect(explorerService.setCharts.mock.calls.length).toBe(1); @@ -122,7 +116,7 @@ describe('explorerChartsContainerService', () => { }); test('call anomalyChangeListener with actual series config', (done) => { - anomalyDataChange(mockAnomalyChartRecords, 1486656000000, 1486670399999); + anomalyDataChange(1140, mockAnomalyChartRecords, 1486656000000, 1486670399999); setImmediate(() => { expect(explorerService.setCharts.mock.calls.length).toBe(2); @@ -138,7 +132,7 @@ describe('explorerChartsContainerService', () => { return d; }); - anomalyDataChange(mockAnomalyChartRecordsClone, 1486656000000, 1486670399999); + anomalyDataChange(1140, mockAnomalyChartRecordsClone, 1486656000000, 1486670399999); setImmediate(() => { expect(explorerService.setCharts.mock.calls.length).toBe(2); @@ -161,7 +155,7 @@ describe('explorerChartsContainerService', () => { mockAnomalyChartRecordsClone[1].partition_field_value = 'AAL.'; expect(() => { - anomalyDataChange(mockAnomalyChartRecordsClone, 1486656000000, 1486670399999); + anomalyDataChange(1140, mockAnomalyChartRecordsClone, 1486656000000, 1486670399999); }).not.toThrow(); setImmediate(() => { diff --git a/x-pack/plugins/ml/public/application/explorer/legacy_utils.ts b/x-pack/plugins/ml/public/application/explorer/legacy_utils.ts deleted file mode 100644 index b85b0401c45ca..0000000000000 --- a/x-pack/plugins/ml/public/application/explorer/legacy_utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// This file includes utils which should eventuelly become obsolete once Anomaly Explorer -// is fully migrated to React. Their purpose is to retain functionality while we migrate step by step. - -export function getChartContainerWidth() { - const chartContainer = document.querySelector('.explorer-charts'); - return Math.floor((chartContainer && chartContainer.clientWidth) || 0); -} From 7976e2bda4c395adc5c9587d95e498d482a92eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 20 Jul 2020 11:39:26 +0100 Subject: [PATCH 07/26] [APM] Testing error rate API and restructuring folders (#72257) * adding error rate and restructuring tests * removing console log * removing console log * adding error rate and restructuring tests * fixing TS error * removing unnecessary files * removing trial tests --- .../apm_api_integration/basic/tests/index.ts | 36 +++++++++----- .../tests/{ => service_maps}/service_maps.ts | 2 +- .../services/{transactions => }/agent_name.ts | 2 +- .../basic/tests/{ => services}/annotations.ts | 2 +- .../{transactions => }/transaction_types.ts | 2 +- .../{ => settings}/agent_configuration.ts | 6 +-- .../basic/tests/{ => settings}/custom_link.ts | 4 +- .../top_traces.expectation.json | 0 .../basic/tests/traces/top_traces.ts | 2 +- .../tests/transaction_groups/error_rate.ts | 47 +++++++++++++++++++ .../expectation/error_rate.json | 42 +++++++++++++++++ .../expectation}/top_transaction_groups.json | 0 .../expectation}/transaction_charts.json | 0 .../top_transaction_groups.ts | 4 +- .../transaction_charts.ts | 4 +- .../apm_api_integration/trial/tests/index.ts | 12 +++-- .../tests/{ => service_maps}/service_maps.ts | 2 +- .../trial/tests/{ => services}/annotations.ts | 2 +- .../tests/{ => services}/rum_services.ts | 2 +- 19 files changed, 138 insertions(+), 33 deletions(-) rename x-pack/test/apm_api_integration/basic/tests/{ => service_maps}/service_maps.ts (93%) rename x-pack/test/apm_api_integration/basic/tests/services/{transactions => }/agent_name.ts (94%) rename x-pack/test/apm_api_integration/basic/tests/{ => services}/annotations.ts (95%) rename x-pack/test/apm_api_integration/basic/tests/services/{transactions => }/transaction_types.ts (94%) rename x-pack/test/apm_api_integration/basic/tests/{ => settings}/agent_configuration.ts (98%) rename x-pack/test/apm_api_integration/basic/tests/{ => settings}/custom_link.ts (96%) rename x-pack/test/apm_api_integration/basic/tests/traces/{ => expectation}/top_traces.expectation.json (100%) create mode 100644 x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts create mode 100644 x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/error_rate.json rename x-pack/test/apm_api_integration/basic/tests/{services/transactions/expectations => transaction_groups/expectation}/top_transaction_groups.json (100%) rename x-pack/test/apm_api_integration/basic/tests/{services/transactions/expectations => transaction_groups/expectation}/transaction_charts.json (100%) rename x-pack/test/apm_api_integration/basic/tests/{services/transactions => transaction_groups}/top_transaction_groups.ts (93%) rename x-pack/test/apm_api_integration/basic/tests/{services/transactions => transaction_groups}/transaction_charts.ts (91%) rename x-pack/test/apm_api_integration/trial/tests/{ => service_maps}/service_maps.ts (99%) rename x-pack/test/apm_api_integration/trial/tests/{ => services}/annotations.ts (99%) rename x-pack/test/apm_api_integration/trial/tests/{ => services}/rum_services.ts (95%) diff --git a/x-pack/test/apm_api_integration/basic/tests/index.ts b/x-pack/test/apm_api_integration/basic/tests/index.ts index 3658208e38d00..873aa478ad080 100644 --- a/x-pack/test/apm_api_integration/basic/tests/index.ts +++ b/x-pack/test/apm_api_integration/basic/tests/index.ts @@ -9,22 +9,32 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont describe('APM specs (basic)', function () { this.tags('ciGroup1'); - loadTestFile(require.resolve('./annotations')); loadTestFile(require.resolve('./feature_controls')); - loadTestFile(require.resolve('./agent_configuration')); - loadTestFile(require.resolve('./custom_link')); - loadTestFile(require.resolve('./service_maps')); - // traces - loadTestFile(require.resolve('./traces/top_traces')); + describe('Service Maps', function () { + loadTestFile(require.resolve('./service_maps/service_maps')); + }); - // services - loadTestFile(require.resolve('./services/top_services')); + describe('Services', function () { + loadTestFile(require.resolve('./services/annotations')); + loadTestFile(require.resolve('./services/top_services')); + loadTestFile(require.resolve('./services/agent_name')); + loadTestFile(require.resolve('./services/transaction_types')); + }); - // services/transaction - loadTestFile(require.resolve('./services/transactions/top_transaction_groups')); - loadTestFile(require.resolve('./services/transactions/transaction_charts')); - loadTestFile(require.resolve('./services/transactions/agent_name')); - loadTestFile(require.resolve('./services/transactions/transaction_types')); + describe('Settings', function () { + loadTestFile(require.resolve('./settings/custom_link')); + loadTestFile(require.resolve('./settings/agent_configuration')); + }); + + describe('Traces', function () { + loadTestFile(require.resolve('./traces/top_traces')); + }); + + describe('Transaction Group', function () { + loadTestFile(require.resolve('./transaction_groups/top_transaction_groups')); + loadTestFile(require.resolve('./transaction_groups/transaction_charts')); + loadTestFile(require.resolve('./transaction_groups/error_rate')); + }); }); } diff --git a/x-pack/test/apm_api_integration/basic/tests/service_maps.ts b/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts similarity index 93% rename from x-pack/test/apm_api_integration/basic/tests/service_maps.ts rename to x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts index 45972aaf529af..b0e503eb7d1eb 100644 --- a/x-pack/test/apm_api_integration/basic/tests/service_maps.ts +++ b/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function serviceMapsApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transactions/agent_name.ts b/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts similarity index 94% rename from x-pack/test/apm_api_integration/basic/tests/services/transactions/agent_name.ts rename to x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts index a9aeaf813aa5c..5be5e43b359f5 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transactions/agent_name.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/annotations.ts b/x-pack/test/apm_api_integration/basic/tests/services/annotations.ts similarity index 95% rename from x-pack/test/apm_api_integration/basic/tests/annotations.ts rename to x-pack/test/apm_api_integration/basic/tests/services/annotations.ts index e0659fe195f93..3136dcef2e985 100644 --- a/x-pack/test/apm_api_integration/basic/tests/annotations.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/annotations.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { JsonObject } from 'src/plugins/kibana_utils/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function annotationApiTests({ getService }: FtrProviderContext) { const supertestWrite = getService('supertestAsApmAnnotationsWriteUser'); diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transactions/transaction_types.ts b/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts similarity index 94% rename from x-pack/test/apm_api_integration/basic/tests/services/transactions/transaction_types.ts rename to x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts index 56844dfcedda1..3e8f320ad6b24 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transactions/transaction_types.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/agent_configuration.ts b/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts similarity index 98% rename from x-pack/test/apm_api_integration/basic/tests/agent_configuration.ts rename to x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts index 1c8097760bc04..283540201b9b5 100644 --- a/x-pack/test/apm_api_integration/basic/tests/agent_configuration.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts @@ -6,9 +6,9 @@ import expect from '@kbn/expect'; import { omit, orderBy } from 'lodash'; -import { AgentConfigurationIntake } from '../../../../plugins/apm/common/agent_configuration/configuration_types'; -import { AgentConfigSearchParams } from '../../../../plugins/apm/server/routes/settings/agent_configuration'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { AgentConfigurationIntake } from '../../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfigSearchParams } from '../../../../../plugins/apm/server/routes/settings/agent_configuration'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function agentConfigurationTests({ getService }: FtrProviderContext) { const supertestRead = getService('supertestAsApmReadUser'); diff --git a/x-pack/test/apm_api_integration/basic/tests/custom_link.ts b/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts similarity index 96% rename from x-pack/test/apm_api_integration/basic/tests/custom_link.ts rename to x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts index ec93d2b3a3b41..9465708db2fba 100644 --- a/x-pack/test/apm_api_integration/basic/tests/custom_link.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts @@ -5,8 +5,8 @@ */ import URL from 'url'; import expect from '@kbn/expect'; -import { CustomLink } from '../../../../plugins/apm/common/custom_link/custom_link_types'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { CustomLink } from '../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function customLinksTests({ getService }: FtrProviderContext) { const supertestRead = getService('supertestAsApmReadUser'); diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.expectation.json b/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json similarity index 100% rename from x-pack/test/apm_api_integration/basic/tests/traces/top_traces.expectation.json rename to x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts index aef208b6fc06b..e96cb20a68fda 100644 --- a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import expectTopTraces from './top_traces.expectation.json'; +import expectTopTraces from './expectation/top_traces.expectation.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts new file mode 100644 index 0000000000000..2c22cbbcce780 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import expectedErrorRate from './expectation/error_rate.json'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + // url parameters + const start = encodeURIComponent('2020-06-29T06:45:00.000Z'); + const end = encodeURIComponent('2020-06-29T06:49:00.000Z'); + const uiFilters = encodeURIComponent(JSON.stringify({})); + + describe('Error rate', () => { + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transaction_groups/error_rate?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + expect(response.status).to.be(200); + expect(response.body).to.eql({ + noHits: true, + erroneousTransactionsRate: [], + average: null, + }); + }); + }); + describe('when data is loaded', () => { + before(() => esArchiver.load('8.0.0')); + after(() => esArchiver.unload('8.0.0')); + + it('returns the transaction error rate', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transaction_groups/error_rate?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + + expect(response.status).to.be(200); + expect(response.body).to.eql(expectedErrorRate); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/error_rate.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/error_rate.json new file mode 100644 index 0000000000000..9ff45ebdbb21b --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/error_rate.json @@ -0,0 +1,42 @@ +{ + "noHits":false, + "erroneousTransactionsRate":[ + { + "x":1593413100000, + "y":null + }, + { + "x":1593413130000, + "y":null + }, + { + "x":1593413160000, + "y":null + }, + { + "x":1593413190000, + "y":null + }, + { + "x":1593413220000, + "y":null + }, + { + "x":1593413250000, + "y":0 + }, + { + "x":1593413280000, + "y":0.14102564102564102 + }, + { + "x":1593413310000, + "y":0.14634146341463414 + }, + { + "x":1593413340000, + "y":null + } + ], + "average":0.09578903481342504 +} \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transactions/expectations/top_transaction_groups.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json similarity index 100% rename from x-pack/test/apm_api_integration/basic/tests/services/transactions/expectations/top_transaction_groups.json rename to x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transactions/expectations/transaction_charts.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/transaction_charts.json similarity index 100% rename from x-pack/test/apm_api_integration/basic/tests/services/transactions/expectations/transaction_charts.json rename to x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/transaction_charts.json diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transactions/top_transaction_groups.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts similarity index 93% rename from x-pack/test/apm_api_integration/basic/tests/services/transactions/top_transaction_groups.ts rename to x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts index bf8d3f6a56e6a..43b2ad5414c7a 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transactions/top_transaction_groups.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import expectedTransactionGroups from './expectations/top_transaction_groups.json'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import expectedTransactionGroups from './expectation/top_transaction_groups.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transactions/transaction_charts.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts similarity index 91% rename from x-pack/test/apm_api_integration/basic/tests/services/transactions/transaction_charts.ts rename to x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts index 7178498863787..68a7499a2389c 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transactions/transaction_charts.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import expectedTransactionCharts from './expectations/transaction_charts.json'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import expectedTransactionCharts from './expectation/transaction_charts.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts index 37328badcb794..fb942f755a063 100644 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ b/x-pack/test/apm_api_integration/trial/tests/index.ts @@ -9,8 +9,14 @@ import { FtrProviderContext } from '../../../api_integration/ftr_provider_contex export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('APM specs (trial)', function () { this.tags('ciGroup1'); - loadTestFile(require.resolve('./annotations')); - loadTestFile(require.resolve('./service_maps')); - loadTestFile(require.resolve('./rum_services')); + + describe('Services', function () { + loadTestFile(require.resolve('./services/annotations')); + loadTestFile(require.resolve('./services/rum_services.ts')); + }); + + describe('Service Maps', function () { + loadTestFile(require.resolve('./service_maps/service_maps')); + }); }); } diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps.ts b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts similarity index 99% rename from x-pack/test/apm_api_integration/trial/tests/service_maps.ts rename to x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts index 0b370f6a30a8b..4002e8cff5bad 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps.ts +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts @@ -6,7 +6,7 @@ import querystring from 'querystring'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function serviceMapsApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/apm_api_integration/trial/tests/annotations.ts b/x-pack/test/apm_api_integration/trial/tests/services/annotations.ts similarity index 99% rename from x-pack/test/apm_api_integration/trial/tests/annotations.ts rename to x-pack/test/apm_api_integration/trial/tests/services/annotations.ts index 662879c495230..c2ddc10c5f1d2 100644 --- a/x-pack/test/apm_api_integration/trial/tests/annotations.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/annotations.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { merge, cloneDeep, isPlainObject } from 'lodash'; import { JsonObject } from 'src/plugins/kibana_utils/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; const DEFAULT_INDEX_NAME = 'observability-annotations'; diff --git a/x-pack/test/apm_api_integration/trial/tests/rum_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts similarity index 95% rename from x-pack/test/apm_api_integration/trial/tests/rum_services.ts rename to x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts index 5505387de54a7..78171a65a11fd 100644 --- a/x-pack/test/apm_api_integration/trial/tests/rum_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); From b1edce8050cc209053d094f1788b7de657123ae2 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 20 Jul 2020 12:50:09 +0200 Subject: [PATCH 08/26] [ML] improve annotation flyout performance (#72299) --- .../annotations/annotation_flyout/index.tsx | 270 ++++++++++-------- 1 file changed, 148 insertions(+), 122 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotation_flyout/index.tsx b/x-pack/plugins/ml/public/application/components/annotations/annotation_flyout/index.tsx index 2196f3d6cc6d2..84abe3ed8a821 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotation_flyout/index.tsx +++ b/x-pack/plugins/ml/public/application/components/annotations/annotation_flyout/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment, FC, ReactNode } from 'react'; +import React, { Component, FC, ReactNode, useCallback } from 'react'; import useObservable from 'react-use/lib/useObservable'; import * as Rx from 'rxjs'; import { cloneDeep } from 'lodash'; @@ -28,6 +28,7 @@ import { import { CommonProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { distinctUntilChanged } from 'rxjs/operators'; import { ANNOTATION_MAX_LENGTH_CHARS, ANNOTATION_EVENT_USER, @@ -60,7 +61,6 @@ interface Entity { } interface Props { - annotation: AnnotationState; chartDetails: { entityData: { entities: Entity[] }; functionLabel: string; @@ -70,25 +70,39 @@ interface Props { } interface State { + annotationState: AnnotationState | null; isDeleteModalVisible: boolean; applyAnnotationToSeries: boolean; } -class AnnotationFlyoutUI extends Component { +export class AnnotationFlyoutUI extends Component { public state: State = { isDeleteModalVisible: false, applyAnnotationToSeries: true, + annotationState: null, }; public annotationSub: Rx.Subscription | null = null; + componentDidMount() { + this.annotationSub = annotation$.subscribe((v) => { + this.setState({ + annotationState: v, + }); + }); + } + + componentWillUnmount() { + this.annotationSub!.unsubscribe(); + } + public annotationTextChangeHandler = (e: React.ChangeEvent) => { - if (this.props.annotation === null) { + if (this.state.annotationState === null) { return; } annotation$.next({ - ...this.props.annotation, + ...this.state.annotationState, annotation: e.target.value, }); }; @@ -102,21 +116,21 @@ class AnnotationFlyoutUI extends Component { }; public deleteHandler = async () => { - const { annotation } = this.props; + const { annotationState } = this.state; const toastNotifications = getToastNotifications(); - if (annotation === null || annotation._id === undefined) { + if (annotationState === null || annotationState._id === undefined) { return; } try { - await ml.annotations.deleteAnnotation(annotation._id); + await ml.annotations.deleteAnnotation(annotationState._id); toastNotifications.addSuccess( i18n.translate( 'xpack.ml.timeSeriesExplorer.timeSeriesChart.deletedAnnotationNotificationMessage', { defaultMessage: 'Deleted annotation for job with ID {jobId}.', - values: { jobId: annotation.job_id }, + values: { jobId: annotationState.job_id }, } ) ); @@ -127,7 +141,7 @@ class AnnotationFlyoutUI extends Component { { defaultMessage: 'An error occurred deleting the annotation for job with ID {jobId}: {error}', - values: { jobId: annotation.job_id, error: JSON.stringify(err) }, + values: { jobId: annotationState.job_id, error: JSON.stringify(err) }, } ) ); @@ -145,13 +159,13 @@ class AnnotationFlyoutUI extends Component { public validateAnnotationText = () => { // Validates the entered text, returning an array of error messages // for display in the form. An empty array is returned if the text is valid. - const { annotation } = this.props; + const { annotationState } = this.state; const errors: string[] = []; - if (annotation === null) { + if (annotationState === null) { return errors; } - if (annotation.annotation.trim().length === 0) { + if (annotationState.annotation.trim().length === 0) { errors.push( i18n.translate('xpack.ml.timeSeriesExplorer.annotationFlyout.noAnnotationTextError', { defaultMessage: 'Enter annotation text', @@ -159,7 +173,7 @@ class AnnotationFlyoutUI extends Component { ); } - const textLength = annotation.annotation.length; + const textLength = annotationState.annotation.length; if (textLength > ANNOTATION_MAX_LENGTH_CHARS) { const charsOver = textLength - ANNOTATION_MAX_LENGTH_CHARS; errors.push( @@ -178,7 +192,8 @@ class AnnotationFlyoutUI extends Component { }; public saveOrUpdateAnnotation = () => { - const { annotation: originalAnnotation, chartDetails, detectorIndex } = this.props; + const { annotationState: originalAnnotation } = this.state; + const { chartDetails, detectorIndex } = this.props; if (originalAnnotation === null) { return; } @@ -262,14 +277,12 @@ class AnnotationFlyoutUI extends Component { }; public render(): ReactNode { - const { annotation, detectors, detectorIndex } = this.props; - const { isDeleteModalVisible } = this.state; + const { detectors, detectorIndex } = this.props; + const { annotationState, isDeleteModalVisible } = this.state; - if (annotation === null) { - return null; - } + if (!annotationState) return null; - const isExistingAnnotation = typeof annotation._id !== 'undefined'; + const isExistingAnnotation = typeof annotationState._id !== 'undefined'; // Check the length of the text is within the max length limit, // and warn if the length is approaching the limit. @@ -279,14 +292,16 @@ class AnnotationFlyoutUI extends Component { let helpText = null; if ( isInvalid === false && - annotation.annotation.length > ANNOTATION_MAX_LENGTH_CHARS * lengthRatioToShowWarning + annotationState.annotation.length > ANNOTATION_MAX_LENGTH_CHARS * lengthRatioToShowWarning ) { helpText = i18n.translate( 'xpack.ml.timeSeriesExplorer.annotationFlyout.approachingMaxLengthWarning', { defaultMessage: '{charsRemaining, number} {charsRemaining, plural, one {character} other {characters}} remaining', - values: { charsRemaining: ANNOTATION_MAX_LENGTH_CHARS - annotation.annotation.length }, + values: { + charsRemaining: ANNOTATION_MAX_LENGTH_CHARS - annotationState.annotation.length, + }, } ); } @@ -295,127 +310,138 @@ class AnnotationFlyoutUI extends Component { detector && 'detector_description' in detector ? detector.detector_description : ''; return ( - - - - -

- {isExistingAnnotation ? ( - - ) : ( - - )} -

-
-
- - + + + + + } + fullWidth + helpText={helpText} + isInvalid={isInvalid} + error={validationErrors} + > + - - + + } - fullWidth - helpText={helpText} - isInvalid={isInvalid} - error={validationErrors} - > - - - - + this.setState({ + applyAnnotationToSeries: !this.state.applyAnnotationToSeries, + }) + } + /> + + + + + + + + + + + {isExistingAnnotation && ( + - } - checked={this.state.applyAnnotationToSeries} - onChange={() => - this.setState({ - applyAnnotationToSeries: !this.state.applyAnnotationToSeries, - }) - } - /> - - - - - - + + )} + + + + {isExistingAnnotation ? ( + ) : ( + - - - - {isExistingAnnotation && ( - - - )} - - - - {isExistingAnnotation ? ( - - ) : ( - - )} - - - - -
+ + + + -
+ ); } } export const AnnotationFlyout: FC = (props) => { - const annotationProp = useObservable(annotation$); + const annotationProp = useObservable( + annotation$.pipe( + distinctUntilChanged((prev, curr) => { + // prevent re-rendering + return prev !== null && curr !== null; + }) + ) + ); - if (annotationProp === undefined) { + const cancelEditingHandler = useCallback(() => { + annotation$.next(null); + }, []); + + if (annotationProp === undefined || annotationProp === null) { return null; } - return ; + const isExistingAnnotation = typeof annotationProp._id !== 'undefined'; + + return ( + + + +

+ {isExistingAnnotation ? ( + + ) : ( + + )} +

+
+
+ +
+ ); }; From 9504c9453b83e85ccc2d0e5d8cd8009faee57463 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 20 Jul 2020 13:47:18 +0100 Subject: [PATCH 09/26] [ML] Adding missing index pattern name to new job wizards (#72400) --- .../public/application/jobs/new_job/pages/new_job/page.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx index 48b044e5371de..8e223b69b00e8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx @@ -210,6 +210,12 @@ export const Page: FC = ({ existingJobsAndGroups, jobType }) => { : {jobCreatorTitle} + + From bf89b3cdd21cff32675b35db945a89f8a4d7247d Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 20 Jul 2020 08:53:09 -0400 Subject: [PATCH 10/26] [Ingest Manager] Do not bumb config revision during config creation (#72270) --- .../server/routes/agent_config/handlers.ts | 1 + .../ingest_manager/server/services/agent_config.ts | 10 ++++++---- .../ingest_manager/server/services/package_config.ts | 6 ++++-- x-pack/plugins/ingest_manager/server/services/setup.ts | 4 +++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts index 718aca89ea4fd..4e4653ec023ce 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/handlers.ts @@ -135,6 +135,7 @@ export const createAgentConfigHandler: RequestHandler< newSysPackageConfig.namespace = agentConfig.namespace; await packageConfigService.create(soClient, callCluster, newSysPackageConfig, { user, + bumpConfigRevision: false, }); } diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index c068b594318c1..0a9adc1f1c593 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -41,7 +41,8 @@ class AgentConfigService { soClient: SavedObjectsClientContract, id: string, agentConfig: Partial, - user?: AuthenticatedUser + user?: AuthenticatedUser, + options: { bumpRevision: boolean } = { bumpRevision: true } ): Promise { const oldAgentConfig = await this.get(soClient, id, false); @@ -60,7 +61,7 @@ class AgentConfigService { await soClient.update(SAVED_OBJECT_TYPE, id, { ...agentConfig, - revision: oldAgentConfig.revision + 1, + ...(options.bumpRevision ? { revision: oldAgentConfig.revision + 1 } : {}), updated_at: new Date().toISOString(), updated_by: user ? user.username : 'system', }); @@ -265,7 +266,7 @@ class AgentConfigService { soClient: SavedObjectsClientContract, id: string, packageConfigIds: string[], - options?: { user?: AuthenticatedUser } + options: { user?: AuthenticatedUser; bumpRevision: boolean } = { bumpRevision: true } ): Promise { const oldAgentConfig = await this.get(soClient, id, false); @@ -281,7 +282,8 @@ class AgentConfigService { [...((oldAgentConfig.package_configs || []) as string[])].concat(packageConfigIds) ), }, - options?.user + options?.user, + { bumpRevision: options.bumpRevision } ); } diff --git a/x-pack/plugins/ingest_manager/server/services/package_config.ts b/x-pack/plugins/ingest_manager/server/services/package_config.ts index e8ca09a83c2b6..c2d465cf7c73f 100644 --- a/x-pack/plugins/ingest_manager/server/services/package_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/package_config.ts @@ -42,7 +42,7 @@ class PackageConfigService { soClient: SavedObjectsClientContract, callCluster: CallESAsCurrentUser, packageConfig: NewPackageConfig, - options?: { id?: string; user?: AuthenticatedUser } + options?: { id?: string; user?: AuthenticatedUser; bumpConfigRevision?: boolean } ): Promise { // Check that its agent config does not have a package config with the same name const parentAgentConfig = await agentConfigService.get(soClient, packageConfig.config_id); @@ -104,6 +104,7 @@ class PackageConfigService { // Assign it to the given agent config await agentConfigService.assignPackageConfigs(soClient, packageConfig.config_id, [newSo.id], { user: options?.user, + bumpRevision: options?.bumpConfigRevision ?? true, }); return { @@ -117,7 +118,7 @@ class PackageConfigService { soClient: SavedObjectsClientContract, packageConfigs: NewPackageConfig[], configId: string, - options?: { user?: AuthenticatedUser } + options?: { user?: AuthenticatedUser; bumpConfigRevision?: boolean } ): Promise { const isoDate = new Date().toISOString(); const { saved_objects: newSos } = await soClient.bulkCreate( @@ -142,6 +143,7 @@ class PackageConfigService { newSos.map((newSo) => newSo.id), { user: options?.user, + bumpRevision: options?.bumpConfigRevision ?? true, } ); diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts index 627abc158143d..c91cae98e17d2 100644 --- a/x-pack/plugins/ingest_manager/server/services/setup.ts +++ b/x-pack/plugins/ingest_manager/server/services/setup.ts @@ -218,5 +218,7 @@ async function addPackageToConfig( config.namespace ); - await packageConfigService.create(soClient, callCluster, newPackageConfig); + await packageConfigService.create(soClient, callCluster, newPackageConfig, { + bumpConfigRevision: false, + }); } From f331cc8b641dc6bc4ce2ae82e404f88f0451d768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Mon, 20 Jul 2020 13:54:36 +0100 Subject: [PATCH 11/26] [Ingest Manager] Set `_meta` in the index.mappings (#72026) Co-authored-by: Elastic Machine --- .../__snapshots__/template.test.ts.snap | 21 +++++++++++++++++++ .../epm/elasticsearch/template/template.ts | 18 +++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap index 219c2de675359..0333fb024a717 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap @@ -87,6 +87,13 @@ exports[`tests loading base.yml: base.yml 1`] = ` "validarray": { "type": "integer" } + }, + "_meta": { + "package": { + "name": "nginx" + }, + "managed_by": "ingest-manager", + "managed": true } }, "aliases": {} @@ -190,6 +197,13 @@ exports[`tests loading coredns.logs.yml: coredns.logs.yml 1`] = ` } } } + }, + "_meta": { + "package": { + "name": "coredns" + }, + "managed_by": "ingest-manager", + "managed": true } }, "aliases": {} @@ -1677,6 +1691,13 @@ exports[`tests loading system.yml: system.yml 1`] = ` } } } + }, + "_meta": { + "package": { + "name": "system" + }, + "managed_by": "ingest-manager", + "managed": true } }, "aliases": {} diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 876573f2270ea..a739806d5868b 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -249,6 +249,15 @@ function getBaseTemplate( packageName: string, composedOfTemplates: string[] ): IndexTemplate { + // Meta information to identify Ingest Manager's managed templates and indices + const _meta = { + package: { + name: packageName, + }, + managed_by: 'ingest-manager', + managed: true, + }; + return { // This takes precedence over all index templates installed by ES by default (logs-*-* and metrics-*-*) // if this number is lower than the ES value (which is 100) this template will never be applied when a data stream @@ -304,19 +313,14 @@ function getBaseTemplate( date_detection: false, // All the properties we know from the fields.yml file properties: mappings.properties, + _meta, }, // To be filled with the aliases that we need aliases: {}, }, data_stream: {}, composed_of: composedOfTemplates, - _meta: { - package: { - name: packageName, - }, - managed_by: 'ingest-manager', - managed: true, - }, + _meta, }; } From 6cf796a4fb2a00165b8bf1a33047dd3688fc3e43 Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Mon, 20 Jul 2020 09:38:30 -0400 Subject: [PATCH 12/26] [Resolver] Selector performance (#72380) * Memoize various selectors * Improve performance of the selectors that calculate the `aria-flowto` attribute. * more tests. --- .../common/endpoint/types.ts | 1 - .../models/indexed_process_tree/index.ts | 53 ++--- .../isometric_taxi_layout.ts | 11 +- .../public/resolver/store/camera/selectors.ts | 65 +++--- .../resolver/store/data/selectors.test.ts | 90 ++++++++ .../public/resolver/store/data/selectors.ts | 208 +++++++++++------- .../resolver/store/mocks/endpoint_event.ts | 37 ++++ .../resolver/store/mocks/resolver_tree.ts | 87 ++++++++ .../public/resolver/store/selectors.test.ts | 117 +--------- .../public/resolver/store/selectors.ts | 35 +-- .../panels/panel_content_related_detail.tsx | 3 +- 11 files changed, 427 insertions(+), 280 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts create mode 100644 x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index b477207b1c5a3..5e7e4d22f8c3c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -482,7 +482,6 @@ export interface LegacyEndpointEvent { type: string; version: string; }; - process?: object; rule?: object; user?: object; event?: { diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts index 35a32d91d8a02..628d0267754f2 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable no-shadow */ + import { uniquePidForProcess, uniqueParentPidForProcess, orderByTime } from '../process_event'; import { IndexedProcessTree } from '../../types'; import { ResolverEvent } from '../../../../common/endpoint/types'; @@ -24,16 +26,15 @@ export function factory( const uniqueProcessPid = uniquePidForProcess(process); idToValue.set(uniqueProcessPid, process); - const uniqueParentPid = uniqueParentPidForProcess(process); - // if its defined and not '' - if (uniqueParentPid) { - let siblings = idToChildren.get(uniqueParentPid); - if (!siblings) { - siblings = []; - idToChildren.set(uniqueParentPid, siblings); - } - siblings.push(process); + // NB: If the value was null or undefined, use `undefined` + const uniqueParentPid: string | undefined = uniqueParentPidForProcess(process) ?? undefined; + + let childrenWithTheSameParent = idToChildren.get(uniqueParentPid); + if (!childrenWithTheSameParent) { + childrenWithTheSameParent = []; + idToChildren.set(uniqueParentPid, childrenWithTheSameParent); } + childrenWithTheSameParent.push(process); } // sort the children of each node @@ -50,9 +51,8 @@ export function factory( /** * Returns an array with any children `ProcessEvent`s of the passed in `process` */ -export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] { - const id = uniquePidForProcess(process); - const currentProcessSiblings = tree.idToChildren.get(id); +export function children(tree: IndexedProcessTree, parentID: string | undefined): ResolverEvent[] { + const currentProcessSiblings = tree.idToChildren.get(parentID); return currentProcessSiblings === undefined ? [] : currentProcessSiblings; } @@ -78,31 +78,6 @@ export function parent( } } -/** - * Returns the following sibling - */ -export function nextSibling( - tree: IndexedProcessTree, - sibling: ResolverEvent -): ResolverEvent | undefined { - const parentNode = parent(tree, sibling); - if (parentNode) { - // The siblings of `sibling` are the children of its parent. - const siblings = children(tree, parentNode); - - // Find the sibling - const index = siblings.indexOf(sibling); - - // if the sibling wasn't found, or if it was the last element in the array, return undefined - if (index === -1 || index === siblings.length - 1) { - return undefined; - } - - // return the next sibling - return siblings[index + 1]; - } -} - /** * Number of processes in the tree */ @@ -133,6 +108,8 @@ export function root(tree: IndexedProcessTree) { export function* levelOrder(tree: IndexedProcessTree) { const rootNode = root(tree); if (rootNode !== null) { - yield* baseLevelOrder(rootNode, children.bind(null, tree)); + yield* baseLevelOrder(rootNode, (parentNode: ResolverEvent): ResolverEvent[] => + children(tree, uniquePidForProcess(parentNode)) + ); } } diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/isometric_taxi_layout.ts b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/isometric_taxi_layout.ts index 6058a40037ad2..11c888d1462f8 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/isometric_taxi_layout.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/isometric_taxi_layout.ts @@ -19,6 +19,7 @@ import * as event from '../../../../common/endpoint/models/event'; import { ResolverEvent } from '../../../../common/endpoint/types'; import * as model from './index'; import { getFriendlyElapsedTime as elapsedTime } from '../../lib/date'; +import { uniquePidForProcess } from '../process_event'; /** * Graph the process tree @@ -146,10 +147,12 @@ function widthsOfProcessSubtrees(indexedProcessTree: IndexedProcessTree): Proces return widths; } - const processesInReverseLevelOrder = [...model.levelOrder(indexedProcessTree)].reverse(); + const processesInReverseLevelOrder: ResolverEvent[] = [ + ...model.levelOrder(indexedProcessTree), + ].reverse(); for (const process of processesInReverseLevelOrder) { - const children = model.children(indexedProcessTree, process); + const children = model.children(indexedProcessTree, uniquePidForProcess(process)); const sumOfWidthOfChildren = function sumOfWidthOfChildren() { return children.reduce(function sum(currentValue, child) { @@ -226,7 +229,7 @@ function processEdgeLineSegments( metadata: edgeLineMetadata, }; - const siblings = model.children(indexedProcessTree, parent); + const siblings = model.children(indexedProcessTree, uniquePidForProcess(parent)); const isFirstChild = process === siblings[0]; if (metadata.isOnlyChild) { @@ -420,7 +423,7 @@ function* levelOrderWithWidths( parentWidth, }; - const siblings = model.children(tree, parent); + const siblings = model.children(tree, uniquePidForProcess(parent)); if (siblings.length === 1) { metadata.isOnlyChild = true; metadata.lastChildWidth = width; diff --git a/x-pack/plugins/security_solution/public/resolver/store/camera/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/camera/selectors.ts index 86d934bd95663..baa049ba42f92 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/camera/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/camera/selectors.ts @@ -164,7 +164,8 @@ export const scale: (state: CameraState) => (time: number) => Vector2 = createSe scalingConstants.maximum ); - return (time) => { + // memoizing this so the vector returned will be the same object reference if called with the same `time`. + return defaultMemoize((time) => { /** * If the animation has completed, return the `scaleNotCountingAnimation`, as * the animation always completes with the scale set back at starting value. @@ -247,12 +248,13 @@ export const scale: (state: CameraState) => (time: number) => Vector2 = createSe */ return [lerpedScale, lerpedScale]; } - }; + }); } else { /** * The scale should be the same in both axes. + * Memoizing this so the vector returned will be the same object reference every time. */ - return () => [scaleNotCountingAnimation, scaleNotCountingAnimation]; + return defaultMemoize(() => [scaleNotCountingAnimation, scaleNotCountingAnimation]); } /** @@ -277,22 +279,26 @@ export const clippingPlanes: ( ) => (time: number) => ClippingPlanes = createSelector( (state) => state.rasterSize, scale, - (rasterSize, scaleAtTime) => (time: number) => { - const [scaleX, scaleY] = scaleAtTime(time); - const renderWidth = rasterSize[0]; - const renderHeight = rasterSize[1]; - const clippingPlaneRight = renderWidth / 2 / scaleX; - const clippingPlaneTop = renderHeight / 2 / scaleY; - - return { - renderWidth, - renderHeight, - clippingPlaneRight, - clippingPlaneTop, - clippingPlaneLeft: -clippingPlaneRight, - clippingPlaneBottom: -clippingPlaneTop, - }; - } + (rasterSize, scaleAtTime) => + /** + * memoizing this for object reference equality. + */ + defaultMemoize((time: number) => { + const [scaleX, scaleY] = scaleAtTime(time); + const renderWidth = rasterSize[0]; + const renderHeight = rasterSize[1]; + const clippingPlaneRight = renderWidth / 2 / scaleX; + const clippingPlaneTop = renderHeight / 2 / scaleY; + + return { + renderWidth, + renderHeight, + clippingPlaneRight, + clippingPlaneTop, + clippingPlaneLeft: -clippingPlaneRight, + clippingPlaneBottom: -clippingPlaneTop, + }; + }) ); /** @@ -323,7 +329,10 @@ export const translation: (state: CameraState) => (time: number) => Vector2 = cr scale, (state) => state.animation, (panning, translationNotCountingCurrentPanning, scaleAtTime, animation) => { - return (time: number) => { + /** + * Memoizing this for object reference equality. + */ + return defaultMemoize((time: number) => { const [scaleX, scaleY] = scaleAtTime(time); if (animation !== undefined && animationIsActive(animation, time)) { return vector2.lerp( @@ -343,7 +352,7 @@ export const translation: (state: CameraState) => (time: number) => Vector2 = cr } else { return translationNotCountingCurrentPanning; } - }; + }); } ); @@ -357,7 +366,10 @@ export const inverseProjectionMatrix: ( clippingPlanes, translation, (clippingPlanesAtTime, translationAtTime) => { - return (time: number) => { + /** + * Memoizing this for object reference equality (and reduced memory churn.) + */ + return defaultMemoize((time: number) => { const { renderWidth, renderHeight, @@ -404,7 +416,7 @@ export const inverseProjectionMatrix: ( translateForCamera, multiply(scaleToClippingPlaneDimensions, multiply(invertY, screenToNDC)) ); - }; + }); } ); @@ -415,7 +427,8 @@ export const viewableBoundingBox: (state: CameraState) => (time: number) => AABB clippingPlanes, inverseProjectionMatrix, (clippingPlanesAtTime, matrixAtTime) => { - return (time: number) => { + // memoizing this so the AABB returned will be the same object reference if called with the same `time`. + return defaultMemoize((time: number) => { const { renderWidth, renderHeight } = clippingPlanesAtTime(time); const matrix = matrixAtTime(time); const bottomLeftCorner: Vector2 = [0, renderHeight]; @@ -424,7 +437,7 @@ export const viewableBoundingBox: (state: CameraState) => (time: number) => AABB minimum: vector2.applyMatrix3(bottomLeftCorner, matrix), maximum: vector2.applyMatrix3(topRightCorner, matrix), }; - }; + }); } ); @@ -436,6 +449,8 @@ export const projectionMatrix: (state: CameraState) => (time: number) => Matrix3 clippingPlanes, translation, (clippingPlanesAtTime, translationAtTime) => { + // memoizing this so the matrix returned will be the same object reference if called with the same `time`. + // this should also save on some memory allocation return defaultMemoize((time: number) => { const { renderWidth, diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts index cf23596db6134..683f8f1a5f84a 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts @@ -9,6 +9,13 @@ import { DataState } from '../../types'; import { dataReducer } from './reducer'; import { DataAction } from './action'; import { createStore } from 'redux'; +import { + mockTreeWithNoAncestorsAnd2Children, + mockTreeWith2AncestorsAndNoChildren, +} from '../mocks/resolver_tree'; +import { uniquePidForProcess } from '../../models/process_event'; +import { EndpointEvent } from '../../../../common/endpoint/types'; + describe('data state', () => { let actions: DataAction[] = []; @@ -263,4 +270,87 @@ describe('data state', () => { }); }); }); + describe('with a tree with no descendants and 2 ancestors', () => { + const originID = 'c'; + const firstAncestorID = 'b'; + const secondAncestorID = 'a'; + beforeEach(() => { + actions.push({ + type: 'serverReturnedResolverData', + payload: { + result: mockTreeWith2AncestorsAndNoChildren({ + originID, + firstAncestorID, + secondAncestorID, + }), + // this value doesn't matter + databaseDocumentID: '', + }, + }); + }); + it('should have no flowto candidate for the origin', () => { + expect(selectors.ariaFlowtoCandidate(state())(originID)).toBe(null); + }); + it('should have no flowto candidate for the first ancestor', () => { + expect(selectors.ariaFlowtoCandidate(state())(firstAncestorID)).toBe(null); + }); + it('should have no flowto candidate for the second ancestor ancestor', () => { + expect(selectors.ariaFlowtoCandidate(state())(secondAncestorID)).toBe(null); + }); + }); + describe('with a tree with 2 children and no ancestors', () => { + const originID = 'c'; + const firstChildID = 'd'; + const secondChildID = 'e'; + beforeEach(() => { + actions.push({ + type: 'serverReturnedResolverData', + payload: { + result: mockTreeWithNoAncestorsAnd2Children({ originID, firstChildID, secondChildID }), + // this value doesn't matter + databaseDocumentID: '', + }, + }); + }); + it('should have no flowto candidate for the origin', () => { + expect(selectors.ariaFlowtoCandidate(state())(originID)).toBe(null); + }); + it('should use the second child as the flowto candidate for the first child', () => { + expect(selectors.ariaFlowtoCandidate(state())(firstChildID)).toBe(secondChildID); + }); + it('should have no flowto candidate for the second child', () => { + expect(selectors.ariaFlowtoCandidate(state())(secondChildID)).toBe(null); + }); + }); + describe('with a tree where the root process has no parent info at all', () => { + const originID = 'c'; + const firstChildID = 'd'; + const secondChildID = 'e'; + beforeEach(() => { + const tree = mockTreeWithNoAncestorsAnd2Children({ originID, firstChildID, secondChildID }); + for (const event of tree.lifecycle) { + // delete the process.parent key, if present + // cast as `EndpointEvent` because `ResolverEvent` can also be `LegacyEndpointEvent` which has no `process` field + delete (event as EndpointEvent).process?.parent; + } + + actions.push({ + type: 'serverReturnedResolverData', + payload: { + result: tree, + // this value doesn't matter + databaseDocumentID: '', + }, + }); + }); + it('should be able to calculate the aria flowto candidates for all processes nodes', () => { + const graphables = selectors.graphableProcesses(state()); + expect(graphables.length).toBe(3); + for (const event of graphables) { + expect(() => { + selectors.ariaFlowtoCandidate(state())(uniquePidForProcess(event)); + }).not.toThrow(); + } + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts index dc17fc70ef8af..109b3abddcc77 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts @@ -19,9 +19,9 @@ import { isGraphableProcess, isTerminatedProcess, uniquePidForProcess, + uniqueParentPidForProcess, } from '../../models/process_event'; import * as indexedProcessTreeModel from '../../models/indexed_process_tree'; -import { isEqual } from '../../models/aabb'; import { ResolverEvent, @@ -62,7 +62,7 @@ export function hasError(state: DataState): boolean { * The last ResolverTree we received, if any. It may be stale (it might not be for the same databaseDocumentID that * we're currently interested in. */ -const resolverTree = (state: DataState): ResolverTree | undefined => { +const resolverTreeResponse = (state: DataState): ResolverTree | undefined => { if (state.lastResponse && state.lastResponse.successful) { return state.lastResponse.result; } else { @@ -73,7 +73,9 @@ const resolverTree = (state: DataState): ResolverTree | undefined => { /** * Process events that will be displayed as terminated. */ -export const terminatedProcesses = createSelector(resolverTree, function (tree?: ResolverTree) { +export const terminatedProcesses = createSelector(resolverTreeResponse, function ( + tree?: ResolverTree +) { if (!tree) { return new Set(); } @@ -90,7 +92,7 @@ export const terminatedProcesses = createSelector(resolverTree, function (tree?: /** * Process events that will be graphed. */ -export const graphableProcesses = createSelector(resolverTree, function (tree?) { +export const graphableProcesses = createSelector(resolverTreeResponse, function (tree?) { if (tree) { return resolverTreeModel.lifecycleEvents(tree).filter(isGraphableProcess); } else { @@ -101,7 +103,7 @@ export const graphableProcesses = createSelector(resolverTree, function (tree?) /** * The 'indexed process tree' contains the tree data, indexed in helpful ways. Used for O(1) access to stuff during graph layout. */ -export const indexedProcessTree = createSelector(graphableProcesses, function indexedTree( +export const tree = createSelector(graphableProcesses, function indexedTree( /* eslint-disable no-shadow */ graphableProcesses /* eslint-enable no-shadow */ @@ -114,13 +116,16 @@ export const indexedProcessTree = createSelector(graphableProcesses, function in */ export const relatedEventsStats: ( state: DataState -) => Map | null = createSelector(resolverTree, (tree?: ResolverTree) => { - if (tree) { - return resolverTreeModel.relatedEventsStats(tree); - } else { - return null; +) => Map | null = createSelector( + resolverTreeResponse, + (resolverTree?: ResolverTree) => { + if (resolverTree) { + return resolverTreeModel.relatedEventsStats(resolverTree); + } else { + return null; + } } -}); +); /** * returns a map of entity_ids to related event data. @@ -133,7 +138,9 @@ export function relatedEventsByEntityId(data: DataState): Map (entityID: string) => (ecsCategory: string) => ResolverEvent[] = createSelector( relatedEventsByEntityId, function provideGettersByCategory( /* eslint-disable no-shadow */ @@ -173,16 +180,16 @@ export function relatedEventsReady(data: DataState): Map { * `true` if there were more children than we got in the last request. */ export function hasMoreChildren(state: DataState): boolean { - const tree = resolverTree(state); - return tree ? resolverTreeModel.hasMoreChildren(tree) : false; + const resolverTree = resolverTreeResponse(state); + return resolverTree ? resolverTreeModel.hasMoreChildren(resolverTree) : false; } /** * `true` if there were more ancestors than we got in the last request. */ export function hasMoreAncestors(state: DataState): boolean { - const tree = resolverTree(state); - return tree ? resolverTreeModel.hasMoreAncestors(tree) : false; + const resolverTree = resolverTreeResponse(state); + return resolverTree ? resolverTreeModel.hasMoreAncestors(resolverTree) : false; } interface RelatedInfoFunctions { @@ -248,7 +255,7 @@ export const relatedEventInfoByEntityId: ( }); }; - const matchingEventsForCategory = defaultMemoize(unmemoizedMatchingEventsForCategory); + const matchingEventsForCategory = unmemoizedMatchingEventsForCategory; /** * The number of events that occurred before the API limit was reached. @@ -313,16 +320,13 @@ export function databaseDocumentIDToFetch(state: DataState): string | null { } } -export const layout = createSelector( - indexedProcessTree, - function processNodePositionsAndEdgeLineSegments( - /* eslint-disable no-shadow */ - indexedProcessTree - /* eslint-enable no-shadow */ - ) { - return isometricTaxiLayout(indexedProcessTree); - } -); +export const layout = createSelector(tree, function processNodePositionsAndEdgeLineSegments( + /* eslint-disable no-shadow */ + indexedProcessTree + /* eslint-enable no-shadow */ +) { + return isometricTaxiLayout(indexedProcessTree); +}); /** * Given a nodeID (aka entity_id) get the indexed process event. @@ -332,8 +336,9 @@ export const layout = createSelector( export const processEventForID: ( state: DataState ) => (nodeID: string) => ResolverEvent | null = createSelector( - indexedProcessTree, - (tree) => (nodeID: string) => indexedProcessTreeModel.processEvent(tree, nodeID) + tree, + (indexedProcessTree) => (nodeID: string) => + indexedProcessTreeModel.processEvent(indexedProcessTree, nodeID) ); /** @@ -349,30 +354,66 @@ export const ariaLevel: (state: DataState) => (nodeID: string) => number | null ); /** - * Returns the following sibling if there is one, or `null`. + * Returns the following sibling if there is one, or `null` if there isn't. + * For root nodes, other root nodes are treated as siblings. + * This is used to calculate the `aria-flowto` attribute. */ -export const followingSibling: ( +export const ariaFlowtoCandidate: ( state: DataState ) => (nodeID: string) => string | null = createSelector( - indexedProcessTree, + tree, processEventForID, - (tree, eventGetter) => { - return (nodeID: string) => { - const event = eventGetter(nodeID); + (indexedProcessTree, eventGetter) => { + // A map of preceding sibling IDs to following sibling IDs or `null`, if there is no following sibling + const memo: Map = new Map(); - // event not found - if (event === null) { - return null; + return function memoizedGetter(/** the unique ID of a node. **/ nodeID: string): string | null { + // Previous calculations are memoized. Check for a value in the memo. + const existingValue = memo.get(nodeID); + + /** + * `undefined` means the key wasn't in the map. + * Note: the value may be null, meaning that we checked and there is no following sibling. + * If there is a value in the map, return it. + */ + if (existingValue !== undefined) { + return existingValue; } - const nextSibling = indexedProcessTreeModel.nextSibling(tree, event); - // next sibling not found - if (nextSibling === undefined) { - return null; + /** + * Getting the following sibling of a node has an `O(n)` time complexity where `n` is the number of children the parent of the node has. + * For this reason, we calculate the following siblings of the node and all of its siblings at once and cache them. + */ + const nodeEvent: ResolverEvent | null = eventGetter(nodeID); + + if (!nodeEvent) { + // this should never happen. + throw new Error('could not find child event in process tree.'); + } + + // nodes with the same parent ID + const children = indexedProcessTreeModel.children( + indexedProcessTree, + uniqueParentPidForProcess(nodeEvent) + ); + + let previousChild: ResolverEvent | null = null; + // Loop over all nodes that have the same parent ID (even if the parent ID is undefined or points to a node that isn't in the tree.) + for (const child of children) { + if (previousChild !== null) { + // Set the `child` as the following sibling of `previousChild`. + memo.set(uniquePidForProcess(previousChild), uniquePidForProcess(child)); + } + // Set the child as the previous child. + previousChild = child; + } + + if (previousChild) { + // if there is a previous child, it has no following sibling. + memo.set(uniquePidForProcess(previousChild), null); } - // return the node ID - return uniquePidForProcess(nextSibling); + return memoizedGetter(nodeID); }; } ); @@ -385,7 +426,7 @@ const spatiallyIndexedLayout: (state: DataState) => rbush = creat edgeLineSegments, /* eslint-enable no-shadow */ }) { - const tree: rbush = new rbush(); + const spatialIndex: rbush = new rbush(); const processesToIndex: IndexedProcessNode[] = []; const edgeLineSegmentsToIndex: IndexedEdgeLineSegment[] = []; @@ -421,50 +462,49 @@ const spatiallyIndexedLayout: (state: DataState) => rbush = creat }; edgeLineSegmentsToIndex.push(indexedLineSegment); } - tree.load([...processesToIndex, ...edgeLineSegmentsToIndex]); - return tree; + spatialIndex.load([...processesToIndex, ...edgeLineSegmentsToIndex]); + return spatialIndex; } ); +/** + * Returns nodes and edge lines that could be visible in the `query`. + */ export const nodesAndEdgelines: ( state: DataState -) => (query: AABB) => VisibleEntites = createSelector(spatiallyIndexedLayout, function (tree) { - // memoize the results of this call to avoid unnecessarily rerunning - let lastBoundingBox: AABB | null = null; - let currentlyVisible: VisibleEntites = { - processNodePositions: new Map(), - connectingEdgeLineSegments: [], - }; - return (boundingBox: AABB) => { - if (lastBoundingBox !== null && isEqual(lastBoundingBox, boundingBox)) { - return currentlyVisible; - } else { - const { - minimum: [minX, minY], - maximum: [maxX, maxY], - } = boundingBox; - const entities = tree.search({ - minX, - minY, - maxX, - maxY, - }); - const visibleProcessNodePositions = new Map( - entities - .filter((entity): entity is IndexedProcessNode => entity.type === 'processNode') - .map((node) => [node.entity, node.position]) - ); - const connectingEdgeLineSegments = entities - .filter((entity): entity is IndexedEdgeLineSegment => entity.type === 'edgeLine') - .map((node) => node.entity); - currentlyVisible = { - processNodePositions: visibleProcessNodePositions, - connectingEdgeLineSegments, - }; - lastBoundingBox = boundingBox; - return currentlyVisible; - } - }; +) => ( + /** + * An axis aligned bounding box (in world corrdinates) to search in. Any entities that might collide with this box will be returned. + */ + query: AABB +) => VisibleEntites = createSelector(spatiallyIndexedLayout, function (spatialIndex) { + /** + * Memoized for performance and object reference equality. + */ + return defaultMemoize((boundingBox: AABB) => { + const { + minimum: [minX, minY], + maximum: [maxX, maxY], + } = boundingBox; + const entities = spatialIndex.search({ + minX, + minY, + maxX, + maxY, + }); + const visibleProcessNodePositions = new Map( + entities + .filter((entity): entity is IndexedProcessNode => entity.type === 'processNode') + .map((node) => [node.entity, node.position]) + ); + const connectingEdgeLineSegments = entities + .filter((entity): entity is IndexedEdgeLineSegment => entity.type === 'edgeLine') + .map((node) => node.entity); + return { + processNodePositions: visibleProcessNodePositions, + connectingEdgeLineSegments, + }; + }); }); /** diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts new file mode 100644 index 0000000000000..b58ea73e1fdc7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EndpointEvent } from '../../../../common/endpoint/types'; + +/** + * Simple mock endpoint event that works for tree layouts. + */ +export function mockEndpointEvent({ + entityID, + name, + parentEntityId, + timestamp, +}: { + entityID: string; + name: string; + parentEntityId: string | undefined; + timestamp: number; +}): EndpointEvent { + return { + '@timestamp': timestamp, + event: { + type: 'start', + category: 'process', + }, + process: { + entity_id: entityID, + name, + parent: { + entity_id: parentEntityId, + }, + }, + } as EndpointEvent; +} diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts new file mode 100644 index 0000000000000..862cf47f73947 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mockEndpointEvent } from './endpoint_event'; +import { ResolverTree, ResolverEvent } from '../../../../common/endpoint/types'; + +export function mockTreeWith2AncestorsAndNoChildren({ + originID, + firstAncestorID, + secondAncestorID, +}: { + secondAncestorID: string; + firstAncestorID: string; + originID: string; +}): ResolverTree { + const secondAncestor: ResolverEvent = mockEndpointEvent({ + entityID: secondAncestorID, + name: 'a', + parentEntityId: 'none', + timestamp: 0, + }); + const firstAncestor: ResolverEvent = mockEndpointEvent({ + entityID: firstAncestorID, + name: 'b', + parentEntityId: secondAncestorID, + timestamp: 1, + }); + const originEvent: ResolverEvent = mockEndpointEvent({ + entityID: originID, + name: 'c', + parentEntityId: firstAncestorID, + timestamp: 2, + }); + return ({ + entityID: originID, + children: { + childNodes: [], + }, + ancestry: { + ancestors: [{ lifecycle: [secondAncestor] }, { lifecycle: [firstAncestor] }], + }, + lifecycle: [originEvent], + } as unknown) as ResolverTree; +} + +export function mockTreeWithNoAncestorsAnd2Children({ + originID, + firstChildID, + secondChildID, +}: { + originID: string; + firstChildID: string; + secondChildID: string; +}): ResolverTree { + const origin: ResolverEvent = mockEndpointEvent({ + entityID: originID, + name: 'c', + parentEntityId: 'none', + timestamp: 0, + }); + const firstChild: ResolverEvent = mockEndpointEvent({ + entityID: firstChildID, + name: 'd', + parentEntityId: originID, + timestamp: 1, + }); + const secondChild: ResolverEvent = mockEndpointEvent({ + entityID: secondChildID, + name: 'e', + parentEntityId: originID, + timestamp: 2, + }); + + return ({ + entityID: originID, + children: { + childNodes: [{ lifecycle: [firstChild] }, { lifecycle: [secondChild] }], + }, + ancestry: { + ancestors: [], + }, + lifecycle: [origin], + } as unknown) as ResolverTree; +} diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts index ba4a5a169c549..df365a078b27f 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts @@ -9,7 +9,10 @@ import { createStore } from 'redux'; import { ResolverAction } from './actions'; import { resolverReducer } from './reducer'; import * as selectors from './selectors'; -import { EndpointEvent, ResolverEvent, ResolverTree } from '../../../common/endpoint/types'; +import { + mockTreeWith2AncestorsAndNoChildren, + mockTreeWithNoAncestorsAnd2Children, +} from './mocks/resolver_tree'; describe('resolver selectors', () => { const actions: ResolverAction[] = []; @@ -33,7 +36,7 @@ describe('resolver selectors', () => { actions.push({ type: 'serverReturnedResolverData', payload: { - result: treeWith2AncestorsAndNoChildren({ + result: mockTreeWith2AncestorsAndNoChildren({ originID, firstAncestorID, secondAncestorID, @@ -71,7 +74,7 @@ describe('resolver selectors', () => { actions.push({ type: 'serverReturnedResolverData', payload: { - result: treeWithNoAncestorsAnd2Children({ originID, firstChildID, secondChildID }), + result: mockTreeWithNoAncestorsAnd2Children({ originID, firstChildID, secondChildID }), // this value doesn't matter databaseDocumentID: '', }, @@ -149,111 +152,3 @@ describe('resolver selectors', () => { }); }); }); -/** - * Simple mock endpoint event that works for tree layouts. - */ -function mockEndpointEvent({ - entityID, - name, - parentEntityId, - timestamp, -}: { - entityID: string; - name: string; - parentEntityId: string; - timestamp: number; -}): EndpointEvent { - return { - '@timestamp': timestamp, - event: { - type: 'start', - category: 'process', - }, - process: { - entity_id: entityID, - name, - parent: { - entity_id: parentEntityId, - }, - }, - } as EndpointEvent; -} - -function treeWith2AncestorsAndNoChildren({ - originID, - firstAncestorID, - secondAncestorID, -}: { - secondAncestorID: string; - firstAncestorID: string; - originID: string; -}): ResolverTree { - const secondAncestor: ResolverEvent = mockEndpointEvent({ - entityID: secondAncestorID, - name: 'a', - parentEntityId: 'none', - timestamp: 0, - }); - const firstAncestor: ResolverEvent = mockEndpointEvent({ - entityID: firstAncestorID, - name: 'b', - parentEntityId: secondAncestorID, - timestamp: 1, - }); - const originEvent: ResolverEvent = mockEndpointEvent({ - entityID: originID, - name: 'c', - parentEntityId: firstAncestorID, - timestamp: 2, - }); - return ({ - entityID: originID, - children: { - childNodes: [], - }, - ancestry: { - ancestors: [{ lifecycle: [secondAncestor] }, { lifecycle: [firstAncestor] }], - }, - lifecycle: [originEvent], - } as unknown) as ResolverTree; -} - -function treeWithNoAncestorsAnd2Children({ - originID, - firstChildID, - secondChildID, -}: { - originID: string; - firstChildID: string; - secondChildID: string; -}): ResolverTree { - const origin: ResolverEvent = mockEndpointEvent({ - entityID: originID, - name: 'c', - parentEntityId: 'none', - timestamp: 0, - }); - const firstChild: ResolverEvent = mockEndpointEvent({ - entityID: firstChildID, - name: 'd', - parentEntityId: originID, - timestamp: 1, - }); - const secondChild: ResolverEvent = mockEndpointEvent({ - entityID: secondChildID, - name: 'e', - parentEntityId: originID, - timestamp: 2, - }); - - return ({ - entityID: originID, - children: { - childNodes: [{ lifecycle: [firstChild] }, { lifecycle: [secondChild] }], - }, - ancestry: { - ancestors: [], - }, - lifecycle: [origin], - } as unknown) as ResolverTree; -} diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts index ff2179dc3a2ae..040e2920ce554 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts @@ -212,17 +212,6 @@ export const graphableProcesses = composeSelectors( dataSelectors.graphableProcesses ); -/** - * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a - * concern-specific selector. `selector` should return the concern-specific state. - */ -function composeSelectors( - selector: (state: OuterState) => InnerState, - secondSelector: (state: InnerState) => ReturnValue -): (state: OuterState) => ReturnValue { - return (state) => secondSelector(selector(state)); -} - const boundingBox = composeSelectors(cameraStateSelector, cameraSelectors.viewableBoundingBox); const nodesAndEdgelines = composeSelectors(dataStateSelector, dataSelectors.nodesAndEdgelines); @@ -246,6 +235,7 @@ export const visibleNodesAndEdgeLines = createSelector(nodesAndEdgelines, boundi boundingBox /* eslint-enable no-shadow */ ) { + // `boundingBox` and `nodesAndEdgelines` are each memoized. return (time: number) => nodesAndEdgelines(boundingBox(time)); }); @@ -261,14 +251,14 @@ export const ariaLevel: ( /** * Takes a nodeID (aka entity_id) and returns the node ID of the node that aria should 'flowto' or null - * If the node has a following sibling that is currently visible, that will be returned, otherwise null. + * If the node has a flowto candidate that is currently visible, that will be returned, otherwise null. */ export const ariaFlowtoNodeID: ( state: ResolverState ) => (time: number) => (nodeID: string) => string | null = createSelector( visibleNodesAndEdgeLines, - composeSelectors(dataStateSelector, dataSelectors.followingSibling), - (visibleNodesAndEdgeLinesAtTime, followingSibling) => { + composeSelectors(dataStateSelector, dataSelectors.ariaFlowtoCandidate), + (visibleNodesAndEdgeLinesAtTime, ariaFlowtoCandidate) => { return defaultMemoize((time: number) => { // get the visible nodes at `time` const { processNodePositions } = visibleNodesAndEdgeLinesAtTime(time); @@ -280,10 +270,23 @@ export const ariaFlowtoNodeID: ( // return the ID of `nodeID`'s following sibling, if it is visible return (nodeID: string): string | null => { - const sibling: string | null = followingSibling(nodeID); + const flowtoNode: string | null = ariaFlowtoCandidate(nodeID); - return sibling === null || nodesVisibleAtTime.has(sibling) === false ? null : sibling; + return flowtoNode === null || nodesVisibleAtTime.has(flowtoNode) === false + ? null + : flowtoNode; }; }); } ); + +/** + * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a + * concern-specific selector. `selector` should return the concern-specific state. + */ +function composeSelectors( + selector: (state: OuterState) => InnerState, + secondSelector: (state: InnerState) => ReturnValue +): (state: OuterState) => ReturnValue { + return (state) => secondSelector(selector(state)); +} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx index 4544381d94955..10e57a09b5da4 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx @@ -169,7 +169,9 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ process, ...relevantData } = relatedEventToShowDetailsFor as ResolverEvent & { + // Type this with various unknown keys so that ts will let us delete those keys ecs: unknown; + process: unknown; }; let displayDate = ''; const sectionData: Array<{ @@ -371,4 +373,3 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ ); }); -RelatedEventDetail.displayName = 'RelatedEventDetail'; From 7ac5fc4e1f0b184cd60fc6d12f0dc058a83fe2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Mon, 20 Jul 2020 16:14:13 +0200 Subject: [PATCH 13/26] =?UTF-8?q?[Security=20Solution][Timeline]=20Fix=20t?= =?UTF-8?q?imeline=20styling=20and=20createFrom=20beh=E2=80=A6=20(#72152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security_solution/common/constants.ts | 5 - .../components/alerts_table/helpers.ts | 2 +- .../security_solution/public/graphql/types.ts | 8 + .../components/open_timeline/helpers.test.ts | 197 ++++++++++++++++++ .../components/open_timeline/helpers.ts | 58 ++++-- .../open_timeline/open_timeline.tsx | 3 +- .../open_timeline_modal/index.tsx | 2 +- .../open_timeline_modal_body.tsx | 66 +++--- .../open_timeline/use_timeline_status.tsx | 4 +- .../data_providers/provider_badge.tsx | 2 +- .../timelines/components/timeline/styles.tsx | 3 +- .../containers/one/index.gql_query.ts | 1 + .../timelines/containers/persist.gql_query.ts | 3 + .../public/timelines/store/timeline/epic.ts | 3 + .../server/lib/timeline/saved_object.ts | 8 +- .../saved_objects/timeline.ts | 1 + 16 files changed, 297 insertions(+), 69 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index b39a038c4cc3c..f934d90c740a5 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -167,8 +167,3 @@ export const showAllOthersBucket: string[] = [ 'destination.ip', 'user.name', ]; - -/* - * This should be set to true after https://github.com/elastic/kibana/pull/67496 is merged - */ -export const enableElasticFilter = false; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts index 5025d782e2aa2..084e4bff7e0ac 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts @@ -148,7 +148,7 @@ export const reformatDataProviderWithNewValue = { // Support for legacy "template-like" timeline behavior that is using hardcoded list of templateFields - if (timelineType === TimelineType.default) { + if (timelineType !== TimelineType.template) { if (templateFields.includes(dataProvider.queryMatch.field)) { const newValue = getStringArray(dataProvider.queryMatch.field, ecsData); if (newValue.length) { diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index 5f8595df23f9b..1808d32547fbe 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -5654,6 +5654,8 @@ export namespace GetOneTimeline { kqlQuery: Maybe; + type: Maybe; + queryMatch: Maybe<_QueryMatch>; }; @@ -5870,6 +5872,8 @@ export namespace PersistTimelineMutation { eventType: Maybe; + excludedRowRendererIds: Maybe; + favorite: Maybe; filters: Maybe; @@ -5932,6 +5936,8 @@ export namespace PersistTimelineMutation { kqlQuery: Maybe; + type: Maybe; + queryMatch: Maybe; and: Maybe; @@ -5964,6 +5970,8 @@ export namespace PersistTimelineMutation { kqlQuery: Maybe; + type: Maybe; + queryMatch: Maybe<_QueryMatch>; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 5759d96b95f9e..f4bd17005fed7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -306,6 +306,203 @@ describe('helpers', () => { width: 1100, }); }); + + test('if duplicates and timeline.timelineType is not matching with outcome timelineType it should return draft with empty title', () => { + const timeline = { + savedObjectId: 'savedObject-1', + title: 'Awesome Timeline', + version: '1', + status: TimelineStatus.active, + timelineType: TimelineType.default, + }; + + const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineType.template); + expect(newTimeline).toEqual({ + columns: [ + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + width: 190, + }, + { + columnHeaderType: 'not-filtered', + id: 'message', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'event.category', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'event.action', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'source.ip', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'destination.ip', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + width: 180, + }, + ], + dataProviders: [], + dateRange: { start: '2020-07-07T08:20:18.966Z', end: '2020-07-08T08:20:18.966Z' }, + description: '', + deletedEventIds: [], + eventIdToNoteIds: {}, + eventType: 'all', + excludedRowRendererIds: [], + filters: [], + highlightedDropAndProviderId: '', + historyIds: [], + id: 'savedObject-1', + isFavorite: false, + isLive: false, + isSelectAllChecked: false, + isLoading: false, + isSaving: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + kqlMode: 'filter', + kqlQuery: { + filterQuery: null, + filterQueryDraft: null, + }, + loadingEventIds: [], + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: 'savedObject-1', + selectedEventIds: {}, + show: false, + showCheckboxes: false, + sort: { + columnId: '@timestamp', + sortDirection: 'desc', + }, + status: TimelineStatus.draft, + title: '', + timelineType: TimelineType.template, + templateTimelineId: null, + templateTimelineVersion: null, + version: '1', + width: 1100, + }); + }); + + test('if duplicates and timeline.timelineType is not matching with outcome timelineType it should return draft with empty title template', () => { + const timeline = { + savedObjectId: 'savedObject-1', + title: 'Awesome Template', + version: '1', + status: TimelineStatus.active, + timelineType: TimelineType.template, + }; + + const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineType.default); + expect(newTimeline).toEqual({ + columns: [ + { + columnHeaderType: 'not-filtered', + id: '@timestamp', + width: 190, + }, + { + columnHeaderType: 'not-filtered', + id: 'message', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'event.category', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'event.action', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'host.name', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'source.ip', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'destination.ip', + width: 180, + }, + { + columnHeaderType: 'not-filtered', + id: 'user.name', + width: 180, + }, + ], + dataProviders: [], + dateRange: { start: '2020-07-07T08:20:18.966Z', end: '2020-07-08T08:20:18.966Z' }, + description: '', + deletedEventIds: [], + eventIdToNoteIds: {}, + eventType: 'all', + excludedRowRendererIds: [], + filters: [], + highlightedDropAndProviderId: '', + historyIds: [], + id: 'savedObject-1', + isFavorite: false, + isLive: false, + isSelectAllChecked: false, + isLoading: false, + isSaving: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + kqlMode: 'filter', + kqlQuery: { + filterQuery: null, + filterQueryDraft: null, + }, + loadingEventIds: [], + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: 'savedObject-1', + selectedEventIds: {}, + show: false, + showCheckboxes: false, + sort: { + columnId: '@timestamp', + sortDirection: 'desc', + }, + status: TimelineStatus.draft, + title: '', + timelineType: TimelineType.default, + templateTimelineId: null, + templateTimelineVersion: null, + version: '1', + width: 1100, + }); + }); + test('if columns are null, we should get the default columns', () => { const timeline = { savedObjectId: 'savedObject-1', diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 9899b38f445f9..af289f94c9a0d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -173,10 +173,6 @@ const getTemplateTimelineId = ( duplicate: boolean, targetTimelineType?: TimelineType ) => { - if (!duplicate) { - return timeline.templateTimelineId; - } - if ( targetTimelineType === TimelineType.default && timeline.timelineType === TimelineType.template @@ -184,18 +180,26 @@ const getTemplateTimelineId = ( return timeline.templateTimelineId; } - // TODO: MOVE TO BACKEND - return uuid.v4(); + return duplicate && timeline.timelineType === TimelineType.template + ? // TODO: MOVE TO THE BACKEND + uuid.v4() + : timeline.templateTimelineId; }; -const convertToDefaultField = ({ and, ...dataProvider }: DataProviderResult) => - deepMerge(dataProvider, { - type: DataProviderType.default, - queryMatch: { - value: - dataProvider.queryMatch!.operator === IS_OPERATOR ? '' : dataProvider.queryMatch!.value, - }, - }); +const convertToDefaultField = ({ and, ...dataProvider }: DataProviderResult) => { + if (dataProvider.type === DataProviderType.template) { + return deepMerge(dataProvider, { + type: DataProviderType.default, + enabled: dataProvider.queryMatch!.operator !== IS_OPERATOR, + queryMatch: { + value: + dataProvider.queryMatch!.operator === IS_OPERATOR ? '' : dataProvider.queryMatch!.value, + }, + }); + } + + return dataProvider; +}; const getDataProviders = ( duplicate: boolean, @@ -212,6 +216,28 @@ const getDataProviders = ( return dataProviders; }; +export const getTimelineTitle = ( + timeline: TimelineResult, + duplicate: boolean, + timelineType?: TimelineType +) => { + const isCreateTimelineFromAction = timelineType && timeline.timelineType !== timelineType; + if (isCreateTimelineFromAction) return ''; + + return duplicate ? `${timeline.title} - Duplicate` : timeline.title || ''; +}; + +export const getTimelineStatus = ( + timeline: TimelineResult, + duplicate: boolean, + timelineType?: TimelineType +) => { + const isCreateTimelineFromAction = timelineType && timeline.timelineType !== timelineType; + if (isCreateTimelineFromAction) return TimelineStatus.draft; + + return duplicate ? TimelineStatus.active : timeline.status; +}; + // eslint-disable-next-line complexity export const defaultTimelineToTimelineModel = ( timeline: TimelineResult, @@ -234,11 +260,11 @@ export const defaultTimelineToTimelineModel = ( pinnedEventIds: setPinnedEventIds(duplicate, timeline.pinnedEventIds), pinnedEventsSaveObject: setPinnedEventsSaveObject(duplicate, timeline.pinnedEventsSaveObject), id: duplicate ? '' : timeline.savedObjectId, - status: duplicate ? TimelineStatus.active : timeline.status, + status: getTimelineStatus(timeline, duplicate, timelineType), savedObjectId: duplicate ? null : timeline.savedObjectId, version: duplicate ? null : timeline.version, timelineType: timelineType ?? timeline.timelineType, - title: duplicate ? `${timeline.title} - Duplicate` : timeline.title || '', + title: getTimelineTitle(timeline, duplicate, timelineType), templateTimelineId: getTemplateTimelineId(timeline, duplicate, timelineType), templateTimelineVersion: duplicate && isTemplate ? 1 : timeline.templateTimelineVersion, }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx index 13786c55e2a8d..d839a1deddf21 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiPanel, EuiBasicTable, EuiSpacer } from '@elastic/eui'; +import { EuiPanel, EuiBasicTable } from '@elastic/eui'; import React, { useCallback, useMemo, useRef } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -183,7 +183,6 @@ export const OpenTimeline = React.memo( /> - {!!timelineFilter && timelineFilter} ( ({ hideActions = [], modalTitle, onClose, onOpen }) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.tsx index b3ae39bf8b346..af2bd53df77db 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.tsx @@ -79,43 +79,43 @@ export const OpenTimelineModalBody = memo( selectedTimelinesCount={selectedItems.length} title={title} /> - <> - - {SearchRowContent} - - - + <> + + {SearchRowContent} + + + ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_status.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_status.tsx index a95f163349f05..8c4c686698c88 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_status.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_status.tsx @@ -33,9 +33,7 @@ export const useTimelineStatus = ({ templateTimelineFilter: JSX.Element[] | null; installPrepackagedTimelines: () => void; } => { - const [selectedTab, setSelectedTab] = useState( - TemplateTimelineType.elastic - ); + const [selectedTab, setSelectedTab] = useState(null); const isTemplateFilterEnabled = useMemo(() => timelineType === TimelineType.template, [ timelineType, ]); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx index af63957d35075..bf2094e7659ee 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx @@ -92,7 +92,7 @@ const ConvertFieldBadge = styled(ProviderFieldBadge)` `; const TemplateFieldBadge: React.FC = ({ type, toggleType }) => { - if (type === DataProviderType.default) { + if (type !== DataProviderType.template) { return ( {i18n.CONVERT_TO_TEMPLATE_FIELD} ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx index eb103d8e7e861..5c992fd640a97 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx @@ -258,8 +258,7 @@ export const EventsTdContent = styled.div.attrs(({ className }) => ({ ? `${width}px` : '100%'}; /* Using width: 100% instead of flex: 1 and max-width: 100% for IE11 */ - > button.euiButtonIcon, - > .euiToolTipAnchor > button.euiButtonIcon { + button.euiButtonIcon { margin-left: ${({ theme }) => `-${theme.eui.paddingSizes.xs}`}; } `; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts b/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts index 0aaeb22d72afc..5e50a7fb3313e 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts @@ -42,6 +42,7 @@ export const oneTimelineQuery = gql` enabled excluded kqlQuery + type queryMatch { field displayField diff --git a/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts b/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts index 6a0609f9158f3..c38aa67ccebb2 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts @@ -32,6 +32,7 @@ export const persistTimelineMutation = gql` enabled excluded kqlQuery + type queryMatch { field displayField @@ -45,6 +46,7 @@ export const persistTimelineMutation = gql` enabled excluded kqlQuery + type queryMatch { field displayField @@ -56,6 +58,7 @@ export const persistTimelineMutation = gql` } description eventType + excludedRowRendererIds favorite { fullName userName diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts index 2f9331ec9db8e..7757794c6dc9a 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts @@ -68,6 +68,7 @@ import { updateTimeline, updateTitle, updateAutoSaveMsg, + setExcludedRowRendererIds, setFilters, setSavedQueryId, startTimelineSaving, @@ -88,9 +89,11 @@ import { ActionTimeline, TimelineEpicDependencies } from './types'; const timelineActionsType = [ applyKqlFilterQuery.type, addProvider.type, + addTimeline.type, dataProviderEdited.type, removeColumn.type, removeProvider.type, + setExcludedRowRendererIds.type, setFilters.type, setSavedQueryId.type, updateColumns.type, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object.ts index 82a2a866a71ff..b50195219f993 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object.ts @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import { SavedObjectsFindOptions } from '../../../../../../src/core/server'; -import { UNAUTHENTICATED_USER, enableElasticFilter } from '../../../common/constants'; +import { UNAUTHENTICATED_USER } from '../../../common/constants'; import { NoteSavedObject } from '../../../common/types/timeline/note'; import { PinnedEventSavedObject } from '../../../common/types/timeline/pinned_event'; import { @@ -153,12 +153,10 @@ const getTimelineTypeFilter = ( templateTimelineType == null ? null : templateTimelineType === TemplateTimelineType.elastic - ? `siem-ui-timeline.attributes.createdBy: "Elsatic"` + ? `siem-ui-timeline.attributes.createdBy: "Elastic"` : `not siem-ui-timeline.attributes.createdBy: "Elastic"`; - const filters = enableElasticFilter - ? [typeFilter, draftFilter, immutableFilter, templateTimelineTypeFilter] - : [typeFilter, draftFilter, immutableFilter]; + const filters = [typeFilter, draftFilter, immutableFilter, templateTimelineTypeFilter]; return filters.filter((f) => f != null).join(' and '); }; diff --git a/x-pack/test/api_integration/apis/security_solution/saved_objects/timeline.ts b/x-pack/test/api_integration/apis/security_solution/saved_objects/timeline.ts index 10ba9621c0430..a399c07e31065 100644 --- a/x-pack/test/api_integration/apis/security_solution/saved_objects/timeline.ts +++ b/x-pack/test/api_integration/apis/security_solution/saved_objects/timeline.ts @@ -114,6 +114,7 @@ export default function ({ getService }: FtrProviderContext) { enabled: true, excluded: false, kqlQuery: '', + type: 'default', queryMatch: { field: 'host.name', displayField: null, From 6c3b900d1183e62a391891c7fc6795f837e74ccb Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Mon, 20 Jul 2020 17:29:28 +0300 Subject: [PATCH 14/26] [Plugin Generator] Generate tsconfig and useDefaultBehaviors (#72040) * improve test stability * add a tsconfig file and useDefaultBehaviors Co-authored-by: Elastic Machine --- .../kbn-plugin-generator/sao_template/sao.js | 6 ++++++ .../sao_template/sao.test.js | 3 +++ .../template/public/components/app.tsx | 2 +- .../sao_template/template/tsconfig.json | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 packages/kbn-plugin-generator/sao_template/template/tsconfig.json diff --git a/packages/kbn-plugin-generator/sao_template/sao.js b/packages/kbn-plugin-generator/sao_template/sao.js index dc4d8a2fc10fb..e5f81a984ee93 100755 --- a/packages/kbn-plugin-generator/sao_template/sao.js +++ b/packages/kbn-plugin-generator/sao_template/sao.js @@ -120,6 +120,11 @@ module.exports = function ({ name, targetPath }) { return !customPath; }, }, + generateTsconfig: { + type: 'confirm', + message: 'Would you like to use a custom tsconfig file?', + default: true, + }, }, filters: { 'public/**/index.scss': 'generateScss', @@ -128,6 +133,7 @@ module.exports = function ({ name, targetPath }) { 'translations/**/*': 'generateTranslations', 'i18nrc.json': 'generateTranslations', 'eslintrc.js': 'generateEslint', + 'tsconfig.json': 'generateTsconfig', }, move: { 'eslintrc.js': '.eslintrc.js', diff --git a/packages/kbn-plugin-generator/sao_template/sao.test.js b/packages/kbn-plugin-generator/sao_template/sao.test.js index 03d95e12d58da..af243326cff33 100755 --- a/packages/kbn-plugin-generator/sao_template/sao.test.js +++ b/packages/kbn-plugin-generator/sao_template/sao.test.js @@ -80,12 +80,14 @@ describe('plugin generator sao integration', () => { generateApi: true, generateScss: false, generateEslint: false, + generateTsconfig: false, }); // check output files expect(res.fileList).toContain('public/plugin.ts'); expect(res.fileList).not.toContain('public/index.scss'); expect(res.fileList).not.toContain('.eslintrc.js'); + expect(res.fileList).not.toContain('tsconfig.json'); }); it('plugin package has correct title', async () => { @@ -136,6 +138,7 @@ describe('plugin generator sao integration', () => { it('includes dotfiles', async () => { const res = await sao.mockPrompt(template); + expect(res.files['tsconfig.json']).toBeTruthy(); expect(res.files['.eslintrc.js']).toBeTruthy(); expect(res.files['.i18nrc.json']).toBeTruthy(); }); diff --git a/packages/kbn-plugin-generator/sao_template/template/public/components/app.tsx b/packages/kbn-plugin-generator/sao_template/template/public/components/app.tsx index 7b259a9c5b99d..d75bd2f01ef23 100644 --- a/packages/kbn-plugin-generator/sao_template/template/public/components/app.tsx +++ b/packages/kbn-plugin-generator/sao_template/template/public/components/app.tsx @@ -73,7 +73,7 @@ export const <%= upperCamelCaseName %>App = ({ basename, notifications, http, na <> - + diff --git a/packages/kbn-plugin-generator/sao_template/template/tsconfig.json b/packages/kbn-plugin-generator/sao_template/template/tsconfig.json new file mode 100644 index 0000000000000..8a3ced743d0fa --- /dev/null +++ b/packages/kbn-plugin-generator/sao_template/template/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*", + ], + "exclude": [] +} From 11182c8ef79cd821ac2b2284b4a3d6295e6ab75a Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Mon, 20 Jul 2020 15:51:22 +0100 Subject: [PATCH 15/26] Fix match phrase and not match phrase comparators (#71850) Co-authored-by: Elastic Machine --- .../alerting/log_threshold/register_log_threshold_alert_type.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts index fbbb38da53929..ab55601f4c475 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts @@ -56,6 +56,8 @@ const criteriaSchema = schema.object({ schema.literal(Comparator.NOT_EQ), schema.literal(Comparator.MATCH), schema.literal(Comparator.NOT_MATCH), + schema.literal(Comparator.MATCH_PHRASE), + schema.literal(Comparator.NOT_MATCH_PHRASE), ]), value: schema.oneOf([schema.number(), schema.string()]), }); From e5c7e9a4741c4472294c650dcb555a4705079abe Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 20 Jul 2020 17:02:54 +0200 Subject: [PATCH 16/26] [Ingest Pipelines] Processor Editor Move Tooltip (#72239) * first implementation of tooltip * Add processor tooltip component files * remove init position from code for now * colocate on change handler and make code a bit cleaner * removed document.body.appendChild logic because EuiPortal does that for us * use correct toggle button api * added test to check button disabled while editing * remove cursor not allowed * simplify logic * assert if against positive * remove unused variable * Remove unused actions const Co-authored-by: Elastic Machine --- .../pipeline_processors_editor.test.tsx | 8 +++ .../components/_shared.scss | 1 + .../components/index.ts | 2 + .../pipeline_processors_editor_item.tsx | 20 ++++-- .../index.ts | 7 +++ ...ipeline_processors_editor_item_toolip.scss | 7 +++ ...ipeline_processors_editor_item_tooltip.tsx | 61 +++++++++++++++++++ .../processor_information.tsx | 32 ++++++++++ .../components/drop_zone_button.tsx | 1 - .../processors_tree/processors_tree.scss | 4 -- .../pipeline_processors_editor/constants.ts | 5 -- .../pipeline_processors_editor/context.tsx | 22 +++++-- .../pipeline_processors_editor/types.ts | 2 +- 13 files changed, 151 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_tooltip/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_tooltip/pipeline_processors_editor_item_toolip.scss create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_tooltip/pipeline_processors_editor_item_tooltip.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item_tooltip/processor_information.tsx diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx index acfa012990b21..df4832f9a45e0 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx @@ -178,5 +178,13 @@ describe('Pipeline Editor', () => { expect(data2.processors).toEqual(testProcessors.processors); expect(data2.on_failure).toEqual([{ test: { if: '1 == 5' } }]); }); + + it('prevents moving a processor while in edit mode', () => { + const { find, exists } = testBed; + find('processors>0.editItemButton').simulate('click'); + expect(exists('processorSettingsForm')).toBe(true); + expect(find('processors>0.moveItemButton').props().disabled).toBe(true); + expect(find('processors>1.moveItemButton').props().disabled).toBe(true); + }); }); }); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/_shared.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/_shared.scss index c7c49c00bb5cf..fe9a54671a00e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/_shared.scss +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/_shared.scss @@ -1,2 +1,3 @@ $dropZoneZIndex: $euiZLevel1; /* Prevent the next item down from obscuring the button */ $cancelButtonZIndex: $euiZLevel2; +$processorItemMouseTooltipZIndex: $euiZLevel3; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts index de0621b187230..b532b2d953e65 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts @@ -19,3 +19,5 @@ export { PipelineProcessorsEditorItem } from './pipeline_processors_editor_item' export { ProcessorRemoveModal } from './processor_remove_modal'; export { OnDoneLoadJsonHandler, LoadFromJsonButton } from './load_from_json'; + +export { PipelineProcessorsItemTooltip, Position } from './pipeline_processors_editor_item_tooltip'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx index 97b57a971ff7d..3fbef4c1b7898 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/pipeline_processors_editor_item/pipeline_processors_editor_item.tsx @@ -51,7 +51,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent = memo( editor, processorsDispatch, }) { - const isDisabled = editor.mode.id !== 'idle'; + const isEditorNotInIdleMode = editor.mode.id !== 'idle'; const isInMoveMode = Boolean(movingProcessor); const isMovingThisProcessor = processor.id === movingProcessor?.id; const isEditingThisProcessor = @@ -83,6 +83,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent = memo( 'pipelineProcessorsEditor__item__moveButton--cancel': isMovingThisProcessor, }); const icon = isMovingThisProcessor ? 'cross' : 'sortable'; + const disabled = isEditorNotInIdleMode && !isMovingThisProcessor; const moveButton = ( = memo( iconType={icon} data-test-subj={dataTestSubj} size="s" - disabled={isDisabled && !isMovingThisProcessor} + isDisabled={disabled} label={label} aria-label={label} - onChange={() => (!isMovingThisProcessor ? onMove() : onCancelMove())} + onChange={() => { + if (isMovingThisProcessor) { + onCancelMove(); + } else { + onMove(); + } + }} /> ); // Remove the tooltip from the DOM to prevent it from lingering if the mouse leave event @@ -132,7 +139,7 @@ export const PipelineProcessorsEditorItem: FunctionComponent = memo( { let nextOptions: Record; if (!nextDescription) { @@ -164,7 +171,8 @@ export const PipelineProcessorsEditorItem: FunctionComponent = memo( {!isInMoveMode && ( = memo( @@ -461,7 +461,7 @@ exports[`isNewKibanaInstance 1`] = ` - Centralize security events for interactive investigation in ready-to-go visualizations. + Protect hosts, analyze security information and events, hunt threats, automate detections, and create cases. } footer={ @@ -478,7 +478,7 @@ exports[`isNewKibanaInstance 1`] = ` } textAlign="left" - title="Security" + title="SIEM + Endpoint Security" titleSize="xs" /> @@ -758,7 +758,7 @@ exports[`mlEnabled 1`] = ` - Centralize security events for interactive investigation in ready-to-go visualizations. + Protect hosts, analyze security information and events, hunt threats, automate detections, and create cases. } footer={ @@ -775,7 +775,7 @@ exports[`mlEnabled 1`] = ` } textAlign="left" - title="Security" + title="SIEM + Endpoint Security" titleSize="xs" /> @@ -1060,7 +1060,7 @@ exports[`render 1`] = ` - Centralize security events for interactive investigation in ready-to-go visualizations. + Protect hosts, analyze security information and events, hunt threats, automate detections, and create cases. } footer={ @@ -1077,7 +1077,7 @@ exports[`render 1`] = ` } textAlign="left" - title="Security" + title="SIEM + Endpoint Security" titleSize="xs" /> diff --git a/src/plugins/home/public/application/components/add_data.js b/src/plugins/home/public/application/components/add_data.js index fa1327b3fcd08..c35b7b04932fb 100644 --- a/src/plugins/home/public/application/components/add_data.js +++ b/src/plugins/home/public/application/components/add_data.js @@ -81,12 +81,12 @@ const AddDataUi = ({ apmUiEnabled, isNewKibanaInstance, intl, mlEnabled }) => { const siemData = { title: intl.formatMessage({ id: 'home.addData.securitySolution.nameTitle', - defaultMessage: 'Security', + defaultMessage: 'SIEM + Endpoint Security', }), description: intl.formatMessage({ id: 'home.addData.securitySolution.nameDescription', defaultMessage: - 'Centralize security events for interactive investigation in ready-to-go visualizations.', + 'Protect hosts, analyze security information and events, hunt threats, automate detections, and create cases.', }), ariaDescribedby: 'aria-describedby.addSiemButtonLabel', }; diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index 3a8f2f0c16b96..a1e7293ce974b 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -63,26 +63,19 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine - + - {indicesExist ? ( - key !== SecurityPageName.detections, navTabs) - : navTabs - } - /> - ) : ( - key === SecurityPageName.overview, navTabs)} - /> - )} + key !== SecurityPageName.detections, navTabs) + : navTabs + } + /> diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/translations.ts b/x-pack/plugins/security_solution/public/common/components/header_global/translations.ts index f67f665434a96..d3205be9bd2fc 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/header_global/translations.ts @@ -6,9 +6,12 @@ import { i18n } from '@kbn/i18n'; -export const SIEM = i18n.translate('xpack.securitySolution.headerGlobal.siem', { - defaultMessage: 'SIEM', -}); +export const SECURITY_SOLUTION = i18n.translate( + 'xpack.securitySolution.headerGlobal.securitySolution', + { + defaultMessage: 'Security solution', + } +); export const BUTTON_ADD_DATA = i18n.translate('xpack.securitySolution.headerGlobal.buttonAddData', { defaultMessage: 'Add data', diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx index 03ad6ad3396f8..8ba7f7da7b8e3 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx @@ -7,6 +7,7 @@ import { act, renderHook } from '@testing-library/react-hooks'; import { useWithSource, indicesExistOrDataTemporarilyUnavailable } from '.'; +import { NO_ALERT_INDEX } from '../../../../common/constants'; import { mockBrowserFields, mockIndexFields, mocksSource } from './mock'; jest.mock('../../lib/kibana'); @@ -79,6 +80,17 @@ describe('Index Fields & Browser Fields', () => { }); }); + test('Make sure we are not querying for NO_ALERT_INDEX and it is not includes in the index pattern', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useWithSource('default', [NO_ALERT_INDEX]) + ); + + await waitForNextUpdate(); + return expect(result.current.indexPattern.title).toEqual( + 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*' + ); + }); + describe('indicesExistOrDataTemporarilyUnavailable', () => { test('it returns true when undefined', () => { let undefVar; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx index cc43dd6f42772..bbd00900105e8 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx @@ -11,7 +11,7 @@ import { useEffect, useMemo, useState } from 'react'; import memoizeOne from 'memoize-one'; import { IIndexPattern } from 'src/plugins/data/public'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; +import { DEFAULT_INDEX_KEY, NO_ALERT_INDEX } from '../../../../common/constants'; import { useUiSetting$ } from '../../lib/kibana'; import { IndexField, SourceQuery } from '../../../graphql/types'; @@ -126,8 +126,9 @@ export const useWithSource = ( ) => { const [configIndex] = useUiSetting$(DEFAULT_INDEX_KEY); const defaultIndex = useMemo(() => { - if (indexToAdd != null && !isEmpty(indexToAdd)) { - return onlyCheckIndexToAdd ? indexToAdd : [...configIndex, ...indexToAdd]; + const filterIndexAdd = (indexToAdd ?? []).filter((item) => item !== NO_ALERT_INDEX); + if (!isEmpty(filterIndexAdd)) { + return onlyCheckIndexToAdd ? filterIndexAdd : [...configIndex, ...filterIndexAdd]; } return configIndex; }, [configIndex, indexToAdd, onlyCheckIndexToAdd]); @@ -138,7 +139,7 @@ export const useWithSource = ( errorMessage: null, indexPattern: getIndexFields(defaultIndex.join(), []), indicesExist: indicesExistOrDataTemporarilyUnavailable(undefined), - loading: false, + loading: true, }); const apolloClient = useApolloClient(); @@ -155,7 +156,7 @@ export const useWithSource = ( try { const result = await apolloClient.query({ query: sourceQuery, - fetchPolicy: 'cache-first', + fetchPolicy: 'network-only', variables: { sourceId, defaultIndex, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx index f93f380469622..99968cd4d9fe8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx @@ -48,7 +48,6 @@ const PrePackagedRulesPromptComponent: React.FC = ( return ( {i18n.PRE_BUILT_TITLE}} body={

{i18n.PRE_BUILT_MSG}

} actions={ diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 7bf151adde5cc..2b842515d0b71 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -33,7 +33,6 @@ import { useKibana } from '../../../../common/lib/kibana'; import { getSchema } from './schema'; import * as I18n from './translations'; import { APP_ID } from '../../../../../common/constants'; -import { SecurityPageName } from '../../../../app/types'; interface StepRuleActionsProps extends RuleStepProps { defaultValues?: ActionsStepRule | null; @@ -86,16 +85,13 @@ const StepRuleActionsComponent: FC = ({ }); const { submit } = form; - // TO DO need to make sure that logic is still valid - const kibanaAbsoluteUrl = useMemo(() => { - const url = application.getUrlForApp(`${APP_ID}:${SecurityPageName.detections}`, { - absolute: true, - }); - if (url != null && url.includes('app/security/alerts')) { - return url.replace('app/security/alerts', 'app/security'); - } - return url; - }, [application]); + const kibanaAbsoluteUrl = useMemo( + () => + application.getUrlForApp(`${APP_ID}`, { + absolute: true, + }), + [application] + ); const onSubmit = useCallback( async (enabled: boolean) => { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx index 9a2f43bb475b1..6257a9980e00c 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx @@ -70,7 +70,7 @@ export const useFetchIndexPatterns = (defaultIndices: string[] = []): Return => apolloClient .query({ query: sourceQuery, - fetchPolicy: 'cache-first', + fetchPolicy: 'network-only', variables: { sourceId: 'default', defaultIndex: indices, diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx index 7b843b4f69447..f4e39ff8227c7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.tsx @@ -138,10 +138,9 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ setShow(false); }, []); // only merge in the default category if the field browser is visible - const browserFieldsWithDefaultCategory = useMemo( - () => (show ? mergeBrowserFieldsWithDefaultCategory(browserFields) : {}), - [show, browserFields] - ); + const browserFieldsWithDefaultCategory = useMemo(() => { + return show ? mergeBrowserFieldsWithDefaultCategory(browserFields) : {}; + }, [show, browserFields]); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx index 5f35bc5212d37..7ee7e12c0ef62 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx @@ -78,8 +78,7 @@ const StatefulSearchOrFilterComponent = React.memo( serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern), }, }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [indexPattern, timelineId] + [applyKqlFilterQuery, indexPattern, timelineId] ); const setFilterQueryDraftFromKueryExpression = useCallback( @@ -91,8 +90,7 @@ const StatefulSearchOrFilterComponent = React.memo( expression, }, }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [timelineId] + [timelineId, setKqlFilterQueryDraft] ); const setFiltersInTimeline = useCallback( @@ -101,8 +99,7 @@ const StatefulSearchOrFilterComponent = React.memo( id: timelineId, filters: newFilters, }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [timelineId] + [timelineId, setFilters] ); const setSavedQueryInTimeline = useCallback( @@ -111,8 +108,7 @@ const StatefulSearchOrFilterComponent = React.memo( id: timelineId, savedQueryId: newSavedQueryId, }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [timelineId] + [timelineId, setSavedQueryId] ); const handleUpdateEventType = useCallback( @@ -121,8 +117,7 @@ const StatefulSearchOrFilterComponent = React.memo( id: timelineId, eventType: newEventType, }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [timelineId] + [timelineId, updateEventType] ); return ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.test.ts index 0d363e1f6f3c2..95e6071e4defc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.test.ts @@ -9,13 +9,13 @@ import { getNotificationResultsLink } from './utils'; describe('utils', () => { it('getNotificationResultsLink', () => { const resultLink = getNotificationResultsLink({ - kibanaSiemAppUrl: 'http://localhost:5601/app/siem', + kibanaSiemAppUrl: 'http://localhost:5601/app/security', id: 'notification-id', from: '00000', to: '1111', }); expect(resultLink).toEqual( - `http://localhost:5601/app/siem#/detections/rules/id/notification-id?timerange=(global:(linkTo:!(timeline),timerange:(from:00000,kind:absolute,to:1111)),timeline:(linkTo:!(global),timerange:(from:00000,kind:absolute,to:1111)))` + `http://localhost:5601/app/security/detections/rules/id/notification-id?timerange=(global:(linkTo:!(timeline),timerange:(from:00000,kind:absolute,to:1111)),timeline:(linkTo:!(global),timerange:(from:00000,kind:absolute,to:1111)))` ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.ts index c91c4490e8eba..983ee86598fa1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/utils.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { APP_PATH } from '../../../../common/constants'; + export const getNotificationResultsLink = ({ - kibanaSiemAppUrl = '/app/siem', + kibanaSiemAppUrl = APP_PATH, id, from, to, @@ -17,5 +19,5 @@ export const getNotificationResultsLink = ({ }) => { if (from == null || to == null) return ''; - return `${kibanaSiemAppUrl}#/detections/rules/id/${id}?timerange=(global:(linkTo:!(timeline),timerange:(from:${from},kind:absolute,to:${to})),timeline:(linkTo:!(global),timerange:(from:${from},kind:absolute,to:${to})))`; + return `${kibanaSiemAppUrl}/detections/rules/id/${id}?timerange=(global:(linkTo:!(timeline),timerange:(from:${from},kind:absolute,to:${to})),timeline:(linkTo:!(global),timerange:(from:${from},kind:absolute,to:${to})))`; }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b87957ae45289..c1f0dc4c0c60c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -14067,7 +14067,6 @@ "xpack.securitySolution.header.editableTitle.editButtonAria": "クリックすると {title} を編集できます", "xpack.securitySolution.header.editableTitle.save": "保存", "xpack.securitySolution.headerGlobal.buttonAddData": "データの追加", - "xpack.securitySolution.headerGlobal.siem": "Security", "xpack.securitySolution.headerPage.pageSubtitle": "前回のイベント: {beat}", "xpack.securitySolution.hooks.useAddToTimeline.addedFieldMessage": "{fieldOrValue}をタイムラインに追加しました", "xpack.securitySolution.host.details.architectureLabel": "アーキテクチャー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 01ffa4833a3bb..0f2a51c8ff889 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -14073,7 +14073,6 @@ "xpack.securitySolution.header.editableTitle.editButtonAria": "通过单击,可以编辑 {title}", "xpack.securitySolution.header.editableTitle.save": "保存", "xpack.securitySolution.headerGlobal.buttonAddData": "添加数据", - "xpack.securitySolution.headerGlobal.siem": "Security", "xpack.securitySolution.headerPage.pageSubtitle": "最后事件:{beat}", "xpack.securitySolution.hooks.useAddToTimeline.addedFieldMessage": "已将 {fieldOrValue} 添加到时间线", "xpack.securitySolution.host.details.architectureLabel": "架构", From 54c3644757d5622f9e516aa3c99cf2ec87d6591e Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 20 Jul 2020 18:35:42 +0300 Subject: [PATCH 19/26] [Alerting][Connectors] Increase the size of the logos (#72419) --- .../sections/action_connector_form/action_type_menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx index bc36c41664e57..7ecb833fdfc9e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx @@ -81,7 +81,7 @@ export const ActionTypeMenu = ({ } + icon={} title={item.name} description={item.selectMessage} isDisabled={!checkEnabledResult.isEnabled} From d744d18b1921655c12b82bb6760f9bc8348e2a3c Mon Sep 17 00:00:00 2001 From: Bohdan Tsymbala Date: Mon, 20 Jul 2020 17:44:17 +0200 Subject: [PATCH 20/26] [ENDPOINT] Added unerolling status for host. (#72303) * Added unerolling status for host. * Added unenrolling status to frontend tests. --- .../common/endpoint/types.ts | 5 ++++ .../endpoint_hosts/view/host_constants.ts | 1 + .../pages/endpoint_hosts/view/index.test.tsx | 25 ++++++++++++------- .../pages/endpoint_hosts/view/index.tsx | 2 +- .../server/endpoint/routes/metadata/index.ts | 1 + .../endpoint/routes/metadata/metadata.test.ts | 4 +-- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 5e7e4d22f8c3c..a982f9ffe8f21 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -419,6 +419,11 @@ export enum HostStatus { * Host is offline as indicated by its checkin status during the last checkin window */ OFFLINE = 'offline', + + /** + * Host is unenrolling as indicated by its checkin status during the last checkin window + */ + UNENROLLING = 'unenrolling', } export type HostInfo = Immutable<{ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/host_constants.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/host_constants.ts index 790bbd3cb98da..4204d4f79f19c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/host_constants.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/host_constants.ts @@ -15,6 +15,7 @@ export const HOST_STATUS_TO_HEALTH_COLOR = Object.freeze< [HostStatus.ERROR]: 'danger', [HostStatus.ONLINE]: 'success', [HostStatus.OFFLINE]: 'subdued', + [HostStatus.UNENROLLING]: 'warning', }); export const POLICY_STATUS_TO_HEALTH_COLOR = Object.freeze< diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index a61088e2edd29..47227244b7066 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -112,14 +112,16 @@ describe('when on the hosts page', () => { let firstPolicyID: string; beforeEach(() => { reactTestingLibrary.act(() => { - const hostListData = mockHostResultList({ total: 3 }); + const hostListData = mockHostResultList({ total: 4 }); firstPolicyID = hostListData.hosts[0].metadata.Endpoint.policy.applied.id; - [HostStatus.ERROR, HostStatus.ONLINE, HostStatus.OFFLINE].forEach((status, index) => { - hostListData.hosts[index] = { - metadata: hostListData.hosts[index].metadata, - host_status: status, - }; - }); + [HostStatus.ERROR, HostStatus.ONLINE, HostStatus.OFFLINE, HostStatus.UNENROLLING].forEach( + (status, index) => { + hostListData.hosts[index] = { + metadata: hostListData.hosts[index].metadata, + host_status: status, + }; + } + ); hostListData.hosts.forEach((item, index) => { generatedPolicyStatuses[index] = item.metadata.Endpoint.policy.applied.status; }); @@ -134,12 +136,12 @@ describe('when on the hosts page', () => { it('should display rows in the table', async () => { const renderResult = render(); const rows = await renderResult.findAllByRole('row'); - expect(rows).toHaveLength(4); + expect(rows).toHaveLength(5); }); it('should show total', async () => { const renderResult = render(); const total = await renderResult.findByTestId('hostListTableTotal'); - expect(total.textContent).toEqual('3 Hosts'); + expect(total.textContent).toEqual('4 Hosts'); }); it('should display correct status', async () => { const renderResult = render(); @@ -157,6 +159,11 @@ describe('when on the hosts page', () => { expect( hostStatuses[2].querySelector('[data-euiicon-type][color="subdued"]') ).not.toBeNull(); + + expect(hostStatuses[3].textContent).toEqual('Unenrolling'); + expect( + hostStatuses[3].querySelector('[data-euiicon-type][color="warning"]') + ).not.toBeNull(); }); it('should display correct policy status', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 4c8d2c5a6df4e..c5ed71cba46d9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -226,7 +226,7 @@ export const HostList = () => { > diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts index cb9889ca0cb76..fe7a8296608d2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts @@ -35,6 +35,7 @@ interface MetadataRequestContext { const HOST_STATUS_MAPPING = new Map([ ['online', HostStatus.ONLINE], ['offline', HostStatus.OFFLINE], + ['unenrolling', HostStatus.UNENROLLING], ]); /** diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 321eb0195aac3..8d967656065d1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -335,7 +335,7 @@ describe('test endpoint route', () => { expect(result.host_status).toEqual(HostStatus.ERROR); }); - it('should return a single endpoint with status error when status is not offline or online', async () => { + it('should return a single endpoint with status error when status is not offline, online or enrolling', async () => { const response = createSearchResponse(new EndpointDocGenerator().generateHostMetadata()); const mockRequest = httpServerMock.createKibanaRequest({ @@ -368,7 +368,7 @@ describe('test endpoint route', () => { expect(result.host_status).toEqual(HostStatus.ERROR); }); - it('should throw error when endpoint egent is not active', async () => { + it('should throw error when endpoint agent is not active', async () => { const response = createSearchResponse(new EndpointDocGenerator().generateHostMetadata()); const mockRequest = httpServerMock.createKibanaRequest({ From 96d965d4e305e7891500b7ad6cb83f4356d6117c Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 20 Jul 2020 17:55:27 +0200 Subject: [PATCH 21/26] Unskip dashboard embeddable rendering tests (#71824) --- test/functional/apps/dashboard/embeddable_rendering.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/apps/dashboard/embeddable_rendering.js b/test/functional/apps/dashboard/embeddable_rendering.js index 9ba0c07c744fc..c00f01d060f4a 100644 --- a/test/functional/apps/dashboard/embeddable_rendering.js +++ b/test/functional/apps/dashboard/embeddable_rendering.js @@ -98,8 +98,7 @@ export default function ({ getService, getPageObjects }) { await dashboardExpect.vegaTextsDoNotExist(['5,000']); }; - // FLAKY: https://github.com/elastic/kibana/issues/46305 - describe.skip('dashboard embeddable rendering', function describeIndexTests() { + describe('dashboard embeddable rendering', function describeIndexTests() { before(async () => { await esArchiver.load('dashboard/current/kibana'); await kibanaServer.uiSettings.replace({ From 2094f33537df6185ec0894316f8af107110bd071 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Mon, 20 Jul 2020 12:28:46 -0400 Subject: [PATCH 22/26] [Security Solution] Cleanup endpoint telemetry (#71950) Co-authored-by: Elastic Machine --- .../server/usage/collector.ts | 2 +- .../server/usage/endpoints/endpoint.mocks.ts | 60 +++- .../server/usage/endpoints/endpoint.test.ts | 332 +++++++++++++++--- .../usage/endpoints/fleet_saved_objects.ts | 16 +- .../server/usage/endpoints/index.ts | 239 +++++++------ 5 files changed, 480 insertions(+), 169 deletions(-) diff --git a/x-pack/plugins/security_solution/server/usage/collector.ts b/x-pack/plugins/security_solution/server/usage/collector.ts index 9740f57450e80..9a7ad6fc2db74 100644 --- a/x-pack/plugins/security_solution/server/usage/collector.ts +++ b/x-pack/plugins/security_solution/server/usage/collector.ts @@ -12,7 +12,7 @@ import { EndpointUsage, getEndpointTelemetryFromFleet } from './endpoints'; export type RegisterCollector = (deps: CollectorDependencies) => void; export interface UsageData { detections: DetectionsUsage; - endpoints: EndpointUsage; + endpoints: EndpointUsage | {}; } export async function getInternalSavedObjectsClient(core: CoreSetup) { diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts index 1369a3d398265..e3f0f7bde2fed 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.mocks.ts @@ -14,6 +14,7 @@ import { FLEET_ENDPOINT_PACKAGE_CONSTANT } from './fleet_saved_objects'; const testAgentId = 'testAgentId'; const testConfigId = 'testConfigId'; +const testHostId = 'randoHostId'; /** Mock OS Platform for endpoint telemetry */ export const MockOSPlatform = 'somePlatform'; @@ -30,6 +31,7 @@ export const MockOSFullName = 'somePlatformFullName'; * @description We request the install and OS related telemetry information from the 'fleet-agents' saved objects in ingest_manager. This mocks that response */ export const mockFleetObjectsResponse = ( + hasDuplicates = true, lastCheckIn = new Date().toISOString() ): SavedObjectsFindResponse => ({ page: 1, @@ -56,7 +58,44 @@ export const mockFleetObjectsResponse = ( host: { hostname: 'testDesktop', name: 'testDesktop', - id: 'randoHostId', + id: testHostId, + }, + os: { + platform: MockOSPlatform, + version: MockOSVersion, + name: MockOSName, + full: MockOSFullName, + }, + }, + packages: [FLEET_ENDPOINT_PACKAGE_CONSTANT, 'system'], + last_checkin: lastCheckIn, + }, + references: [], + updated_at: lastCheckIn, + version: 'WzI4MSwxXQ==', + score: 0, + }, + { + type: AGENT_SAVED_OBJECT_TYPE, + id: testAgentId, + attributes: { + active: true, + id: 'oldTestAgentId', + config_id: 'randoConfigId', + type: 'PERMANENT', + user_provided_metadata: {}, + enrolled_at: lastCheckIn, + current_error_events: [], + local_metadata: { + elastic: { + agent: { + id: 'oldTestAgentId', + }, + }, + host: { + hostname: 'testDesktop', + name: 'testDesktop', + id: hasDuplicates ? testHostId : 'oldRandoHostId', }, os: { platform: MockOSPlatform, @@ -76,7 +115,10 @@ export const mockFleetObjectsResponse = ( ], }); -const mockPolicyPayload = (malwareStatus: 'success' | 'warning' | 'failure') => +const mockPolicyPayload = ( + policyStatus: 'success' | 'warning' | 'failure', + policyMode: 'prevent' | 'detect' | 'off' = 'prevent' +) => JSON.stringify({ 'endpoint-security': { Endpoint: { @@ -105,7 +147,7 @@ const mockPolicyPayload = (malwareStatus: 'success' | 'warning' | 'failure') => file: 'info', }, malware: { - mode: 'prevent', + mode: policyMode, }, }, windows: { @@ -122,7 +164,7 @@ const mockPolicyPayload = (malwareStatus: 'success' | 'warning' | 'failure') => file: 'info', }, malware: { - mode: 'prevent', + mode: policyMode, }, }, }, @@ -151,11 +193,11 @@ const mockPolicyPayload = (malwareStatus: 'success' | 'warning' | 'failure') => 'detect_file_open_events', 'detect_sync_image_load_events', ], - status: `${malwareStatus}`, + status: `${policyStatus}`, }, }, }, - status: `${malwareStatus}`, + status: `${policyStatus}`, }, }, }, @@ -186,7 +228,9 @@ const mockPolicyPayload = (malwareStatus: 'success' | 'warning' | 'failure') => */ export const mockFleetEventsObjectsResponse = ( running?: boolean, - updatedDate = new Date().toISOString() + updatedDate = new Date().toISOString(), + policyStatus: 'success' | 'failure' = running ? 'success' : 'failure', + policyMode: 'prevent' | 'detect' | 'off' = 'prevent' ): SavedObjectsFindResponse => { return { page: 1, @@ -204,7 +248,7 @@ export const mockFleetEventsObjectsResponse = ( message: `Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to ${ running ? 'RUNNING' : 'FAILED' }: `, - payload: mockPolicyPayload(running ? 'success' : 'failure'), + payload: running ? mockPolicyPayload(policyStatus, policyMode) : undefined, config_id: testConfigId, }, references: [], diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts index 06755192bd818..e2f7a3be6d80a 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/endpoint.test.ts @@ -51,17 +51,31 @@ describe('test security solution endpoint telemetry', () => { `); }); + describe('when a request for endpoint agents fails', () => { + it('should return an empty object', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.reject(Error('No agents for you')) + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled(); + expect(endpointUsage).toEqual({}); + }); + }); + describe('when an agent has not been installed', () => { it('should return the default shape if no agents are found', async () => { getFleetSavedObjectsMetadataSpy.mockImplementation(() => Promise.resolve({ saved_objects: [], total: 0, per_page: 0, page: 0 }) ); - const emptyEndpointTelemetryData = await endpointTelemetry.getEndpointTelemetryFromFleet( + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( mockSavedObjectsRepository ); expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled(); - expect(emptyEndpointTelemetryData).toEqual({ + expect(endpointUsage).toEqual({ total_installed: 0, active_within_last_24_hours: 0, os: [], @@ -76,68 +90,274 @@ describe('test security solution endpoint telemetry', () => { }); }); - describe('when an agent has been installed', () => { - it('should show one enpoint installed but it is inactive', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => - Promise.resolve(mockFleetObjectsResponse()) - ); - getLatestFleetEndpointEventSpy.mockImplementation(() => - Promise.resolve(mockFleetEventsObjectsResponse()) - ); + describe('when agent(s) have been installed', () => { + describe('when a request for events has failed', () => { + it('should show only one endpoint installed but it is inactive', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse()) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.reject(Error('No events for you')) + ); - const emptyEndpointTelemetryData = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository - ); - expect(emptyEndpointTelemetryData).toEqual({ - total_installed: 1, - active_within_last_24_hours: 0, - os: [ - { - full_name: MockOSFullName, - platform: MockOSPlatform, - version: MockOSVersion, - count: 1, - }, - ], - policies: { - malware: { - failure: 1, - active: 0, - inactive: 0, + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 1, + active_within_last_24_hours: 0, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 1, + }, + ], + policies: { + malware: { + failure: 0, + active: 0, + inactive: 0, + }, }, - }, + }); }); }); - it('should show one endpoint installed and it is active', async () => { - getFleetSavedObjectsMetadataSpy.mockImplementation(() => - Promise.resolve(mockFleetObjectsResponse()) - ); - getLatestFleetEndpointEventSpy.mockImplementation(() => - Promise.resolve(mockFleetEventsObjectsResponse(true)) - ); + describe('when a request for events is successful', () => { + it('should show one endpoint installed but endpoint has failed to run', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse()) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.resolve(mockFleetEventsObjectsResponse()) + ); - const emptyEndpointTelemetryData = await endpointTelemetry.getEndpointTelemetryFromFleet( - mockSavedObjectsRepository - ); - expect(emptyEndpointTelemetryData).toEqual({ - total_installed: 1, - active_within_last_24_hours: 1, - os: [ - { - full_name: MockOSFullName, - platform: MockOSPlatform, - version: MockOSVersion, - count: 1, + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 1, + active_within_last_24_hours: 0, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 1, + }, + ], + policies: { + malware: { + failure: 0, + active: 0, + inactive: 0, + }, }, - ], - policies: { - malware: { - failure: 0, - active: 1, - inactive: 0, + }); + }); + + it('should show two endpoints installed but both endpoints have failed to run', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse(false)) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.resolve(mockFleetEventsObjectsResponse()) + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 2, + active_within_last_24_hours: 0, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 2, + }, + ], + policies: { + malware: { + failure: 0, + active: 0, + inactive: 0, + }, }, - }, + }); + }); + + it('should show two endpoints installed but agents have not checked in within past day', async () => { + const twoDaysAgo = new Date(); + twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); + const twoDaysAgoISOString = twoDaysAgo.toISOString(); + + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse(false, twoDaysAgoISOString)) + ); + getLatestFleetEndpointEventSpy.mockImplementation( + () => Promise.resolve(mockFleetEventsObjectsResponse(true, twoDaysAgoISOString)) // agent_id doesn't matter for mock here + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 2, + active_within_last_24_hours: 0, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 2, + }, + ], + policies: { + malware: { + failure: 0, + active: 2, + inactive: 0, + }, + }, + }); + }); + + it('should show one endpoint installed and endpoint is running', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse()) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.resolve(mockFleetEventsObjectsResponse(true)) + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 1, + active_within_last_24_hours: 1, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 1, + }, + ], + policies: { + malware: { + failure: 0, + active: 1, + inactive: 0, + }, + }, + }); + }); + + describe('malware policy', () => { + it('should have failed to enable', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse()) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.resolve( + mockFleetEventsObjectsResponse(true, new Date().toISOString(), 'failure') + ) + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 1, + active_within_last_24_hours: 1, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 1, + }, + ], + policies: { + malware: { + failure: 1, + active: 0, + inactive: 0, + }, + }, + }); + }); + + it('should be enabled successfully', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse()) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.resolve(mockFleetEventsObjectsResponse(true)) + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 1, + active_within_last_24_hours: 1, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 1, + }, + ], + policies: { + malware: { + failure: 0, + active: 1, + inactive: 0, + }, + }, + }); + }); + + it('should be disabled successfully', async () => { + getFleetSavedObjectsMetadataSpy.mockImplementation(() => + Promise.resolve(mockFleetObjectsResponse()) + ); + getLatestFleetEndpointEventSpy.mockImplementation(() => + Promise.resolve( + mockFleetEventsObjectsResponse(true, new Date().toISOString(), 'success', 'off') + ) + ); + + const endpointUsage = await endpointTelemetry.getEndpointTelemetryFromFleet( + mockSavedObjectsRepository + ); + expect(endpointUsage).toEqual({ + total_installed: 1, + active_within_last_24_hours: 1, + os: [ + { + full_name: MockOSFullName, + platform: MockOSPlatform, + version: MockOSVersion, + count: 1, + }, + ], + policies: { + malware: { + failure: 0, + active: 0, + inactive: 1, + }, + }, + }); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts index 7e05fdec36169..42c1ec0e2eed2 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/fleet_saved_objects.ts @@ -16,8 +16,16 @@ export const FLEET_ENDPOINT_PACKAGE_CONSTANT = FleetDefaultPackages.endpoint; export const getFleetSavedObjectsMetadata = async (savedObjectsClient: ISavedObjectsRepository) => savedObjectsClient.find({ + // Get up to 10000 agents with endpoint installed type: AGENT_SAVED_OBJECT_TYPE, - fields: ['packages', 'last_checkin', 'local_metadata'], + fields: [ + 'packages', + 'last_checkin', + 'local_metadata.agent.id', + 'local_metadata.host.id', + 'local_metadata.elastic.agent.id', + 'local_metadata.os', + ], filter: `${AGENT_SAVED_OBJECT_TYPE}.attributes.packages: ${FLEET_ENDPOINT_PACKAGE_CONSTANT}`, perPage: 10000, sortField: 'enrolled_at', @@ -29,9 +37,11 @@ export const getLatestFleetEndpointEvent = async ( agentId: string ) => savedObjectsClient.find({ + // Get the most recent endpoint event. type: AGENT_EVENT_SAVED_OBJECT_TYPE, - filter: `${AGENT_EVENT_SAVED_OBJECT_TYPE}.attributes.agent_id: ${agentId} and ${AGENT_EVENT_SAVED_OBJECT_TYPE}.attributes.message: "${FLEET_ENDPOINT_PACKAGE_CONSTANT}"`, - perPage: 1, // Get the most recent endpoint event. + fields: ['agent_id', 'subtype', 'payload'], + filter: `${AGENT_EVENT_SAVED_OBJECT_TYPE}.attributes.message: "${FLEET_ENDPOINT_PACKAGE_CONSTANT}"`, + perPage: 1, sortField: 'timestamp', sortOrder: 'desc', search: agentId, diff --git a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts index ab5669d503275..9e071f4adff25 100644 --- a/x-pack/plugins/security_solution/server/usage/endpoints/index.ts +++ b/x-pack/plugins/security_solution/server/usage/endpoints/index.ts @@ -3,8 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { cloneDeep } from 'lodash'; import { ISavedObjectsRepository } from 'src/core/server'; +import { SavedObject } from './../../../../../../src/core/types/saved_objects'; +import { Agent, NewAgentEvent } from './../../../../ingest_manager/common/types/models/agent'; import { AgentMetadata } from '../../../../ingest_manager/common/types/models/agent'; import { getFleetSavedObjectsMetadata, getLatestFleetEndpointEvent } from './fleet_saved_objects'; @@ -51,7 +53,7 @@ export interface AgentLocalMetadata extends AgentMetadata { } type OSTracker = Record; -type AgentDailyActiveTracker = Map; + /** * @description returns an empty telemetry object to be incrmented and updated within the `getEndpointTelemetryFromFleet` fn */ @@ -69,13 +71,14 @@ export const getDefaultEndpointTelemetry = (): EndpointUsage => ({ }); /** - * @description this fun + * @description this function updates the os telemetry. We use the fullName field as the key as it contains the name and version details. + * If it has already been tracked, the count will be updated, otherwise a tracker will be initialized for that fullName. */ -export const trackEndpointOSTelemetry = ( +export const updateEndpointOSTelemetry = ( os: AgentLocalMetadata['os'], osTracker: OSTracker ): OSTracker => { - const updatedOSTracker = { ...osTracker }; + const updatedOSTracker = cloneDeep(osTracker); const { version: osVersion, platform: osPlatform, full: osFullName } = os; if (osFullName && osVersion) { if (updatedOSTracker[osFullName]) updatedOSTracker[osFullName].count += 1; @@ -93,18 +96,32 @@ export const trackEndpointOSTelemetry = ( }; /** - * @description This iterates over all unique agents that currently track an endpoint package. It takes a list of agents who have checked in in the last 24 hours - * and then checks whether those agents have endpoints whose latest status is 'RUNNING' to determine an active_within_last_24_hours. Since the policy information is also tracked in these events - * we pull out the status of the current protection (malware) type. This must be done in a compound manner as the desired status is reflected in the config, and the successful application of that policy - * is tracked in the policy.applied.response.configurations[protectionsType].status. Using these two we can determine whether the policy is toggled on, off, or failed to turn on. + * @description we take the latest endpoint specific agent event, get the status of the endpoint, and if it is running + * and the agent itself has been active within the last 24 hours, we can safely assume the endpoint has been active within + * the same time span. */ -export const addEndpointDailyActivityAndPolicyDetailsToTelemetry = async ( - agentDailyActiveTracker: AgentDailyActiveTracker, - savedObjectsClient: ISavedObjectsRepository, - endpointTelemetry: EndpointUsage -): Promise => { - const updatedEndpointTelemetry = { ...endpointTelemetry }; +export const updateEndpointDailyActiveCount = ( + latestEndpointEvent: SavedObject, + lastAgentCheckin: Agent['last_checkin'], + currentCount: number +) => { + const aDayAgo = new Date(); + aDayAgo.setDate(aDayAgo.getDate() - 1); + + const agentWasActiveOverLastDay = !!lastAgentCheckin && new Date(lastAgentCheckin) > aDayAgo; + return agentWasActiveOverLastDay && latestEndpointEvent.attributes.subtype === 'RUNNING' + ? currentCount + 1 + : currentCount; +}; +/** + * @description We take the latest endpoint specific agent event, and as long as it provides the payload with policy details, we will parse that policy + * to populate the success of it's application. The policy is provided in the agent health checks. + */ +export const updateEndpointPolicyTelemetry = ( + latestEndpointEvent: SavedObject, + policiesTracker: PoliciesTelemetry +): PoliciesTelemetry => { const policyHostTypeToPolicyType = { Linux: 'linux', macOs: 'mac', @@ -112,58 +129,60 @@ export const addEndpointDailyActivityAndPolicyDetailsToTelemetry = async ( }; const enabledMalwarePolicyTypes = ['prevent', 'detect']; - for (const agentId of agentDailyActiveTracker.keys()) { - const { saved_objects: agentEvents } = await getLatestFleetEndpointEvent( - savedObjectsClient, - agentId - ); - - const latestEndpointEvent = agentEvents[0]; - if (latestEndpointEvent) { - /* - We can assume that if the last status of the endpoint is RUNNING and the agent has checked in within the last 24 hours - then the endpoint has still been running within the last 24 hours. - */ - const { subtype, payload } = latestEndpointEvent.attributes; - const endpointIsActive = - subtype === 'RUNNING' && agentDailyActiveTracker.get(agentId) === true; - - if (endpointIsActive) { - updatedEndpointTelemetry.active_within_last_24_hours += 1; - } + // The policy details are sent as a string on the 'payload' attribute of the agent event + const { payload } = latestEndpointEvent.attributes; - // The policy details are sent as a string on the 'payload' attribute of the agent event - const endpointPolicyDetails = payload ? JSON.parse(payload) : null; - if (endpointPolicyDetails) { - // We get the setting the user desired to enable (treating prevent and detect as 'active' states) and then see if it succeded or failed. - const hostType = - policyHostTypeToPolicyType[ - endpointPolicyDetails['endpoint-security']?.host?.os?.name as EndpointOSNames - ]; - const userDesiredMalwareState = - endpointPolicyDetails['endpoint-security'].Endpoint?.configuration?.inputs[0]?.policy[ - hostType - ]?.malware?.mode; - - const isAnActiveMalwareState = enabledMalwarePolicyTypes.includes(userDesiredMalwareState); - const malwareStatus = - endpointPolicyDetails['endpoint-security'].Endpoint?.policy?.applied?.response - ?.configurations?.malware?.status; - - if (isAnActiveMalwareState && malwareStatus !== 'failure') { - updatedEndpointTelemetry.policies.malware.active += 1; - } - if (!isAnActiveMalwareState) { - updatedEndpointTelemetry.policies.malware.inactive += 1; - } - if (isAnActiveMalwareState && malwareStatus === 'failure') { - updatedEndpointTelemetry.policies.malware.failure += 1; - } - } - } + if (!payload) { + // This payload may not always be provided depending on the state of the endpoint. Guard again situations where it is not sent + return policiesTracker; + } + + let endpointPolicyPayload; + try { + endpointPolicyPayload = JSON.parse(latestEndpointEvent.attributes.payload); + } catch (error) { + return policiesTracker; + } + + // Get the platform: windows, mac, or linux + const hostType = + policyHostTypeToPolicyType[ + endpointPolicyPayload['endpoint-security']?.host?.os?.name as EndpointOSNames + ]; + // Get whether the malware setting for the platform on the most recently provided config is active (prevent or detect is on) or off + const userDesiredMalwareState = + endpointPolicyPayload['endpoint-security'].Endpoint?.configuration?.inputs[0]?.policy[hostType] + ?.malware?.mode; + + // Get the status of the application of the malware protection + const malwareStatus = + endpointPolicyPayload['endpoint-security'].Endpoint?.policy?.applied?.response?.configurations + ?.malware?.status; + + if (!userDesiredMalwareState || !malwareStatus) { + // If we get policy information without the mode or status, then nothing to track or update + return policiesTracker; } - return updatedEndpointTelemetry; + const updatedPoliciesTracker = { + malware: { ...policiesTracker.malware }, + }; + + const isAnActiveMalwareState = enabledMalwarePolicyTypes.includes(userDesiredMalwareState); + + // we only check for 'not failure' as the 'warning' state for malware is still technically actively enabled (with warnings) + const successfullyEnabled = !!malwareStatus && malwareStatus !== 'failure'; + const failedToEnable = !!malwareStatus && malwareStatus === 'failure'; + + if (isAnActiveMalwareState && successfullyEnabled) { + updatedPoliciesTracker.malware.active += 1; + } else if (!isAnActiveMalwareState && successfullyEnabled) { + updatedPoliciesTracker.malware.inactive += 1; + } else if (isAnActiveMalwareState && failedToEnable) { + updatedPoliciesTracker.malware.failure += 1; + } + + return updatedPoliciesTracker; }; /** @@ -173,53 +192,71 @@ export const addEndpointDailyActivityAndPolicyDetailsToTelemetry = async ( * to confirm whether or not the endpoint is still active */ export const getEndpointTelemetryFromFleet = async ( - savedObjectsClient: ISavedObjectsRepository -): Promise => { - // Retrieve every agent that references the endpoint as an installed package. It will not be listed if it was never installed - const { saved_objects: endpointAgents } = await getFleetSavedObjectsMetadata(savedObjectsClient); + soClient: ISavedObjectsRepository +): Promise => { + // Retrieve every agent (max 10000) that references the endpoint as an installed package. It will not be listed if it was never installed + let endpointAgents; + try { + const response = await getFleetSavedObjectsMetadata(soClient); + endpointAgents = response.saved_objects; + } catch (error) { + // Better to provide an empty object rather than default telemetry as this better informs us of an error + return {}; + } + + const endpointAgentsCount = endpointAgents.length; const endpointTelemetry = getDefaultEndpointTelemetry(); // If there are no installed endpoints return the default telemetry object - if (!endpointAgents || endpointAgents.length < 1) return endpointTelemetry; + if (!endpointAgents || endpointAgentsCount < 1) return endpointTelemetry; // Use unique hosts to prevent any potential duplicates const uniqueHostIds: Set = new Set(); - // Need agents to get events data for those that have run in last 24 hours as well as policy details - const agentDailyActiveTracker: AgentDailyActiveTracker = new Map(); - - const aDayAgo = new Date(); - aDayAgo.setDate(aDayAgo.getDate() - 1); let osTracker: OSTracker = {}; + let dailyActiveCount = 0; + let policyTracker: PoliciesTelemetry = { malware: { active: 0, inactive: 0, failure: 0 } }; + + for (let i = 0; i < endpointAgentsCount; i += 1) { + const { attributes: metadataAttributes } = endpointAgents[i]; + const { last_checkin: lastCheckin, local_metadata: localMetadata } = metadataAttributes; + const { host, os, elastic } = localMetadata as AgentLocalMetadata; // AgentMetadata is just an empty blob, casting for our use case + + if (!uniqueHostIds.has(host.id)) { + uniqueHostIds.add(host.id); + const agentId = elastic?.agent?.id; + osTracker = updateEndpointOSTelemetry(os, osTracker); + + if (agentId) { + let agentEvents; + try { + const response = await getLatestFleetEndpointEvent(soClient, agentId); + agentEvents = response.saved_objects; + } catch (error) { + // If the request fails we do not obtain `active within last 24 hours for this agent` or policy specifics + } - const endpointMetadataTelemetry = endpointAgents.reduce( - (metadataTelemetry, { attributes: metadataAttributes }) => { - const { last_checkin: lastCheckin, local_metadata: localMetadata } = metadataAttributes; - const { host, os, elastic } = localMetadata as AgentLocalMetadata; // AgentMetadata is just an empty blob, casting for our use case - - if (host && uniqueHostIds.has(host.id)) { - // use hosts since new agents could potentially be re-installed on existing hosts - return metadataTelemetry; - } else { - uniqueHostIds.add(host.id); - const isActiveWithinLastDay = !!lastCheckin && new Date(lastCheckin) > aDayAgo; - agentDailyActiveTracker.set(elastic.agent.id, isActiveWithinLastDay); - osTracker = trackEndpointOSTelemetry(os, osTracker); - return metadataTelemetry; + // AgentEvents will have a max length of 1 + if (agentEvents && agentEvents.length > 0) { + const latestEndpointEvent = agentEvents[0]; + dailyActiveCount = updateEndpointDailyActiveCount( + latestEndpointEvent, + lastCheckin, + dailyActiveCount + ); + policyTracker = updateEndpointPolicyTelemetry(latestEndpointEvent, policyTracker); + } } - }, - endpointTelemetry - ); + } + } - // All unique hosts with an endpoint installed. + // All unique hosts with an endpoint installed, thus all unique endpoint installs endpointTelemetry.total_installed = uniqueHostIds.size; + // Set the daily active count for the endpoints + endpointTelemetry.active_within_last_24_hours = dailyActiveCount; // Get the objects to populate our OS Telemetry - endpointMetadataTelemetry.os = Object.values(osTracker); - // Populate endpoint telemetry with the finalized 24 hour count and policy details - const finalizedEndpointTelemetryData = await addEndpointDailyActivityAndPolicyDetailsToTelemetry( - agentDailyActiveTracker, - savedObjectsClient, - endpointMetadataTelemetry - ); - - return finalizedEndpointTelemetryData; + endpointTelemetry.os = Object.values(osTracker); + // Provide the updated policy information + endpointTelemetry.policies = policyTracker; + + return endpointTelemetry; }; From 5741a868bc524dbb8363b85f4a0f37fd8ab321f8 Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 20 Jul 2020 09:32:41 -0700 Subject: [PATCH 23/26] Revert "skip flaky suite (#72146)" This reverts commit 45a4393459e0400171564f1d096784ebc97cc8ed. --- test/functional/apps/dashboard/dashboard_error_handling.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/apps/dashboard/dashboard_error_handling.ts b/test/functional/apps/dashboard/dashboard_error_handling.ts index 38803739ff129..6bd8327a110b9 100644 --- a/test/functional/apps/dashboard/dashboard_error_handling.ts +++ b/test/functional/apps/dashboard/dashboard_error_handling.ts @@ -28,8 +28,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { /** * Common test suite for testing exception scenarious within dashboard */ - // Flaky: https://github.com/elastic/kibana/issues/72146 - describe.skip('dashboard error handling', () => { + describe('dashboard error handling', () => { before(async () => { await esArchiver.loadIfNeeded('dashboard/current/kibana'); await PageObjects.common.navigateToApp('dashboard'); From 75e4c7a2b73c5e0606e11cb3fecf08337a0ea81e Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Mon, 20 Jul 2020 12:40:59 -0400 Subject: [PATCH 24/26] [Resolver] no longer pass related event stats to process node component (#72435) --- .../public/resolver/store/data/selectors.ts | 38 ++++--------------- .../public/resolver/store/selectors.ts | 6 ++- .../public/resolver/view/map.tsx | 4 -- .../public/resolver/view/panel.tsx | 5 ++- .../resolver/view/process_event_dot.tsx | 20 +++------- 5 files changed, 20 insertions(+), 53 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts index 109b3abddcc77..4098c6fc6c5dd 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts @@ -116,13 +116,14 @@ export const tree = createSelector(graphableProcesses, function indexedTree( */ export const relatedEventsStats: ( state: DataState -) => Map | null = createSelector( +) => (nodeID: string) => ResolverNodeStats | undefined = createSelector( resolverTreeResponse, (resolverTree?: ResolverTree) => { if (resolverTree) { - return resolverTreeModel.relatedEventsStats(resolverTree); + const map = resolverTreeModel.relatedEventsStats(resolverTree); + return (nodeID: string) => map.get(nodeID); } else { - return null; + return () => undefined; } } ); @@ -213,12 +214,8 @@ export const relatedEventInfoByEntityId: ( relatedEventsStats /* eslint-enable no-shadow */ ) { - if (!relatedEventsStats) { - // If there are no related event stats, there are no related event info objects - return () => null; - } return (entityId) => { - const stats = relatedEventsStats.get(entityId); + const stats = relatedEventsStats(entityId); if (!stats) { return null; } @@ -524,37 +521,16 @@ export function databaseDocumentIDToAbort(state: DataState): string | null { } } -/** - * `ResolverNodeStats` for a process (`ResolverEvent`) - */ -const relatedEventStatsForProcess: ( - state: DataState -) => (event: ResolverEvent) => ResolverNodeStats | null = createSelector( - relatedEventsStats, - (statsMap) => { - if (!statsMap) { - return () => null; - } - return (event: ResolverEvent) => { - const nodeStats = statsMap.get(uniquePidForProcess(event)); - if (!nodeStats) { - return null; - } - return nodeStats; - }; - } -); - /** * The sum of all related event categories for a process. */ export const relatedEventTotalForProcess: ( state: DataState ) => (event: ResolverEvent) => number | null = createSelector( - relatedEventStatsForProcess, + relatedEventsStats, (statsForProcess) => { return (event: ResolverEvent) => { - const stats = statsForProcess(event); + const stats = statsForProcess(uniquePidForProcess(event)); if (!stats) { return null; } diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts index 040e2920ce554..09293d0b3b683 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts @@ -10,7 +10,7 @@ import * as dataSelectors from './data/selectors'; import * as uiSelectors from './ui/selectors'; import { ResolverState, IsometricTaxiLayout } from '../types'; import { uniquePidForProcess } from '../models/process_event'; -import { ResolverEvent } from '../../../common/endpoint/types'; +import { ResolverEvent, ResolverNodeStats } from '../../../common/endpoint/types'; /** * A matrix that when applied to a Vector2 will convert it from world coordinates to screen coordinates. @@ -99,7 +99,9 @@ export const terminatedProcesses = composeSelectors( /** * Returns a map of `ResolverEvent` entity_id to their related event and alert statistics */ -export const relatedEventsStats = composeSelectors( +export const relatedEventsStats: ( + state: ResolverState +) => (nodeID: string) => ResolverNodeStats | undefined = composeSelectors( dataStateSelector, dataSelectors.relatedEventsStats ); diff --git a/x-pack/plugins/security_solution/public/resolver/view/map.tsx b/x-pack/plugins/security_solution/public/resolver/view/map.tsx index b366e2f220652..930e96c3f3e40 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/map.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/map.tsx @@ -60,7 +60,6 @@ export const ResolverMap = React.memo(function ({ const { processNodePositions, connectingEdgeLineSegments } = useSelector( selectors.visibleNodesAndEdgeLines )(timeAtRender); - const relatedEventsStats = useSelector(selectors.relatedEventsStats); const terminatedProcesses = useSelector(selectors.terminatedProcesses); const { projectionMatrix, ref, onMouseDown } = useCamera(); const isLoading = useSelector(selectors.isLoading); @@ -110,9 +109,6 @@ export const ResolverMap = React.memo(function ({ position={position} projectionMatrix={projectionMatrix} event={processEvent} - relatedEventsStatsForProcess={ - relatedEventsStats ? relatedEventsStats.get(entityId(processEvent)) : undefined - } isProcessTerminated={terminatedProcesses.has(processEntityId)} isProcessOrigin={false} timeAtRender={timeAtRender} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx index 47ce9b949fa59..efb2d95396ef5 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -99,8 +99,9 @@ const PanelContent = memo(function PanelContent() { const relatedEventStats = useSelector(selectors.relatedEventsStats); const { crumbId, crumbEvent } = queryParams; - const relatedStatsForIdFromParams: ResolverNodeStats | undefined = - idFromParams && relatedEventStats ? relatedEventStats.get(idFromParams) : undefined; + const relatedStatsForIdFromParams: ResolverNodeStats | undefined = idFromParams + ? relatedEventStats(idFromParams) + : undefined; /** * Determine which set of breadcrumbs to display based on the query parameters diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 7666d1ac7c88a..aab4193bf031d 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -14,7 +14,7 @@ import { NodeSubMenu, subMenuAssets } from './submenu'; import { applyMatrix3 } from '../models/vector2'; import { Vector2, Matrix3 } from '../types'; import { SymbolIds, useResolverTheme, calculateResolverFontSize } from './assets'; -import { ResolverEvent, ResolverNodeStats } from '../../../common/endpoint/types'; +import { ResolverEvent } from '../../../common/endpoint/types'; import { useResolverDispatch } from './use_resolver_dispatch'; import * as eventModel from '../../../common/endpoint/models/event'; import * as processEventModel from '../models/process_event'; @@ -73,7 +73,6 @@ const UnstyledProcessEventDot = React.memo( projectionMatrix, isProcessTerminated, isProcessOrigin, - relatedEventsStatsForProcess, timeAtRender, }: { /** @@ -100,12 +99,6 @@ const UnstyledProcessEventDot = React.memo( * Whether or not to show the process as the originating event. */ isProcessOrigin: boolean; - /** - * A collection of events related to the current node and statistics (e.g. counts indexed by event type) - * to provide the user some visibility regarding the contents thereof. - * Statistics for the number of related events and alerts for this process node - */ - relatedEventsStatsForProcess?: ResolverNodeStats; /** * The time (unix epoch) at render. @@ -127,6 +120,7 @@ const UnstyledProcessEventDot = React.memo( const activeDescendantId = useSelector(selectors.uiActiveDescendantId); const selectedDescendantId = useSelector(selectors.uiSelectedDescendantId); const nodeID = processEventModel.uniquePidForProcess(event); + const relatedEventStats = useSelector(selectors.relatedEventsStats)(nodeID); // define a standard way of giving HTML IDs to nodes based on their entity_id/nodeID. // this is used to link nodes via aria attributes @@ -270,15 +264,13 @@ const UnstyledProcessEventDot = React.memo( const relatedEventOptions = useMemo(() => { const relatedStatsList = []; - if (!relatedEventsStatsForProcess) { + if (!relatedEventStats) { // Return an empty set of options if there are no stats to report return []; } // If we have entries to show, map them into options to display in the selectable list - for (const [category, total] of Object.entries( - relatedEventsStatsForProcess.events.byCategory - )) { + for (const [category, total] of Object.entries(relatedEventStats.events.byCategory)) { relatedStatsList.push({ prefix: , optionTitle: category, @@ -296,9 +288,9 @@ const UnstyledProcessEventDot = React.memo( }); } return relatedStatsList; - }, [relatedEventsStatsForProcess, dispatch, event, pushToQueryParams, nodeID]); + }, [relatedEventStats, dispatch, event, pushToQueryParams, nodeID]); - const relatedEventStatusOrOptions = !relatedEventsStatsForProcess + const relatedEventStatusOrOptions = !relatedEventStats ? subMenuAssets.initialMenuStatus : relatedEventOptions; From afae94a85ec8d58221f673c23f76aa664fe0cea0 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 20 Jul 2020 11:00:06 -0600 Subject: [PATCH 25/26] [SIEM][Detection Engine][Lists] Adds conflict versioning and io-ts improvements to lists (#72337) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary * Adds conflict versioning by exposing the "_version" from the saved object system. It renames "version" to "_version" so that we can use regular "version" later for versioning things for pre-packaged lists abilities. * Utilizes `t.OutputOf` in the requests and the data types to give us more correctly types * Removes the `Identity` utility as that is adding confusion and can confuse vs code rather than improves things * Removes extra types that were causing confusion which was an idiom from io-ts * Changes the wording of `Partial` by removing that and instead focuses the request types on either client side or server side at this point. NOTE: The UI can migrate to holding onto the `_version` and then push it back down when it wants to migrate to using the conflict resolution. If the UI does not push it down, then a value of undefined will be used which is indicating that no conflict errors are wanted. Output example of posting an exception list: ❯ ./post_exception_list.sh ```ts { "_tags": [ "endpoint", "process", "malware", "os:linux" ], "_version": "Wzk4NiwxXQ==", "created_at": "2020-07-17T18:59:22.872Z", "created_by": "yo", "description": "This is a sample endpoint type exception", "id": "a08795b0-c85f-11ea-b1a6-c155df988a92", "list_id": "simple_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", "tags": [ "user added string for a tag", "malware" ], "tie_breaker_id": "b789ec05-3e0f-4344-a156-0c0f5b6e2f9c", "type": "detection", "updated_at": "2020-07-17T18:59:22.891Z", "updated_by": "yo" } ``` Output example of posting an exception list item ❯ ./post_exception_list_item.sh ```ts { "_tags": [ "endpoint", "process", "malware", "os:linux" ], "_version": "Wzk4NywxXQ==", "comments": [], "created_at": "2020-07-17T18:59:30.286Z", "created_by": "yo", "description": "This is a sample endpoint type exception", "entries": [ { "field": "actingProcess.file.signer", "operator": "excluded", "type": "exists" }, { "field": "host.name", "operator": "included", "type": "match_any", "value": [ "some host", "another host" ] } ], "id": "a4f2b800-c85f-11ea-b1a6-c155df988a92", "item_id": "simple_list_item", "list_id": "simple_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", "tags": [ "user added string for a tag", "malware" ], "tie_breaker_id": "1dc456bc-7aa9-44b4-bca3-131689cf729f", "type": "simple", "updated_at": "2020-07-17T18:59:30.304Z", "updated_by": "yo" } ``` Output example of when you get an exception list: ❯ ./get_exception_list.sh simple_list ```ts { "_tags": [ "endpoint", "process", "malware", "os:linux" ], "_version": "WzEwNzcsMV0=", "created_at": "2020-07-17T18:59:22.872Z", "created_by": "yo", "description": "Different description", "id": "a08795b0-c85f-11ea-b1a6-c155df988a92", "list_id": "simple_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", "tags": [ "user added string for a tag", "malware" ], "tie_breaker_id": "b789ec05-3e0f-4344-a156-0c0f5b6e2f9c", "type": "endpoint", "updated_at": "2020-07-17T20:01:24.958Z", "updated_by": "yo" } ``` Example of the error you get if you do an update of an exception list and someone else has changed it: ```ts { "message": "[exception-list:a08795b0-c85f-11ea-b1a6-c155df988a92]: version conflict, required seqNo [1074], primary term [1]. current document has seqNo [1077] and primary term [1]: [version_conflict_engine_exception] [exception-list:a08795b0-c85f-11ea-b1a6-c155df988a92]: version conflict, required seqNo [1074], primary term [1]. current document has seqNo [1077] and primary term [1], with { index_uuid=\"a2mgXBO6Tl2ULDq-MTs1Tw\" & shard=\"0\" & index=\".kibana-hassanabad_1\" }", "status_code": 409 } ``` Lists are the same way and flavor, they encode the _version the same way that saved objects do. To see those work you run these scripts: ```ts ./post_list.sh ./post_list_item.sh ./find_list.sh ./find_list_item.sh ``` ### Checklist - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios --- x-pack/plugins/lists/common/constants.mock.ts | 1 + .../lists/common/schemas/common/schemas.ts | 4 ++ .../elastic_query/create_es_bulk_type.ts | 2 +- .../index_es_list_item_schema.ts | 2 +- .../elastic_query/index_es_list_schema.ts | 2 +- .../update_es_list_item_schema.ts | 2 +- .../elastic_query/update_es_list_schema.ts | 2 +- .../create_endpoint_list_item_schema.ts | 28 ++++++-------- .../create_exception_list_item_schema.ts | 33 +++++++---------- .../request/create_exception_list_schema.ts | 24 ++++++------ .../request/create_list_item_schema.ts | 8 ++-- .../schemas/request/create_list_schema.ts | 6 +-- .../delete_endpoint_list_item_schema.ts | 7 +++- .../delete_exception_list_item_schema.ts | 5 ++- .../request/delete_exception_list_schema.ts | 8 +++- .../request/delete_list_item_schema.ts | 8 ++-- .../schemas/request/delete_list_schema.ts | 3 +- .../request/export_list_item_query_schema.ts | 5 ++- .../find_endpoint_list_item_schema.mock.ts | 8 ++-- .../find_endpoint_list_item_schema.test.ts | 6 +-- .../request/find_endpoint_list_item_schema.ts | 9 +---- .../find_exception_list_item_schema.mock.ts | 12 +++--- .../find_exception_list_item_schema.test.ts | 16 +++++--- .../find_exception_list_item_schema.ts | 15 ++------ .../find_exception_list_schema.mock.ts | 8 ++-- .../find_exception_list_schema.test.ts | 15 +++++--- .../request/find_exception_list_schema.ts | 15 ++------ .../request/find_list_item_schema.mock.ts | 9 ++--- .../request/find_list_item_schema.test.ts | 10 ++--- .../schemas/request/find_list_item_schema.ts | 8 ++-- .../schemas/request/find_list_schema.mock.ts | 1 + .../schemas/request/find_list_schema.test.ts | 5 +-- .../schemas/request/find_list_schema.ts | 3 +- .../request/import_list_item_query_schema.ts | 8 ++-- .../request/import_list_item_schema.ts | 3 +- .../schemas/request/patch_list_item_schema.ts | 12 +++--- .../schemas/request/patch_list_schema.ts | 10 ++--- .../request/read_endpoint_list_item_schema.ts | 9 +---- .../read_exception_list_item_schema.ts | 13 ++----- .../request/read_exception_list_schema.ts | 13 ++----- .../schemas/request/read_list_item_schema.ts | 6 +-- .../schemas/request/read_list_schema.ts | 2 +- .../update_endpoint_list_item_schema.mock.ts | 1 + .../update_endpoint_list_item_schema.ts | 28 +++++++------- .../update_exception_list_item_schema.mock.ts | 1 + .../update_exception_list_item_schema.ts | 33 +++++++---------- .../update_exception_list_schema.mock.ts | 1 + .../request/update_exception_list_schema.ts | 30 ++++++++------- .../request/update_list_item_schema.ts | 11 ++++-- .../schemas/request/update_list_schema.ts | 9 +++-- .../create_endpoint_list_schema.test.ts | 2 +- .../exception_list_item_schema.mock.ts | 36 ++++++++++++------ .../response/exception_list_item_schema.ts | 2 + .../response/exception_list_schema.mock.ts | 25 +++++++++---- .../schemas/response/exception_list_schema.ts | 2 + .../schemas/response/list_item_schema.mock.ts | 1 + .../schemas/response/list_item_schema.ts | 2 + .../schemas/response/list_schema.mock.ts | 1 + .../common/schemas/response/list_schema.ts | 2 + .../schemas/types/default_comments_array.ts | 8 +--- .../types/default_create_comments_array.ts | 4 +- .../schemas/types/default_entries_array.ts | 8 +--- .../common/schemas/types/default_namespace.ts | 4 +- .../types/default_namespace_array.test.ts | 18 ++++----- .../schemas/types/default_namespace_array.ts | 4 +- .../types/default_update_comments_array.ts | 4 +- .../schemas/types/empty_string_array.ts | 2 - .../types/non_empty_string_array.test.ts | 16 ++++---- .../schemas/types/non_empty_string_array.ts | 4 +- x-pack/plugins/lists/common/types.ts | 8 ---- .../server/routes/find_list_item_route.ts | 4 +- .../server/routes/patch_list_item_route.ts | 3 +- .../lists/server/routes/patch_list_route.ts | 4 +- .../routes/update_endpoint_list_item_route.ts | 2 + .../update_exception_list_item_route.ts | 2 + .../routes/update_exception_list_route.ts | 2 + .../server/routes/update_list_item_route.ts | 3 +- .../lists/server/routes/update_list_route.ts | 4 +- .../updates/simple_update.json | 2 +- .../exception_lists/exception_list_client.ts | 6 +++ .../exception_list_client_types.ts | 4 ++ .../exception_lists/update_exception_list.ts | 6 +++ .../update_exception_list_item.ts | 6 +++ .../server/services/exception_lists/utils.ts | 8 ++++ .../server/services/items/create_list_item.ts | 2 + .../server/services/items/find_list_item.ts | 7 +++- .../server/services/items/get_list_item.ts | 7 +++- .../services/items/update_list_item.mock.ts | 1 + .../server/services/items/update_list_item.ts | 7 ++++ .../server/services/lists/create_list.ts | 2 + .../lists/server/services/lists/find_list.ts | 7 +++- .../lists/server/services/lists/get_list.ts | 7 +++- .../server/services/lists/list_client.ts | 4 ++ .../services/lists/list_client_types.ts | 3 ++ .../server/services/lists/update_list.mock.ts | 1 + .../server/services/lists/update_list.ts | 7 ++++ .../server/services/utils/decode_version.ts | 37 +++++++++++++++++++ .../services/utils/encode_hit_version.ts | 27 ++++++++++++++ .../utils/transform_elastic_to_list.ts | 3 ++ .../utils/transform_elastic_to_list_item.ts | 2 + .../schemas/types/default_actions_array.ts | 6 +-- .../schemas/types/default_boolean_false.ts | 4 +- .../schemas/types/default_boolean_true.ts | 4 +- .../schemas/types/default_empty_string.ts | 4 +- .../schemas/types/default_export_file_name.ts | 4 +- .../schemas/types/default_from_string.ts | 4 +- .../schemas/types/default_interval_string.ts | 4 +- .../schemas/types/default_language_string.ts | 4 +- .../types/default_max_signals_number.ts | 8 +--- .../schemas/types/default_page.ts | 4 +- .../schemas/types/default_per_page.ts | 4 +- .../types/default_risk_score_mapping_array.ts | 8 ++-- .../types/default_severity_mapping_array.ts | 8 ++-- .../schemas/types/default_string_array.ts | 7 ++-- .../types/default_string_boolean_false.ts | 2 +- .../schemas/types/default_threat_array.ts | 4 +- .../schemas/types/default_throttle_null.ts | 4 +- .../schemas/types/default_to_string.ts | 4 +- .../schemas/types/default_uuid.ts | 4 +- .../schemas/types/default_version_number.ts | 4 +- .../schemas/types/lists_default_array.test.ts | 4 +- .../schemas/types/lists_default_array.ts | 4 +- .../schemas/types/only_false_allowed.ts | 4 +- .../schemas/types/positive_integer.ts | 2 - .../positive_integer_greater_than_zero.ts | 2 - .../schemas/types/references_default_array.ts | 4 +- .../components/exceptions/helpers.test.tsx | 16 ++++---- .../exception_item/exception_details.test.tsx | 8 ++-- 128 files changed, 517 insertions(+), 435 deletions(-) create mode 100644 x-pack/plugins/lists/server/services/utils/decode_version.ts create mode 100644 x-pack/plugins/lists/server/services/utils/encode_hit_version.ts diff --git a/x-pack/plugins/lists/common/constants.mock.ts b/x-pack/plugins/lists/common/constants.mock.ts index 4924ba24426af..6ed1d19611c68 100644 --- a/x-pack/plugins/lists/common/constants.mock.ts +++ b/x-pack/plugins/lists/common/constants.mock.ts @@ -60,3 +60,4 @@ export const TAGS = []; export const COMMENTS = []; export const FILTER = 'name:Nicolas Bourbaki'; export const CURSOR = 'c29tZXN0cmluZ2ZvcnlvdQ=='; +export const _VERSION = 'WzI5NywxXQ=='; diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts index 6199a5f16f109..8f1666bb542d9 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -307,3 +307,7 @@ export type Deserializer = t.TypeOf; export const deserializerOrUndefined = t.union([deserializer, t.undefined]); export type DeserializerOrUndefined = t.TypeOf; + +export const _version = t.string; +export const _versionOrUndefined = t.union([_version, t.undefined]); +export type _VersionOrUndefined = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.ts b/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.ts index 4a825382c06e4..3104ee27c57de 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.ts @@ -14,4 +14,4 @@ export const createEsBulkTypeSchema = t.exact( }) ); -export type CreateEsBulkTypeSchema = t.TypeOf; +export type CreateEsBulkTypeSchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.ts index 006600ee5b7fd..8dc5a376d1495 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.ts @@ -38,4 +38,4 @@ export const indexEsListItemSchema = t.intersection([ esDataTypeUnion, ]); -export type IndexEsListItemSchema = t.TypeOf; +export type IndexEsListItemSchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.ts index fd1018bc46a8d..3ee598291149f 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.ts @@ -38,4 +38,4 @@ export const indexEsListSchema = t.exact( }) ); -export type IndexEsListSchema = t.TypeOf; +export type IndexEsListSchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts index e4cf46bc39429..20187de535a8e 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts @@ -21,4 +21,4 @@ export const updateEsListItemSchema = t.intersection([ esDataTypeUnion, ]); -export type UpdateEsListItemSchema = t.TypeOf; +export type UpdateEsListItemSchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts index 8f23f3744e563..80b9733908d39 100644 --- a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts @@ -26,4 +26,4 @@ export const updateEsListSchema = t.exact( }) ); -export type UpdateEsListSchema = t.TypeOf; +export type UpdateEsListSchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts index 5311c7a43cdb5..3f0e1a12894d4 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts @@ -19,7 +19,7 @@ import { name, tags, } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { CreateCommentsArray, DefaultCreateCommentsArray, DefaultEntryArray } from '../types'; import { EntriesArray } from '../types/entries'; import { DefaultUuid } from '../../siem_common_deps'; @@ -44,20 +44,16 @@ export const createEndpointListItemSchema = t.intersection([ ), ]); -export type CreateEndpointListItemSchemaPartial = Identity< - t.TypeOf ->; -export type CreateEndpointListItemSchema = RequiredKeepUndefined< - t.TypeOf ->; +export type CreateEndpointListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type CreateEndpointListItemSchemaDecoded = Identity< - Omit & { - _tags: _Tags; - comments: CreateCommentsArray; - tags: Tags; - item_id: ItemId; - entries: EntriesArray; - } ->; +export type CreateEndpointListItemSchemaDecoded = Omit< + RequiredKeepUndefined>, + '_tags' | 'tags' | 'item_id' | 'entries' | 'comments' +> & { + _tags: _Tags; + comments: CreateCommentsArray; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; +}; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts index 4b7db3eee35bc..c2ccf18ed8720 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts @@ -21,7 +21,7 @@ import { namespace_type, tags, } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { CreateCommentsArray, DefaultCreateCommentsArray, @@ -53,24 +53,17 @@ export const createExceptionListItemSchema = t.intersection([ ), ]); -export type CreateExceptionListItemSchemaPartial = Identity< - t.TypeOf ->; -export type CreateExceptionListItemSchema = RequiredKeepUndefined< - t.TypeOf ->; +export type CreateExceptionListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type CreateExceptionListItemSchemaDecoded = Identity< - Omit< - CreateExceptionListItemSchema, - '_tags' | 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' - > & { - _tags: _Tags; - comments: CreateCommentsArray; - tags: Tags; - item_id: ItemId; - entries: EntriesArray; - namespace_type: NamespaceType; - } ->; +export type CreateExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined>, + '_tags' | 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' +> & { + _tags: _Tags; + comments: CreateCommentsArray; + tags: Tags; + item_id: ItemId; + entries: EntriesArray; + namespace_type: NamespaceType; +}; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts index 66cca4ab9ca53..8f714760621ff 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts @@ -20,7 +20,7 @@ import { namespace_type, tags, } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { DefaultUuid } from '../../siem_common_deps'; import { NamespaceType } from '../types'; @@ -43,17 +43,15 @@ export const createExceptionListSchema = t.intersection([ ), ]); -export type CreateExceptionListSchemaPartial = Identity>; -export type CreateExceptionListSchema = RequiredKeepUndefined< - t.TypeOf ->; +export type CreateExceptionListSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type CreateExceptionListSchemaDecoded = Identity< - Omit & { - _tags: _Tags; - tags: Tags; - list_id: ListId; - namespace_type: NamespaceType; - } ->; +export type CreateExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined>, + '_tags' | 'tags' | 'list_id' | 'namespace_type' +> & { + _tags: _Tags; + tags: Tags; + list_id: ListId; + namespace_type: NamespaceType; +}; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts index 6d16f2074864c..351eae48a638d 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { id, list_id, meta, value } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; export const createListItemSchema = t.intersection([ t.exact( @@ -21,5 +21,7 @@ export const createListItemSchema = t.intersection([ t.exact(t.partial({ id, meta })), ]); -export type CreateListItemSchemaPartial = Identity>; -export type CreateListItemSchema = RequiredKeepUndefined>; +export type CreateListItemSchema = t.OutputOf; +export type CreateListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts index fcf4465f88c8d..38d6167ea63f3 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { description, deserializer, id, meta, name, serializer, type } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; export const createListSchema = t.intersection([ t.exact( @@ -20,5 +20,5 @@ export const createListSchema = t.intersection([ t.exact(t.partial({ deserializer, id, meta, serializer })), ]); -export type CreateListSchemaPartial = Identity>; -export type CreateListSchema = RequiredKeepUndefined>; +export type CreateListSchema = t.OutputOf; +export type CreateListSchemaDecoded = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_endpoint_list_item_schema.ts index 311af3a4c0437..5af5bcd17e744 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_endpoint_list_item_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { id, item_id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const deleteEndpointListItemSchema = t.exact( t.partial({ @@ -17,7 +18,9 @@ export const deleteEndpointListItemSchema = t.exact( }) ); -export type DeleteEndpointListItemSchema = t.TypeOf; +export type DeleteEndpointListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type DeleteEndpointListItemSchemaDecoded = DeleteEndpointListItemSchema; +export type DeleteEndpointListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts index 909960c9fffc0..da6516f4b6fe4 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_item_schema.ts @@ -10,6 +10,7 @@ import * as t from 'io-ts'; import { id, item_id, namespace_type } from '../common/schemas'; import { NamespaceType } from '../types'; +import { RequiredKeepUndefined } from '../../types'; export const deleteExceptionListItemSchema = t.exact( t.partial({ @@ -19,11 +20,11 @@ export const deleteExceptionListItemSchema = t.exact( }) ); -export type DeleteExceptionListItemSchema = t.TypeOf; +export type DeleteExceptionListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. export type DeleteExceptionListItemSchemaDecoded = Omit< - DeleteExceptionListItemSchema, + RequiredKeepUndefined>, 'namespace_type' > & { namespace_type: NamespaceType; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts index 3bf5e7a4d0782..0911a9342f7a9 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_exception_list_schema.ts @@ -10,6 +10,7 @@ import * as t from 'io-ts'; import { id, list_id, namespace_type } from '../common/schemas'; import { NamespaceType } from '../types'; +import { RequiredKeepUndefined } from '../../types'; export const deleteExceptionListSchema = t.exact( t.partial({ @@ -19,9 +20,12 @@ export const deleteExceptionListSchema = t.exact( }) ); -export type DeleteExceptionListSchema = t.TypeOf; +export type DeleteExceptionListSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type DeleteExceptionListSchemaDecoded = Omit & { +export type DeleteExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined>, + 'namespace_type' +> & { namespace_type: NamespaceType; }; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts index 91887395e747d..5e2425271c463 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { id, list_id, valueOrUndefined } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; export const deleteListItemSchema = t.intersection([ t.exact( @@ -20,5 +20,7 @@ export const deleteListItemSchema = t.intersection([ t.exact(t.partial({ id, list_id })), ]); -export type DeleteListItemSchemaPartial = Identity>; -export type DeleteListItemSchema = RequiredKeepUndefined>; +export type DeleteListItemSchema = t.OutputOf; +export type DeleteListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_list_schema.ts index 6f6fc7a9ea33c..830e7fe695d1d 100644 --- a/x-pack/plugins/lists/common/schemas/request/delete_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/delete_list_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const deleteListSchema = t.exact( t.type({ @@ -16,5 +17,5 @@ export const deleteListSchema = t.exact( }) ); -export type DeleteListSchema = t.TypeOf; +export type DeleteListSchema = RequiredKeepUndefined>; export type DeleteListSchemaEncoded = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts index 58092ffc563b1..8d14f015d3805 100644 --- a/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { list_id } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const exportListItemQuerySchema = t.exact( t.type({ @@ -17,5 +18,7 @@ export const exportListItemQuerySchema = t.exact( }) ); -export type ExportListItemQuerySchema = t.TypeOf; +export type ExportListItemQuerySchema = RequiredKeepUndefined< + t.TypeOf +>; export type ExportListItemQuerySchemaEncoded = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.mock.ts index bff55dedf3064..469936eae96c9 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.mock.ts @@ -7,11 +7,11 @@ import { FILTER } from '../../constants.mock'; import { - FindEndpointListItemSchemaPartial, - FindEndpointListItemSchemaPartialDecoded, + FindEndpointListItemSchema, + FindEndpointListItemSchemaDecoded, } from './find_endpoint_list_item_schema'; -export const getFindEndpointListItemSchemaMock = (): FindEndpointListItemSchemaPartial => ({ +export const getFindEndpointListItemSchemaMock = (): FindEndpointListItemSchema => ({ filter: FILTER, page: '1', per_page: '25', @@ -19,7 +19,7 @@ export const getFindEndpointListItemSchemaMock = (): FindEndpointListItemSchemaP sort_order: undefined, }); -export const getFindEndpointListItemSchemaDecodedMock = (): FindEndpointListItemSchemaPartialDecoded => ({ +export const getFindEndpointListItemSchemaDecodedMock = (): FindEndpointListItemSchemaDecoded => ({ filter: FILTER, page: 1, per_page: 25, diff --git a/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.test.ts index f9eeaa33230f9..8249b1e2d49c2 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.test.ts @@ -14,7 +14,7 @@ import { getFindEndpointListItemSchemaMock, } from './find_endpoint_list_item_schema.mock'; import { - FindEndpointListItemSchemaPartial, + FindEndpointListItemSchema, findEndpointListItemSchema, } from './find_endpoint_list_item_schema'; @@ -29,7 +29,7 @@ describe('find_endpoint_list_item_schema', () => { }); test('it should validate and empty object since everything is optional and has defaults', () => { - const payload: FindEndpointListItemSchemaPartial = {}; + const payload: FindEndpointListItemSchema = {}; const decoded = findEndpointListItemSchema.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); @@ -98,7 +98,7 @@ describe('find_endpoint_list_item_schema', () => { }); test('it should not allow an extra key to be sent in', () => { - const payload: FindEndpointListItemSchemaPartial & { + const payload: FindEndpointListItemSchema & { extraKey: string; } = { ...getFindEndpointListItemSchemaMock(), extraKey: 'some new value' }; const decoded = findEndpointListItemSchema.decode(payload); diff --git a/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.ts index c9ee46994d720..bc839ce1346f3 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_endpoint_list_item_schema.ts @@ -22,16 +22,9 @@ export const findEndpointListItemSchema = t.exact( }) ); -export type FindEndpointListItemSchemaPartial = t.OutputOf; - -// This type is used after a decode since some things are defaults after a decode. -export type FindEndpointListItemSchemaPartialDecoded = t.TypeOf; +export type FindEndpointListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. export type FindEndpointListItemSchemaDecoded = RequiredKeepUndefined< - FindEndpointListItemSchemaPartialDecoded ->; - -export type FindEndpointListItemSchema = RequiredKeepUndefined< t.TypeOf >; diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.mock.ts index f22e6685fe0ac..d2733531eada4 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.mock.ts @@ -7,11 +7,11 @@ import { FILTER, LIST_ID, NAMESPACE_TYPE } from '../../constants.mock'; import { - FindExceptionListItemSchemaPartial, - FindExceptionListItemSchemaPartialDecoded, + FindExceptionListItemSchema, + FindExceptionListItemSchemaDecoded, } from './find_exception_list_item_schema'; -export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchemaPartial => ({ +export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchema => ({ filter: FILTER, list_id: LIST_ID, namespace_type: NAMESPACE_TYPE, @@ -21,7 +21,7 @@ export const getFindExceptionListItemSchemaMock = (): FindExceptionListItemSchem sort_order: undefined, }); -export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListItemSchemaPartial => ({ +export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListItemSchema => ({ filter: 'name:Sofia Kovalevskaya,name:Hypatia,name:Sophie Germain', list_id: 'list-1,list-2,list-3', namespace_type: 'single,single,agnostic', @@ -31,7 +31,7 @@ export const getFindExceptionListItemSchemaMultipleMock = (): FindExceptionListI sort_order: undefined, }); -export const getFindExceptionListItemSchemaDecodedMock = (): FindExceptionListItemSchemaPartialDecoded => ({ +export const getFindExceptionListItemSchemaDecodedMock = (): FindExceptionListItemSchemaDecoded => ({ filter: [FILTER], list_id: [LIST_ID], namespace_type: [NAMESPACE_TYPE], @@ -41,7 +41,7 @@ export const getFindExceptionListItemSchemaDecodedMock = (): FindExceptionListIt sort_order: undefined, }); -export const getFindExceptionListItemSchemaDecodedMultipleMock = (): FindExceptionListItemSchemaPartialDecoded => ({ +export const getFindExceptionListItemSchemaDecodedMultipleMock = (): FindExceptionListItemSchemaDecoded => ({ filter: ['name:Sofia Kovalevskaya', 'name:Hypatia', 'name:Sophie Germain'], list_id: ['list-1', 'list-2', 'list-3'], namespace_type: ['single', 'single', 'agnostic'], diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.test.ts index ba64bb434d50b..f402f22b093ad 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.test.ts @@ -17,8 +17,8 @@ import { getFindExceptionListItemSchemaMultipleMock, } from './find_exception_list_item_schema.mock'; import { - FindExceptionListItemSchemaPartial, - FindExceptionListItemSchemaPartialDecoded, + FindExceptionListItemSchema, + FindExceptionListItemSchemaDecoded, findExceptionListItemSchema, } from './find_exception_list_item_schema'; @@ -42,15 +42,19 @@ describe('find_list_item_schema', () => { }); test('it should validate just a list_id where it decodes into an array for list_id and adds a namespace_type of "single" as an array', () => { - const payload: FindExceptionListItemSchemaPartial = { list_id: LIST_ID }; + const payload: FindExceptionListItemSchema = { list_id: LIST_ID }; const decoded = findExceptionListItemSchema.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindExceptionListItemSchemaPartialDecoded = { + const expected: FindExceptionListItemSchemaDecoded = { filter: [], list_id: [LIST_ID], namespace_type: ['single'], + page: undefined, + per_page: undefined, + sort_field: undefined, + sort_order: undefined, }; expect(message.schema).toEqual(expected); }); @@ -86,7 +90,7 @@ describe('find_list_item_schema', () => { const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindExceptionListItemSchemaPartialDecoded = { + const expected: FindExceptionListItemSchemaDecoded = { ...getFindExceptionListItemSchemaDecodedMock(), filter: [], }; @@ -118,7 +122,7 @@ describe('find_list_item_schema', () => { }); test('it should not allow an extra key to be sent in', () => { - const payload: FindExceptionListItemSchemaPartial & { + const payload: FindExceptionListItemSchema & { extraKey: string; } = { ...getFindExceptionListItemSchemaMock(), extraKey: 'some new value' }; const decoded = findExceptionListItemSchema.decode(payload); diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts index aa53fa0fd912c..634c080d70b75 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_item_schema.ts @@ -36,22 +36,13 @@ export const findExceptionListItemSchema = t.intersection([ ), ]); -export type FindExceptionListItemSchemaPartial = t.OutputOf; +export type FindExceptionListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type FindExceptionListItemSchemaPartialDecoded = Omit< - t.TypeOf, +export type FindExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined>, 'namespace_type' | 'filter' > & { filter: EmptyStringArrayDecoded; namespace_type: DefaultNamespaceArrayTypeDecoded; }; - -// This type is used after a decode since some things are defaults after a decode. -export type FindExceptionListItemSchemaDecoded = RequiredKeepUndefined< - FindExceptionListItemSchemaPartialDecoded ->; - -export type FindExceptionListItemSchema = RequiredKeepUndefined< - t.TypeOf ->; diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.mock.ts index 8080d10ca451c..96f4b7e1cbd63 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.mock.ts @@ -7,11 +7,11 @@ import { FILTER, NAMESPACE_TYPE } from '../../constants.mock'; import { - FindExceptionListSchemaPartial, - FindExceptionListSchemaPartialDecoded, + FindExceptionListSchema, + FindExceptionListSchemaDecoded, } from './find_exception_list_schema'; -export const getFindExceptionListSchemaMock = (): FindExceptionListSchemaPartial => ({ +export const getFindExceptionListSchemaMock = (): FindExceptionListSchema => ({ filter: FILTER, namespace_type: NAMESPACE_TYPE, page: '1', @@ -20,7 +20,7 @@ export const getFindExceptionListSchemaMock = (): FindExceptionListSchemaPartial sort_order: undefined, }); -export const getFindExceptionListSchemaDecodedMock = (): FindExceptionListSchemaPartialDecoded => ({ +export const getFindExceptionListSchemaDecodedMock = (): FindExceptionListSchemaDecoded => ({ filter: FILTER, namespace_type: NAMESPACE_TYPE, page: 1, diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.test.ts index 42356066176d5..ef96346c732b8 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.test.ts @@ -14,8 +14,8 @@ import { getFindExceptionListSchemaMock, } from './find_exception_list_schema.mock'; import { - FindExceptionListSchemaPartial, - FindExceptionListSchemaPartialDecoded, + FindExceptionListSchema, + FindExceptionListSchemaDecoded, findExceptionListSchema, } from './find_exception_list_schema'; @@ -30,13 +30,18 @@ describe('find_exception_list_schema', () => { }); test('it should validate and empty object since everything is optional and will respond only with namespace_type filled out to be "single"', () => { - const payload: FindExceptionListSchemaPartial = {}; + const payload: FindExceptionListSchema = {}; const decoded = findExceptionListSchema.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindExceptionListSchemaPartialDecoded = { + const expected: FindExceptionListSchemaDecoded = { + filter: undefined, namespace_type: 'single', + page: undefined, + per_page: undefined, + sort_field: undefined, + sort_order: undefined, }; expect(message.schema).toEqual(expected); }); @@ -102,7 +107,7 @@ describe('find_exception_list_schema', () => { }); test('it should not allow an extra key to be sent in', () => { - const payload: FindExceptionListSchemaPartial & { + const payload: FindExceptionListSchema & { extraKey: string; } = { ...getFindExceptionListSchemaMock(), extraKey: 'some new value' }; const decoded = findExceptionListSchema.decode(payload); diff --git a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts index 4fa9d2e42c5d1..7ce01c79bbe42 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_exception_list_schema.ts @@ -24,21 +24,12 @@ export const findExceptionListSchema = t.exact( }) ); -export type FindExceptionListSchemaPartial = t.OutputOf; +export type FindExceptionListSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type FindExceptionListSchemaPartialDecoded = Omit< - t.TypeOf, +export type FindExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined>, 'namespace_type' > & { namespace_type: NamespaceType; }; - -// This type is used after a decode since some things are defaults after a decode. -export type FindExceptionListSchemaDecoded = RequiredKeepUndefined< - FindExceptionListSchemaPartialDecoded ->; - -export type FindExceptionListSchema = RequiredKeepUndefined< - t.TypeOf ->; diff --git a/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.mock.ts index a1e91f6acd264..ccde93eec4580 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.mock.ts @@ -6,12 +6,9 @@ import { CURSOR, FILTER, LIST_ID } from '../../constants.mock'; -import { - FindListItemSchemaPartial, - FindListItemSchemaPartialDecoded, -} from './find_list_item_schema'; +import { FindListItemSchema, FindListItemSchemaDecoded } from './find_list_item_schema'; -export const getFindListItemSchemaMock = (): FindListItemSchemaPartial => ({ +export const getFindListItemSchemaMock = (): FindListItemSchema => ({ cursor: CURSOR, filter: FILTER, list_id: LIST_ID, @@ -21,7 +18,7 @@ export const getFindListItemSchemaMock = (): FindListItemSchemaPartial => ({ sort_order: undefined, }); -export const getFindListItemSchemaDecodedMock = (): FindListItemSchemaPartialDecoded => ({ +export const getFindListItemSchemaDecodedMock = (): FindListItemSchemaDecoded => ({ cursor: CURSOR, filter: FILTER, list_id: LIST_ID, diff --git a/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.test.ts index 42803fffc53c2..59d4b4485b578 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.test.ts @@ -15,8 +15,8 @@ import { getFindListItemSchemaMock, } from './find_list_item_schema.mock'; import { - FindListItemSchemaPartial, - FindListItemSchemaPartialDecoded, + FindListItemSchema, + FindListItemSchemaDecoded, findListItemSchema, } from './find_list_item_schema'; @@ -31,12 +31,12 @@ describe('find_list_item_schema', () => { }); test('it should validate just a list_id where it decodes into an array for list_id and adds a namespace_type of "single"', () => { - const payload: FindListItemSchemaPartial = { list_id: LIST_ID }; + const payload: FindListItemSchema = { list_id: LIST_ID }; const decoded = findListItemSchema.decode(payload); const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindListItemSchemaPartialDecoded = { + const expected: FindListItemSchemaDecoded = { cursor: undefined, filter: undefined, list_id: LIST_ID, @@ -97,7 +97,7 @@ describe('find_list_item_schema', () => { }); test('it should not allow an extra key to be sent in', () => { - const payload: FindListItemSchemaPartial & { + const payload: FindListItemSchema & { extraKey: string; } = { ...getFindListItemSchemaMock(), extraKey: 'some new value' }; const decoded = findListItemSchema.decode(payload); diff --git a/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.ts index bbd3c7b5ec642..ba3dfc6ee33ec 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_list_item_schema.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { cursor, filter, list_id, sort_field, sort_order } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { StringToPositiveNumber } from '../types/string_to_positive_number'; export const findListItemSchema = t.intersection([ @@ -26,9 +26,7 @@ export const findListItemSchema = t.intersection([ ), ]); -export type FindListItemSchemaPartial = Identity>; +export type FindListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type FindListItemSchemaPartialDecoded = RequiredKeepUndefined< - t.TypeOf ->; +export type FindListItemSchemaDecoded = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/request/find_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/find_list_schema.mock.ts index dcb18dac8cfb6..bb9e15a439b3b 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_list_schema.mock.ts @@ -17,6 +17,7 @@ export const getFindListSchemaMock = (): FindListSchemaEncoded => ({ }); export const getFindListSchemaDecodedMock = (): FindListSchema => ({ + cursor: undefined, filter: FILTER, page: 1, per_page: 25, diff --git a/x-pack/plugins/lists/common/schemas/request/find_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/find_list_schema.test.ts index a343fb4b08bfc..63f29a64b4bf9 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_list_schema.test.ts @@ -10,7 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { exactCheck, foldLeftRight, getPaths } from '../../siem_common_deps'; import { getFindListSchemaDecodedMock, getFindListSchemaMock } from './find_list_schema.mock'; -import { FindListSchema, FindListSchemaEncoded, findListSchema } from './find_list_schema'; +import { FindListSchemaEncoded, findListSchema } from './find_list_schema'; describe('find_list_schema', () => { test('it should validate a typical find item request', () => { @@ -28,8 +28,7 @@ describe('find_list_schema', () => { const checked = exactCheck(payload, decoded); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([]); - const expected: FindListSchema = {}; - expect(message.schema).toEqual(expected); + expect(message.schema).toEqual(payload); }); test('it should validate with page missing', () => { diff --git a/x-pack/plugins/lists/common/schemas/request/find_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/find_list_schema.ts index 212232f6bc9c1..e5020cc8eff84 100644 --- a/x-pack/plugins/lists/common/schemas/request/find_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/find_list_schema.ts @@ -10,6 +10,7 @@ import * as t from 'io-ts'; import { cursor, filter, sort_field, sort_order } from '../common/schemas'; import { StringToPositiveNumber } from '../types/string_to_positive_number'; +import { RequiredKeepUndefined } from '../../types'; export const findListSchema = t.exact( t.partial({ @@ -22,5 +23,5 @@ export const findListSchema = t.exact( }) ); -export type FindListSchema = t.TypeOf; +export type FindListSchema = RequiredKeepUndefined>; export type FindListSchemaEncoded = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts index 2c671466702e0..e45f77ca18ae1 100644 --- a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts @@ -8,14 +8,14 @@ import * as t from 'io-ts'; +import { RequiredKeepUndefined } from '../../types'; import { deserializer, list_id, serializer, type } from '../common/schemas'; -import { Identity } from '../../types'; export const importListItemQuerySchema = t.exact( t.partial({ deserializer, list_id, serializer, type }) ); -export type ImportListItemQuerySchemaPartial = Identity>; - -export type ImportListItemQuerySchema = t.TypeOf; +export type ImportListItemQuerySchema = RequiredKeepUndefined< + t.TypeOf +>; export type ImportListItemQuerySchemaEncoded = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.ts index 7370eecf690c7..671aeda757eff 100644 --- a/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { file } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const importListItemSchema = t.exact( t.type({ @@ -16,5 +17,5 @@ export const importListItemSchema = t.exact( }) ); -export type ImportListItemSchema = t.TypeOf; +export type ImportListItemSchema = RequiredKeepUndefined>; export type ImportListItemSchemaEncoded = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts index 2016069f32fb3..9c5284c15ca99 100644 --- a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts @@ -8,8 +8,8 @@ import * as t from 'io-ts'; -import { id, meta, value } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { _version, id, meta, value } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const patchListItemSchema = t.intersection([ t.exact( @@ -17,8 +17,10 @@ export const patchListItemSchema = t.intersection([ id, }) ), - t.exact(t.partial({ meta, value })), + t.exact(t.partial({ _version, meta, value })), ]); -export type PatchListItemSchemaPartial = Identity>; -export type PatchListItemSchema = RequiredKeepUndefined>; +export type PatchListItemSchema = t.OutputOf; +export type PatchListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts index 653a42dc91653..e0cd1571afc81 100644 --- a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts @@ -8,8 +8,8 @@ import * as t from 'io-ts'; -import { description, id, meta, name } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { _version, description, id, meta, name } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const patchListSchema = t.intersection([ t.exact( @@ -17,8 +17,8 @@ export const patchListSchema = t.intersection([ id, }) ), - t.exact(t.partial({ description, meta, name })), + t.exact(t.partial({ _version, description, meta, name })), ]); -export type PatchListSchemaPartial = Identity>; -export type PatchListSchema = RequiredKeepUndefined>>; +export type PatchListSchema = t.OutputOf; +export type PatchListSchemaDecoded = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/request/read_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_endpoint_list_item_schema.ts index 22750f5db6a1d..d6c54e289effe 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_endpoint_list_item_schema.ts @@ -18,14 +18,9 @@ export const readEndpointListItemSchema = t.exact( }) ); -export type ReadEndpointListItemSchemaPartial = t.TypeOf; - -// This type is used after a decode since some things are defaults after a decode. -export type ReadEndpointListItemSchemaPartialDecoded = ReadEndpointListItemSchemaPartial; +export type ReadEndpointListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. export type ReadEndpointListItemSchemaDecoded = RequiredKeepUndefined< - ReadEndpointListItemSchemaPartialDecoded + t.TypeOf >; - -export type ReadEndpointListItemSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts index d8864a6fc66e5..a2ba8126c7788 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_exception_list_item_schema.ts @@ -20,19 +20,12 @@ export const readExceptionListItemSchema = t.exact( }) ); -export type ReadExceptionListItemSchemaPartial = t.TypeOf; +export type ReadExceptionListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type ReadExceptionListItemSchemaPartialDecoded = Omit< - ReadExceptionListItemSchemaPartial, +export type ReadExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined>, 'namespace_type' > & { namespace_type: NamespaceType; }; - -// This type is used after a decode since some things are defaults after a decode. -export type ReadExceptionListItemSchemaDecoded = RequiredKeepUndefined< - ReadExceptionListItemSchemaPartialDecoded ->; - -export type ReadExceptionListItemSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts index 613fb22a99d61..f22eca6a8ab15 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_exception_list_schema.ts @@ -20,19 +20,12 @@ export const readExceptionListSchema = t.exact( }) ); -export type ReadExceptionListSchemaPartial = t.TypeOf; +export type ReadExceptionListSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type ReadExceptionListSchemaPartialDecoded = Omit< - ReadExceptionListSchemaPartial, +export type ReadExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined>, 'namespace_type' > & { namespace_type: NamespaceType; }; - -// This type is used after a decode since some things are defaults after a decode. -export type ReadExceptionListSchemaDecoded = RequiredKeepUndefined< - ReadExceptionListSchemaPartialDecoded ->; - -export type ReadExceptionListSchema = RequiredKeepUndefined; diff --git a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts index 394c1f1e2289a..063f430aa9cea 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts @@ -9,9 +9,9 @@ import * as t from 'io-ts'; import { id, list_id, value } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; export const readListItemSchema = t.exact(t.partial({ id, list_id, value })); -export type ReadListItemSchemaPartial = Identity>; -export type ReadListItemSchema = RequiredKeepUndefined>; +export type ReadListItemSchema = t.OutputOf; +export type ReadListItemSchemaDecoded = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/request/read_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_list_schema.ts index 8803346709c31..e395875462cb4 100644 --- a/x-pack/plugins/lists/common/schemas/request/read_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/read_list_schema.ts @@ -16,4 +16,4 @@ export const readListSchema = t.exact( }) ); -export type ReadListSchema = t.TypeOf; +export type ReadListSchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts index 30bbbe2d22ea4..0847389dac922 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts @@ -21,6 +21,7 @@ import { UpdateEndpointListItemSchema } from './update_endpoint_list_item_schema export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSchema => ({ _tags: _TAGS, + _version: undefined, comments: COMMENTS, description: DESCRIPTION, entries: ENTRIES, diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts index dbe38f6d468e2..4430aa98b8e3d 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts @@ -12,6 +12,7 @@ import { Tags, _Tags, _tags, + _version, description, exceptionListItemType, id, @@ -19,7 +20,7 @@ import { name, tags, } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { DefaultEntryArray, DefaultUpdateCommentsArray, @@ -38,6 +39,7 @@ export const updateEndpointListItemSchema = t.intersection([ t.exact( t.partial({ _tags, // defaults to empty array if not set during decode + _version, // defaults to undefined if not set during decode comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode entries: DefaultEntryArray, // defaults to empty array if not set during decode id, // defaults to undefined if not set during decode @@ -48,19 +50,15 @@ export const updateEndpointListItemSchema = t.intersection([ ), ]); -export type UpdateEndpointListItemSchemaPartial = Identity< - t.TypeOf ->; -export type UpdateEndpointListItemSchema = RequiredKeepUndefined< - t.TypeOf ->; +export type UpdateEndpointListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type UpdateEndpointListItemSchemaDecoded = Identity< - Omit & { - _tags: _Tags; - comments: UpdateCommentsArray; - tags: Tags; - entries: EntriesArray; - } ->; +export type UpdateEndpointListItemSchemaDecoded = Omit< + RequiredKeepUndefined>, + '_tags' | 'tags' | 'entries' | 'comments' +> & { + _tags: _Tags; + comments: UpdateCommentsArray; + tags: Tags; + entries: EntriesArray; +}; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts index e8936f0bdc6d4..90d70c273f490 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts @@ -22,6 +22,7 @@ import { UpdateExceptionListItemSchema } from './update_exception_list_item_sche export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ _tags: _TAGS, + _version: undefined, comments: COMMENTS, description: DESCRIPTION, entries: ENTRIES, diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts index 20a63e0fc7dac..9e0a1759fc9f4 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts @@ -12,6 +12,7 @@ import { Tags, _Tags, _tags, + _version, description, exceptionListItemType, id, @@ -20,7 +21,7 @@ import { namespace_type, tags, } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { DefaultEntryArray, DefaultUpdateCommentsArray, @@ -40,6 +41,7 @@ export const updateExceptionListItemSchema = t.intersection([ t.exact( t.partial({ _tags, // defaults to empty array if not set during decode + _version, // defaults to undefined if not set during decode comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode entries: DefaultEntryArray, // defaults to empty array if not set during decode id, // defaults to undefined if not set during decode @@ -51,23 +53,16 @@ export const updateExceptionListItemSchema = t.intersection([ ), ]); -export type UpdateExceptionListItemSchemaPartial = Identity< - t.TypeOf ->; -export type UpdateExceptionListItemSchema = RequiredKeepUndefined< - t.TypeOf ->; +export type UpdateExceptionListItemSchema = t.OutputOf; // This type is used after a decode since some things are defaults after a decode. -export type UpdateExceptionListItemSchemaDecoded = Identity< - Omit< - UpdateExceptionListItemSchema, - '_tags' | 'tags' | 'entries' | 'namespace_type' | 'comments' - > & { - _tags: _Tags; - comments: UpdateCommentsArray; - tags: Tags; - entries: EntriesArray; - namespace_type: NamespaceType; - } ->; +export type UpdateExceptionListItemSchemaDecoded = Omit< + RequiredKeepUndefined>, + '_tags' | 'tags' | 'entries' | 'namespace_type' | 'comments' +> & { + _tags: _Tags; + comments: UpdateCommentsArray; + tags: Tags; + entries: EntriesArray; + namespace_type: NamespaceType; +}; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts index 48a8baf9aea16..22af29e6af0b7 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts @@ -10,6 +10,7 @@ import { UpdateExceptionListSchema } from './update_exception_list_schema'; export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ _tags: _TAGS, + _version: undefined, description: DESCRIPTION, id: ID, list_id: LIST_ID, diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts index 0b5f3a8a01794..5d7294ae27af2 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts @@ -12,14 +12,17 @@ import { Tags, _Tags, _tags, + _version, description, exceptionListType, + id, + list_id, meta, name, namespace_type, tags, } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { RequiredKeepUndefined } from '../../types'; import { NamespaceType } from '../types'; export const updateExceptionListSchema = t.intersection([ @@ -33,8 +36,9 @@ export const updateExceptionListSchema = t.intersection([ t.exact( t.partial({ _tags, // defaults to empty array if not set during decode - id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode - list_id: t.union([t.string, t.undefined]), // defaults to undefined if not set during decode + _version, // defaults to undefined if not set during decode + id, // defaults to undefined if not set during decode + list_id, // defaults to undefined if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode tags, // defaults to empty array if not set during decode @@ -42,16 +46,14 @@ export const updateExceptionListSchema = t.intersection([ ), ]); -export type UpdateExceptionListSchemaPartial = Identity>; -export type UpdateExceptionListSchema = RequiredKeepUndefined< - t.TypeOf ->; +export type UpdateExceptionListSchema = t.OutputOf; // This type is used after a decode since the arrays turn into defaults of empty arrays. -export type UpdateExceptionListSchemaDecoded = Identity< - Omit & { - _tags: _Tags; - tags: Tags; - namespace_type: NamespaceType; - } ->; +export type UpdateExceptionListSchemaDecoded = Omit< + RequiredKeepUndefined>, + '_tags | tags | namespace_type' +> & { + _tags: _Tags; + tags: Tags; + namespace_type: NamespaceType; +}; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts index 3a42cf28665f5..c6ed5ef0e9517 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts @@ -8,8 +8,8 @@ import * as t from 'io-ts'; -import { id, meta, value } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { _version, id, meta, value } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const updateListItemSchema = t.intersection([ t.exact( @@ -20,10 +20,13 @@ export const updateListItemSchema = t.intersection([ ), t.exact( t.partial({ + _version, // defaults to undefined if not set during decode meta, // defaults to undefined if not set during decode }) ), ]); -export type UpdateListItemSchemaPartial = Identity>; -export type UpdateListItemSchema = RequiredKeepUndefined>; +export type UpdateListItemSchema = t.OutputOf; +export type UpdateListItemSchemaDecoded = RequiredKeepUndefined< + t.TypeOf +>; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts index 4c5c8c429a14c..19a39d362c241 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts @@ -8,8 +8,8 @@ import * as t from 'io-ts'; -import { description, id, meta, name } from '../common/schemas'; -import { Identity, RequiredKeepUndefined } from '../../types'; +import { _version, description, id, meta, name } from '../common/schemas'; +import { RequiredKeepUndefined } from '../../types'; export const updateListSchema = t.intersection([ t.exact( @@ -21,10 +21,11 @@ export const updateListSchema = t.intersection([ ), t.exact( t.partial({ + _version, // defaults to undefined if not set during decode meta, // defaults to undefined if not set during decode }) ), ]); -export type UpdateListSchemaPartial = Identity>; -export type UpdateListSchema = RequiredKeepUndefined>; +export type UpdateListSchema = t.OutputOf; +export type UpdateListSchemaDecoded = RequiredKeepUndefined>; diff --git a/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts index d346ea72ca310..646cc3d97f8ee 100644 --- a/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts @@ -41,7 +41,7 @@ describe('create_endpoint_list_schema', () => { const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'invalid keys "_tags,["endpoint","process","malware","os:linux"],created_at,created_by,description,id,meta,{},name,namespace_type,tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by"', + 'invalid keys "_tags,["endpoint","process","malware","os:linux"],_version,created_at,created_by,description,id,meta,{},name,namespace_type,tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts index 9e1a88ceb28bd..c0d04c9823ca3 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts @@ -3,26 +3,38 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ENTRIES } from '../../constants.mock'; +import { + COMMENTS, + DATE_NOW, + DESCRIPTION, + ENTRIES, + ITEM_TYPE, + META, + NAME, + NAMESPACE_TYPE, + TIE_BREAKER, + USER, +} from '../../constants.mock'; import { ExceptionListItemSchema } from './exception_list_item_schema'; export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ _tags: ['endpoint', 'process', 'malware', 'os:linux'], - comments: [], - created_at: '2020-04-23T00:19:13.289Z', - created_by: 'user_name', - description: 'This is a sample endpoint type exception', + _version: undefined, + comments: COMMENTS, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, entries: ENTRIES, id: '1', item_id: 'endpoint_list_item', list_id: 'endpoint_list_id', - meta: {}, - name: 'Sample Endpoint Exception List', - namespace_type: 'single', + meta: META, + name: NAME, + namespace_type: NAMESPACE_TYPE, tags: ['user added string for a tag', 'malware'], - tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f', - type: 'simple', - updated_at: '2020-04-23T00:19:13.289Z', - updated_by: 'user_name', + tie_breaker_id: TIE_BREAKER, + type: ITEM_TYPE, + updated_at: DATE_NOW, + updated_by: USER, }); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts index c8440e9d3f3d0..54907f3f8a854 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts @@ -10,6 +10,7 @@ import * as t from 'io-ts'; import { _tags, + _versionOrUndefined, created_at, created_by, description, @@ -30,6 +31,7 @@ import { commentsArray, entriesArray } from '../types'; export const exceptionListItemSchema = t.exact( t.type({ _tags, + _version: _versionOrUndefined, comments: commentsArray, created_at, created_by, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts index 906dcf6560ee5..f790ad9544d53 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts @@ -4,23 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ +import { + DATE_NOW, + DESCRIPTION, + ENDPOINT_TYPE, + META, + TIE_BREAKER, + USER, + _VERSION, +} from '../../constants.mock'; import { ENDPOINT_LIST_ID } from '../..'; import { ExceptionListSchema } from './exception_list_schema'; - export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ _tags: ['endpoint', 'process', 'malware', 'os:linux'], - created_at: '2020-04-23T00:19:13.289Z', - created_by: 'user_name', - description: 'This is a sample endpoint type exception', + _version: _VERSION, + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, id: '1', list_id: ENDPOINT_LIST_ID, - meta: {}, + meta: META, name: 'Sample Endpoint Exception List', namespace_type: 'agnostic', tags: ['user added string for a tag', 'malware'], - tie_breaker_id: '77fd1909-6786-428a-a671-30229a719c1f', - type: 'endpoint', - updated_at: '2020-04-23T00:19:13.289Z', + tie_breaker_id: TIE_BREAKER, + type: ENDPOINT_TYPE, + updated_at: DATE_NOW, updated_by: 'user_name', }); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts index 0fb2bfca4a48f..11c23bc2ff354 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts @@ -10,6 +10,7 @@ import * as t from 'io-ts'; import { _tags, + _versionOrUndefined, created_at, created_by, description, @@ -28,6 +29,7 @@ import { export const exceptionListSchema = t.exact( t.type({ _tags, + _version: _versionOrUndefined, created_at, created_by, description, diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts index 16e8057974917..e122f6a2bbe3b 100644 --- a/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/list_item_schema.mock.ts @@ -17,6 +17,7 @@ import { } from '../../../common/constants.mock'; export const getListItemResponseMock = (): ListItemSchema => ({ + _version: undefined, created_at: DATE_NOW, created_by: USER, deserializer: undefined, diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/list_item_schema.ts index c2104aaf18b53..9ee801298f950 100644 --- a/x-pack/plugins/lists/common/schemas/response/list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/list_item_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; /* eslint-disable @typescript-eslint/camelcase */ import { + _versionOrUndefined, created_at, created_by, deserializerOrUndefined, @@ -25,6 +26,7 @@ import { export const listItemSchema = t.exact( t.type({ + _version: _versionOrUndefined, created_at, created_by, deserializer: deserializerOrUndefined, diff --git a/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts index c165c4ed8e745..339beddb00f8e 100644 --- a/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/list_schema.mock.ts @@ -17,6 +17,7 @@ import { } from '../../../common/constants.mock'; export const getListResponseMock = (): ListSchema => ({ + _version: undefined, created_at: DATE_NOW, created_by: USER, description: DESCRIPTION, diff --git a/x-pack/plugins/lists/common/schemas/response/list_schema.ts b/x-pack/plugins/lists/common/schemas/response/list_schema.ts index 1950831bee694..7e2bc202a6520 100644 --- a/x-pack/plugins/lists/common/schemas/response/list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/list_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { + _versionOrUndefined, created_at, created_by, description, @@ -25,6 +26,7 @@ import { export const listSchema = t.exact( t.type({ + _version: _versionOrUndefined, created_at, created_by, description, diff --git a/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts index e8be299246ab8..342cf8b0d7091 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_comments_array.ts @@ -9,17 +9,11 @@ import { Either } from 'fp-ts/lib/Either'; import { CommentsArray, comments } from './comments'; -export type DefaultCommentsArrayC = t.Type; - /** * Types the DefaultCommentsArray as: * - If null or undefined, then a default array of type entry will be set */ -export const DefaultCommentsArray: DefaultCommentsArrayC = new t.Type< - CommentsArray, - CommentsArray, - unknown ->( +export const DefaultCommentsArray = new t.Type( 'DefaultCommentsArray', t.array(comments).is, (input): Either => diff --git a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts index 51431b9c39850..7fd79782836e3 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_create_comments_array.ts @@ -9,13 +9,11 @@ import { Either } from 'fp-ts/lib/Either'; import { CreateCommentsArray, createComments } from './create_comments'; -export type DefaultCreateCommentsArrayC = t.Type; - /** * Types the DefaultCreateComments as: * - If null or undefined, then a default array of type entry will be set */ -export const DefaultCreateCommentsArray: DefaultCreateCommentsArrayC = new t.Type< +export const DefaultCreateCommentsArray = new t.Type< CreateCommentsArray, CreateCommentsArray, unknown diff --git a/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts b/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts index da67fb286affa..a85fdf8537f39 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_entries_array.ts @@ -9,17 +9,11 @@ import { Either } from 'fp-ts/lib/Either'; import { EntriesArray, entriesArray } from './entries'; -export type DefaultEntriesArrayC = t.Type; - /** * Types the DefaultEntriesArray as: * - If null or undefined, then a default array of type entry will be set */ -export const DefaultEntryArray: DefaultEntriesArrayC = new t.Type< - EntriesArray, - EntriesArray, - unknown ->( +export const DefaultEntryArray = new t.Type( 'DefaultEntryArray', entriesArray.is, (input): Either => diff --git a/x-pack/plugins/lists/common/schemas/types/default_namespace.ts b/x-pack/plugins/lists/common/schemas/types/default_namespace.ts index ecc45d3c84313..b61497a78aff0 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_namespace.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_namespace.ts @@ -14,12 +14,10 @@ export type NamespaceType = t.TypeOf; * Types the DefaultNamespace as: * - If null or undefined, then a default string/enumeration of "single" will be used. */ -export const DefaultNamespace = new t.Type( +export const DefaultNamespace = new t.Type( 'DefaultNamespace', namespaceType.is, (input, context): Either => input == null ? t.success('single') : namespaceType.validate(input, context), t.identity ); - -export type DefaultNamespaceC = typeof DefaultNamespace; diff --git a/x-pack/plugins/lists/common/schemas/types/default_namespace_array.test.ts b/x-pack/plugins/lists/common/schemas/types/default_namespace_array.test.ts index 055f93069950e..255c89959b610 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_namespace_array.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_namespace_array.test.ts @@ -9,11 +9,11 @@ import { left } from 'fp-ts/lib/Either'; import { foldLeftRight, getPaths } from '../../siem_common_deps'; -import { DefaultNamespaceArray, DefaultNamespaceArrayTypeEncoded } from './default_namespace_array'; +import { DefaultNamespaceArray, DefaultNamespaceArrayType } from './default_namespace_array'; describe('default_namespace_array', () => { test('it should validate "null" single item as an array with a "single" value', () => { - const payload: DefaultNamespaceArrayTypeEncoded = null; + const payload: DefaultNamespaceArrayType = null; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -33,7 +33,7 @@ describe('default_namespace_array', () => { }); test('it should validate "undefined" item as an array with a "single" value', () => { - const payload: DefaultNamespaceArrayTypeEncoded = undefined; + const payload: DefaultNamespaceArrayType = undefined; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -42,7 +42,7 @@ describe('default_namespace_array', () => { }); test('it should validate "single" as an array of a "single" value', () => { - const payload: DefaultNamespaceArrayTypeEncoded = 'single'; + const payload: DefaultNamespaceArrayType = 'single'; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -51,7 +51,7 @@ describe('default_namespace_array', () => { }); test('it should validate "agnostic" as an array of a "agnostic" value', () => { - const payload: DefaultNamespaceArrayTypeEncoded = 'agnostic'; + const payload: DefaultNamespaceArrayType = 'agnostic'; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -60,7 +60,7 @@ describe('default_namespace_array', () => { }); test('it should validate "single,agnostic" as an array of 2 values of ["single", "agnostic"] values', () => { - const payload: DefaultNamespaceArrayTypeEncoded = 'agnostic,single'; + const payload: DefaultNamespaceArrayType = 'agnostic,single'; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -69,7 +69,7 @@ describe('default_namespace_array', () => { }); test('it should validate 3 elements of "single,agnostic,single" as an array of 3 values of ["single", "agnostic", "single"] values', () => { - const payload: DefaultNamespaceArrayTypeEncoded = 'single,agnostic,single'; + const payload: DefaultNamespaceArrayType = 'single,agnostic,single'; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -78,7 +78,7 @@ describe('default_namespace_array', () => { }); test('it should validate 3 elements of "single,agnostic, single" as an array of 3 values of ["single", "agnostic", "single"] values when there are spaces', () => { - const payload: DefaultNamespaceArrayTypeEncoded = ' single, agnostic, single '; + const payload: DefaultNamespaceArrayType = ' single, agnostic, single '; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -87,7 +87,7 @@ describe('default_namespace_array', () => { }); test('it should not validate 3 elements of "single,agnostic,junk" since the 3rd value is junk', () => { - const payload: DefaultNamespaceArrayTypeEncoded = 'single,agnostic,junk'; + const payload: DefaultNamespaceArrayType = 'single,agnostic,junk'; const decoded = DefaultNamespaceArray.decode(payload); const message = pipe(decoded, foldLeftRight); diff --git a/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts b/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts index c4099a48ffbcc..b0ae85e65b22a 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_namespace_array.ts @@ -39,7 +39,5 @@ export const DefaultNamespaceArray = new t.Type< String ); -export type DefaultNamespaceC = typeof DefaultNamespaceArray; - -export type DefaultNamespaceArrayTypeEncoded = t.OutputOf; +export type DefaultNamespaceArrayType = t.OutputOf; export type DefaultNamespaceArrayTypeDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts index c2593826a6358..854b7cf7ada7e 100644 --- a/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/default_update_comments_array.ts @@ -9,13 +9,11 @@ import { Either } from 'fp-ts/lib/Either'; import { UpdateCommentsArray, updateCommentsArray } from './update_comments'; -export type DefaultUpdateCommentsArrayC = t.Type; - /** * Types the DefaultCommentsUpdate as: * - If null or undefined, then a default array of type entry will be set */ -export const DefaultUpdateCommentsArray: DefaultUpdateCommentsArrayC = new t.Type< +export const DefaultUpdateCommentsArray = new t.Type< UpdateCommentsArray, UpdateCommentsArray, unknown diff --git a/x-pack/plugins/lists/common/schemas/types/empty_string_array.ts b/x-pack/plugins/lists/common/schemas/types/empty_string_array.ts index 389dc4a410cc9..bbaa66260e76e 100644 --- a/x-pack/plugins/lists/common/schemas/types/empty_string_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/empty_string_array.ts @@ -39,7 +39,5 @@ export const EmptyStringArray = new t.Type; export type EmptyStringArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.test.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.test.ts index 6124487cdd7fb..fac088568f85e 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.test.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.test.ts @@ -9,11 +9,11 @@ import { left } from 'fp-ts/lib/Either'; import { foldLeftRight, getPaths } from '../../siem_common_deps'; -import { NonEmptyStringArray, NonEmptyStringArrayEncoded } from './non_empty_string_array'; +import { NonEmptyStringArray } from './non_empty_string_array'; describe('non_empty_string_array', () => { test('it should NOT validate "null"', () => { - const payload: NonEmptyStringArrayEncoded | null = null; + const payload: NonEmptyStringArray | null = null; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -24,7 +24,7 @@ describe('non_empty_string_array', () => { }); test('it should NOT validate "undefined"', () => { - const payload: NonEmptyStringArrayEncoded | undefined = undefined; + const payload: NonEmptyStringArray | undefined = undefined; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -35,7 +35,7 @@ describe('non_empty_string_array', () => { }); test('it should NOT validate a single value of an empty string ""', () => { - const payload: NonEmptyStringArrayEncoded = ''; + const payload: NonEmptyStringArray = ''; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -46,7 +46,7 @@ describe('non_empty_string_array', () => { }); test('it should validate a single value of "a" into an array of size 1 of ["a"]', () => { - const payload: NonEmptyStringArrayEncoded = 'a'; + const payload: NonEmptyStringArray = 'a'; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -55,7 +55,7 @@ describe('non_empty_string_array', () => { }); test('it should validate 2 values of "a,b" into an array of size 2 of ["a", "b"]', () => { - const payload: NonEmptyStringArrayEncoded = 'a,b'; + const payload: NonEmptyStringArray = 'a,b'; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -64,7 +64,7 @@ describe('non_empty_string_array', () => { }); test('it should validate 3 values of "a,b,c" into an array of size 3 of ["a", "b", "c"]', () => { - const payload: NonEmptyStringArrayEncoded = 'a,b,c'; + const payload: NonEmptyStringArray = 'a,b,c'; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); @@ -84,7 +84,7 @@ describe('non_empty_string_array', () => { }); test('it should validate 3 values of " a, b, c " into an array of size 3 of ["a", "b", "c"] even though they have spaces', () => { - const payload: NonEmptyStringArrayEncoded = ' a, b, c '; + const payload: NonEmptyStringArray = ' a, b, c '; const decoded = NonEmptyStringArray.decode(payload); const message = pipe(decoded, foldLeftRight); diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts index c4a640e7cdbad..90475f7935875 100644 --- a/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_string_array.ts @@ -35,7 +35,5 @@ export const NonEmptyStringArray = new t.Type( String ); -export type NonEmptyStringArrayC = typeof NonEmptyStringArray; - -export type NonEmptyStringArrayEncoded = t.OutputOf; +export type NonEmptyStringArray = t.OutputOf; export type NonEmptyStringArrayDecoded = t.TypeOf; diff --git a/x-pack/plugins/lists/common/types.ts b/x-pack/plugins/lists/common/types.ts index 1539c5ae01ff5..cee5567a55a6c 100644 --- a/x-pack/plugins/lists/common/types.ts +++ b/x-pack/plugins/lists/common/types.ts @@ -20,11 +20,3 @@ export type RequiredKeepUndefined = { [K in keyof T]-?: [T[K]] } extends infe ? { [K in keyof U]: U[K][0] } : never : never; - -/** - * This is just a helper to cleanup nasty intersections and unions to make them - * readable from io.ts, it's an identity that strips away the uglyness of them. - */ -export type Identity = { - [P in keyof T]: T[P]; -}; diff --git a/x-pack/plugins/lists/server/routes/find_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_list_item_route.ts index 52d534b08df2b..8a0e06aa0c7d8 100644 --- a/x-pack/plugins/lists/server/routes/find_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_item_route.ts @@ -10,7 +10,7 @@ import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/siem_common_deps'; import { - FindListItemSchemaPartialDecoded, + FindListItemSchemaDecoded, findListItemSchema, foundListItemSchema, } from '../../common/schemas'; @@ -26,7 +26,7 @@ export const findListItemRoute = (router: IRouter): void => { }, path: `${LIST_ITEM_URL}/_find`, validate: { - query: buildRouteValidation( + query: buildRouteValidation( findListItemSchema ), }, diff --git a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts index f706559dffdbd..9a74beb45bafd 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts @@ -27,9 +27,10 @@ export const patchListItemRoute = (router: IRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const { value, id, meta } = request.body; + const { value, id, meta, _version } = request.body; const lists = getListClient(context); const listItem = await lists.updateListItem({ + _version, id, meta, value, diff --git a/x-pack/plugins/lists/server/routes/patch_list_route.ts b/x-pack/plugins/lists/server/routes/patch_list_route.ts index 3a0d8714a14cd..06a76559dee9a 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_route.ts @@ -27,9 +27,9 @@ export const patchListRoute = (router: IRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const { name, description, id, meta } = request.body; + const { name, description, id, meta, _version } = request.body; const lists = getListClient(context); - const list = await lists.updateList({ description, id, meta, name }); + const list = await lists.updateList({ _version, description, id, meta, name }); if (list == null) { return siemResponse.error({ body: `list id: "${id}" found found`, diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts index 1ecf4e8a9765d..8415c64633a06 100644 --- a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts @@ -41,6 +41,7 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { meta, type, _tags, + _version, comments, entries, item_id: itemId, @@ -49,6 +50,7 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { const exceptionLists = getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateEndpointListItem({ _tags, + _version, comments, description, entries, diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index f6c7bcebedc13..2aa1e016d51ed 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -41,6 +41,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { meta, type, _tags, + _version, comments, entries, item_id: itemId, @@ -50,6 +51,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { const exceptionLists = getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateExceptionListItem({ _tags, + _version, comments, description, entries, diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index cff78614d05ba..331ec064fa663 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -36,6 +36,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { try { const { _tags, + _version, tags, name, description, @@ -54,6 +55,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { } else { const list = await exceptionLists.updateExceptionList({ _tags, + _version, description, id, listId, diff --git a/x-pack/plugins/lists/server/routes/update_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_list_item_route.ts index 3e231e319104b..0f5d11afcda09 100644 --- a/x-pack/plugins/lists/server/routes/update_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_item_route.ts @@ -27,9 +27,10 @@ export const updateListItemRoute = (router: IRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const { value, id, meta } = request.body; + const { value, id, meta, _version } = request.body; const lists = getListClient(context); const listItem = await lists.updateListItem({ + _version, id, meta, value, diff --git a/x-pack/plugins/lists/server/routes/update_list_route.ts b/x-pack/plugins/lists/server/routes/update_list_route.ts index a6d9f8329c7c8..2fae910c1b398 100644 --- a/x-pack/plugins/lists/server/routes/update_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_route.ts @@ -27,9 +27,9 @@ export const updateListRoute = (router: IRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const { name, description, id, meta } = request.body; + const { name, description, id, meta, _version } = request.body; const lists = getListClient(context); - const list = await lists.updateList({ description, id, meta, name }); + const list = await lists.updateList({ _version, description, id, meta, name }); if (list == null) { return siemResponse.error({ body: `list id: "${id}" found found`, diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json index a7fbe1ea48c02..8d07b29d7b428 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json @@ -1,5 +1,5 @@ { - "list_id": "endpoint_list", + "list_id": "simple_list", "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "endpoint", diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index 5c9607e2d956d..08b1f517036a9 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -131,6 +131,7 @@ export class ExceptionListClient { */ public updateEndpointListItem = async ({ _tags, + _version, comments, description, entries, @@ -145,6 +146,7 @@ export class ExceptionListClient { await this.createEndpointList(); return updateExceptionListItem({ _tags, + _version, comments, description, entries, @@ -198,6 +200,7 @@ export class ExceptionListClient { public updateExceptionList = async ({ _tags, + _version, id, description, listId, @@ -210,6 +213,7 @@ export class ExceptionListClient { const { savedObjectsClient, user } = this; return updateExceptionList({ _tags, + _version, description, id, listId, @@ -270,6 +274,7 @@ export class ExceptionListClient { public updateExceptionListItem = async ({ _tags, + _version, comments, description, entries, @@ -284,6 +289,7 @@ export class ExceptionListClient { const { savedObjectsClient, user } = this; return updateExceptionListItem({ _tags, + _version, comments, description, entries, diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index 89f8310281648..b972b6564bb8a 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -38,6 +38,7 @@ import { UpdateCommentsArray, _Tags, _TagsOrUndefined, + _VersionOrUndefined, } from '../../../common/schemas'; export interface ConstructorOptions { @@ -64,6 +65,7 @@ export interface CreateExceptionListOptions { export interface UpdateExceptionListOptions { _tags: _TagsOrUndefined; + _version: _VersionOrUndefined; id: IdOrUndefined; listId: ListIdOrUndefined; namespaceType: NamespaceType; @@ -130,6 +132,7 @@ export interface CreateEndpointListItemOptions { export interface UpdateExceptionListItemOptions { _tags: _TagsOrUndefined; + _version: _VersionOrUndefined; comments: UpdateCommentsArray; entries: EntriesArrayOrUndefined; id: IdOrUndefined; @@ -144,6 +147,7 @@ export interface UpdateExceptionListItemOptions { export interface UpdateEndpointListItemOptions { _tags: _TagsOrUndefined; + _version: _VersionOrUndefined; comments: UpdateCommentsArray; entries: EntriesArrayOrUndefined; id: IdOrUndefined; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts index a739366c67331..99c42e56f4888 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts @@ -18,6 +18,7 @@ import { NamespaceType, TagsOrUndefined, _TagsOrUndefined, + _VersionOrUndefined, } from '../../../common/schemas'; import { getSavedObjectType, transformSavedObjectUpdateToExceptionList } from './utils'; @@ -26,6 +27,7 @@ import { getExceptionList } from './get_exception_list'; interface UpdateExceptionListOptions { id: IdOrUndefined; _tags: _TagsOrUndefined; + _version: _VersionOrUndefined; name: NameOrUndefined; description: DescriptionOrUndefined; savedObjectsClient: SavedObjectsClientContract; @@ -40,6 +42,7 @@ interface UpdateExceptionListOptions { export const updateExceptionList = async ({ _tags, + _version, id, savedObjectsClient, namespaceType, @@ -67,6 +70,9 @@ export const updateExceptionList = async ({ tags, type, updated_by: user, + }, + { + version: _version, } ); return transformSavedObjectUpdateToExceptionList({ exceptionList, savedObject }); diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts index a5ed1e38df374..f26dd7e18dd5c 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts @@ -20,6 +20,7 @@ import { TagsOrUndefined, UpdateCommentsArrayOrUndefined, _TagsOrUndefined, + _VersionOrUndefined, } from '../../../common/schemas'; import { @@ -33,6 +34,7 @@ interface UpdateExceptionListItemOptions { id: IdOrUndefined; comments: UpdateCommentsArrayOrUndefined; _tags: _TagsOrUndefined; + _version: _VersionOrUndefined; name: NameOrUndefined; description: DescriptionOrUndefined; entries: EntriesArrayOrUndefined; @@ -48,6 +50,7 @@ interface UpdateExceptionListItemOptions { export const updateExceptionListItem = async ({ _tags, + _version, comments, entries, id, @@ -89,6 +92,9 @@ export const updateExceptionListItem = async ({ tags, type, updated_by: user, + }, + { + version: _version, } ); return transformSavedObjectUpdateToExceptionListItem({ diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.ts index ded39933fe9d8..d5e1965efcc89 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/utils.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/utils.ts @@ -72,6 +72,7 @@ export const transformSavedObjectToExceptionList = ({ }): ExceptionListSchema => { const dateNow = new Date().toISOString(); const { + version: _version, attributes: { _tags, created_at, @@ -93,6 +94,7 @@ export const transformSavedObjectToExceptionList = ({ // TODO: Do a throw if after the decode this is not the correct "list_type: list" return { _tags, + _version, created_at, created_by, description, @@ -118,6 +120,7 @@ export const transformSavedObjectUpdateToExceptionList = ({ }): ExceptionListSchema => { const dateNow = new Date().toISOString(); const { + version: _version, attributes: { _tags, description, meta, name, tags, type, updated_by: updatedBy }, id, updated_at: updatedAt, @@ -127,6 +130,7 @@ export const transformSavedObjectUpdateToExceptionList = ({ // TODO: Do a throw if after the decode this is not the correct "list_type: list" return { _tags: _tags ?? exceptionList._tags, + _version, created_at: exceptionList.created_at, created_by: exceptionList.created_by, description: description ?? exceptionList.description, @@ -150,6 +154,7 @@ export const transformSavedObjectToExceptionListItem = ({ }): ExceptionListItemSchema => { const dateNow = new Date().toISOString(); const { + version: _version, attributes: { _tags, comments, @@ -174,6 +179,7 @@ export const transformSavedObjectToExceptionListItem = ({ // TODO: Do a throw if item_id or entries is not defined. return { _tags, + _version, comments: comments ?? [], created_at, created_by, @@ -202,6 +208,7 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ }): ExceptionListItemSchema => { const dateNow = new Date().toISOString(); const { + version: _version, attributes: { _tags, comments, @@ -223,6 +230,7 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ // defaulting return { _tags: _tags ?? exceptionListItem._tags, + _version, comments: comments ?? exceptionListItem.comments, created_at: exceptionListItem.created_at, created_by: exceptionListItem.created_by, diff --git a/x-pack/plugins/lists/server/services/items/create_list_item.ts b/x-pack/plugins/lists/server/services/items/create_list_item.ts index aa17fc00b25c6..5332e2f6c7f4e 100644 --- a/x-pack/plugins/lists/server/services/items/create_list_item.ts +++ b/x-pack/plugins/lists/server/services/items/create_list_item.ts @@ -18,6 +18,7 @@ import { Type, } from '../../../common/schemas'; import { transformListItemToElasticQuery } from '../utils'; +import { encodeHitVersion } from '../utils/encode_hit_version'; export interface CreateListItemOptions { deserializer: DeserializerOrUndefined; @@ -75,6 +76,7 @@ export const createListItem = async ({ }); return { + _version: encodeHitVersion(response), id: response._id, type, value, diff --git a/x-pack/plugins/lists/server/services/items/find_list_item.ts b/x-pack/plugins/lists/server/services/items/find_list_item.ts index 93fa682631b06..a997b10004e76 100644 --- a/x-pack/plugins/lists/server/services/items/find_list_item.ts +++ b/x-pack/plugins/lists/server/services/items/find_list_item.ts @@ -5,6 +5,7 @@ */ import { LegacyAPICaller } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; import { Filter, @@ -82,7 +83,10 @@ export const findListItem = async ({ }); if (scroll.validSearchAfterFound) { - const response = await callCluster('search', { + // Note: This typing of response = await callCluster> + // is because when you pass in seq_no_primary_term: true it does a "fall through" type and you have + // to explicitly define the type . + const response = await callCluster>('search', { body: { query, search_after: scroll.searchAfter, @@ -90,6 +94,7 @@ export const findListItem = async ({ }, ignoreUnavailable: true, index: listItemIndex, + seq_no_primary_term: true, size: perPage, }); return { diff --git a/x-pack/plugins/lists/server/services/items/get_list_item.ts b/x-pack/plugins/lists/server/services/items/get_list_item.ts index 6f2a7ad63a973..ad9ae8763fe94 100644 --- a/x-pack/plugins/lists/server/services/items/get_list_item.ts +++ b/x-pack/plugins/lists/server/services/items/get_list_item.ts @@ -5,6 +5,7 @@ */ import { LegacyAPICaller } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; import { Id, ListItemSchema, SearchEsListItemSchema } from '../../../common/schemas'; import { transformElasticToListItem } from '../utils'; @@ -21,7 +22,10 @@ export const getListItem = async ({ callCluster, listItemIndex, }: GetListItemOptions): Promise => { - const listItemES = await callCluster('search', { + // Note: This typing of response = await callCluster> + // is because when you pass in seq_no_primary_term: true it does a "fall through" type and you have + // to explicitly define the type . + const listItemES = await callCluster>('search', { body: { query: { term: { @@ -31,6 +35,7 @@ export const getListItem = async ({ }, ignoreUnavailable: true, index: listItemIndex, + seq_no_primary_term: true, }); if (listItemES.hits.hits.length) { diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.mock.ts b/x-pack/plugins/lists/server/services/items/update_list_item.mock.ts index 7ee8664b04d6b..8fc4b55338344 100644 --- a/x-pack/plugins/lists/server/services/items/update_list_item.mock.ts +++ b/x-pack/plugins/lists/server/services/items/update_list_item.mock.ts @@ -15,6 +15,7 @@ import { } from '../../../common/constants.mock'; export const getUpdateListItemOptionsMock = (): UpdateListItemOptions => ({ + _version: undefined, callCluster: getCallClusterMock(), dateNow: DATE_NOW, id: LIST_ITEM_ID, diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.ts b/x-pack/plugins/lists/server/services/items/update_list_item.ts index eb20f1cfe3b30..8387d916b12f2 100644 --- a/x-pack/plugins/lists/server/services/items/update_list_item.ts +++ b/x-pack/plugins/lists/server/services/items/update_list_item.ts @@ -12,12 +12,16 @@ import { ListItemSchema, MetaOrUndefined, UpdateEsListItemSchema, + _VersionOrUndefined, } from '../../../common/schemas'; import { transformListItemToElasticQuery } from '../utils'; +import { decodeVersion } from '../utils/decode_version'; +import { encodeHitVersion } from '../utils/encode_hit_version'; import { getListItem } from './get_list_item'; export interface UpdateListItemOptions { + _version: _VersionOrUndefined; id: Id; value: string | null | undefined; callCluster: LegacyAPICaller; @@ -28,6 +32,7 @@ export interface UpdateListItemOptions { } export const updateListItem = async ({ + _version, id, value, callCluster, @@ -57,6 +62,7 @@ export const updateListItem = async ({ }; const response = await callCluster('update', { + ...decodeVersion(_version), body: { doc, }, @@ -65,6 +71,7 @@ export const updateListItem = async ({ refresh: 'wait_for', }); return { + _version: encodeHitVersion(response), created_at: listItem.created_at, created_by: listItem.created_by, deserializer: listItem.deserializer, diff --git a/x-pack/plugins/lists/server/services/lists/create_list.ts b/x-pack/plugins/lists/server/services/lists/create_list.ts index 3d396cf4d5af9..f97399e6dc131 100644 --- a/x-pack/plugins/lists/server/services/lists/create_list.ts +++ b/x-pack/plugins/lists/server/services/lists/create_list.ts @@ -8,6 +8,7 @@ import uuid from 'uuid'; import { CreateDocumentResponse } from 'elasticsearch'; import { LegacyAPICaller } from 'kibana/server'; +import { encodeHitVersion } from '../utils/encode_hit_version'; import { Description, DeserializerOrUndefined, @@ -70,6 +71,7 @@ export const createList = async ({ refresh: 'wait_for', }); return { + _version: encodeHitVersion(response), id: response._id, ...body, }; diff --git a/x-pack/plugins/lists/server/services/lists/find_list.ts b/x-pack/plugins/lists/server/services/lists/find_list.ts index 86cead9e868d6..363794b6affe4 100644 --- a/x-pack/plugins/lists/server/services/lists/find_list.ts +++ b/x-pack/plugins/lists/server/services/lists/find_list.ts @@ -5,6 +5,7 @@ */ import { LegacyAPICaller } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; import { Filter, @@ -71,7 +72,10 @@ export const findList = async ({ }); if (scroll.validSearchAfterFound) { - const response = await callCluster('search', { + // Note: This typing of response = await callCluster> + // is because when you pass in seq_no_primary_term: true it does a "fall through" type and you have + // to explicitly define the type . + const response = await callCluster>('search', { body: { query, search_after: scroll.searchAfter, @@ -79,6 +83,7 @@ export const findList = async ({ }, ignoreUnavailable: true, index: listIndex, + seq_no_primary_term: true, size: perPage, }); return { diff --git a/x-pack/plugins/lists/server/services/lists/get_list.ts b/x-pack/plugins/lists/server/services/lists/get_list.ts index 13550eb7d30dd..860e8e9f97f87 100644 --- a/x-pack/plugins/lists/server/services/lists/get_list.ts +++ b/x-pack/plugins/lists/server/services/lists/get_list.ts @@ -5,6 +5,7 @@ */ import { LegacyAPICaller } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; import { Id, ListSchema, SearchEsListSchema } from '../../../common/schemas'; import { transformElasticToList } from '../utils/transform_elastic_to_list'; @@ -20,7 +21,10 @@ export const getList = async ({ callCluster, listIndex, }: GetListOptions): Promise => { - const response = await callCluster('search', { + // Note: This typing of response = await callCluster> + // is because when you pass in seq_no_primary_term: true it does a "fall through" type and you have + // to explicitly define the type . + const response = await callCluster>('search', { body: { query: { term: { @@ -30,6 +34,7 @@ export const getList = async ({ }, ignoreUnavailable: true, index: listIndex, + seq_no_primary_term: true, }); const list = transformElasticToList({ response }); return list[0] ?? null; diff --git a/x-pack/plugins/lists/server/services/lists/list_client.ts b/x-pack/plugins/lists/server/services/lists/list_client.ts index 4acc2e7092491..9bece64fa943f 100644 --- a/x-pack/plugins/lists/server/services/lists/list_client.ts +++ b/x-pack/plugins/lists/server/services/lists/list_client.ts @@ -395,6 +395,7 @@ export class ListClient { }; public updateListItem = async ({ + _version, id, value, meta, @@ -402,6 +403,7 @@ export class ListClient { const { callCluster, user } = this; const listItemIndex = this.getListItemIndex(); return updateListItem({ + _version, callCluster, id, listItemIndex, @@ -412,6 +414,7 @@ export class ListClient { }; public updateList = async ({ + _version, id, name, description, @@ -420,6 +423,7 @@ export class ListClient { const { callCluster, user } = this; const listIndex = this.getListIndex(); return updateList({ + _version, callCluster, description, id, diff --git a/x-pack/plugins/lists/server/services/lists/list_client_types.ts b/x-pack/plugins/lists/server/services/lists/list_client_types.ts index 68a018fa2fc16..7fa1727be118b 100644 --- a/x-pack/plugins/lists/server/services/lists/list_client_types.ts +++ b/x-pack/plugins/lists/server/services/lists/list_client_types.ts @@ -26,6 +26,7 @@ import { SortFieldOrUndefined, SortOrderOrUndefined, Type, + _VersionOrUndefined, } from '../../../common/schemas'; import { ConfigType } from '../../config'; @@ -106,12 +107,14 @@ export interface CreateListItemOptions { } export interface UpdateListItemOptions { + _version: _VersionOrUndefined; id: Id; value: string | null | undefined; meta: MetaOrUndefined; } export interface UpdateListOptions { + _version: _VersionOrUndefined; id: Id; name: NameOrUndefined; description: DescriptionOrUndefined; diff --git a/x-pack/plugins/lists/server/services/lists/update_list.mock.ts b/x-pack/plugins/lists/server/services/lists/update_list.mock.ts index ff974b6e7352b..fc3d63277c5b5 100644 --- a/x-pack/plugins/lists/server/services/lists/update_list.mock.ts +++ b/x-pack/plugins/lists/server/services/lists/update_list.mock.ts @@ -16,6 +16,7 @@ import { } from '../../../common/constants.mock'; export const getUpdateListOptionsMock = (): UpdateListOptions => ({ + _version: undefined, callCluster: getCallClusterMock(), dateNow: DATE_NOW, description: DESCRIPTION, diff --git a/x-pack/plugins/lists/server/services/lists/update_list.ts b/x-pack/plugins/lists/server/services/lists/update_list.ts index f84ca787eaa7c..fba57ca744f9d 100644 --- a/x-pack/plugins/lists/server/services/lists/update_list.ts +++ b/x-pack/plugins/lists/server/services/lists/update_list.ts @@ -7,6 +7,8 @@ import { CreateDocumentResponse } from 'elasticsearch'; import { LegacyAPICaller } from 'kibana/server'; +import { decodeVersion } from '../utils/decode_version'; +import { encodeHitVersion } from '../utils/encode_hit_version'; import { DescriptionOrUndefined, Id, @@ -14,11 +16,13 @@ import { MetaOrUndefined, NameOrUndefined, UpdateEsListSchema, + _VersionOrUndefined, } from '../../../common/schemas'; import { getList } from '.'; export interface UpdateListOptions { + _version: _VersionOrUndefined; id: Id; callCluster: LegacyAPICaller; listIndex: string; @@ -30,6 +34,7 @@ export interface UpdateListOptions { } export const updateList = async ({ + _version, id, name, description, @@ -52,12 +57,14 @@ export const updateList = async ({ updated_by: user, }; const response = await callCluster('update', { + ...decodeVersion(_version), body: { doc }, id, index: listIndex, refresh: 'wait_for', }); return { + _version: encodeHitVersion(response), created_at: list.created_at, created_by: list.created_by, description: description ?? list.description, diff --git a/x-pack/plugins/lists/server/services/utils/decode_version.ts b/x-pack/plugins/lists/server/services/utils/decode_version.ts new file mode 100644 index 0000000000000..e5fb9b54bcf97 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/decode_version.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// Similar to the src/core/server/saved_objects/version/decode_version.ts +// with the notable differences in that it is more tolerant and does not throw saved object specific errors +// but rather just returns an empty object if it cannot parse the version or cannot find one. +export const decodeVersion = ( + version: string | undefined +): + | { + ifSeqNo: number; + ifPrimaryTerm: number; + } + | {} => { + if (version != null) { + try { + const decoded = Buffer.from(version, 'base64').toString('utf8'); + const parsed = JSON.parse(decoded); + if (Array.isArray(parsed) && Number.isInteger(parsed[0]) && Number.isInteger(parsed[1])) { + return { + ifPrimaryTerm: parsed[1], + ifSeqNo: parsed[0], + }; + } else { + return {}; + } + } catch (err) { + // do nothing here, this is on purpose and we want to return any empty object when we can't parse. + return {}; + } + } else { + return {}; + } +}; diff --git a/x-pack/plugins/lists/server/services/utils/encode_hit_version.ts b/x-pack/plugins/lists/server/services/utils/encode_hit_version.ts new file mode 100644 index 0000000000000..42dccfbac7340 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/encode_hit_version.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * Very similar to the encode_hit_version from saved object system from here: + * src/core/server/saved_objects/version/encode_hit_version.ts + * + * with the most notably change is that it doesn't do any throws but rather just returns undefined + * if _seq_no or _primary_term does not exist. + * @param response The response to encode into a version by using _seq_no and _primary_term + */ +export const encodeHitVersion = (hit: T): string | undefined => { + // Have to do this "as cast" here as these two types aren't included in the SearchResponse hit type + const { _seq_no: seqNo, _primary_term: primaryTerm } = (hit as unknown) as { + _seq_no: number; + _primary_term: number; + }; + + if (seqNo == null || primaryTerm == null) { + return undefined; + } else { + return Buffer.from(JSON.stringify([seqNo, primaryTerm]), 'utf8').toString('base64'); + } +}; diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list.ts b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list.ts index bb1ae1d4b9ff3..fb226d91fe395 100644 --- a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list.ts +++ b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list.ts @@ -8,6 +8,8 @@ import { SearchResponse } from 'elasticsearch'; import { ListArraySchema, SearchEsListSchema } from '../../../common/schemas'; +import { encodeHitVersion } from './encode_hit_version'; + export interface TransformElasticToListOptions { response: SearchResponse; } @@ -17,6 +19,7 @@ export const transformElasticToList = ({ }: TransformElasticToListOptions): ListArraySchema => { return response.hits.hits.map((hit) => { return { + _version: encodeHitVersion(hit), id: hit._id, ...hit._source, }; diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts index a59b3b383cd2a..26fe15e9106fe 100644 --- a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts +++ b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts @@ -9,6 +9,7 @@ import { SearchResponse } from 'elasticsearch'; import { ListItemArraySchema, SearchEsListItemSchema, Type } from '../../../common/schemas'; import { ErrorWithStatusCode } from '../../error_with_status_code'; +import { encodeHitVersion } from './encode_hit_version'; import { findSourceValue } from './find_source_value'; export interface TransformElasticToListItemOptions { @@ -40,6 +41,7 @@ export const transformElasticToListItem = ({ throw new ErrorWithStatusCode(`Was expected ${type} to not be null/undefined`, 400); } else { return { + _version: encodeHitVersion(hit), created_at, created_by, deserializer, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts index c69ae591f5ddc..e6b00aeac0d9c 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_actions_array.ts @@ -10,14 +10,12 @@ import { actions, Actions } from '../common/schemas'; /** * Types the DefaultStringArray as: - * - If null or undefined, then a default action array will be set + * - If undefined, then a default action array will be set */ -export const DefaultActionsArray = new t.Type( +export const DefaultActionsArray = new t.Type( 'DefaultActionsArray', actions.is, (input, context): Either => input == null ? t.success([]) : actions.validate(input, context), t.identity ); - -export type DefaultActionsArrayC = typeof DefaultActionsArray; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts index 0cab6525779a6..5ace191c5f86f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_false.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultBooleanFalse as: * - If null or undefined, then a default false will be set */ -export const DefaultBooleanFalse = new t.Type( +export const DefaultBooleanFalse = new t.Type( 'DefaultBooleanFalse', t.boolean.is, (input, context): Either => input == null ? t.success(false) : t.boolean.validate(input, context), t.identity ); - -export type DefaultBooleanFalseC = typeof DefaultBooleanFalse; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts index 6997652b72636..92167287d23f5 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_boolean_true.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultBooleanTrue as: * - If null or undefined, then a default true will be set */ -export const DefaultBooleanTrue = new t.Type( +export const DefaultBooleanTrue = new t.Type( 'DefaultBooleanTrue', t.boolean.is, (input, context): Either => input == null ? t.success(true) : t.boolean.validate(input, context), t.identity ); - -export type DefaultBooleanTrueC = typeof DefaultBooleanTrue; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts index a1103c4aa8d0e..185cccd86313e 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_empty_string.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultEmptyString as: * - If null or undefined, then a default of an empty string "" will be used */ -export const DefaultEmptyString = new t.Type( +export const DefaultEmptyString = new t.Type( 'DefaultEmptyString', t.string.is, (input, context): Either => input == null ? t.success('') : t.string.validate(input, context), t.identity ); - -export type DefaultEmptyStringC = typeof DefaultEmptyString; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts index 4c7f663e7f46d..12efda77e5435 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_export_file_name.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultExportFileName as: * - If null or undefined, then a default of "export.ndjson" will be used */ -export const DefaultExportFileName = new t.Type( +export const DefaultExportFileName = new t.Type( 'DefaultExportFileName', t.string.is, (input, context): Either => input == null ? t.success('export.ndjson') : t.string.validate(input, context), t.identity ); - -export type DefaultExportFileNameC = typeof DefaultExportFileName; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts index b6b432858eb92..a85ea58b26478 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_from_string.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultFromString as: * - If null or undefined, then a default of the string "now-6m" will be used */ -export const DefaultFromString = new t.Type( +export const DefaultFromString = new t.Type( 'DefaultFromString', t.string.is, (input, context): Either => input == null ? t.success('now-6m') : t.string.validate(input, context), t.identity ); - -export type DefaultFromStringC = typeof DefaultFromString; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts index 9492374ffe91e..4001af46e7ddf 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_interval_string.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultIntervalString as: * - If null or undefined, then a default of the string "5m" will be used */ -export const DefaultIntervalString = new t.Type( +export const DefaultIntervalString = new t.Type( 'DefaultIntervalString', t.string.is, (input, context): Either => input == null ? t.success('5m') : t.string.validate(input, context), t.identity ); - -export type DefaultIntervalStringC = typeof DefaultIntervalString; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts index 1e05a46d7273c..afa83c484698f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_language_string.ts @@ -12,12 +12,10 @@ import { language } from '../common/schemas'; * Types the DefaultLanguageString as: * - If null or undefined, then a default of the string "kuery" will be used */ -export const DefaultLanguageString = new t.Type( +export const DefaultLanguageString = new t.Type( 'DefaultLanguageString', t.string.is, (input, context): Either => input == null ? t.success('kuery') : language.validate(input, context), t.identity ); - -export type DefaultLanguageStringC = typeof DefaultLanguageString; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts index d3c48b5522f57..518af32dcf2b4 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_max_signals_number.ts @@ -16,11 +16,7 @@ import { DEFAULT_MAX_SIGNALS } from '../../../constants'; * - greater than 1 * - If undefined then it will use DEFAULT_MAX_SIGNALS (100) as the default */ -export const DefaultMaxSignalsNumber: DefaultMaxSignalsNumberC = new t.Type< - number, - number, - unknown ->( +export const DefaultMaxSignalsNumber = new t.Type( 'DefaultMaxSignals', t.number.is, (input, context): Either => { @@ -28,5 +24,3 @@ export const DefaultMaxSignalsNumber: DefaultMaxSignalsNumberC = new t.Type< }, t.identity ); - -export type DefaultMaxSignalsNumberC = t.Type; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts index 96e01d381e34b..f3a997e3cc897 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_page.ts @@ -14,7 +14,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_ * - If null or undefined, then a default of 1 will be used * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero */ -export const DefaultPage = new t.Type( +export const DefaultPage = new t.Type( 'DefaultPerPage', t.number.is, (input, context): Either => { @@ -28,5 +28,3 @@ export const DefaultPage = new t.Type( }, t.identity ); - -export type DefaultPageC = typeof DefaultPage; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts index b78de8b35cede..72e817b10a600 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_per_page.ts @@ -14,7 +14,7 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_ * - If null or undefined, then a default of 20 will be used * - If the number is 0 or less this will not validate as it has to be a positive number greater than zero */ -export const DefaultPerPage = new t.Type( +export const DefaultPerPage = new t.Type( 'DefaultPerPage', t.number.is, (input, context): Either => { @@ -28,5 +28,3 @@ export const DefaultPerPage = new t.Type( }, t.identity ); - -export type DefaultPerPageC = typeof DefaultPerPage; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts index ba74045b4e32c..bf88ece913767 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_risk_score_mapping_array.ts @@ -13,12 +13,14 @@ import { risk_score_mapping, RiskScoreMapping } from '../common/schemas'; * Types the DefaultStringArray as: * - If null or undefined, then a default risk_score_mapping array will be set */ -export const DefaultRiskScoreMappingArray = new t.Type( +export const DefaultRiskScoreMappingArray = new t.Type< + RiskScoreMapping, + RiskScoreMapping | undefined, + unknown +>( 'DefaultRiskScoreMappingArray', risk_score_mapping.is, (input, context): Either => input == null ? t.success([]) : risk_score_mapping.validate(input, context), t.identity ); - -export type DefaultRiskScoreMappingArrayC = typeof DefaultRiskScoreMappingArray; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts index 8e68b73148af1..56b0ac1b75982 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_severity_mapping_array.ts @@ -13,12 +13,14 @@ import { severity_mapping, SeverityMapping } from '../common/schemas'; * Types the DefaultStringArray as: * - If null or undefined, then a default severity_mapping array will be set */ -export const DefaultSeverityMappingArray = new t.Type( +export const DefaultSeverityMappingArray = new t.Type< + SeverityMapping, + SeverityMapping | undefined, + unknown +>( 'DefaultSeverityMappingArray', severity_mapping.is, (input, context): Either => input == null ? t.success([]) : severity_mapping.validate(input, context), t.identity ); - -export type DefaultSeverityMappingArrayC = typeof DefaultSeverityMappingArray; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts index a8c53c230acd9..a973bbb37cac7 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_array.ts @@ -9,14 +9,13 @@ import { Either } from 'fp-ts/lib/Either'; /** * Types the DefaultStringArray as: - * - If null or undefined, then a default array will be set + * - If undefined, then a default array will be set + * - If an array is sent in, then the array will be validated to ensure all elements are a string */ -export const DefaultStringArray = new t.Type( +export const DefaultStringArray = new t.Type( 'DefaultStringArray', t.array(t.string).is, (input, context): Either => input == null ? t.success([]) : t.array(t.string).validate(input, context), t.identity ); - -export type DefaultStringArrayC = typeof DefaultStringArray; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts index aa070c171d7ea..eba805048fe3a 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_string_boolean_false.ts @@ -12,7 +12,7 @@ import { Either } from 'fp-ts/lib/Either'; * - If a string this will convert the string to a boolean * - If null or undefined, then a default false will be set */ -export const DefaultStringBooleanFalse = new t.Type( +export const DefaultStringBooleanFalse = new t.Type( 'DefaultStringBooleanFalse', t.boolean.is, (input, context): Either => { diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts index 5499a3c1e3064..3b3c20f003e6e 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_threat_array.ts @@ -12,12 +12,10 @@ import { Threat, threat } from '../common/schemas'; * Types the DefaultThreatArray as: * - If null or undefined, then an empty array will be set */ -export const DefaultThreatArray = new t.Type( +export const DefaultThreatArray = new t.Type( 'DefaultThreatArray', threat.is, (input, context): Either => input == null ? t.success([]) : threat.validate(input, context), t.identity ); - -export type DefaultThreatArrayC = typeof DefaultThreatArray; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts index b76a35c0265a0..c4cf9a07ed359 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_throttle_null.ts @@ -12,12 +12,10 @@ import { ThrottleOrNull, throttle } from '../common/schemas'; * Types the DefaultThrottleNull as: * - If null or undefined, then a null will be set */ -export const DefaultThrottleNull = new t.Type( +export const DefaultThrottleNull = new t.Type( 'DefaultThreatNull', throttle.is, (input, context): Either => input == null ? t.success(null) : throttle.validate(input, context), t.identity ); - -export type DefaultThrottleNullC = typeof DefaultThrottleNull; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts index 158eedc121c53..6fe247f05e7e6 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_to_string.ts @@ -11,12 +11,10 @@ import { Either } from 'fp-ts/lib/Either'; * Types the DefaultToString as: * - If null or undefined, then a default of the string "now" will be used */ -export const DefaultToString = new t.Type( +export const DefaultToString = new t.Type( 'DefaultToString', t.string.is, (input, context): Either => input == null ? t.success('now') : t.string.validate(input, context), t.identity ); - -export type DefaultToStringC = typeof DefaultToString; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts index 74e32e083cc44..5f1d51ba84369 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_uuid.ts @@ -15,12 +15,10 @@ import { NonEmptyString } from './non_empty_string'; * - If null or undefined, then a default string uuid.v4() will be * created otherwise it will be checked just against an empty string */ -export const DefaultUuid = new t.Type( +export const DefaultUuid = new t.Type( 'DefaultUuid', t.string.is, (input, context): Either => input == null ? t.success(uuid.v4()) : NonEmptyString.validate(input, context), t.identity ); - -export type DefaultUuidC = typeof DefaultUuid; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts index 832c942291c32..bbba7c5b8f3bb 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_version_number.ts @@ -12,12 +12,10 @@ import { version, Version } from '../common/schemas'; * Types the DefaultVersionNumber as: * - If null or undefined, then a default of the number 1 will be used */ -export const DefaultVersionNumber = new t.Type( +export const DefaultVersionNumber = new t.Type( 'DefaultVersionNumber', version.is, (input, context): Either => input == null ? t.success(1) : version.validate(input, context), t.identity ); - -export type DefaultVersionNumberC = typeof DefaultVersionNumber; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.test.ts index 2268e47bd1149..59819947ddddf 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.test.ts @@ -9,7 +9,7 @@ import { left } from 'fp-ts/lib/Either'; import { foldLeftRight, getPaths } from '../../../test_utils'; -import { DefaultListArray, DefaultListArrayC } from './lists_default_array'; +import { DefaultListArray } from './lists_default_array'; import { getListArrayMock } from './lists.mock'; describe('lists_default_array', () => { @@ -51,7 +51,7 @@ describe('lists_default_array', () => { test('it should not validate an array of non accepted types', () => { // Terrible casting for purpose of tests - const payload = ([1] as unknown) as DefaultListArrayC; + const payload = [1] as unknown; const decoded = DefaultListArray.decode(payload); const message = pipe(decoded, foldLeftRight); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts index ac5666cad23a7..9260d7cbdb2a8 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists_default_array.ts @@ -9,13 +9,11 @@ import { Either } from 'fp-ts/lib/Either'; import { ListArray, list } from './lists'; -export type DefaultListArrayC = t.Type; - /** * Types the DefaultListArray as: * - If null or undefined, then a default array of type list will be set */ -export const DefaultListArray: DefaultListArrayC = new t.Type( +export const DefaultListArray = new t.Type( 'DefaultListArray', t.array(list).is, (input, context): Either => diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts index b22562e2ab9dc..add926598a137 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/only_false_allowed.ts @@ -13,7 +13,7 @@ import { Either } from 'fp-ts/lib/Either'; * - If true is sent in then this will return an error * - If false is sent in then this will allow it only false */ -export const OnlyFalseAllowed = new t.Type( +export const OnlyFalseAllowed = new t.Type( 'DefaultBooleanTrue', t.boolean.is, (input, context): Either => { @@ -29,5 +29,3 @@ export const OnlyFalseAllowed = new t.Type( }, t.identity ); - -export type OnlyFalseAllowedC = typeof OnlyFalseAllowed; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts index 298487a3fae98..0dc6e2c700395 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer.ts @@ -22,5 +22,3 @@ export const PositiveInteger = new t.Type( }, t.identity ); - -export type PositiveIntegerC = typeof PositiveInteger; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts index 8aeaf99a19eb1..2aa17c6c68955 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/positive_integer_greater_than_zero.ts @@ -22,5 +22,3 @@ export const PositiveIntegerGreaterThanZero = new t.Type( +export const ReferencesDefaultArray = new t.Type( 'referencesWithDefaultArray', t.array(t.string).is, (input, context): Either => input == null ? t.success([]) : t.array(t.string).validate(input, context), t.identity ); - -export type ReferencesDefaultArrayC = typeof ReferencesDefaultArray; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index daa8589613daa..7171d3c6b815e 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -398,11 +398,11 @@ describe('Exception helpers', () => { title: 'OS', }, { - description: 'April 23rd 2020 @ 00:19:13', + description: 'April 20th 2020 @ 15:25:31', title: 'Date created', }, { - description: 'user_name', + description: 'some user', title: 'Created by', }, ]; @@ -417,11 +417,11 @@ describe('Exception helpers', () => { const result = getDescriptionListContent(payload); const expected: DescriptionListItem[] = [ { - description: 'April 23rd 2020 @ 00:19:13', + description: 'April 20th 2020 @ 15:25:31', title: 'Date created', }, { - description: 'user_name', + description: 'some user', title: 'Created by', }, { @@ -440,11 +440,11 @@ describe('Exception helpers', () => { const result = getDescriptionListContent(payload); const expected: DescriptionListItem[] = [ { - description: 'April 23rd 2020 @ 00:19:13', + description: 'April 20th 2020 @ 15:25:31', title: 'Date created', }, { - description: 'user_name', + description: 'some user', title: 'Created by', }, ]; @@ -520,12 +520,12 @@ describe('Exception helpers', () => { const expected = { _tags: ['endpoint', 'process', 'malware', 'os:linux'], comments: [], - description: 'This is a sample endpoint type exception', + description: 'some description', entries: ENTRIES, id: '1', item_id: 'endpoint_list_item', meta: {}, - name: 'Sample Endpoint Exception List', + name: 'some name', namespace_type: 'single', tags: ['user added string for a tag', 'malware'], type: 'simple', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx index f5b34b7838d25..4fc744c2c9d01 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/exception_details.test.tsx @@ -179,7 +179,7 @@ describe('ExceptionDetails', () => { expect(wrapper.find('EuiDescriptionListTitle').at(1).text()).toEqual('Date created'); expect(wrapper.find('EuiDescriptionListDescription').at(1).text()).toEqual( - 'April 23rd 2020 @ 00:19:13' + 'April 20th 2020 @ 15:25:31' ); }); @@ -196,7 +196,7 @@ describe('ExceptionDetails', () => { ); expect(wrapper.find('EuiDescriptionListTitle').at(2).text()).toEqual('Created by'); - expect(wrapper.find('EuiDescriptionListDescription').at(2).text()).toEqual('user_name'); + expect(wrapper.find('EuiDescriptionListDescription').at(2).text()).toEqual('some user'); }); test('it renders the description if one is included on the exception item', () => { @@ -212,8 +212,6 @@ describe('ExceptionDetails', () => { ); expect(wrapper.find('EuiDescriptionListTitle').at(3).text()).toEqual('Comment'); - expect(wrapper.find('EuiDescriptionListDescription').at(3).text()).toEqual( - 'This is a sample endpoint type exception' - ); + expect(wrapper.find('EuiDescriptionListDescription').at(3).text()).toEqual('some description'); }); }); From 8a4daffcfdb90b8ff776ea051266516441c6fda2 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 20 Jul 2020 11:00:59 -0600 Subject: [PATCH 26/26] [SIEM][Detection Engine][Lists] Adds list permissions (#72335) ## Summary * Adds list permissions as a feature control to SIEM. * Separates the controls between two, one of which is `access:lists-all` and the other is `access:lists-read` * Grants SIEM the ability to utilize both depending on which feature mode the space is in. --- .../routes/create_endpoint_list_item_route.ts | 2 +- .../routes/create_endpoint_list_route.ts | 2 +- .../routes/create_exception_list_item_route.ts | 2 +- .../routes/create_exception_list_route.ts | 2 +- .../server/routes/create_list_index_route.ts | 2 +- .../server/routes/create_list_item_route.ts | 2 +- .../lists/server/routes/create_list_route.ts | 2 +- .../routes/delete_endpoint_list_item_route.ts | 2 +- .../routes/delete_exception_list_item_route.ts | 2 +- .../routes/delete_exception_list_route.ts | 2 +- .../server/routes/delete_list_index_route.ts | 2 +- .../server/routes/delete_list_item_route.ts | 2 +- .../lists/server/routes/delete_list_route.ts | 2 +- .../server/routes/export_list_item_route.ts | 2 +- .../routes/find_endpoint_list_item_route.ts | 2 +- .../routes/find_exception_list_item_route.ts | 2 +- .../server/routes/find_exception_list_route.ts | 2 +- .../server/routes/find_list_item_route.ts | 2 +- .../lists/server/routes/find_list_route.ts | 2 +- .../server/routes/import_list_item_route.ts | 2 +- .../server/routes/patch_list_item_route.ts | 2 +- .../lists/server/routes/patch_list_route.ts | 2 +- .../routes/read_endpoint_list_item_route.ts | 2 +- .../routes/read_exception_list_item_route.ts | 2 +- .../server/routes/read_exception_list_route.ts | 2 +- .../server/routes/read_list_index_route.ts | 2 +- .../server/routes/read_list_item_route.ts | 2 +- .../lists/server/routes/read_list_route.ts | 2 +- .../server/routes/read_privileges_route.ts | 2 +- .../routes/update_endpoint_list_item_route.ts | 2 +- .../routes/update_exception_list_item_route.ts | 2 +- .../routes/update_exception_list_route.ts | 2 +- .../server/routes/update_list_item_route.ts | 2 +- .../lists/server/routes/update_list_route.ts | 2 +- .../plugins/security_solution/server/plugin.ts | 18 ++++++++++++++++-- 35 files changed, 50 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts index b6eacc3b7dd04..5ff2a9d9df9f4 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts @@ -21,7 +21,7 @@ export const createEndpointListItemRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: ENDPOINT_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts index cac69ce65623f..b1e589be67cd1 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts @@ -26,7 +26,7 @@ export const createEndpointListRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: ENDPOINT_LIST_URL, validate: false, diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts index c331eeb4bd2d0..e4885c7393bd4 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -22,7 +22,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: EXCEPTION_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts index bd29a65c9450a..897d82d6a9ba0 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -21,7 +21,7 @@ export const createExceptionListRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: EXCEPTION_LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/create_list_index_route.ts b/x-pack/plugins/lists/server/routes/create_list_index_route.ts index 5ec2b36da61b0..1bffdd6bd5b5f 100644 --- a/x-pack/plugins/lists/server/routes/create_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_index_route.ts @@ -17,7 +17,7 @@ export const createListIndexRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_INDEX, validate: false, diff --git a/x-pack/plugins/lists/server/routes/create_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_list_item_route.ts index 8ac5db3c7fd1c..656d6af2c6c9a 100644 --- a/x-pack/plugins/lists/server/routes/create_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_item_route.ts @@ -17,7 +17,7 @@ export const createListItemRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/create_list_route.ts b/x-pack/plugins/lists/server/routes/create_list_route.ts index eee7517523b0f..ff041699054c9 100644 --- a/x-pack/plugins/lists/server/routes/create_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_route.ts @@ -17,7 +17,7 @@ export const createListRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts index b8946c542b27e..2d5028bd9525a 100644 --- a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts @@ -21,7 +21,7 @@ export const deleteEndpointListItemRoute = (router: IRouter): void => { router.delete( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: ENDPOINT_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts index f363252dada50..06ff051925407 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts @@ -21,7 +21,7 @@ export const deleteExceptionListItemRoute = (router: IRouter): void => { router.delete( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: EXCEPTION_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts index b1bf705dcc5f6..f2bf517f55ae3 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts @@ -21,7 +21,7 @@ export const deleteExceptionListRoute = (router: IRouter): void => { router.delete( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: EXCEPTION_LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts index cb2e16b3602a7..be58d8aeed17d 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts @@ -33,7 +33,7 @@ export const deleteListIndexRoute = (router: IRouter): void => { router.delete( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_INDEX, validate: false, diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts index bb278ba436725..50313cd1294ae 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -17,7 +17,7 @@ export const deleteListItemRoute = (router: IRouter): void => { router.delete( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/delete_list_route.ts b/x-pack/plugins/lists/server/routes/delete_list_route.ts index 600e4b00c29ca..4eeb6d8f126ad 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_route.ts @@ -17,7 +17,7 @@ export const deleteListRoute = (router: IRouter): void => { router.delete( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/export_list_item_route.ts b/x-pack/plugins/lists/server/routes/export_list_item_route.ts index 8148c9b1ed824..98167931c4346 100644 --- a/x-pack/plugins/lists/server/routes/export_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/export_list_item_route.ts @@ -18,7 +18,7 @@ export const exportListItemRoute = (router: IRouter): void => { router.post( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: `${LIST_ITEM_URL}/_export`, validate: { diff --git a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts index 7374ff7dc92ea..9f83761cc501a 100644 --- a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts @@ -21,7 +21,7 @@ export const findEndpointListItemRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: `${ENDPOINT_LIST_ITEM_URL}/_find`, validate: { diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts index a318d653450c7..270aad85796b2 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -21,7 +21,7 @@ export const findExceptionListItemRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: `${EXCEPTION_LIST_ITEM_URL}/_find`, validate: { diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts index 97e1de834cd37..c5cae7a1e0bb8 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts @@ -21,7 +21,7 @@ export const findExceptionListRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: `${EXCEPTION_LIST_URL}/_find`, validate: { diff --git a/x-pack/plugins/lists/server/routes/find_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_list_item_route.ts index 8a0e06aa0c7d8..533dc74aa3694 100644 --- a/x-pack/plugins/lists/server/routes/find_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_item_route.ts @@ -22,7 +22,7 @@ export const findListItemRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: `${LIST_ITEM_URL}/_find`, validate: { diff --git a/x-pack/plugins/lists/server/routes/find_list_route.ts b/x-pack/plugins/lists/server/routes/find_list_route.ts index 2fa43c6368b5c..268eb36a5e26e 100644 --- a/x-pack/plugins/lists/server/routes/find_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_route.ts @@ -18,7 +18,7 @@ export const findListRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: `${LIST_URL}/_find`, validate: { diff --git a/x-pack/plugins/lists/server/routes/import_list_item_route.ts b/x-pack/plugins/lists/server/routes/import_list_item_route.ts index 2e629d7516dd1..5e88ca0f2569a 100644 --- a/x-pack/plugins/lists/server/routes/import_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/import_list_item_route.ts @@ -26,7 +26,7 @@ export const importListItemRoute = (router: IRouter, config: ConfigType): void = maxBytes: config.maxImportPayloadBytes, parse: false, }, - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: `${LIST_ITEM_URL}/_import`, validate: { diff --git a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts index 9a74beb45bafd..d975e80079ab7 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts @@ -17,7 +17,7 @@ export const patchListItemRoute = (router: IRouter): void => { router.patch( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/patch_list_route.ts b/x-pack/plugins/lists/server/routes/patch_list_route.ts index 06a76559dee9a..681581c6ff6bd 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_route.ts @@ -17,7 +17,7 @@ export const patchListRoute = (router: IRouter): void => { router.patch( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts index 5e7ed901bf0cb..fd932746ce990 100644 --- a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts @@ -21,7 +21,7 @@ export const readEndpointListItemRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: ENDPOINT_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts index c4e969b27fcf4..fe8256fbda5cd 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts @@ -21,7 +21,7 @@ export const readExceptionListItemRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: EXCEPTION_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts index 6cb91c10aea55..0512876d298d4 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts @@ -21,7 +21,7 @@ export const readExceptionListRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: EXCEPTION_LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/read_list_index_route.ts b/x-pack/plugins/lists/server/routes/read_list_index_route.ts index 4664bed3e7a8b..87a4d85e0d254 100644 --- a/x-pack/plugins/lists/server/routes/read_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_index_route.ts @@ -17,7 +17,7 @@ export const readListIndexRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: LIST_INDEX, validate: false, diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts index 24011d3b50d27..b7cf2b9f7123b 100644 --- a/x-pack/plugins/lists/server/routes/read_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.ts @@ -17,7 +17,7 @@ export const readListItemRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/read_list_route.ts b/x-pack/plugins/lists/server/routes/read_list_route.ts index 34924b70fd4df..4bce09ecd3bde 100644 --- a/x-pack/plugins/lists/server/routes/read_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_route.ts @@ -17,7 +17,7 @@ export const readListRoute = (router: IRouter): void => { router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/read_privileges_route.ts b/x-pack/plugins/lists/server/routes/read_privileges_route.ts index 892b6406a28ec..a4ec878613608 100644 --- a/x-pack/plugins/lists/server/routes/read_privileges_route.ts +++ b/x-pack/plugins/lists/server/routes/read_privileges_route.ts @@ -20,7 +20,7 @@ export const readPrivilegesRoute = ( router.get( { options: { - tags: ['access:lists'], + tags: ['access:lists-read'], }, path: LIST_PRIVILEGES_URL, validate: false, diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts index 8415c64633a06..f717dc0fb3392 100644 --- a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts @@ -21,7 +21,7 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { router.put( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: ENDPOINT_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index 2aa1e016d51ed..293435b3f6202 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -21,7 +21,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { router.put( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: EXCEPTION_LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index 331ec064fa663..403a9f6db934f 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -21,7 +21,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { router.put( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: EXCEPTION_LIST_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/update_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_list_item_route.ts index 0f5d11afcda09..d479bc63b64bd 100644 --- a/x-pack/plugins/lists/server/routes/update_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_item_route.ts @@ -17,7 +17,7 @@ export const updateListItemRoute = (router: IRouter): void => { router.put( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_ITEM_URL, validate: { diff --git a/x-pack/plugins/lists/server/routes/update_list_route.ts b/x-pack/plugins/lists/server/routes/update_list_route.ts index 2fae910c1b398..78aed23db13fc 100644 --- a/x-pack/plugins/lists/server/routes/update_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_route.ts @@ -17,7 +17,7 @@ export const updateListRoute = (router: IRouter): void => { router.put( { options: { - tags: ['access:lists'], + tags: ['access:lists-all'], }, path: LIST_URL, validate: { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 17192057d2ad3..22b55c64a1657 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -167,7 +167,14 @@ export class Plugin implements IPlugin