From 7d12b7650f7ab29022455b16754bb24c52702365 Mon Sep 17 00:00:00 2001 From: Maryia Lapata Date: Mon, 16 Mar 2020 10:36:39 +0300 Subject: [PATCH 01/76] [NP] Graph migration (#59409) * Move graph to NP * Styles * Clean up * Fix eslint * Fix ESlint * Fix path * Fix container height * Clean up * Update index.ts * Update graph_client_workspace.js * Refactoring * Remove unused methods * Update graph_client_workspace.test.js * Rename npData to data * Move Readme * Inline parsing discover url * Remove import of legacy styles * Update README Co-authored-by: Elastic Machine --- x-pack/legacy/plugins/graph/index.ts | 12 - x-pack/legacy/plugins/graph/public/icon.png | Bin 6203 -> 0 bytes x-pack/legacy/plugins/graph/public/index.ts | 26 -- .../plugins/graph/public/legacy_imports.ts | 9 - x-pack/legacy/plugins/graph/public/plugin.ts | 75 ---- x-pack/{legacy => }/plugins/graph/README.md | 3 +- x-pack/plugins/graph/kibana.json | 2 +- .../plugins/graph/public/_main.scss | 0 .../plugins/graph/public/_mixins.scss | 0 .../angular/graph_client_workspace.d.ts | 0 .../public/angular/graph_client_workspace.js | 403 ++++++++---------- .../angular/graph_client_workspace.test.js | 10 +- .../public/angular/templates/_graph.scss | 0 .../public/angular/templates/_index.scss | 0 .../public/angular/templates/_inspect.scss | 0 .../public/angular/templates/_sidebar.scss | 0 .../graph/public/angular/templates/index.html | 0 .../angular/templates/listing_ng_wrapper.html | 0 .../{legacy => }/plugins/graph/public/app.js | 29 +- .../plugins/graph/public/application.ts | 41 +- .../plugins/graph/public/badge.js | 0 .../plugins/graph/public/components/_app.scss | 0 .../graph/public/components/_index.scss | 0 .../graph/public/components/_search_bar.scss | 0 .../public/components/_source_modal.scss | 0 .../plugins/graph/public/components/app.tsx | 2 +- .../field_manager/_field_editor.scss | 0 .../field_manager/_field_picker.scss | 0 .../components/field_manager/_index.scss | 0 .../components/field_manager/field_editor.tsx | 2 +- .../field_manager/field_manager.test.tsx | 0 .../field_manager/field_manager.tsx | 0 .../components/field_manager/field_picker.tsx | 2 +- .../public/components/field_manager/index.ts | 0 .../graph/public/components/graph_title.tsx | 0 .../graph_visualization.test.tsx.snap | 0 .../_graph_visualization.scss | 0 .../graph_visualization/_index.scss | 0 .../graph_visualization.test.tsx | 0 .../graph_visualization.tsx | 0 .../components/graph_visualization/index.ts | 0 .../guidance_panel/_guidance_panel.scss | 0 .../components/guidance_panel/_index.scss | 0 .../guidance_panel/guidance_panel.tsx | 2 +- .../public/components/guidance_panel/index.ts | 0 .../graph/public/components/helpers.ts | 0 .../public/components/legacy_icon/_index.scss | 0 .../components/legacy_icon/_legacy_icon.scss | 0 .../public/components/legacy_icon/index.ts | 0 .../components/legacy_icon/legacy_icon.tsx | 0 .../graph/public/components/listing.tsx | 2 +- .../graph/public/components/save_modal.tsx | 5 +- .../public/components/search_bar.test.tsx | 4 +- .../graph/public/components/search_bar.tsx | 4 +- .../public/components/settings/_index.scss | 0 .../components/settings/_legacy_icon.scss | 0 .../settings/_url_template_list.scss | 0 .../settings/advanced_settings_form.tsx | 0 .../components/settings/blacklist_form.tsx | 0 .../graph/public/components/settings/index.ts | 0 .../components/settings/settings.test.tsx | 0 .../public/components/settings/settings.tsx | 0 .../components/settings/url_template_form.tsx | 0 .../components/settings/url_template_list.tsx | 0 .../settings/use_list_keys.test.tsx | 0 .../components/settings/use_list_keys.ts | 0 .../graph/public/components/source_modal.tsx | 0 .../graph/public/components/source_picker.tsx | 2 +- .../components/venn_diagram/_index.scss | 0 .../venn_diagram/_venn_diagram.scss | 0 .../public/components/venn_diagram/index.ts | 0 .../venn_diagram/venn_diagram.test.tsx | 0 .../components/venn_diagram/venn_diagram.tsx | 0 .../graph/public/helpers/as_observable.ts | 0 .../graph/public/helpers/format_http_error.ts | 0 .../graph/public/helpers/kql_encoder.test.ts | 0 .../graph/public/helpers/kql_encoder.ts | 0 .../graph/public/helpers/outlink_encoders.ts | 0 .../graph/public/helpers/style_choices.ts | 0 .../graph/public/helpers/url_template.ts | 0 .../plugins/graph/public/index.scss | 3 - x-pack/plugins/graph/public/index.ts | 2 - x-pack/plugins/graph/public/plugin.ts | 69 ++- .../public/services/fetch_top_nodes.test.ts | 0 .../graph/public/services/fetch_top_nodes.ts | 0 .../public/services/index_pattern_cache.ts | 2 +- .../services/persistence/deserialize.test.ts | 2 +- .../services/persistence/deserialize.ts | 2 +- .../public/services/persistence/index.ts | 0 .../services/persistence/saved_workspace.ts | 2 +- .../persistence/saved_workspace_loader.ts | 2 +- .../saved_workspace_references.test.ts | 0 .../persistence/saved_workspace_references.ts | 0 .../services/persistence/serialize.test.ts | 0 .../public/services/persistence/serialize.ts | 0 .../graph/public/services/save_modal.tsx | 0 .../graph/public/services/source_modal.tsx | 0 .../plugins/graph/public/services/url.ts | 0 .../state_management/advanced_settings.ts | 0 .../state_management/datasource.sagas.ts | 2 +- .../state_management/datasource.test.ts | 2 +- .../public/state_management/datasource.ts | 0 .../graph/public/state_management/fields.ts | 0 .../graph/public/state_management/global.ts | 0 .../graph/public/state_management/helpers.ts | 0 .../graph/public/state_management/index.ts | 0 .../public/state_management/legacy.test.ts | 0 .../public/state_management/meta_data.test.ts | 0 .../public/state_management/meta_data.ts | 0 .../graph/public/state_management/mocks.ts | 6 +- .../state_management/persistence.test.ts | 0 .../public/state_management/persistence.ts | 0 .../graph/public/state_management/store.ts | 8 +- .../state_management/url_templates.test.ts | 8 +- .../public/state_management/url_templates.ts | 39 +- .../public/state_management/workspace.ts | 0 .../plugins/graph/public/types/app_state.ts | 2 +- .../plugins/graph/public/types/config.ts | 0 .../plugins/graph/public/types/index.ts | 0 .../plugins/graph/public/types/persistence.ts | 2 +- .../graph/public/types/workspace_state.ts | 2 +- 121 files changed, 339 insertions(+), 451 deletions(-) delete mode 100644 x-pack/legacy/plugins/graph/public/icon.png delete mode 100644 x-pack/legacy/plugins/graph/public/index.ts delete mode 100644 x-pack/legacy/plugins/graph/public/legacy_imports.ts delete mode 100644 x-pack/legacy/plugins/graph/public/plugin.ts rename x-pack/{legacy => }/plugins/graph/README.md (93%) rename x-pack/{legacy => }/plugins/graph/public/_main.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/_mixins.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/graph_client_workspace.d.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/graph_client_workspace.js (85%) rename x-pack/{legacy => }/plugins/graph/public/angular/graph_client_workspace.test.js (97%) rename x-pack/{legacy => }/plugins/graph/public/angular/templates/_graph.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/templates/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/templates/_inspect.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/templates/_sidebar.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/templates/index.html (100%) rename x-pack/{legacy => }/plugins/graph/public/angular/templates/listing_ng_wrapper.html (100%) rename x-pack/{legacy => }/plugins/graph/public/app.js (96%) rename x-pack/{legacy => }/plugins/graph/public/application.ts (81%) rename x-pack/{legacy => }/plugins/graph/public/badge.js (100%) rename x-pack/{legacy => }/plugins/graph/public/components/_app.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/_search_bar.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/_source_modal.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/app.tsx (96%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/_field_editor.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/_field_picker.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/field_editor.tsx (99%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/field_manager.test.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/field_manager.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/field_picker.tsx (98%) rename x-pack/{legacy => }/plugins/graph/public/components/field_manager/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_title.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_visualization/_graph_visualization.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_visualization/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_visualization/graph_visualization.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/graph_visualization/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/guidance_panel/_guidance_panel.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/guidance_panel/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/guidance_panel/guidance_panel.tsx (98%) rename x-pack/{legacy => }/plugins/graph/public/components/guidance_panel/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/helpers.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/legacy_icon/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/legacy_icon/_legacy_icon.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/legacy_icon/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/legacy_icon/legacy_icon.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/listing.tsx (98%) rename x-pack/{legacy => }/plugins/graph/public/components/save_modal.tsx (96%) rename x-pack/{legacy => }/plugins/graph/public/components/search_bar.test.tsx (95%) rename x-pack/{legacy => }/plugins/graph/public/components/search_bar.tsx (98%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/_legacy_icon.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/_url_template_list.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/advanced_settings_form.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/blacklist_form.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/settings.test.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/settings.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/url_template_form.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/url_template_list.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/use_list_keys.test.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/settings/use_list_keys.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/source_modal.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/source_picker.tsx (94%) rename x-pack/{legacy => }/plugins/graph/public/components/venn_diagram/_index.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/venn_diagram/_venn_diagram.scss (100%) rename x-pack/{legacy => }/plugins/graph/public/components/venn_diagram/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/components/venn_diagram/venn_diagram.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/as_observable.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/format_http_error.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/kql_encoder.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/kql_encoder.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/outlink_encoders.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/style_choices.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/helpers/url_template.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/index.scss (71%) rename x-pack/{legacy => }/plugins/graph/public/services/fetch_top_nodes.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/fetch_top_nodes.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/index_pattern_cache.ts (90%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/deserialize.test.ts (98%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/deserialize.ts (99%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/saved_workspace.ts (97%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/saved_workspace_loader.ts (95%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/saved_workspace_references.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/saved_workspace_references.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/serialize.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/persistence/serialize.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/services/save_modal.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/services/source_modal.tsx (100%) rename x-pack/{legacy => }/plugins/graph/public/services/url.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/advanced_settings.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/datasource.sagas.ts (96%) rename x-pack/{legacy => }/plugins/graph/public/state_management/datasource.test.ts (97%) rename x-pack/{legacy => }/plugins/graph/public/state_management/datasource.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/fields.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/global.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/helpers.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/legacy.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/meta_data.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/meta_data.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/mocks.ts (94%) rename x-pack/{legacy => }/plugins/graph/public/state_management/persistence.test.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/persistence.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/state_management/store.ts (93%) rename x-pack/{legacy => }/plugins/graph/public/state_management/url_templates.test.ts (91%) rename x-pack/{legacy => }/plugins/graph/public/state_management/url_templates.ts (82%) rename x-pack/{legacy => }/plugins/graph/public/state_management/workspace.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/types/app_state.ts (94%) rename x-pack/{legacy => }/plugins/graph/public/types/config.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/types/index.ts (100%) rename x-pack/{legacy => }/plugins/graph/public/types/persistence.ts (95%) rename x-pack/{legacy => }/plugins/graph/public/types/workspace_state.ts (97%) diff --git a/x-pack/legacy/plugins/graph/index.ts b/x-pack/legacy/plugins/graph/index.ts index b2d6fd3957d64..5122796335e45 100644 --- a/x-pack/legacy/plugins/graph/index.ts +++ b/x-pack/legacy/plugins/graph/index.ts @@ -4,31 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { resolve } from 'path'; import { i18n } from '@kbn/i18n'; // @ts-ignore import migrations from './migrations'; import mappings from './mappings.json'; import { LegacyPluginInitializer } from '../../../../src/legacy/plugin_discovery/types'; -import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; export const graph: LegacyPluginInitializer = kibana => { return new kibana.Plugin({ id: 'graph', configPrefix: 'xpack.graph', - publicDir: resolve(__dirname, 'public'), require: ['kibana', 'elasticsearch', 'xpack_main'], uiExports: { - app: { - title: 'Graph', - order: 9000, - icon: 'plugins/graph/icon.png', - euiIconType: 'graphApp', - main: 'plugins/graph/index', - category: DEFAULT_APP_CATEGORIES.analyze, - }, - styleSheetPaths: resolve(__dirname, 'public/index.scss'), mappings, migrations, }, diff --git a/x-pack/legacy/plugins/graph/public/icon.png b/x-pack/legacy/plugins/graph/public/icon.png deleted file mode 100644 index f2a16437209c797b96ac09d5b8ab9f22d0383664..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6203 zcmch4c|6qL_y22#$^jnz>$w;hEM?DHNIwSY<<$$80B}}*Zmsd3IK!-FFSjtV{q8Q56M&!TWDi`PnCRp1Ylf5r z)p`{=00Xwb)sb~4^+BFT7kRsGtdogao7jE1`PNKb8EIZVzBvWg{2JA9vi^?xSKm=` z#0n_g9=tIR_-A4%MT~;zEq@oE;9{Gksa8%SHJ{Zf`R(S|TU$v7yT^`}I%32V7VPiP ztC3#QshhWTK8FY$m)jQ@bBYJInDeGzk0lg{)*k)1!dyz@tJ=q=%2ax+Tl@ z>5(cPKkq(09TW)MOh|Ggb*l-w-|csCyc=ige8*l=u^HhuVXtAEtl=hOe=JyBijnKW z=H3q_lSF5^FU7z=5Ta62%QhOZ~S>F`dTvF$zUh}xNdR66nU=`1B^gh${uJj(S zX|C^6-=lwp@C)1)h;BA_Js{yF5hn3`Z>59^#o`(4Q#yrtUiI@4DT+siP=@aX`$H>- z!o^cRiagMHVEZ8A!PLw17XmIA^b%k4oqu&f=+!0Lo6k;Mcw;?Y$n!7aRd1oph3Z1h z;trb-n`GlXs)_!Gz?`s-E&s%LQ50Swltr-v17_={4SbwPC58CDr0oug5n0HNR>< z=HE7+^E~VMhozJ$l}o{waF;e}_F28jR?O{F^=1w>IE>b`hB@o#>s|TX@ts4zNgtYb zBQNg#v-ju`z4!7FeG!xhuFc$y-d1CQ>r;4Tl$pEGLEY zDVGK;5mvoNgTq`z1B%Nk35sfmkR@rMc@!ZE!MszVIE7r>N{o};P zXG<&Tr*!X4=GZ=1(HcH})A;w*+g2xnn$&r$Y?O$p68GN9FIn1}^nIar_WSoA*1n&8 zKfAz2;M&77rO&FB2P&=Hm6Xy1z7Z$AKT4&=eO&z}zaFqOC{BpA=q@O;DYh0wSg5>E z3U%1)5K1?%F~9h)`iT4Xk9B#zC`5QoN{KbSOV0)(b zI@sPignV$B>PLN1yjF68J~?K0=5lZNGSPcr;LEAXk8?BTGf!rGsH?cz*QaVc4STz8 z&kGbuEhJ7zUG_fpB{2Vn=WfmC&X2HshoLf$tFm)XuBM^yacp&(EmgWJy=4s@lnjVY*p}Rby^)*wZks z6;t+%XP_@(=yh!^jmO(YGjX4}j^9KU$#)=YE;FRu@8TiB4~7$U|BO5f4;#0h&hMxn zs&5~m+^SsWo-rpX_2|?FQ4be*Khn#uVNc&J$dS1uWA?ssCxWdu?iSRnk!+XyDCdCQ z!uq$UwDITo=eap&4D|k`3=vl?HYr|CXPs(1j5~7;kbyrfDlf0LEPa_ucA9_Z_Y1x1 zI;c>2x=62B&n&`k>#O-|2j7`1tF3c(y2XLNEN6~)vp?PX?aAm3_`LR6*(r}$!)}_D zs`ODDmZmFIEw2Ty%ppT~e_9MV-AE37z4?A%>j&3wp&z%(wo9)tyhqoZ8P1*cI3czm zPh9Ty{hd#7Uwh2j%zEWd$R{S}$jV9yNY*UkJJP;Br#%<#{1{=oL!1eCH8rv|wsE@C zev>uNw@&V?EhZK&U1z^4e67u0z!bo6@b_(>4{H4(4gV-{+Ft#ue}LUC z52%Qfm}o8F>6$PCTD{0vFI8jVAwU+U!hR5gqH$B7qsmkhWsw{tW>ot=I1Vlbq{2vA z+w-srRt{H}ED4&00Z_Ev-jy>VguXkHtViVDdXL$)k_!7zH$eb@I~)i6?N~enm0oQI z%(GtY?5Vt!brBDtA9y*kwIk}33WN8-b{wLVXuz1{X01vEnh#a%+L{)gf48cwM+Tez zl5$j369@R0_b%$*vO>V^Yu!x{&bHK;3VnklhH2@{{JQ_6x9TdOPUJGBq-89sViNHG7lnmhOCY(9c>?V1@?_l9egIGGMR+Fdjb&b>Lqh^z2 zp$JEQKoduhtKcV;PPQrXDZ`Mm&S{M&&?W9oZ7ykzjic%iG~cI!{Uwa%o1?-hCzoY8 zc&QR<(JZ{b;aWKdn%vd5w#125~s9q#PQL zX{=sa(oUWJd9EVDY7m;Hr$+&HMH0 z!njZua*zwC=Nzzvf#V3JAUPV_rVj3HSIaqVslR_ghRDfsrRW4e$)NWFIc@QE2tE{z zB+%($vTLj(feF1I-XdymRC_ELaKJ#7N*d^4oWNLuCBgx7x8F4#--U8k|5i6iAY zG~Qv{+^&6li2lJd4d#F;s$|9YhwYSScoAp}7USp8T}t~%S|j3gn7dFajGN^OLx3*i zgG9nWj+dH#+yvYSr)o$%2FpMwmF)VFn;QdiaFyUEnP$7YcB-&fkF!LeP$l3wBlrv> zH3GoPjIMx1n&SPx)-5|wiN<(|kcCKX4$w522>u2~YKz)~mSLf(AU9#wF0o`3$Aogi z?N7D8GGmBK-6kiCJ5aUj75U(B`(5wC8?xnS0K|dHK+{;Ra;Oq8fD9v-(Q)7r#{%yB z|7-7K3Q3x8W`$Wt9ZC2B*LGyikRCK0fu~$3X|dUUd?UH|FhV5;MN@um!7(mE(Dt&6 zcn#%4D-|mn28hoO^IsJYDnQEpzyYgyVupedUT#!DPI z=A3rr!-`A&VmKr8BEX=4f_L~o{f{cB4a~Q+%dbi7 zD6@}62USxJ2e?I)wA|nX^B3tb!lcg>nA`)$#c+b@35)k8ua z@@OWx?i^f5-K`khd4{rafLl{rlE*=svPF6xrS4r(rnrawu0SuK{zdaKKaq@$WybyP-o$;R3C*|uDs;7IwBGEMmmSLZu1O6tkdxnDBJ+31=LrEh=|4$+3&Y8NL?Zy2xx~`1w8?=pFp4$&!4$zBMT%A<0tVqwnU0pg> zcAwZzxzL+?>(!n6hVI-t+)6~^(x=k6Os=+{ie>A%`I#bw>R~s2ICB?i7yi=Qh|>cP zleQHVx`N^^V%cu^H1_GnV@|8wvE#aY)%3}akVABLBmCQ6w0Ozu`Qj-*M)F59g;Sqy zDNF!;{N|NxOTYF6Cc11bsloQDDI%S29RIdy9f9j{G)I$1d5f4)U9L_3?@bZ;usU%Z z7**q{Xu8oLE9-Tx1y@UKY$%F`Dg(gu3T7uGr7~<~SeHC1(||vA-TeoyqQ$iU2Il$# zrwznr@9WRDiRsuZD!7`IvN=Tala@TXb*-zIE(ZIoJMI{daWkr3LW?=Rxyv+a?Q*XI z8vCTlRHhNR%l-XYLmxkRR(hLP=&8`K9nx{-bmvokP27RXZri)*MSZh33M*QSS`zzU z0`VCBksmP#$q}!8kjLW+9O_>0y=hu^l|8X|!>haK9PDEh14B9S<7SN&uqA@BHx1xUW+Y%pzRRp zsj$7`L*k|4b|6tZd`aBttT+zfq#eCA&jUuJ?=%=}p%Re)Li>A*8;M{XUNZU%R)dmY zoTUqa(dGP^Txy1OKfWE7S%CwK6B^|z3QV6*JqCH;qQ zl!geU)-Hlr&Ui*|d%dRNk(gApD+!7ugLb&7?2oH^Kq0yeMWerP>HtmfL1#984M#C; z#*Km@o!mui$yC^&QpH6$juQcHwb69ux(3H2c;;>Hvj zc}2i+jv2~kgmSP(xU}mFPDH?!CU7uHS$`#_uT73tuaek?Br#lH&8^R7o2JU#RKsfl zP%^731l}K<&iGk}v{TG3O$g{;|6+(w7g|rML)sI{e@cTXs#)N7LfvY}WnqVqB=y6z z0xmt*Y46usX+-y9OF84=gXoS7SkrBbueG_s=JgYZ?Vv<0w|K{xMOu zObC+!vh|QxZpW9u_$P0+WtojSlW+#e`4*LxhnX%}R24_Zx~=VG8Ea zdLbin_2j7ttG+JdjW=Jzozs?W50#7x`m;e~CscGP)4pK!kR^9ONUpf3vKmi~fBD { - const instance = new GraphPlugin(); - instance.setup(npSetup.core, { - ...(npSetup.plugins as XpackNpSetupDeps), - }); - instance.start(npStart.core, { - npData: npStart.plugins.data, - navigation: npStart.plugins.navigation, - }); -})(); diff --git a/x-pack/legacy/plugins/graph/public/legacy_imports.ts b/x-pack/legacy/plugins/graph/public/legacy_imports.ts deleted file mode 100644 index 274cdc65232e2..0000000000000 --- a/x-pack/legacy/plugins/graph/public/legacy_imports.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'ace'; - -export { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public'; diff --git a/x-pack/legacy/plugins/graph/public/plugin.ts b/x-pack/legacy/plugins/graph/public/plugin.ts deleted file mode 100644 index 4ccaf6b5dfa27..0000000000000 --- a/x-pack/legacy/plugins/graph/public/plugin.ts +++ /dev/null @@ -1,75 +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. - */ - -// NP type imports -import { - AppMountParameters, - CoreSetup, - CoreStart, - Plugin, - SavedObjectsClientContract, -} from 'src/core/public'; -import { Plugin as DataPlugin } from 'src/plugins/data/public'; -import { Storage } from '../../../../../src/plugins/kibana_utils/public'; -import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; -import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public'; -import { initAngularBootstrap } from '../../../../../src/plugins/kibana_legacy/public'; -import { GraphSetup } from '../../../../plugins/graph/public'; - -export interface GraphPluginStartDependencies { - npData: ReturnType; - navigation: NavigationStart; -} - -export interface GraphPluginSetupDependencies { - licensing: LicensingPluginSetup; - graph: GraphSetup; -} - -export class GraphPlugin implements Plugin { - private navigationStart: NavigationStart | null = null; - private npDataStart: ReturnType | null = null; - private savedObjectsClient: SavedObjectsClientContract | null = null; - - setup(core: CoreSetup, { licensing, graph }: GraphPluginSetupDependencies) { - initAngularBootstrap(); - core.application.register({ - id: 'graph', - title: 'Graph', - mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { renderApp } = await import('./application'); - return renderApp({ - ...params, - licensing, - navigation: this.navigationStart!, - npData: this.npDataStart!, - savedObjectsClient: this.savedObjectsClient!, - addBasePath: core.http.basePath.prepend, - getBasePath: core.http.basePath.get, - canEditDrillDownUrls: graph.config.canEditDrillDownUrls, - graphSavePolicy: graph.config.savePolicy, - storage: new Storage(window.localStorage), - capabilities: coreStart.application.capabilities.graph, - coreStart, - chrome: coreStart.chrome, - config: coreStart.uiSettings, - toastNotifications: coreStart.notifications.toasts, - indexPatterns: this.npDataStart!.indexPatterns, - overlays: coreStart.overlays, - }); - }, - }); - } - - start(core: CoreStart, { npData, navigation }: GraphPluginStartDependencies) { - this.navigationStart = navigation; - this.npDataStart = npData; - this.savedObjectsClient = core.savedObjects.client; - } - - stop() {} -} diff --git a/x-pack/legacy/plugins/graph/README.md b/x-pack/plugins/graph/README.md similarity index 93% rename from x-pack/legacy/plugins/graph/README.md rename to x-pack/plugins/graph/README.md index f402b35bba49f..9cc2617abe94c 100644 --- a/x-pack/legacy/plugins/graph/README.md +++ b/x-pack/plugins/graph/README.md @@ -8,7 +8,7 @@ Graph shows only up in the side bar if your server is running on a platinum or t * Run tests `node x-pack/scripts/jest.js --watch plugins/graph` * Run type check `node scripts/type_check.js --project=x-pack/tsconfig.json` -* Run linter `node scripts/eslint.js x-pack/legacy/plugins/graph` +* Run linter `node scripts/eslint.js x-pack/plugins/graph` * Run functional tests (make sure to stop dev server) * Server `cd x-pack && node ./scripts/functional_tests_server.js` * Tests `cd x-pack && node ../scripts/functional_test_runner.js --config ./test/functional/config.js --grep=graph` @@ -21,7 +21,6 @@ Currently most of the state handling is done by a central angular controller. Th * `angular/` contains all code using javascript and angular. Rewriting this code in typescript and react is currently ongoing. When the migration is finished, this folder will go away * `components/` contains react components for various parts of the interface. Components can hold local UI state (e.g. current form data), everything else should be passed in from the caller. Styles should reside in a component-specific stylesheet -* `hacks/` contains files that need to run before the actual app is started. When moving to the new platform, this folder will go away. * `services/` contains functions that encapsule other parts of Kibana. Stateful dependencies are passed in from the outside. Components should not rely on services directly but have callbacks passed in. Once the migration to redux/saga is complete, only sagas will use services. * `helpers/` contains side effect free helper functions that can be imported and used from components and services * `state_management/` contains reducers, action creators, selectors and sagas. It also exports the central store creator diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 0c77b446fa28d..cf3a5fab92f56 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["licensing"], + "requiredPlugins": ["licensing", "data", "navigation"], "optionalPlugins": ["home"], "configPath": ["xpack", "graph"] } diff --git a/x-pack/legacy/plugins/graph/public/_main.scss b/x-pack/plugins/graph/public/_main.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/_main.scss rename to x-pack/plugins/graph/public/_main.scss diff --git a/x-pack/legacy/plugins/graph/public/_mixins.scss b/x-pack/plugins/graph/public/_mixins.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/_mixins.scss rename to x-pack/plugins/graph/public/_mixins.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.d.ts b/x-pack/plugins/graph/public/angular/graph_client_workspace.d.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.d.ts rename to x-pack/plugins/graph/public/angular/graph_client_workspace.d.ts diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js b/x-pack/plugins/graph/public/angular/graph_client_workspace.js similarity index 85% rename from x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js rename to x-pack/plugins/graph/public/angular/graph_client_workspace.js index 14d4248dc664d..a7d98a42404ec 100644 --- a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.js +++ b/x-pack/plugins/graph/public/angular/graph_client_workspace.js @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import $ from 'jquery'; + // Kibana wrapper const d3 = require('d3'); @@ -79,7 +81,7 @@ module.exports = (function() { self.redo = reverseOperation.undo; } - function GroupOperation(receiver, orphan, vm) { + function GroupOperation(receiver, orphan) { const self = this; self.receiver = receiver; self.orphan = orphan; @@ -91,7 +93,7 @@ module.exports = (function() { }; } - function UnGroupOperation(parent, child, vm) { + function UnGroupOperation(parent, child) { const self = this; self.parent = parent; self.child = child; @@ -152,9 +154,7 @@ module.exports = (function() { if (lastOps) { this.stopLayout(); this.redoLog.push(lastOps); - for (const i in lastOps) { - lastOps[i].undo(); - } + lastOps.forEach(ops => ops.undo()); this.runLayout(); } }; @@ -163,29 +163,22 @@ module.exports = (function() { if (lastOps) { this.stopLayout(); this.undoLog.push(lastOps); - for (const i in lastOps) { - lastOps[i].redo(); - } + lastOps.forEach(ops => ops.redo()); this.runLayout(); } }; //Determines if 2 nodes are connected via an edge this.areLinked = function(a, b) { - if (a == b) return true; - const allEdges = this.edges; - for (const e in allEdges) { - if (e.source == a) { - if (e.target == b) { - return true; - } + if (a === b) return true; + this.edges.forEach(e => { + if (e.source === a && e.target === b) { + return true; } - if (e.source == b) { - if (e.target == a) { - return true; - } + if (e.source === b && e.target === a) { + return true; } - } + }); return false; }; @@ -193,47 +186,43 @@ module.exports = (function() { this.selectAll = function() { self.selectedNodes = []; - for (const n in self.nodes) { - const node = self.nodes[n]; - if (node.parent == undefined) { + self.nodes.forEach(node => { + if (node.parent === undefined) { node.isSelected = true; self.selectedNodes.push(node); } else { node.isSelected = false; } - } + }); }; this.selectNone = function() { self.selectedNodes = []; - for (const n in self.nodes) { - const node = self.nodes[n]; + self.nodes.forEach(node => { node.isSelected = false; - } + }); }; this.selectInvert = function() { self.selectedNodes = []; - for (const n in self.nodes) { - const node = self.nodes[n]; - if (node.parent != undefined) { - continue; + self.nodes.forEach(node => { + if (node.parent !== undefined) { + return; } node.isSelected = !node.isSelected; if (node.isSelected) { self.selectedNodes.push(node); } - } + }); }; this.selectNodes = function(nodes) { - for (const n in nodes) { - const node = nodes[n]; + nodes.forEach(node => { node.isSelected = true; if (self.selectedNodes.indexOf(node) < 0) { self.selectedNodes.push(node); } - } + }); }; this.selectNode = function(node) { @@ -247,29 +236,27 @@ module.exports = (function() { let allAndGrouped = self.returnUnpackedGroupeds(self.selectedNodes); // Nothing selected so process all nodes - if (allAndGrouped.length == 0) { + if (allAndGrouped.length === 0) { allAndGrouped = self.nodes.slice(0); } const undoOperations = []; - for (const i in allAndGrouped) { - const node = allAndGrouped[i]; + allAndGrouped.forEach(node => { //We set selected to false because despite being deleted, node objects sit in an undo log node.isSelected = false; delete self.nodesMap[node.id]; undoOperations.push(new ReverseOperation(new AddNodeOperation(node, self))); - } + }); self.arrRemoveAll(self.nodes, allAndGrouped); self.arrRemoveAll(self.selectedNodes, allAndGrouped); const danglingEdges = self.edges.filter(function(edge) { return self.nodes.indexOf(edge.source) < 0 || self.nodes.indexOf(edge.target) < 0; }); - for (const i in danglingEdges) { - const edge = danglingEdges[i]; + danglingEdges.forEach(edge => { delete self.edgesMap[edge.id]; undoOperations.push(new ReverseOperation(new AddEdgeOperation(edge, self))); - } + }); self.addUndoLogEntry(undoOperations); self.arrRemoveAll(self.edges, danglingEdges); self.runLayout(); @@ -277,8 +264,7 @@ module.exports = (function() { this.selectNeighbours = function() { const newSelections = []; - for (const n in self.edges) { - const edge = self.edges[n]; + self.edges.forEach(edge => { if (!edge.topSrc.isSelected) { if (self.selectedNodes.indexOf(edge.topTarget) >= 0) { if (newSelections.indexOf(edge.topSrc) < 0) { @@ -293,18 +279,17 @@ module.exports = (function() { } } } - } - for (const i in newSelections) { - const newlySelectedNode = newSelections[i]; + }); + newSelections.forEach(newlySelectedNode => { self.selectedNodes.push(newlySelectedNode); newlySelectedNode.isSelected = true; - } + }); }; this.selectNone = function() { - for (const n in self.selectedNodes) { - self.selectedNodes[n].isSelected = false; - } + self.selectedNodes.forEach(node => { + node.isSelected = false; + }); self.selectedNodes = []; }; @@ -318,30 +303,25 @@ module.exports = (function() { }; this.colorSelected = function(colorNum) { - const selections = self.getAllSelectedNodes(); - for (const i in selections) { - selections[i].color = colorNum; - } + self.getAllSelectedNodes().forEach(node => { + node.color = colorNum; + }); }; this.getSelectionsThatAreGrouped = function() { const result = []; - const selections = self.selectedNodes; - for (const i in selections) { - const node = selections[i]; + self.selectedNodes.forEach(node => { if (node.numChildren > 0) { result.push(node); } - } + }); return result; }; this.ungroupSelection = function() { - const selections = self.getSelectionsThatAreGrouped(); - for (const i in selections) { - const node = selections[i]; + self.getSelectionsThatAreGrouped().forEach(node => { self.ungroup(node); - } + }); }; this.toggleNodeSelection = function(node) { @@ -371,7 +351,7 @@ module.exports = (function() { if (result.indexOf(topLevelTarget) >= 0) { //visible top-level node is selected - add all nesteds starting from bottom up let target = edge.target; - while (target.parent != undefined) { + while (target.parent !== undefined) { if (result.indexOf(target) < 0) { result.push(target); } @@ -382,7 +362,7 @@ module.exports = (function() { if (result.indexOf(topLevelSource) >= 0) { //visible top-level node is selected - add all nesteds starting from bottom up let source = edge.source; - while (source.parent != undefined) { + while (source.parent !== undefined) { if (result.indexOf(source) < 0) { result.push(source); } @@ -425,22 +405,21 @@ module.exports = (function() { this.getNeighbours = function(node) { const neighbourNodes = []; - for (const e in self.edges) { - const edge = self.edges[e]; - if (edge.topSrc == edge.topTarget) { - continue; + self.edges.forEach(edge => { + if (edge.topSrc === edge.topTarget) { + return; } - if (edge.topSrc == node) { + if (edge.topSrc === node) { if (neighbourNodes.indexOf(edge.topTarget) < 0) { neighbourNodes.push(edge.topTarget); } } - if (edge.topTarget == node) { + if (edge.topTarget === node) { if (neighbourNodes.indexOf(edge.topSrc) < 0) { neighbourNodes.push(edge.topSrc); } } - } + }); return neighbourNodes; }; @@ -448,7 +427,7 @@ module.exports = (function() { this.buildNodeQuery = function(topLevelNode) { let containedNodes = [topLevelNode]; containedNodes = self.returnUnpackedGroupeds(containedNodes); - if (containedNodes.length == 1) { + if (containedNodes.length === 1) { //Simple case - return a single-term query const tq = {}; tq[topLevelNode.data.field] = topLevelNode.data.term; @@ -457,17 +436,16 @@ module.exports = (function() { }; } const termsByField = {}; - for (const i in containedNodes) { - const node = containedNodes[i]; + containedNodes.forEach(node => { let termsList = termsByField[node.data.field]; if (!termsList) { termsList = []; termsByField[node.data.field] = termsList; } termsList.push(node.data.term); - } + }); //Single field case - if (Object.keys(termsByField).length == 1) { + if (Object.keys(termsByField).length === 1) { return { terms: termsByField, }; @@ -479,11 +457,13 @@ module.exports = (function() { }, }; for (const field in termsByField) { - const tq = {}; - tq[field] = termsByField[field]; - q.bool.should.push({ - terms: tq, - }); + if (termsByField.hasOwnProperty(field)) { + const tq = {}; + tq[field] = termsByField[field]; + q.bool.should.push({ + terms: tq, + }); + } } return q; }; @@ -503,39 +483,40 @@ module.exports = (function() { // is potentially a reduced set of nodes if the client has used any // grouping of nodes into parent nodes. const effectiveEdges = []; - const edges = self.edges; - for (const e in edges) { - const edge = edges[e]; + self.edges.forEach(edge => { let topSrc = edge.source; let topTarget = edge.target; - while (topSrc.parent != undefined) { + while (topSrc.parent !== undefined) { topSrc = topSrc.parent; } - while (topTarget.parent != undefined) { + while (topTarget.parent !== undefined) { topTarget = topTarget.parent; } edge.topSrc = topSrc; edge.topTarget = topTarget; - if (topSrc != topTarget) { + if (topSrc !== topTarget) { effectiveEdges.push({ source: topSrc, target: topTarget, }); } - } + }); const visibleNodes = self.nodes.filter(function(n) { - return n.parent == undefined; + return n.parent === undefined; }); //reset then roll-up all the counts const allNodes = self.nodes; - for (const n in allNodes) { - const node = allNodes[n]; + allNodes.forEach(node => { node.numChildren = 0; - } + }); + for (const n in allNodes) { + if (!allNodes.hasOwnProperty(n)) { + continue; + } let node = allNodes[n]; - while (node.parent != undefined) { + while (node.parent !== undefined) { node = node.parent; node.numChildren = node.numChildren + 1; } @@ -551,36 +532,34 @@ module.exports = (function() { .theta(0.99) .alpha(0.5) .size([800, 600]) - .on('tick', function(e) { + .on('tick', function() { const nodeArray = self.nodes; let hasRollups = false; //Update the position of all "top level nodes" - for (const i in nodeArray) { - const n = nodeArray[i]; + nodeArray.forEach(n => { //Code to support roll-ups - if (n.parent == undefined) { + if (n.parent === undefined) { n.kx = n.x; n.ky = n.y; } else { hasRollups = true; } - } + }); if (hasRollups) { - for (const i in nodeArray) { - const n = nodeArray[i]; + nodeArray.forEach(n => { //Code to support roll-ups - if (n.parent != undefined) { + if (n.parent !== undefined) { // Is a grouped node - inherit parent's position so edges point into parent // d3 thinks it has moved it to x and y but we have final say using kx and ky. let topLevelNode = n.parent; - while (topLevelNode.parent != undefined) { + while (topLevelNode.parent !== undefined) { topLevelNode = topLevelNode.parent; } n.kx = topLevelNode.x; n.ky = topLevelNode.y; } - } + }); } if (self.changeHandler) { // Hook to allow any client to respond to position changes @@ -597,11 +576,11 @@ module.exports = (function() { this.groupSelections = function(node) { const ops = []; self.nodes.forEach(function(otherNode) { - if (otherNode != node && otherNode.isSelected && otherNode.parent == undefined) { + if (otherNode !== node && otherNode.isSelected && otherNode.parent === undefined) { otherNode.parent = node; otherNode.isSelected = false; self.arrRemove(self.selectedNodes, otherNode); - ops.push(new GroupOperation(node, otherNode, self)); + ops.push(new GroupOperation(node, otherNode)); } }); self.selectNone(); @@ -614,11 +593,11 @@ module.exports = (function() { const neighbours = self.getNeighbours(node); const ops = []; neighbours.forEach(function(otherNode) { - if (otherNode != node && otherNode.parent == undefined) { + if (otherNode !== node && otherNode.parent === undefined) { otherNode.parent = node; otherNode.isSelected = false; self.arrRemove(self.selectedNodes, otherNode); - ops.push(new GroupOperation(node, otherNode, self)); + ops.push(new GroupOperation(node, otherNode)); } }); self.addUndoLogEntry(ops); @@ -633,11 +612,11 @@ module.exports = (function() { const selClone = self.selectedNodes.slice(); const ops = []; selClone.forEach(function(otherNode) { - if (otherNode != targetNode && otherNode.parent == undefined) { + if (otherNode !== targetNode && otherNode.parent === undefined) { otherNode.parent = targetNode; otherNode.isSelected = false; self.arrRemove(self.selectedNodes, otherNode); - ops.push(new GroupOperation(targetNode, otherNode, self)); + ops.push(new GroupOperation(targetNode, otherNode)); } }); self.addUndoLogEntry(ops); @@ -647,9 +626,9 @@ module.exports = (function() { this.ungroup = function(node) { const ops = []; self.nodes.forEach(function(other) { - if (other.parent == node) { + if (other.parent === node) { other.parent = undefined; - ops.push(new UnGroupOperation(node, other, self)); + ops.push(new UnGroupOperation(node, other)); } }); self.addUndoLogEntry(ops); @@ -669,12 +648,11 @@ module.exports = (function() { danglingEdges.push(edge); } }); - for (const n in selection) { - const node = selection[n]; + selection.forEach(node => { delete self.nodesMap[node.id]; self.blacklistedNodes.push(node); node.isSelected = false; - } + }); self.arrRemoveAll(self.nodes, selection); self.arrRemoveAll(self.edges, danglingEdges); self.selectedNodes = []; @@ -722,9 +700,7 @@ module.exports = (function() { for (let hopNum = 0; hopNum < numHops; hopNum++) { const arr = []; - for (const f in fieldsChoice) { - const field = fieldsChoice[f].name; - const hopSize = fieldsChoice[f].hopSize; + fieldsChoice.forEach(({ name: field, hopSize }) => { const excludes = excludeNodesByField[field]; const stepField = { field: field, @@ -735,7 +711,7 @@ module.exports = (function() { stepField.exclude = excludes; } arr.push(stepField); - } + }); step.vertices = arr; if (hopNum < numHops - 1) { // if (s < (stepSizes.length - 1)) { @@ -814,8 +790,7 @@ module.exports = (function() { //Remove nodes we already have const dedupedNodes = []; - for (const o in newData.nodes) { - const node = newData.nodes[o]; + newData.nodes.forEach(node => { //Assign an ID node.id = self.makeNodeId(node.field, node.term); if (!this.nodesMap[node.id]) { @@ -825,14 +800,13 @@ module.exports = (function() { } dedupedNodes.push(node); } - } + }); if (dedupedNodes.length > 0 && this.options.nodeLabeller) { // A hook for client code to attach labels etc to newly introduced nodes. this.options.nodeLabeller(dedupedNodes); } - for (const o in dedupedNodes) { - const dedupedNode = dedupedNodes[o]; + dedupedNodes.forEach(dedupedNode => { let label = dedupedNode.term; if (dedupedNode.label) { label = dedupedNode.label; @@ -856,10 +830,9 @@ module.exports = (function() { this.nodes.push(node); lastOps.push(new AddNodeOperation(node, self)); this.nodesMap[node.id] = node; - } + }); - for (const o in newData.edges) { - const edge = newData.edges[o]; + newData.edges.forEach(edge => { const src = newData.nodes[edge.source]; const target = newData.nodes[edge.target]; edge.id = this.makeEdgeId(src.id, target.id); @@ -873,7 +846,7 @@ module.exports = (function() { existingEdge.weight = Math.max(existingEdge.weight, edge.weight); //TODO update width too? existingEdge.doc_count = Math.max(existingEdge.doc_count, edge.doc_count); - continue; + return; } const newEdge = { source: srcWrapperObj, @@ -890,7 +863,7 @@ module.exports = (function() { this.edgesMap[newEdge.id] = newEdge; this.edges.push(newEdge); lastOps.push(new AddEdgeOperation(newEdge, self)); - } + }); if (lastOps.length > 0) { self.addUndoLogEntry(lastOps); @@ -907,7 +880,7 @@ module.exports = (function() { self.arrRemove(self.selectedNodes, child); } child.parent = parent; - self.addUndoLogEntry([new GroupOperation(parent, child, self)]); + self.addUndoLogEntry([new GroupOperation(parent, child)]); self.runLayout(); }; @@ -922,7 +895,7 @@ module.exports = (function() { this.expandSelecteds = function(targetOptions = {}) { let startNodes = self.getAllSelectedNodes(); - if (startNodes.length == 0) { + if (startNodes.length === 0) { startNodes = self.nodes; } const clone = startNodes.slice(); @@ -1000,11 +973,13 @@ module.exports = (function() { const primaryVertices = []; const secondaryVertices = []; for (const fieldName in nodesByField) { - primaryVertices.push({ - field: fieldName, - include: nodesByField[fieldName], - min_doc_count: parseInt(self.options.exploreControls.minDocCount), - }); + if (nodesByField.hasOwnProperty(fieldName)) { + primaryVertices.push({ + field: fieldName, + include: nodesByField[fieldName], + min_doc_count: parseInt(self.options.exploreControls.minDocCount), + }); + } } let targetFields = this.options.vertex_fields; @@ -1013,11 +988,11 @@ module.exports = (function() { } //Identify target fields - for (const f in targetFields) { - const fieldName = targetFields[f].name; + targetFields.forEach(targetField => { + const fieldName = targetField.name; // Sometimes the target field is disabled from loading new hops so we need to use the last valid figure const hopSize = - targetFields[f].hopSize > 0 ? targetFields[f].hopSize : targetFields[f].lastValidHopSize; + targetField.hopSize > 0 ? targetField.hopSize : targetField.lastValidHopSize; const fieldHop = { field: fieldName, @@ -1026,7 +1001,7 @@ module.exports = (function() { }; fieldHop.exclude = excludeNodesByField[fieldName]; secondaryVertices.push(fieldHop); - } + }); const request = { controls: self.buildControls(), @@ -1038,33 +1013,27 @@ module.exports = (function() { self.lastRequest = JSON.stringify(request, null, '\t'); graphExplorer(self.options.indexName, request, function(data) { self.lastResponse = JSON.stringify(data, null, '\t'); - const nodes = []; const edges = []; //Label fields with a field number for CSS styling - for (const n in data.vertices) { - const node = data.vertices[n]; - for (const f in targetFields) { - const fieldDef = targetFields[f]; - if (node.field == fieldDef.name) { + data.vertices.forEach(node => { + targetFields.some(fieldDef => { + if (node.field === fieldDef.name) { node.color = fieldDef.color; node.icon = fieldDef.icon; node.fieldDef = fieldDef; - break; + return true; } - } - } + return false; + }); + }); // Size the edges based on the maximum weight const minLineSize = 2; const maxLineSize = 10; let maxEdgeWeight = 0.00000001; - for (const e in data.connections) { - const edge = data.connections[e]; + data.connections.forEach(edge => { maxEdgeWeight = Math.max(maxEdgeWeight, edge.weight); - } - for (const e in data.connections) { - const edge = data.connections[e]; edges.push({ source: edge.source, target: edge.target, @@ -1072,7 +1041,7 @@ module.exports = (function() { weight: edge.weight, width: Math.max(minLineSize, (edge.weight / maxEdgeWeight) * maxLineSize), }); - } + }); // Add the new nodes and edges into the existing workspace's graph self.mergeGraph({ @@ -1087,8 +1056,7 @@ module.exports = (function() { let trimmedEdges = []; const maxNumEdgesToReturn = 5; //Trim here to just the new edges that are most interesting. - for (const o in newEdges) { - const edge = newEdges[o]; + newEdges.forEach(edge => { const src = newNodes[edge.source]; const target = newNodes[edge.target]; const srcId = src.field + '..' + src.term; @@ -1097,25 +1065,25 @@ module.exports = (function() { const existingSrcNode = self.nodesMap[srcId]; const existingTargetNode = self.nodesMap[targetId]; if (existingSrcNode != null && existingTargetNode != null) { - if (existingSrcNode.parent != undefined && existingTargetNode.parent != undefined) { + if (existingSrcNode.parent !== undefined && existingTargetNode.parent !== undefined) { // both nodes are rolled-up and grouped so this edge would not be a visible // change to the graph - lose it in favour of any other visible ones. - continue; + return; } } else { console.log('Error? Missing nodes ' + srcId + ' or ' + targetId, self.nodesMap); - continue; + return; } const existingEdge = self.edgesMap[id]; if (existingEdge) { existingEdge.weight = Math.max(existingEdge.weight, edge.weight); existingEdge.doc_count = Math.max(existingEdge.doc_count, edge.doc_count); - continue; + return; } else { trimmedEdges.push(edge); } - } + }); if (trimmedEdges.length > maxNumEdgesToReturn) { //trim to only the most interesting ones trimmedEdges.sort(function(a, b) { @@ -1132,12 +1100,11 @@ module.exports = (function() { if (!startNodes) { nodes = self.nodes; } - for (const bs in nodes) { - const node = nodes[bs]; - if (node.parent == undefined) { + nodes.forEach(node => { + if (node.parent === undefined) { shoulds.push(self.buildNodeQuery(node)); } - } + }); return { bool: { should: shoulds, @@ -1256,7 +1223,7 @@ module.exports = (function() { const t2 = keyedBuckets[ids[1]].doc_count; const t1AndT2 = bucket.doc_count; // Calc the significant_terms score to prioritize selection of interesting links - bucket.weight = self.JLHScore( + bucket.weight = self.jLHScore( t1AndT2, Math.max(t1, t2), Math.min(t1, t2), @@ -1276,7 +1243,7 @@ module.exports = (function() { return; } const ids = bucket.key.split('|'); - if (ids.length == 2) { + if (ids.length === 2) { // Bucket represents an edge const srcNode = nodesForLinking[ids[0]]; const targetNode = nodesForLinking[ids[1]]; @@ -1340,16 +1307,18 @@ module.exports = (function() { txtsByFieldType[node.data.field] = txt; }); for (const field in txtsByFieldType) { - likeQueries.push({ - more_like_this: { - like: txtsByFieldType[field], - min_term_freq: 1, - minimum_should_match: '20%', - min_doc_freq: 1, - boost_terms: 2, - max_query_terms: 25, - }, - }); + if (txtsByFieldType.hasOwnProperty(field)) { + likeQueries.push({ + more_like_this: { + like: txtsByFieldType[field], + min_term_freq: 1, + minimum_should_match: '20%', + min_doc_freq: 1, + boost_terms: 2, + max_query_terms: 25, + }, + }); + } } const excludeNodesByField = {}; @@ -1397,10 +1366,10 @@ module.exports = (function() { }; this.getSelectedIntersections = function(callback) { - if (self.selectedNodes.length == 0) { + if (self.selectedNodes.length === 0) { return self.getAllIntersections(callback, self.nodes); } - if (self.selectedNodes.length == 1) { + if (self.selectedNodes.length === 1) { const selectedNode = self.selectedNodes[0]; const neighbourNodes = self.getNeighbours(selectedNode); neighbourNodes.push(selectedNode); @@ -1409,7 +1378,7 @@ module.exports = (function() { return self.getAllIntersections(callback, self.getAllSelectedNodes()); }; - this.JLHScore = function(subsetFreq, subsetSize, supersetFreq, supersetSize) { + this.jLHScore = function(subsetFreq, subsetSize, supersetFreq, supersetSize) { const subsetProbability = subsetFreq / subsetSize; const supersetProbability = supersetFreq / supersetSize; @@ -1432,7 +1401,7 @@ module.exports = (function() { this.getAllIntersections = function(callback, nodes) { //Ensure these are all top-level nodes only nodes = nodes.filter(function(n) { - return n.parent == undefined; + return n.parent === undefined; }); const allQueries = nodes.map(function(node) { @@ -1468,44 +1437,42 @@ module.exports = (function() { }, }, }; - for (const n in allQueries) { + allQueries.forEach((query, n) => { // Add aggs to get intersection stats with root node. - request.aggs.sources.filters.filters['bg' + n] = allQueries[n]; - request.aggs.sources.aggs.targets.filters.filters['fg' + n] = allQueries[n]; - } - const dataForServer = JSON.stringify(request); + request.aggs.sources.filters.filters['bg' + n] = query; + request.aggs.sources.aggs.targets.filters.filters['fg' + n] = query; + }); searcher(self.options.indexName, request, function(data) { const termIntersects = []; const fullDocCounts = []; const allDocCount = data.aggregations.all.doc_count; // Gather the background stats for all nodes. - for (const n in nodes) { + nodes.forEach((rootNode, n) => { fullDocCounts.push(data.aggregations.sources.buckets['bg' + n].doc_count); - } - for (const n in nodes) { - const rootNode = nodes[n]; + }); + + nodes.forEach((rootNode, n) => { const t1 = fullDocCounts[n]; const baseAgg = data.aggregations.sources.buckets['bg' + n].targets.buckets; - for (const l in nodes) { + nodes.forEach((leafNode, l) => { const t2 = fullDocCounts[l]; - const leafNode = nodes[l]; - if (l == n) { - continue; + if (l === n) { + return; } if (t1 > t2) { // We should get the same stats for t2->t1 from the t1->t2 bucket path - continue; + return; } - if (t1 == t2) { + if (t1 === t2) { if (rootNode.id > leafNode.id) { // We should get the same stats for t2->t1 from the t1->t2 bucket path - continue; + return; } } const t1AndT2 = baseAgg['fg' + l].doc_count; - if (t1AndT2 == 0) { - continue; + if (t1AndT2 === 0) { + return; } const neighbourNode = nodes[l]; let t1Label = rootNode.data.label; @@ -1521,7 +1488,7 @@ module.exports = (function() { // var mergeConfidence=t1AndT2/t1; // So using Significance heuristic instead - const mergeConfidence = self.JLHScore(t1AndT2, t2, t1, allDocCount); + const mergeConfidence = self.jLHScore(t1AndT2, t2, t1, allDocCount); const termIntersect = { id1: rootNode.id, @@ -1536,16 +1503,16 @@ module.exports = (function() { overlap: t1AndT2, }; termIntersects.push(termIntersect); - } - } + }); + }); termIntersects.sort(function(a, b) { - if (b.mergeConfidence != a.mergeConfidence) { + if (b.mergeConfidence !== a.mergeConfidence) { return b.mergeConfidence - a.mergeConfidence; } // If of equal similarity use the size of the overlap as // a measure of magnitude/significance for tie-breaker. - if (b.overlap != a.overlap) { + if (b.overlap !== a.overlap) { return b.overlap - a.overlap; } //All other things being equal we now favour where t2 NOT t1 is small. @@ -1563,32 +1530,28 @@ module.exports = (function() { self.lastRequest = JSON.stringify(request, null, '\t'); graphExplorer(self.options.indexName, request, function(data) { self.lastResponse = JSON.stringify(data, null, '\t'); - const nodes = []; const edges = []; //Label the nodes with field number for CSS styling - for (const n in data.vertices) { - const node = data.vertices[n]; - for (const f in self.options.vertex_fields) { - const fieldDef = self.options.vertex_fields[f]; - if (node.field == fieldDef.name) { + data.vertices.forEach(node => { + self.options.vertex_fields.some(fieldDef => { + if (node.field === fieldDef.name) { node.color = fieldDef.color; node.icon = fieldDef.icon; node.fieldDef = fieldDef; - break; + return true; } - } - } + return false; + }); + }); //Size the edges depending on weight const minLineSize = 2; const maxLineSize = 10; let maxEdgeWeight = 0.00000001; - for (const e in data.connections) { - const edge = data.connections[e]; + data.connections.forEach(edge => { maxEdgeWeight = Math.max(maxEdgeWeight, edge.weight); - } - for (const e in data.connections) { - const edge = data.connections[e]; + }); + data.connections.forEach(edge => { edges.push({ source: edge.source, target: edge.target, @@ -1596,7 +1559,7 @@ module.exports = (function() { weight: edge.weight, width: Math.max(minLineSize, (edge.weight / maxEdgeWeight) * maxLineSize), }); - } + }); self.mergeGraph( { diff --git a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.test.js b/x-pack/plugins/graph/public/angular/graph_client_workspace.test.js similarity index 97% rename from x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.test.js rename to x-pack/plugins/graph/public/angular/graph_client_workspace.test.js index 6179467966764..6f81a443086c0 100644 --- a/x-pack/legacy/plugins/graph/public/angular/graph_client_workspace.test.js +++ b/x-pack/plugins/graph/public/angular/graph_client_workspace.test.js @@ -77,7 +77,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); expect(workspace.edges.length).toEqual(1); @@ -119,7 +119,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); expect(workspace.edges.length).toEqual(1); @@ -201,7 +201,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.selectedNodes.length).toEqual(0); @@ -264,7 +264,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); @@ -320,7 +320,7 @@ describe('graphui-workspace', function() { }, ], }; - workspace.simpleSearch('myquery', {}, 2); + workspace.simpleSearch('myquery', undefined, 2); expect(workspace.nodes.length).toEqual(2); diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_graph.scss b/x-pack/plugins/graph/public/angular/templates/_graph.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_graph.scss rename to x-pack/plugins/graph/public/angular/templates/_graph.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_index.scss b/x-pack/plugins/graph/public/angular/templates/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_index.scss rename to x-pack/plugins/graph/public/angular/templates/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_inspect.scss b/x-pack/plugins/graph/public/angular/templates/_inspect.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_inspect.scss rename to x-pack/plugins/graph/public/angular/templates/_inspect.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/_sidebar.scss b/x-pack/plugins/graph/public/angular/templates/_sidebar.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/_sidebar.scss rename to x-pack/plugins/graph/public/angular/templates/_sidebar.scss diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/index.html b/x-pack/plugins/graph/public/angular/templates/index.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/index.html rename to x-pack/plugins/graph/public/angular/templates/index.html diff --git a/x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html b/x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html similarity index 100% rename from x-pack/legacy/plugins/graph/public/angular/templates/listing_ng_wrapper.html rename to x-pack/plugins/graph/public/angular/templates/listing_ng_wrapper.html diff --git a/x-pack/legacy/plugins/graph/public/app.js b/x-pack/plugins/graph/public/app.js similarity index 96% rename from x-pack/legacy/plugins/graph/public/app.js rename to x-pack/plugins/graph/public/app.js index df968681a38e2..72dddc2b9f813 100644 --- a/x-pack/legacy/plugins/graph/public/app.js +++ b/x-pack/plugins/graph/public/app.js @@ -6,13 +6,12 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import 'ace'; import React from 'react'; import { Provider } from 'react-redux'; import { isColorDark, hexToRgb } from '@elastic/eui'; -import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -import { showSaveModal } from '../../../../../src/plugins/saved_objects/public'; +import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; +import { showSaveModal } from '../../../../src/plugins/saved_objects/public'; import appTemplate from './angular/templates/index.html'; import listingTemplate from './angular/templates/listing_ng_wrapper.html'; @@ -41,7 +40,7 @@ export function initGraphApp(angularModule, deps) { indexPatterns, addBasePath, getBasePath, - npData, + data, config, savedWorkspaceLoader, capabilities, @@ -107,7 +106,7 @@ export function initGraphApp(angularModule, deps) { .when('/home', { template: listingTemplate, badge: getReadonlyBadge, - controller($location, $scope) { + controller: function($location, $scope) { $scope.listingLimit = config.get('savedObjects:listingLimit'); $scope.create = () => { $location.url(getNewPath()); @@ -249,6 +248,7 @@ export function initGraphApp(angularModule, deps) { const store = createGraphStore({ basePath: getBasePath(), + addBasePath, indexPatternProvider: $scope.indexPatternProvider, indexPatterns: $route.current.locals.indexPatterns, createWorkspace: (indexPattern, exploreControls) => { @@ -301,7 +301,7 @@ export function initGraphApp(angularModule, deps) { }); // register things on scope passed down to react components - $scope.pluginDataStart = npData; + $scope.pluginDataStart = data; $scope.storage = storage; $scope.coreStart = coreStart; $scope.loading = false; @@ -420,11 +420,13 @@ export function initGraphApp(angularModule, deps) { while (found) { found = false; for (const i in $scope.detail.mergeCandidates) { - const mc = $scope.detail.mergeCandidates[i]; - if (mc.id1 === childId || mc.id2 === childId) { - $scope.detail.mergeCandidates.splice(i, 1); - found = true; - break; + if ($scope.detail.mergeCandidates.hasOwnProperty(i)) { + const mc = $scope.detail.mergeCandidates[i]; + if (mc.id1 === childId || mc.id2 === childId) { + $scope.detail.mergeCandidates.splice(i, 1); + found = true; + break; + } } } } @@ -434,8 +436,7 @@ export function initGraphApp(angularModule, deps) { $scope.handleMergeCandidatesCallback = function(termIntersects) { const mergeCandidates = []; - for (const i in termIntersects) { - const ti = termIntersects[i]; + termIntersects.forEach(ti => { mergeCandidates.push({ id1: ti.id1, id2: ti.id2, @@ -445,7 +446,7 @@ export function initGraphApp(angularModule, deps) { v2: ti.v2, overlap: ti.overlap, }); - } + }); $scope.detail = { mergeCandidates }; }; diff --git a/x-pack/legacy/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts similarity index 81% rename from x-pack/legacy/plugins/graph/public/application.ts rename to x-pack/plugins/graph/public/application.ts index 536382e62d473..4f7bdd69db356 100644 --- a/x-pack/legacy/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -9,34 +9,36 @@ // They can stay even after NP cutover import angular from 'angular'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; - +import '../../../../webpackShims/ace'; +// required for i18nIdDirective +import 'angular-sanitize'; // type imports import { AppMountContext, ChromeStart, - LegacyCoreStart, + CoreStart, + PluginInitializerContext, SavedObjectsClientContract, ToastsStart, IUiSettingsClient, OverlayStart, } from 'kibana/public'; -import { configureAppAngularModule } from './legacy_imports'; // @ts-ignore import { initGraphApp } from './app'; -import { - Plugin as DataPlugin, - IndexPatternsContract, -} from '../../../../../src/plugins/data/public'; -import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; -import { checkLicense } from '../../../../plugins/graph/common/check_license'; -import { NavigationPublicPluginStart as NavigationStart } from '../../../../../src/plugins/navigation/public'; +import { Plugin as DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public'; +import { LicensingPluginSetup } from '../../licensing/public'; +import { checkLicense } from '../common/check_license'; +import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; import { createSavedWorkspacesLoader } from './services/persistence/saved_workspace_loader'; -import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { + addAppRedirectMessageToUrl, + configureAppAngularModule, createTopNavDirective, createTopNavHelper, -} from '../../../../../src/plugins/kibana_legacy/public'; -import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_legacy/public'; +} from '../../../../src/plugins/kibana_legacy/public'; + +import './index.scss'; /** * These are dependencies of the Graph app besides the base dependencies @@ -45,6 +47,8 @@ import { addAppRedirectMessageToUrl } from '../../../../../src/plugins/kibana_le * itself changes */ export interface GraphDependencies { + pluginInitializerContext: PluginInitializerContext; + core: CoreStart; element: HTMLElement; appBasePath: string; capabilities: Record>; @@ -55,7 +59,7 @@ export interface GraphDependencies { config: IUiSettingsClient; toastNotifications: ToastsStart; indexPatterns: IndexPatternsContract; - npData: ReturnType; + data: ReturnType; savedObjectsClient: SavedObjectsClientContract; addBasePath: (url: string) => string; getBasePath: () => string; @@ -67,7 +71,11 @@ export interface GraphDependencies { export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) => { const graphAngularModule = createLocalAngularModule(deps.navigation); - configureAppAngularModule(graphAngularModule, deps.coreStart as LegacyCoreStart, true); + configureAppAngularModule( + graphAngularModule, + { core: deps.core, env: deps.pluginInitializerContext.env }, + true + ); const licenseSubscription = deps.licensing.license$.subscribe(license => { const info = checkLicense(license); @@ -81,7 +89,7 @@ export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) const savedWorkspaceLoader = createSavedWorkspacesLoader({ chrome: deps.coreStart.chrome, - indexPatterns: deps.npData.indexPatterns, + indexPatterns: deps.data.indexPatterns, overlays: deps.coreStart.overlays, savedObjectsClient: deps.coreStart.savedObjects.client, basePath: deps.coreStart.http.basePath, @@ -113,6 +121,7 @@ function mountGraphApp(appBasePath: string, element: HTMLElement) { // make angular-within-angular possible const $injector = angular.bootstrap(mountpoint, [moduleName]); element.appendChild(mountpoint); + element.setAttribute('class', 'kbnLocalApplicationWrapper'); return $injector; } diff --git a/x-pack/legacy/plugins/graph/public/badge.js b/x-pack/plugins/graph/public/badge.js similarity index 100% rename from x-pack/legacy/plugins/graph/public/badge.js rename to x-pack/plugins/graph/public/badge.js diff --git a/x-pack/legacy/plugins/graph/public/components/_app.scss b/x-pack/plugins/graph/public/components/_app.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_app.scss rename to x-pack/plugins/graph/public/components/_app.scss diff --git a/x-pack/legacy/plugins/graph/public/components/_index.scss b/x-pack/plugins/graph/public/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_index.scss rename to x-pack/plugins/graph/public/components/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/_search_bar.scss b/x-pack/plugins/graph/public/components/_search_bar.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_search_bar.scss rename to x-pack/plugins/graph/public/components/_search_bar.scss diff --git a/x-pack/legacy/plugins/graph/public/components/_source_modal.scss b/x-pack/plugins/graph/public/components/_source_modal.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/_source_modal.scss rename to x-pack/plugins/graph/public/components/_source_modal.scss diff --git a/x-pack/legacy/plugins/graph/public/components/app.tsx b/x-pack/plugins/graph/public/components/app.tsx similarity index 96% rename from x-pack/legacy/plugins/graph/public/components/app.tsx rename to x-pack/plugins/graph/public/components/app.tsx index 957a8f66907a1..a57842eaf23f5 100644 --- a/x-pack/legacy/plugins/graph/public/components/app.tsx +++ b/x-pack/plugins/graph/public/components/app.tsx @@ -18,7 +18,7 @@ import { GraphStore } from '../state_management'; import { GuidancePanel } from './guidance_panel'; import { GraphTitle } from './graph_title'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; export interface GraphAppProps extends SearchBarProps { coreStart: CoreStart; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/_field_editor.scss b/x-pack/plugins/graph/public/components/field_manager/_field_editor.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/_field_editor.scss rename to x-pack/plugins/graph/public/components/field_manager/_field_editor.scss diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/_field_picker.scss b/x-pack/plugins/graph/public/components/field_manager/_field_picker.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/_field_picker.scss rename to x-pack/plugins/graph/public/components/field_manager/_field_picker.scss diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/_index.scss b/x-pack/plugins/graph/public/components/field_manager/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/_index.scss rename to x-pack/plugins/graph/public/components/field_manager/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx b/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx similarity index 99% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_editor.tsx index 9c7cffa775781..78e4180aa2b2a 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_editor.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_editor.tsx @@ -29,7 +29,7 @@ import classNames from 'classnames'; import { WorkspaceField } from '../../types'; import { iconChoices } from '../../helpers/style_choices'; import { LegacyIcon } from '../legacy_icon'; -import { FieldIcon } from '../../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public'; import { UpdateableFieldProperties } from './field_manager'; import { isEqual } from '../helpers'; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_manager.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx b/x-pack/plugins/graph/public/components/field_manager/field_manager.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_manager.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx b/x-pack/plugins/graph/public/components/field_manager/field_picker.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx rename to x-pack/plugins/graph/public/components/field_manager/field_picker.tsx index 30f1fcffd4f67..f2dc9ba0c6490 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx +++ b/x-pack/plugins/graph/public/components/field_manager/field_picker.tsx @@ -9,7 +9,7 @@ import { EuiPopover, EuiSelectable, EuiBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; import { WorkspaceField } from '../../types'; -import { FieldIcon } from '../../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public'; export interface FieldPickerProps { fieldMap: Record; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/index.ts b/x-pack/plugins/graph/public/components/field_manager/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/field_manager/index.ts rename to x-pack/plugins/graph/public/components/field_manager/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/graph_title.tsx b/x-pack/plugins/graph/public/components/graph_title.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_title.tsx rename to x-pack/plugins/graph/public/components/graph_title.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap b/x-pack/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap rename to x-pack/plugins/graph/public/components/graph_visualization/__snapshots__/graph_visualization.test.tsx.snap diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/_graph_visualization.scss b/x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/_graph_visualization.scss rename to x-pack/plugins/graph/public/components/graph_visualization/_graph_visualization.scss diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/_index.scss b/x-pack/plugins/graph/public/components/graph_visualization/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/_index.scss rename to x-pack/plugins/graph/public/components/graph_visualization/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx b/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx rename to x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.tsx b/x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/graph_visualization.tsx rename to x-pack/plugins/graph/public/components/graph_visualization/graph_visualization.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/graph_visualization/index.ts b/x-pack/plugins/graph/public/components/graph_visualization/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/graph_visualization/index.ts rename to x-pack/plugins/graph/public/components/graph_visualization/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss b/x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/_guidance_panel.scss rename to x-pack/plugins/graph/public/components/guidance_panel/_guidance_panel.scss diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss b/x-pack/plugins/graph/public/components/guidance_panel/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/_index.scss rename to x-pack/plugins/graph/public/components/guidance_panel/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx rename to x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx index d1fcbea2ff5b7..3990abfe87ab3 100644 --- a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx +++ b/x-pack/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -30,7 +30,7 @@ import { import { IndexPatternSavedObject } from '../../types'; import { openSourceModal } from '../../services/source_modal'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; export interface GuidancePanelProps { onFillWorkspace: () => void; diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts b/x-pack/plugins/graph/public/components/guidance_panel/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/guidance_panel/index.ts rename to x-pack/plugins/graph/public/components/guidance_panel/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/helpers.ts b/x-pack/plugins/graph/public/components/helpers.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/helpers.ts rename to x-pack/plugins/graph/public/components/helpers.ts diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/_index.scss b/x-pack/plugins/graph/public/components/legacy_icon/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/_index.scss rename to x-pack/plugins/graph/public/components/legacy_icon/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/_legacy_icon.scss b/x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/_legacy_icon.scss rename to x-pack/plugins/graph/public/components/legacy_icon/_legacy_icon.scss diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/index.ts b/x-pack/plugins/graph/public/components/legacy_icon/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/index.ts rename to x-pack/plugins/graph/public/components/legacy_icon/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/legacy_icon/legacy_icon.tsx b/x-pack/plugins/graph/public/components/legacy_icon/legacy_icon.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/legacy_icon/legacy_icon.tsx rename to x-pack/plugins/graph/public/components/legacy_icon/legacy_icon.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/listing.tsx b/x-pack/plugins/graph/public/components/listing.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/listing.tsx rename to x-pack/plugins/graph/public/components/listing.tsx index 5fa6111b1a244..aeecc3ab103f3 100644 --- a/x-pack/legacy/plugins/graph/public/components/listing.tsx +++ b/x-pack/plugins/graph/public/components/listing.tsx @@ -10,7 +10,7 @@ import React, { Fragment } from 'react'; import { EuiEmptyPrompt, EuiLink, EuiButton } from '@elastic/eui'; import { CoreStart, ApplicationStart } from 'kibana/public'; -import { TableListView } from '../../../../../../src/plugins/kibana_react/public'; +import { TableListView } from '../../../../../src/plugins/kibana_react/public'; import { GraphWorkspaceSavedObject } from '../types'; export interface ListingProps { diff --git a/x-pack/legacy/plugins/graph/public/components/save_modal.tsx b/x-pack/plugins/graph/public/components/save_modal.tsx similarity index 96% rename from x-pack/legacy/plugins/graph/public/components/save_modal.tsx rename to x-pack/plugins/graph/public/components/save_modal.tsx index a7329c10e93d7..c4459fb1a794f 100644 --- a/x-pack/legacy/plugins/graph/public/components/save_modal.tsx +++ b/x-pack/plugins/graph/public/components/save_modal.tsx @@ -7,10 +7,7 @@ import React, { useState } from 'react'; import { EuiFormRow, EuiTextArea, EuiCallOut, EuiSpacer, EuiSwitch } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - SavedObjectSaveModal, - OnSaveProps, -} from '../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectSaveModal, OnSaveProps } from '../../../../../src/plugins/saved_objects/public'; import { GraphSavePolicy } from '../types/config'; diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx b/x-pack/plugins/graph/public/components/search_bar.test.tsx similarity index 95% rename from x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx rename to x-pack/plugins/graph/public/components/search_bar.test.tsx index 95b7dd22e9fcf..10778124e2011 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.test.tsx @@ -9,9 +9,9 @@ import { SearchBar, OuterSearchBarProps } from './search_bar'; import React, { ReactElement } from 'react'; import { CoreStart } from 'src/core/public'; import { act } from 'react-dom/test-utils'; -import { IndexPattern, QueryStringInput } from '../../../../../../src/plugins/data/public'; +import { IndexPattern, QueryStringInput } from '../../../../../src/plugins/data/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { I18nProvider } from '@kbn/i18n/react'; import { openSourceModal } from '../services/source_modal'; diff --git a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx b/x-pack/plugins/graph/public/components/search_bar.tsx similarity index 98% rename from x-pack/legacy/plugins/graph/public/components/search_bar.tsx rename to x-pack/plugins/graph/public/components/search_bar.tsx index c7c5830cadfe1..ab6d94a78ceec 100644 --- a/x-pack/legacy/plugins/graph/public/components/search_bar.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.tsx @@ -18,14 +18,14 @@ import { IndexpatternDatasource, } from '../state_management'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { IndexPattern, QueryStringInput, IDataPluginServices, Query, esKuery, -} from '../../../../../../src/plugins/data/public'; +} from '../../../../../src/plugins/data/public'; export interface OuterSearchBarProps { isLoading: boolean; diff --git a/x-pack/legacy/plugins/graph/public/components/settings/_index.scss b/x-pack/plugins/graph/public/components/settings/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/_index.scss rename to x-pack/plugins/graph/public/components/settings/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/settings/_legacy_icon.scss b/x-pack/plugins/graph/public/components/settings/_legacy_icon.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/_legacy_icon.scss rename to x-pack/plugins/graph/public/components/settings/_legacy_icon.scss diff --git a/x-pack/legacy/plugins/graph/public/components/settings/_url_template_list.scss b/x-pack/plugins/graph/public/components/settings/_url_template_list.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/_url_template_list.scss rename to x-pack/plugins/graph/public/components/settings/_url_template_list.scss diff --git a/x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx b/x-pack/plugins/graph/public/components/settings/advanced_settings_form.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/advanced_settings_form.tsx rename to x-pack/plugins/graph/public/components/settings/advanced_settings_form.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx b/x-pack/plugins/graph/public/components/settings/blacklist_form.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/blacklist_form.tsx rename to x-pack/plugins/graph/public/components/settings/blacklist_form.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/index.ts b/x-pack/plugins/graph/public/components/settings/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/index.ts rename to x-pack/plugins/graph/public/components/settings/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx b/x-pack/plugins/graph/public/components/settings/settings.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx rename to x-pack/plugins/graph/public/components/settings/settings.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.tsx b/x-pack/plugins/graph/public/components/settings/settings.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/settings.tsx rename to x-pack/plugins/graph/public/components/settings/settings.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/url_template_form.tsx b/x-pack/plugins/graph/public/components/settings/url_template_form.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/url_template_form.tsx rename to x-pack/plugins/graph/public/components/settings/url_template_form.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx b/x-pack/plugins/graph/public/components/settings/url_template_list.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/url_template_list.tsx rename to x-pack/plugins/graph/public/components/settings/url_template_list.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.test.tsx b/x-pack/plugins/graph/public/components/settings/use_list_keys.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.test.tsx rename to x-pack/plugins/graph/public/components/settings/use_list_keys.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.ts b/x-pack/plugins/graph/public/components/settings/use_list_keys.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/settings/use_list_keys.ts rename to x-pack/plugins/graph/public/components/settings/use_list_keys.ts diff --git a/x-pack/legacy/plugins/graph/public/components/source_modal.tsx b/x-pack/plugins/graph/public/components/source_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/source_modal.tsx rename to x-pack/plugins/graph/public/components/source_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/source_picker.tsx b/x-pack/plugins/graph/public/components/source_picker.tsx similarity index 94% rename from x-pack/legacy/plugins/graph/public/components/source_picker.tsx rename to x-pack/plugins/graph/public/components/source_picker.tsx index 65a431202fc98..9172f6ba1c65c 100644 --- a/x-pack/legacy/plugins/graph/public/components/source_picker.tsx +++ b/x-pack/plugins/graph/public/components/source_picker.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { CoreStart } from 'src/core/public'; -import { SavedObjectFinderUi } from '../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectFinderUi } from '../../../../../src/plugins/saved_objects/public'; import { IndexPatternSavedObject } from '../types'; export interface SourcePickerProps { diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/_index.scss b/x-pack/plugins/graph/public/components/venn_diagram/_index.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/_index.scss rename to x-pack/plugins/graph/public/components/venn_diagram/_index.scss diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/_venn_diagram.scss b/x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/_venn_diagram.scss rename to x-pack/plugins/graph/public/components/venn_diagram/_venn_diagram.scss diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/index.ts b/x-pack/plugins/graph/public/components/venn_diagram/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/index.ts rename to x-pack/plugins/graph/public/components/venn_diagram/index.ts diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx b/x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx rename to x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.test.tsx diff --git a/x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.tsx b/x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/components/venn_diagram/venn_diagram.tsx rename to x-pack/plugins/graph/public/components/venn_diagram/venn_diagram.tsx diff --git a/x-pack/legacy/plugins/graph/public/helpers/as_observable.ts b/x-pack/plugins/graph/public/helpers/as_observable.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/as_observable.ts rename to x-pack/plugins/graph/public/helpers/as_observable.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts b/x-pack/plugins/graph/public/helpers/format_http_error.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/format_http_error.ts rename to x-pack/plugins/graph/public/helpers/format_http_error.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/kql_encoder.test.ts b/x-pack/plugins/graph/public/helpers/kql_encoder.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/kql_encoder.test.ts rename to x-pack/plugins/graph/public/helpers/kql_encoder.test.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/kql_encoder.ts b/x-pack/plugins/graph/public/helpers/kql_encoder.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/kql_encoder.ts rename to x-pack/plugins/graph/public/helpers/kql_encoder.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/outlink_encoders.ts b/x-pack/plugins/graph/public/helpers/outlink_encoders.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/outlink_encoders.ts rename to x-pack/plugins/graph/public/helpers/outlink_encoders.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/style_choices.ts b/x-pack/plugins/graph/public/helpers/style_choices.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/style_choices.ts rename to x-pack/plugins/graph/public/helpers/style_choices.ts diff --git a/x-pack/legacy/plugins/graph/public/helpers/url_template.ts b/x-pack/plugins/graph/public/helpers/url_template.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/helpers/url_template.ts rename to x-pack/plugins/graph/public/helpers/url_template.ts diff --git a/x-pack/legacy/plugins/graph/public/index.scss b/x-pack/plugins/graph/public/index.scss similarity index 71% rename from x-pack/legacy/plugins/graph/public/index.scss rename to x-pack/plugins/graph/public/index.scss index 067b2c300626d..f4e38de3e93a4 100644 --- a/x-pack/legacy/plugins/graph/public/index.scss +++ b/x-pack/plugins/graph/public/index.scss @@ -1,6 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - /* Graph plugin styles */ // Prefix all styles with "gph" to avoid conflicts. diff --git a/x-pack/plugins/graph/public/index.ts b/x-pack/plugins/graph/public/index.ts index 7b2ce67631713..690d2e88dd9c9 100644 --- a/x-pack/plugins/graph/public/index.ts +++ b/x-pack/plugins/graph/public/index.ts @@ -10,5 +10,3 @@ import { ConfigSchema } from '../config'; export const plugin = (initializerContext: PluginInitializerContext) => new GraphPlugin(initializerContext); - -export { GraphSetup } from './plugin'; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index e911b400349f8..5521de705b6ec 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -6,8 +6,14 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart } from 'kibana/public'; -import { Plugin } from 'src/core/public'; +import { AppMountParameters, Plugin } from 'src/core/public'; import { PluginInitializerContext } from 'kibana/public'; + +import { Storage } from '../../../../src/plugins/kibana_utils/public'; +import { initAngularBootstrap } from '../../../../src/plugins/kibana_legacy/public'; +import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; + import { toggleNavLink } from './services/toggle_nav_link'; import { LicensingPluginSetup } from '../../licensing/public'; import { checkLicense } from '../common/check_license'; @@ -15,6 +21,7 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup, } from '../../../../src/plugins/home/public'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; import { ConfigSchema } from '../config'; export interface GraphPluginSetupDependencies { @@ -22,12 +29,21 @@ export interface GraphPluginSetupDependencies { home?: HomePublicPluginSetup; } -export class GraphPlugin implements Plugin<{ config: Readonly }, void> { +export interface GraphPluginStartDependencies { + navigation: NavigationStart; + data: DataPublicPluginStart; +} + +export class GraphPlugin + implements Plugin { private licensing: LicensingPluginSetup | null = null; constructor(private initializerContext: PluginInitializerContext) {} - setup(core: CoreSetup, { licensing, home }: GraphPluginSetupDependencies) { + setup( + core: CoreSetup, + { licensing, home }: GraphPluginSetupDependencies + ) { this.licensing = licensing; if (home) { @@ -44,15 +60,42 @@ export class GraphPlugin implements Plugin<{ config: Readonly }, v }); } - return { - /** - * The configuration is temporarily exposed to allow the legacy graph plugin to consume - * the setting. Once the graph plugin is migrated completely, this will become an implementation - * detail. - * @deprecated - */ - config: this.initializerContext.config.get(), - }; + const config = this.initializerContext.config.get(); + + initAngularBootstrap(); + core.application.register({ + id: 'graph', + title: 'Graph', + order: 9000, + appRoute: '/app/graph', + euiIconType: 'graphApp', + category: DEFAULT_APP_CATEGORIES.analyze, + mount: async (params: AppMountParameters) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + const { renderApp } = await import('./application'); + return renderApp({ + ...params, + pluginInitializerContext: this.initializerContext, + licensing, + core: coreStart, + navigation: pluginsStart.navigation, + data: pluginsStart.data, + savedObjectsClient: coreStart.savedObjects.client, + addBasePath: core.http.basePath.prepend, + getBasePath: core.http.basePath.get, + canEditDrillDownUrls: config.canEditDrillDownUrls, + graphSavePolicy: config.savePolicy, + storage: new Storage(window.localStorage), + capabilities: coreStart.application.capabilities.graph, + coreStart, + chrome: coreStart.chrome, + config: coreStart.uiSettings, + toastNotifications: coreStart.notifications.toasts, + indexPatterns: pluginsStart.data!.indexPatterns, + overlays: coreStart.overlays, + }); + }, + }); } start(core: CoreStart) { @@ -66,5 +109,3 @@ export class GraphPlugin implements Plugin<{ config: Readonly }, v stop() {} } - -export type GraphSetup = ReturnType; diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts b/x-pack/plugins/graph/public/services/fetch_top_nodes.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts rename to x-pack/plugins/graph/public/services/fetch_top_nodes.test.ts diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts b/x-pack/plugins/graph/public/services/fetch_top_nodes.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.ts rename to x-pack/plugins/graph/public/services/fetch_top_nodes.ts diff --git a/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts b/x-pack/plugins/graph/public/services/index_pattern_cache.ts similarity index 90% rename from x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts rename to x-pack/plugins/graph/public/services/index_pattern_cache.ts index 9bbda0b551193..9cc466b9c20ab 100644 --- a/x-pack/legacy/plugins/graph/public/services/index_pattern_cache.ts +++ b/x-pack/plugins/graph/public/services/index_pattern_cache.ts @@ -5,7 +5,7 @@ */ import { IndexPatternProvider } from '../types'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; export function createCachedIndexPatternProvider( indexPatternGetter: (id: string) => Promise diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts b/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts similarity index 98% rename from x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts rename to x-pack/plugins/graph/public/services/persistence/deserialize.test.ts index efef3d246ac98..3dda41fcdbdb6 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts +++ b/x-pack/plugins/graph/public/services/persistence/deserialize.test.ts @@ -8,7 +8,7 @@ import { GraphWorkspaceSavedObject, Workspace } from '../../types'; import { savedWorkspaceToAppState } from './deserialize'; import { createWorkspace } from '../../angular/graph_client_workspace'; import { outlinkEncoders } from '../../helpers/outlink_encoders'; -import { IndexPattern } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../../src/plugins/data/public'; describe('deserialize', () => { let savedWorkspace: GraphWorkspaceSavedObject; diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts b/x-pack/plugins/graph/public/services/persistence/deserialize.ts similarity index 99% rename from x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts rename to x-pack/plugins/graph/public/services/persistence/deserialize.ts index 947e56a6de6eb..06106ed4c4f3f 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts +++ b/x-pack/plugins/graph/public/services/persistence/deserialize.ts @@ -27,7 +27,7 @@ import { import { IndexPattern, indexPatterns as indexPatternsUtils, -} from '../../../../../../../src/plugins/data/public'; +} from '../../../../../../src/plugins/data/public'; const defaultAdvancedSettings: AdvancedSettings = { useSignificance: true, diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/index.ts b/x-pack/plugins/graph/public/services/persistence/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/index.ts rename to x-pack/plugins/graph/public/services/persistence/index.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace.ts similarity index 97% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace.ts index 025d5e6935902..e2bd885dc7209 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace.ts +++ b/x-pack/plugins/graph/public/services/persistence/saved_workspace.ts @@ -9,7 +9,7 @@ import { SavedObject, createSavedObjectClass, SavedObjectKibanaServices, -} from '../../../../../../../src/plugins/saved_objects/public'; +} from '../../../../../../src/plugins/saved_objects/public'; export interface SavedWorkspace extends SavedObject { wsState?: string; diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace_loader.ts similarity index 95% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace_loader.ts index d9bb119006e78..fb64fbadfbf7c 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_loader.ts +++ b/x-pack/plugins/graph/public/services/persistence/saved_workspace_loader.ts @@ -7,7 +7,7 @@ import { IBasePath } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { SavedObjectKibanaServices } from '../../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectKibanaServices } from '../../../../../../src/plugins/saved_objects/public'; import { createSavedWorkspaceClass } from './saved_workspace'; export function createSavedWorkspacesLoader( diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.test.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace_references.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.test.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace_references.test.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.ts b/x-pack/plugins/graph/public/services/persistence/saved_workspace_references.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/saved_workspace_references.ts rename to x-pack/plugins/graph/public/services/persistence/saved_workspace_references.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts b/x-pack/plugins/graph/public/services/persistence/serialize.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts rename to x-pack/plugins/graph/public/services/persistence/serialize.test.ts diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts b/x-pack/plugins/graph/public/services/persistence/serialize.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/persistence/serialize.ts rename to x-pack/plugins/graph/public/services/persistence/serialize.ts diff --git a/x-pack/legacy/plugins/graph/public/services/save_modal.tsx b/x-pack/plugins/graph/public/services/save_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/save_modal.tsx rename to x-pack/plugins/graph/public/services/save_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/services/source_modal.tsx b/x-pack/plugins/graph/public/services/source_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/source_modal.tsx rename to x-pack/plugins/graph/public/services/source_modal.tsx diff --git a/x-pack/legacy/plugins/graph/public/services/url.ts b/x-pack/plugins/graph/public/services/url.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/services/url.ts rename to x-pack/plugins/graph/public/services/url.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts b/x-pack/plugins/graph/public/state_management/advanced_settings.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/advanced_settings.ts rename to x-pack/plugins/graph/public/state_management/advanced_settings.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.sagas.ts b/x-pack/plugins/graph/public/state_management/datasource.sagas.ts similarity index 96% rename from x-pack/legacy/plugins/graph/public/state_management/datasource.sagas.ts rename to x-pack/plugins/graph/public/state_management/datasource.sagas.ts index 34d39e71dec55..018b3b42b9157 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/datasource.sagas.ts +++ b/x-pack/plugins/graph/public/state_management/datasource.sagas.ts @@ -17,7 +17,7 @@ import { setDatasource, requestDatasource, } from './datasource'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; /** * Saga loading field information when the datasource is switched. This will overwrite current settings diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.test.ts b/x-pack/plugins/graph/public/state_management/datasource.test.ts similarity index 97% rename from x-pack/legacy/plugins/graph/public/state_management/datasource.test.ts rename to x-pack/plugins/graph/public/state_management/datasource.test.ts index 041098a9aaae5..84f3741604e20 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/datasource.test.ts +++ b/x-pack/plugins/graph/public/state_management/datasource.test.ts @@ -10,7 +10,7 @@ import { datasourceSelector, requestDatasource } from './datasource'; import { datasourceSaga } from './datasource.sagas'; import { fieldsSelector } from './fields'; import { updateSettings } from './advanced_settings'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; const waitForPromise = () => new Promise(r => setTimeout(r)); diff --git a/x-pack/legacy/plugins/graph/public/state_management/datasource.ts b/x-pack/plugins/graph/public/state_management/datasource.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/datasource.ts rename to x-pack/plugins/graph/public/state_management/datasource.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/fields.ts b/x-pack/plugins/graph/public/state_management/fields.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/fields.ts rename to x-pack/plugins/graph/public/state_management/fields.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/global.ts b/x-pack/plugins/graph/public/state_management/global.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/global.ts rename to x-pack/plugins/graph/public/state_management/global.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/helpers.ts b/x-pack/plugins/graph/public/state_management/helpers.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/helpers.ts rename to x-pack/plugins/graph/public/state_management/helpers.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/index.ts b/x-pack/plugins/graph/public/state_management/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/index.ts rename to x-pack/plugins/graph/public/state_management/index.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/legacy.test.ts b/x-pack/plugins/graph/public/state_management/legacy.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/legacy.test.ts rename to x-pack/plugins/graph/public/state_management/legacy.test.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/meta_data.test.ts b/x-pack/plugins/graph/public/state_management/meta_data.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/meta_data.test.ts rename to x-pack/plugins/graph/public/state_management/meta_data.test.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/meta_data.ts b/x-pack/plugins/graph/public/state_management/meta_data.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/meta_data.ts rename to x-pack/plugins/graph/public/state_management/meta_data.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/mocks.ts b/x-pack/plugins/graph/public/state_management/mocks.ts similarity index 94% rename from x-pack/legacy/plugins/graph/public/state_management/mocks.ts rename to x-pack/plugins/graph/public/state_management/mocks.ts index 01d6927b9b886..d06f8a7b3ef0b 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/mocks.ts +++ b/x-pack/plugins/graph/public/state_management/mocks.ts @@ -10,7 +10,7 @@ import { createStore, applyMiddleware, AnyAction } from 'redux'; import { ChromeStart } from 'kibana/public'; import { GraphStoreDependencies, createRootReducer, GraphStore, GraphState } from './store'; import { Workspace, GraphWorkspaceSavedObject, IndexPatternSavedObject } from '../types'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; jest.mock('ui/new_platform'); @@ -49,7 +49,7 @@ export function createMockGraphStore({ } as unknown) as GraphWorkspaceSavedObject; const mockedDeps: jest.Mocked = { - basePath: 'basepath', + addBasePath: jest.fn((url: string) => url), changeUrl: jest.fn(), chrome: ({ setBreadcrumbs: jest.fn(), @@ -83,7 +83,7 @@ export function createMockGraphStore({ }; const sagaMiddleware = createSagaMiddleware(); - const rootReducer = createRootReducer(mockedDeps.basePath); + const rootReducer = createRootReducer(mockedDeps.addBasePath); const initializedRootReducer = (state: GraphState | undefined, action: AnyAction) => rootReducer(state || (initialStateOverwrites as GraphState), action); diff --git a/x-pack/legacy/plugins/graph/public/state_management/persistence.test.ts b/x-pack/plugins/graph/public/state_management/persistence.test.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/persistence.test.ts rename to x-pack/plugins/graph/public/state_management/persistence.test.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/persistence.ts b/x-pack/plugins/graph/public/state_management/persistence.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/persistence.ts rename to x-pack/plugins/graph/public/state_management/persistence.ts diff --git a/x-pack/legacy/plugins/graph/public/state_management/store.ts b/x-pack/plugins/graph/public/state_management/store.ts similarity index 93% rename from x-pack/legacy/plugins/graph/public/state_management/store.ts rename to x-pack/plugins/graph/public/state_management/store.ts index ecb7335fee5aa..4aeef0338923b 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/store.ts +++ b/x-pack/plugins/graph/public/state_management/store.ts @@ -46,7 +46,7 @@ export interface GraphState { } export interface GraphStoreDependencies { - basePath: string; + addBasePath: (url: string) => string; indexPatternProvider: IndexPatternProvider; indexPatterns: IndexPatternSavedObject[]; createWorkspace: (index: string, advancedSettings: AdvancedSettings) => void; @@ -65,10 +65,10 @@ export interface GraphStoreDependencies { I18nContext: I18nStart['Context']; } -export function createRootReducer(basePath: string) { +export function createRootReducer(addBasePath: (url: string) => string) { return combineReducers({ fields: fieldsReducer, - urlTemplates: urlTemplatesReducer(basePath), + urlTemplates: urlTemplatesReducer(addBasePath), advancedSettings: advancedSettingsReducer, datasource: datasourceReducer, metaData: metaDataReducer, @@ -91,7 +91,7 @@ function registerSagas(sagaMiddleware: SagaMiddleware, deps: GraphStoreD export const createGraphStore = (deps: GraphStoreDependencies) => { const sagaMiddleware = createSagaMiddleware(); - const rootReducer = createRootReducer(deps.basePath); + const rootReducer = createRootReducer(deps.addBasePath); const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); diff --git a/x-pack/legacy/plugins/graph/public/state_management/url_templates.test.ts b/x-pack/plugins/graph/public/state_management/url_templates.test.ts similarity index 91% rename from x-pack/legacy/plugins/graph/public/state_management/url_templates.test.ts rename to x-pack/plugins/graph/public/state_management/url_templates.test.ts index c4a3b0fb776a0..c265b2ec277d2 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/url_templates.test.ts +++ b/x-pack/plugins/graph/public/state_management/url_templates.test.ts @@ -10,9 +10,11 @@ import { outlinkEncoders } from '../helpers/outlink_encoders'; import { UrlTemplate } from '../types'; describe('url_templates', () => { + const addBasePath = (url: string) => url; + describe('reducer', () => { it('should create a default template as soon as datasource is known', () => { - const templates = urlTemplatesReducer('basepath')( + const templates = urlTemplatesReducer(addBasePath)( [], requestDatasource({ type: 'indexpattern', @@ -28,7 +30,7 @@ describe('url_templates', () => { }); it('should keep non-default templates when switching datasource', () => { - const templates = urlTemplatesReducer('basepath')( + const templates = urlTemplatesReducer(addBasePath)( [ { description: 'default template', @@ -52,7 +54,7 @@ describe('url_templates', () => { }); it('should remove isDefault flag when saving a template even if it is spreaded in', () => { - const templates = urlTemplatesReducer('basepath')( + const templates = urlTemplatesReducer(addBasePath)( [ { description: 'abc', diff --git a/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts b/x-pack/plugins/graph/public/state_management/url_templates.ts similarity index 82% rename from x-pack/legacy/plugins/graph/public/state_management/url_templates.ts rename to x-pack/plugins/graph/public/state_management/url_templates.ts index eac29d0ec9116..a0fb9503421a4 100644 --- a/x-pack/legacy/plugins/graph/public/state_management/url_templates.ts +++ b/x-pack/plugins/graph/public/state_management/url_templates.ts @@ -6,10 +6,10 @@ import actionCreatorFactory from 'typescript-fsa'; import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; -import { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; import { i18n } from '@kbn/i18n'; import rison from 'rison-node'; import { takeEvery, select } from 'redux-saga/effects'; +import { format, parse } from 'url'; import { GraphState, GraphStoreDependencies } from './store'; import { UrlTemplate } from '../types'; import { reset } from './global'; @@ -17,6 +17,7 @@ import { setDatasource, IndexpatternDatasource, requestDatasource } from './data import { outlinkEncoders } from '../helpers/outlink_encoders'; import { urlTemplatePlaceholder } from '../helpers/url_template'; import { matchesOne } from './helpers'; +import { modifyUrl } from '../../../../../src/core/utils'; const actionCreator = actionCreatorFactory('x-pack/graph/urlTemplates'); @@ -32,30 +33,32 @@ const initialTemplates: UrlTemplatesState = []; function generateDefaultTemplate( datasource: IndexpatternDatasource, - basePath: string + addBasePath: (url: string) => string ): UrlTemplate { - const kUrl = new KibanaParsedUrl({ - appId: 'kibana', - basePath, - appPath: '/discover', - }); - - kUrl.addQueryParameter( - '_a', - rison.encode({ + const appPath = modifyUrl('/discover', parsed => { + parsed.query._a = rison.encode({ columns: ['_source'], index: datasource.id, interval: 'auto', query: { language: 'kuery', query: urlTemplatePlaceholder }, sort: ['_score', 'desc'], - }) - ); + }); + }); + const parsedAppPath = parse(`/app/kibana#${appPath}`, true, true); + const formattedAppPath = format({ + protocol: parsedAppPath.protocol, + host: parsedAppPath.host, + pathname: parsedAppPath.pathname, + query: parsedAppPath.query, + hash: parsedAppPath.hash, + }); // replace the URI encoded version of the tag with the unescaped version // so it can be found with String.replace, regexp, etc. - const discoverUrl = kUrl - .getRootRelativePath() - .replace(encodeURIComponent(urlTemplatePlaceholder), urlTemplatePlaceholder); + const discoverUrl = addBasePath(formattedAppPath).replace( + encodeURIComponent(urlTemplatePlaceholder), + urlTemplatePlaceholder + ); return { url: discoverUrl, @@ -68,7 +71,7 @@ function generateDefaultTemplate( }; } -export const urlTemplatesReducer = (basePath: string) => +export const urlTemplatesReducer = (addBasePath: (url: string) => string) => reducerWithInitialState(initialTemplates) .case(reset, () => initialTemplates) .cases([requestDatasource, setDatasource], (templates, datasource) => { @@ -76,7 +79,7 @@ export const urlTemplatesReducer = (basePath: string) => return initialTemplates; } const customTemplates = templates.filter(template => !template.isDefault); - return [...customTemplates, generateDefaultTemplate(datasource, basePath)]; + return [...customTemplates, generateDefaultTemplate(datasource, addBasePath)]; }) .case(loadTemplates, (_currentTemplates, newTemplates) => newTemplates) .case(saveTemplate, (templates, { index: indexToUpdate, template: updatedTemplate }) => { diff --git a/x-pack/legacy/plugins/graph/public/state_management/workspace.ts b/x-pack/plugins/graph/public/state_management/workspace.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/state_management/workspace.ts rename to x-pack/plugins/graph/public/state_management/workspace.ts diff --git a/x-pack/legacy/plugins/graph/public/types/app_state.ts b/x-pack/plugins/graph/public/types/app_state.ts similarity index 94% rename from x-pack/legacy/plugins/graph/public/types/app_state.ts rename to x-pack/plugins/graph/public/types/app_state.ts index 876f2cf23b53a..21e584182785a 100644 --- a/x-pack/legacy/plugins/graph/public/types/app_state.ts +++ b/x-pack/plugins/graph/public/types/app_state.ts @@ -7,7 +7,7 @@ import { SimpleSavedObject } from 'src/core/public'; import { FontawesomeIcon } from '../helpers/style_choices'; import { OutlinkEncoder } from '../helpers/outlink_encoders'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; export interface UrlTemplate { url: string; diff --git a/x-pack/legacy/plugins/graph/public/types/config.ts b/x-pack/plugins/graph/public/types/config.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/types/config.ts rename to x-pack/plugins/graph/public/types/config.ts diff --git a/x-pack/legacy/plugins/graph/public/types/index.ts b/x-pack/plugins/graph/public/types/index.ts similarity index 100% rename from x-pack/legacy/plugins/graph/public/types/index.ts rename to x-pack/plugins/graph/public/types/index.ts diff --git a/x-pack/legacy/plugins/graph/public/types/persistence.ts b/x-pack/plugins/graph/public/types/persistence.ts similarity index 95% rename from x-pack/legacy/plugins/graph/public/types/persistence.ts rename to x-pack/plugins/graph/public/types/persistence.ts index cdaee5db202d8..b0209153c82e3 100644 --- a/x-pack/legacy/plugins/graph/public/types/persistence.ts +++ b/x-pack/plugins/graph/public/types/persistence.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject } from '../../../../../../src/plugins/saved_objects/public'; +import { SavedObject } from '../../../../../src/plugins/saved_objects/public'; import { AdvancedSettings, UrlTemplate, WorkspaceField } from './app_state'; import { WorkspaceNode, WorkspaceEdge } from './workspace_state'; diff --git a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts b/x-pack/plugins/graph/public/types/workspace_state.ts similarity index 97% rename from x-pack/legacy/plugins/graph/public/types/workspace_state.ts rename to x-pack/plugins/graph/public/types/workspace_state.ts index 37a962fd569ce..8c4178eda890f 100644 --- a/x-pack/legacy/plugins/graph/public/types/workspace_state.ts +++ b/x-pack/plugins/graph/public/types/workspace_state.ts @@ -6,7 +6,7 @@ import { FontawesomeIcon } from '../helpers/style_choices'; import { WorkspaceField, AdvancedSettings } from './app_state'; -import { JsonObject } from '../../../../../../src/plugins/kibana_utils/public'; +import { JsonObject } from '../../../../../src/plugins/kibana_utils/public'; export interface WorkspaceNode { x: number; From 746e236869927812c0d66906fe1abecb63e9ca36 Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 16 Mar 2020 10:44:28 +0100 Subject: [PATCH 02/76] [SIEM] Adds 'Closes and opens signals' Cypress test (#59950) * adds signals data * adds 'closes and opens signals' * refactors test * adds extra check to see that the selected number of signals is correct Co-authored-by: Elastic Machine --- .../cypress/integration/detections.spec.ts | 114 + .../siem/cypress/screens/detections.ts | 16 + .../plugins/siem/cypress/tasks/detections.ts | 46 +- .../plugins/siem/cypress/tasks/es_archiver.ts | 8 + .../utility_bar/utility_bar_action.tsx | 16 +- .../utility_bar/utility_bar_text.tsx | 5 +- .../signals/signals_filter_group/index.tsx | 2 + .../signals/signals_utility_bar/index.tsx | 7 +- .../es_archives/signals/data.json.gz | Bin 0 -> 58602 bytes .../es_archives/signals/mappings.json | 7602 +++++++++++++++++ 10 files changed, 7809 insertions(+), 7 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts create mode 100644 x-pack/test/siem_cypress/es_archives/signals/data.json.gz create mode 100644 x-pack/test/siem_cypress/es_archives/signals/mappings.json diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts new file mode 100644 index 0000000000000..1624586d4ca14 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -0,0 +1,114 @@ +/* + * 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 { + NUMBER_OF_SIGNALS, + SELECTED_SIGNALS, + SHOWING_SIGNALS, + SIGNALS, +} from '../screens/detections'; + +import { + closeSignals, + goToClosedSignals, + goToOpenedSignals, + openSignals, + selectNumberOfSignals, + waitForSignalsPanelToBeLoaded, + waitForSignals, + waitForSignalsToBeLoaded, +} from '../tasks/detections'; +import { esArchiverLoad } from '../tasks/es_archiver'; +import { loginAndWaitForPage } from '../tasks/login'; + +import { DETECTIONS } from '../urls/navigation'; + +describe('Detections', () => { + before(() => { + esArchiverLoad('signals'); + loginAndWaitForPage(DETECTIONS); + }); + + it('Closes and opens signals', () => { + waitForSignalsPanelToBeLoaded(); + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignals} signals`); + + const numberOfSignalsToBeClosed = 3; + selectNumberOfSignals(numberOfSignalsToBeClosed); + + cy.get(SELECTED_SIGNALS) + .invoke('text') + .should('eql', `Selected ${numberOfSignalsToBeClosed} signals`); + + closeSignals(); + waitForSignals(); + cy.reload(); + waitForSignals(); + + const expectedNumberOfSignalsAfterClosing = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignalsAfterClosing.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignalsAfterClosing.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signals`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + + const numberOfSignalsToBeOpened = 1; + selectNumberOfSignals(numberOfSignalsToBeOpened); + + cy.get(SELECTED_SIGNALS) + .invoke('text') + .should('eql', `Selected ${numberOfSignalsToBeOpened} signal`); + + openSignals(); + waitForSignals(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + goToClosedSignals(); + waitForSignals(); + + const expectedNumberOfClosedSignalsAfterOpened = 2; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', expectedNumberOfClosedSignalsAfterOpened.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfClosedSignalsAfterOpened.toString()} signals`); + cy.get(SIGNALS).should('have.length', expectedNumberOfClosedSignalsAfterOpened); + + goToOpenedSignals(); + waitForSignals(); + + const expectedNumberOfOpenedSignals = + +numberOfSignals - expectedNumberOfClosedSignalsAfterOpened; + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfOpenedSignals.toString()} signals`); + + cy.get('[data-test-subj="server-side-event-count"]') + .invoke('text') + .should('eql', expectedNumberOfOpenedSignals.toString()); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8089b028a10d4..8b5ba23578807 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -4,6 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +export const CLOSED_SIGNALS_BTN = '[data-test-subj="closedSignals"]'; + export const LOADING_SIGNALS_PANEL = '[data-test-subj="loading-signals-panel"]'; export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal-detection-rules"]'; + +export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; + +export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; + +export const SELECTED_SIGNALS = '[data-test-subj="selectedSignals"]'; + +export const SHOWING_SIGNALS = '[data-test-subj="showingSignals"]'; + +export const SIGNALS = '[data-test-subj="event"]'; + +export const SIGNAL_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 4a0a565a74e27..21a0c136b90df 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -4,7 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN } from '../screens/detections'; +import { + CLOSED_SIGNALS_BTN, + LOADING_SIGNALS_PANEL, + MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNALS_BTN, + OPENED_SIGNALS_BTN, + SIGNALS, + SIGNAL_CHECKBOX, +} from '../screens/detections'; +import { REFRESH_BUTTON } from '../screens/siem_header'; + +export const closeSignals = () => { + cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); +}; + +export const goToClosedSignals = () => { + cy.get(CLOSED_SIGNALS_BTN).click({ force: true }); +}; export const goToManageSignalDetectionRules = () => { cy.get(MANAGE_SIGNAL_DETECTION_RULES_BTN) @@ -12,6 +29,28 @@ export const goToManageSignalDetectionRules = () => { .click({ force: true }); }; +export const goToOpenedSignals = () => { + cy.get(OPENED_SIGNALS_BTN).click({ force: true }); +}; + +export const openSignals = () => { + cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); +}; + +export const selectNumberOfSignals = (numberOfSignals: number) => { + for (let i = 0; i < numberOfSignals; i++) { + cy.get(SIGNAL_CHECKBOX) + .eq(i) + .click({ force: true }); + } +}; + +export const waitForSignals = () => { + cy.get(REFRESH_BUTTON) + .invoke('text') + .should('not.equal', 'Updating'); +}; + export const waitForSignalsIndexToBeCreated = () => { cy.request({ url: '/api/detection_engine/index', retryOnStatusCodeFailure: true }).then( response => { @@ -26,3 +65,8 @@ export const waitForSignalsPanelToBeLoaded = () => { cy.get(LOADING_SIGNALS_PANEL).should('exist'); cy.get(LOADING_SIGNALS_PANEL).should('not.exist'); }; + +export const waitForSignalsToBeLoaded = () => { + const expectedNumberOfDisplayedSignals = 25; + cy.get(SIGNALS).should('have.length', expectedNumberOfDisplayedSignals); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts index 72c95cba2361b..1743fcb561064 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts @@ -12,6 +12,14 @@ export const esArchiverLoadEmptyKibana = () => { ); }; +export const esArchiverLoad = (folder: string) => { + cy.exec( + `node ../../../../scripts/es_archiver load ${folder} --dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( + 'ELASTICSEARCH_URL' + )} --kibana-url ${Cypress.config().baseUrl}` + ); +}; + export const esArchiverUnloadEmptyKibana = () => { cy.exec( `node ../../../../scripts/es_archiver empty_kibana unload empty--dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( diff --git a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx index d3e2be0e8f816..19e884e326390 100644 --- a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx +++ b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx @@ -42,11 +42,23 @@ Popover.displayName = 'Popover'; export interface UtilityBarActionProps extends LinkIconProps { popoverContent?: (closePopover: () => void) => React.ReactNode; + dataTestSubj?: string; } export const UtilityBarAction = React.memo( - ({ children, color, disabled, href, iconSide, iconSize, iconType, onClick, popoverContent }) => ( - + ({ + children, + color, + dataTestSubj, + disabled, + href, + iconSide, + iconSize, + iconType, + onClick, + popoverContent, + }) => ( + {popoverContent ? ( (({ children }) => ( - {children} +export const UtilityBarText = React.memo(({ children, dataTestSubj }) => ( + {children} )); UtilityBarText.displayName = 'UtilityBarText'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx index 3c1317d463f8e..a8dd22863e3c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx @@ -32,6 +32,7 @@ const SignalsTableFilterGroupComponent: React.FC = ({ onFilterGroupChange return ( = ({ onFilterGroupChange diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx index 25c0424cadf11..2000a699ab18d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx @@ -65,13 +65,15 @@ const SignalsUtilityBarComponent: React.FC = ({ - {i18n.SHOWING_SIGNALS(formattedTotalCount, totalCount)} + + {i18n.SHOWING_SIGNALS(formattedTotalCount, totalCount)} + {canUserCRUD && hasIndexWrite && ( <> - + {i18n.SELECTED_SIGNALS( showClearSelection ? formattedTotalCount : formattedSelectedEventsCount, showClearSelection ? totalCount : Object.keys(selectedEventIds).length @@ -79,6 +81,7 @@ const SignalsUtilityBarComponent: React.FC = ({ +#yKt5Ih8T7#Ii;oPprM-zGWd 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

<@<3bF$Vx!=P?#9{ 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 literal 0 HcmV?d00001 diff --git a/x-pack/test/siem_cypress/es_archives/signals/mappings.json b/x-pack/test/siem_cypress/es_archives/signals/mappings.json new file mode 100644 index 0000000000000..114faa0dae336 --- /dev/null +++ b/x-pack/test/siem_cypress/es_archives/signals/mappings.json @@ -0,0 +1,7602 @@ +{ + "type": "index", + "value": { + "aliases": { + ".siem-signals-default": { + "is_write_index": true + } + }, + "index": ".siem-signals-default-000001", + "mappings": { + "dynamic": "false", + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "doc_values": false, + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signal": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_event": { + "properties": { + "action": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "code": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "original": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "outcome": { + "type": "keyword" + }, + "provider": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_time": { + "type": "date" + }, + "parent": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "false_positives": { + "type": "keyword" + }, + "filters": { + "type": "object" + }, + "from": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "language": { + "type": "keyword" + }, + "max_signals": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "output_index": { + "type": "keyword" + }, + "query": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "risk_score": { + "type": "keyword" + }, + "rule_id": { + "type": "keyword" + }, + "saved_id": { + "type": "keyword" + }, + "severity": { + "type": "keyword" + }, + "size": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": ".siem-signals-default", + "rollover_alias": ".siem-signals-default" + }, + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + "auditbeat-7.6.0": { + "is_write_index": true + } + }, + "index": "auditbeat-7.6.0-2020.03.11-000001", + "mappings": { + "_meta": { + "beat": "auditbeat", + "version": "7.6.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "dns.answers": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "dns.answers.*" + } + }, + { + "log.syslog": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "log.syslog.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.annotations.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "auditd": { + "properties": { + "data": { + "properties": { + "a0": { + "ignore_above": 1024, + "type": "keyword" + }, + "a1": { + "ignore_above": 1024, + "type": "keyword" + }, + "a2": { + "ignore_above": 1024, + "type": "keyword" + }, + "a3": { + "ignore_above": 1024, + "type": "keyword" + }, + "a[0-3]": { + "ignore_above": 1024, + "type": "keyword" + }, + "acct": { + "ignore_above": 1024, + "type": "keyword" + }, + "acl": { + "ignore_above": 1024, + "type": "keyword" + }, + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "added": { + "ignore_above": 1024, + "type": "keyword" + }, + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "apparmor": { + "ignore_above": 1024, + "type": "keyword" + }, + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "argc": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_limit": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_wait_time": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_failure": { + "ignore_above": 1024, + "type": "keyword" + }, + "banners": { + "ignore_above": 1024, + "type": "keyword" + }, + "bool": { + "ignore_above": 1024, + "type": "keyword" + }, + "bus": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "capability": { + "ignore_above": 1024, + "type": "keyword" + }, + "cgroup": { + "ignore_above": 1024, + "type": "keyword" + }, + "changed": { + "ignore_above": 1024, + "type": "keyword" + }, + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "cmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "compat": { + "ignore_above": 1024, + "type": "keyword" + }, + "daddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "default-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "dmac": { + "ignore_above": 1024, + "type": "keyword" + }, + "dport": { + "ignore_above": 1024, + "type": "keyword" + }, + "enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "entries": { + "ignore_above": 1024, + "type": "keyword" + }, + "exit": { + "ignore_above": 1024, + "type": "keyword" + }, + "fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "fd": { + "ignore_above": 1024, + "type": "keyword" + }, + "fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "feature": { + "ignore_above": 1024, + "type": "keyword" + }, + "fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "file": { + "ignore_above": 1024, + "type": "keyword" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "format": { + "ignore_above": 1024, + "type": "keyword" + }, + "fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "grantors": { + "ignore_above": 1024, + "type": "keyword" + }, + "grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "hook": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "icmp_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "igid": { + "ignore_above": 1024, + "type": "keyword" + }, + "img-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "inif": { + "ignore_above": 1024, + "type": "keyword" + }, + "ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "invalid_context": { + "ignore_above": 1024, + "type": "keyword" + }, + "ioctlcmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipx-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "items": { + "ignore_above": 1024, + "type": "keyword" + }, + "iuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "ksize": { + "ignore_above": 1024, + "type": "keyword" + }, + "laddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "len": { + "ignore_above": 1024, + "type": "keyword" + }, + "list": { + "ignore_above": 1024, + "type": "keyword" + }, + "lport": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "macproto": { + "ignore_above": 1024, + "type": "keyword" + }, + "maj": { + "ignore_above": 1024, + "type": "keyword" + }, + "major": { + "ignore_above": 1024, + "type": "keyword" + }, + "minor": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "ignore_above": 1024, + "type": "keyword" + }, + "msg": { + "ignore_above": 1024, + "type": "keyword" + }, + "nargs": { + "ignore_above": 1024, + "type": "keyword" + }, + "net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ocomm": { + "ignore_above": 1024, + "type": "keyword" + }, + "oflag": { + "ignore_above": 1024, + "type": "keyword" + }, + "old": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_val": { + "ignore_above": 1024, + "type": "keyword" + }, + "op": { + "ignore_above": 1024, + "type": "keyword" + }, + "opid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oses": { + "ignore_above": 1024, + "type": "keyword" + }, + "outif": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "ignore_above": 1024, + "type": "keyword" + }, + "per": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm_mask": { + "ignore_above": 1024, + "type": "keyword" + }, + "permissive": { + "ignore_above": 1024, + "type": "keyword" + }, + "pfs": { + "ignore_above": 1024, + "type": "keyword" + }, + "printer": { + "ignore_above": 1024, + "type": "keyword" + }, + "prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "proto": { + "ignore_above": 1024, + "type": "keyword" + }, + "qbytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "range": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "removed": { + "ignore_above": 1024, + "type": "keyword" + }, + "res": { + "ignore_above": 1024, + "type": "keyword" + }, + "resrc": { + "ignore_above": 1024, + "type": "keyword" + }, + "rport": { + "ignore_above": 1024, + "type": "keyword" + }, + "sauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "scontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "selected-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperm": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperms": { + "ignore_above": 1024, + "type": "keyword" + }, + "seqno": { + "ignore_above": 1024, + "type": "keyword" + }, + "seresult": { + "ignore_above": 1024, + "type": "keyword" + }, + "ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "sig": { + "ignore_above": 1024, + "type": "keyword" + }, + "sigev_signo": { + "ignore_above": 1024, + "type": "keyword" + }, + "smac": { + "ignore_above": 1024, + "type": "keyword" + }, + "socket": { + "properties": { + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "ignore_above": 1024, + "type": "keyword" + }, + "saddr": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "spid": { + "ignore_above": 1024, + "type": "keyword" + }, + "sport": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "subj": { + "ignore_above": 1024, + "type": "keyword" + }, + "success": { + "ignore_above": 1024, + "type": "keyword" + }, + "syscall": { + "ignore_above": 1024, + "type": "keyword" + }, + "table": { + "ignore_above": 1024, + "type": "keyword" + }, + "tclass": { + "ignore_above": 1024, + "type": "keyword" + }, + "tcontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "uri": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "val": { + "ignore_above": 1024, + "type": "keyword" + }, + "ver": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "watch": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "paths": { + "properties": { + "dev": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "item": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "nametype": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_role": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_user": { + "ignore_above": 1024, + "type": "keyword" + }, + "objtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "ogid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ouid": { + "ignore_above": 1024, + "type": "keyword" + }, + "rdev": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "actor": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "how": { + "ignore_above": 1024, + "type": "keyword" + }, + "object": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "fields": { + "raw": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "selinux": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "setgid": { + "type": "boolean" + }, + "setuid": { + "type": "boolean" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "jolokia": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secured": { + "type": "boolean" + }, + "server": { + "properties": { + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "socket": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "system": { + "properties": { + "audit": { + "properties": { + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "boottime": { + "type": "date" + }, + "containerized": { + "type": "boolean" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timezone": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "package": { + "properties": { + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "installtime": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "release": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "summary": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "properties": { + "last_changed": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shell": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_information": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tracing": { + "properties": { + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "audit": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "filesystem": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name_map": { + "type": "object" + }, + "saved": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "selinux": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": "auditbeat", + "rollover_alias": "auditbeat-7.6.0" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1", + "query": { + "default_field": [ + "message", + "tags", + "agent.ephemeral_id", + "agent.id", + "agent.name", + "agent.type", + "agent.version", + "as.organization.name", + "client.address", + "client.as.organization.name", + "client.domain", + "client.geo.city_name", + "client.geo.continent_name", + "client.geo.country_iso_code", + "client.geo.country_name", + "client.geo.name", + "client.geo.region_iso_code", + "client.geo.region_name", + "client.mac", + "client.registered_domain", + "client.top_level_domain", + "client.user.domain", + "client.user.email", + "client.user.full_name", + "client.user.group.domain", + "client.user.group.id", + "client.user.group.name", + "client.user.hash", + "client.user.id", + "client.user.name", + "cloud.account.id", + "cloud.availability_zone", + "cloud.instance.id", + "cloud.instance.name", + "cloud.machine.type", + "cloud.provider", + "cloud.region", + "container.id", + "container.image.name", + "container.image.tag", + "container.name", + "container.runtime", + "destination.address", + "destination.as.organization.name", + "destination.domain", + "destination.geo.city_name", + "destination.geo.continent_name", + "destination.geo.country_iso_code", + "destination.geo.country_name", + "destination.geo.name", + "destination.geo.region_iso_code", + "destination.geo.region_name", + "destination.mac", + "destination.registered_domain", + "destination.top_level_domain", + "destination.user.domain", + "destination.user.email", + "destination.user.full_name", + "destination.user.group.domain", + "destination.user.group.id", + "destination.user.group.name", + "destination.user.hash", + "destination.user.id", + "destination.user.name", + "dns.answers.class", + "dns.answers.data", + "dns.answers.name", + "dns.answers.type", + "dns.header_flags", + "dns.id", + "dns.op_code", + "dns.question.class", + "dns.question.name", + "dns.question.registered_domain", + "dns.question.subdomain", + "dns.question.top_level_domain", + "dns.question.type", + "dns.response_code", + "dns.type", + "ecs.version", + "error.code", + "error.id", + "error.message", + "error.stack_trace", + "error.type", + "event.action", + "event.category", + "event.code", + "event.dataset", + "event.hash", + "event.id", + "event.kind", + "event.module", + "event.original", + "event.outcome", + "event.provider", + "event.timezone", + "event.type", + "file.device", + "file.directory", + "file.extension", + "file.gid", + "file.group", + "file.hash.md5", + "file.hash.sha1", + "file.hash.sha256", + "file.hash.sha512", + "file.inode", + "file.mode", + "file.name", + "file.owner", + "file.path", + "file.target_path", + "file.type", + "file.uid", + "geo.city_name", + "geo.continent_name", + "geo.country_iso_code", + "geo.country_name", + "geo.name", + "geo.region_iso_code", + "geo.region_name", + "group.domain", + "group.id", + "group.name", + "hash.md5", + "hash.sha1", + "hash.sha256", + "hash.sha512", + "host.architecture", + "host.geo.city_name", + "host.geo.continent_name", + "host.geo.country_iso_code", + "host.geo.country_name", + "host.geo.name", + "host.geo.region_iso_code", + "host.geo.region_name", + "host.hostname", + "host.id", + "host.mac", + "host.name", + "host.os.family", + "host.os.full", + "host.os.kernel", + "host.os.name", + "host.os.platform", + "host.os.version", + "host.type", + "host.user.domain", + "host.user.email", + "host.user.full_name", + "host.user.group.domain", + "host.user.group.id", + "host.user.group.name", + "host.user.hash", + "host.user.id", + "host.user.name", + "http.request.body.content", + "http.request.method", + "http.request.referrer", + "http.response.body.content", + "http.version", + "log.level", + "log.logger", + "log.origin.file.name", + "log.origin.function", + "log.original", + "log.syslog.facility.name", + "log.syslog.severity.name", + "network.application", + "network.community_id", + "network.direction", + "network.iana_number", + "network.name", + "network.protocol", + "network.transport", + "network.type", + "observer.geo.city_name", + "observer.geo.continent_name", + "observer.geo.country_iso_code", + "observer.geo.country_name", + "observer.geo.name", + "observer.geo.region_iso_code", + "observer.geo.region_name", + "observer.hostname", + "observer.mac", + "observer.name", + "observer.os.family", + "observer.os.full", + "observer.os.kernel", + "observer.os.name", + "observer.os.platform", + "observer.os.version", + "observer.product", + "observer.serial_number", + "observer.type", + "observer.vendor", + "observer.version", + "organization.id", + "organization.name", + "os.family", + "os.full", + "os.kernel", + "os.name", + "os.platform", + "os.version", + "package.architecture", + "package.checksum", + "package.description", + "package.install_scope", + "package.license", + "package.name", + "package.path", + "package.version", + "process.args", + "text", + "process.executable", + "process.hash.md5", + "process.hash.sha1", + "process.hash.sha256", + "process.hash.sha512", + "process.name", + "text", + "text", + "text", + "text", + "text", + "process.thread.name", + "process.title", + "process.working_directory", + "server.address", + "server.as.organization.name", + "server.domain", + "server.geo.city_name", + "server.geo.continent_name", + "server.geo.country_iso_code", + "server.geo.country_name", + "server.geo.name", + "server.geo.region_iso_code", + "server.geo.region_name", + "server.mac", + "server.registered_domain", + "server.top_level_domain", + "server.user.domain", + "server.user.email", + "server.user.full_name", + "server.user.group.domain", + "server.user.group.id", + "server.user.group.name", + "server.user.hash", + "server.user.id", + "server.user.name", + "service.ephemeral_id", + "service.id", + "service.name", + "service.node.name", + "service.state", + "service.type", + "service.version", + "source.address", + "source.as.organization.name", + "source.domain", + "source.geo.city_name", + "source.geo.continent_name", + "source.geo.country_iso_code", + "source.geo.country_name", + "source.geo.name", + "source.geo.region_iso_code", + "source.geo.region_name", + "source.mac", + "source.registered_domain", + "source.top_level_domain", + "source.user.domain", + "source.user.email", + "source.user.full_name", + "source.user.group.domain", + "source.user.group.id", + "source.user.group.name", + "source.user.hash", + "source.user.id", + "source.user.name", + "threat.framework", + "threat.tactic.id", + "threat.tactic.name", + "threat.tactic.reference", + "threat.technique.id", + "threat.technique.name", + "threat.technique.reference", + "tracing.trace.id", + "tracing.transaction.id", + "url.domain", + "url.extension", + "url.fragment", + "url.full", + "url.original", + "url.password", + "url.path", + "url.query", + "url.registered_domain", + "url.scheme", + "url.top_level_domain", + "url.username", + "user.domain", + "user.email", + "user.full_name", + "user.group.domain", + "user.group.id", + "user.group.name", + "user.hash", + "user.id", + "user.name", + "user_agent.device.name", + "user_agent.name", + "text", + "user_agent.original", + "user_agent.os.family", + "user_agent.os.full", + "user_agent.os.kernel", + "user_agent.os.name", + "user_agent.os.platform", + "user_agent.os.version", + "user_agent.version", + "text", + "agent.hostname", + "timeseries.instance", + "cloud.project.id", + "cloud.image.id", + "host.os.build", + "host.os.codename", + "kubernetes.pod.name", + "kubernetes.pod.uid", + "kubernetes.namespace", + "kubernetes.node.name", + "kubernetes.replicaset.name", + "kubernetes.deployment.name", + "kubernetes.statefulset.name", + "kubernetes.container.name", + "kubernetes.container.image", + "jolokia.agent.version", + "jolokia.agent.id", + "jolokia.server.product", + "jolokia.server.version", + "jolokia.server.vendor", + "jolokia.url", + "raw", + "file.origin", + "file.selinux.user", + "file.selinux.role", + "file.selinux.domain", + "file.selinux.level", + "user.audit.id", + "user.audit.name", + "user.effective.id", + "user.effective.name", + "user.effective.group.id", + "user.effective.group.name", + "user.filesystem.id", + "user.filesystem.name", + "user.filesystem.group.id", + "user.filesystem.group.name", + "user.saved.id", + "user.saved.name", + "user.saved.group.id", + "user.saved.group.name", + "user.selinux.user", + "user.selinux.role", + "user.selinux.domain", + "user.selinux.level", + "user.selinux.category", + "source.path", + "destination.path", + "auditd.message_type", + "auditd.session", + "auditd.result", + "auditd.summary.actor.primary", + "auditd.summary.actor.secondary", + "auditd.summary.object.type", + "auditd.summary.object.primary", + "auditd.summary.object.secondary", + "auditd.summary.how", + "auditd.paths.inode", + "auditd.paths.dev", + "auditd.paths.obj_user", + "auditd.paths.obj_role", + "auditd.paths.obj_domain", + "auditd.paths.obj_level", + "auditd.paths.objtype", + "auditd.paths.ouid", + "auditd.paths.rdev", + "auditd.paths.nametype", + "auditd.paths.ogid", + "auditd.paths.item", + "auditd.paths.mode", + "auditd.paths.name", + "auditd.data.action", + "auditd.data.minor", + "auditd.data.acct", + "auditd.data.addr", + "auditd.data.cipher", + "auditd.data.id", + "auditd.data.entries", + "auditd.data.kind", + "auditd.data.ksize", + "auditd.data.spid", + "auditd.data.arch", + "auditd.data.argc", + "auditd.data.major", + "auditd.data.unit", + "auditd.data.table", + "auditd.data.terminal", + "auditd.data.grantors", + "auditd.data.direction", + "auditd.data.op", + "auditd.data.tty", + "auditd.data.syscall", + "auditd.data.data", + "auditd.data.family", + "auditd.data.mac", + "auditd.data.pfs", + "auditd.data.items", + "auditd.data.a0", + "auditd.data.a1", + "auditd.data.a2", + "auditd.data.a3", + "auditd.data.hostname", + "auditd.data.lport", + "auditd.data.rport", + "auditd.data.exit", + "auditd.data.fp", + "auditd.data.laddr", + "auditd.data.sport", + "auditd.data.capability", + "auditd.data.nargs", + "auditd.data.new-enabled", + "auditd.data.audit_backlog_limit", + "auditd.data.dir", + "auditd.data.cap_pe", + "auditd.data.model", + "auditd.data.new_pp", + "auditd.data.old-enabled", + "auditd.data.oauid", + "auditd.data.old", + "auditd.data.banners", + "auditd.data.feature", + "auditd.data.vm-ctx", + "auditd.data.opid", + "auditd.data.seperms", + "auditd.data.seresult", + "auditd.data.new-rng", + "auditd.data.old-net", + "auditd.data.sigev_signo", + "auditd.data.ino", + "auditd.data.old_enforcing", + "auditd.data.old-vcpu", + "auditd.data.range", + "auditd.data.res", + "auditd.data.added", + "auditd.data.fam", + "auditd.data.nlnk-pid", + "auditd.data.subj", + "auditd.data.a[0-3]", + "auditd.data.cgroup", + "auditd.data.kernel", + "auditd.data.ocomm", + "auditd.data.new-net", + "auditd.data.permissive", + "auditd.data.class", + "auditd.data.compat", + "auditd.data.fi", + "auditd.data.changed", + "auditd.data.msg", + "auditd.data.dport", + "auditd.data.new-seuser", + "auditd.data.invalid_context", + "auditd.data.dmac", + "auditd.data.ipx-net", + "auditd.data.iuid", + "auditd.data.macproto", + "auditd.data.obj", + "auditd.data.ipid", + "auditd.data.new-fs", + "auditd.data.vm-pid", + "auditd.data.cap_pi", + "auditd.data.old-auid", + "auditd.data.oses", + "auditd.data.fd", + "auditd.data.igid", + "auditd.data.new-disk", + "auditd.data.parent", + "auditd.data.len", + "auditd.data.oflag", + "auditd.data.uuid", + "auditd.data.code", + "auditd.data.nlnk-grp", + "auditd.data.cap_fp", + "auditd.data.new-mem", + "auditd.data.seperm", + "auditd.data.enforcing", + "auditd.data.new-chardev", + "auditd.data.old-rng", + "auditd.data.outif", + "auditd.data.cmd", + "auditd.data.hook", + "auditd.data.new-level", + "auditd.data.sauid", + "auditd.data.sig", + "auditd.data.audit_backlog_wait_time", + "auditd.data.printer", + "auditd.data.old-mem", + "auditd.data.perm", + "auditd.data.old_pi", + "auditd.data.state", + "auditd.data.format", + "auditd.data.new_gid", + "auditd.data.tcontext", + "auditd.data.maj", + "auditd.data.watch", + "auditd.data.device", + "auditd.data.grp", + "auditd.data.bool", + "auditd.data.icmp_type", + "auditd.data.new_lock", + "auditd.data.old_prom", + "auditd.data.acl", + "auditd.data.ip", + "auditd.data.new_pi", + "auditd.data.default-context", + "auditd.data.inode_gid", + "auditd.data.new-log_passwd", + "auditd.data.new_pe", + "auditd.data.selected-context", + "auditd.data.cap_fver", + "auditd.data.file", + "auditd.data.net", + "auditd.data.virt", + "auditd.data.cap_pp", + "auditd.data.old-range", + "auditd.data.resrc", + "auditd.data.new-range", + "auditd.data.obj_gid", + "auditd.data.proto", + "auditd.data.old-disk", + "auditd.data.audit_failure", + "auditd.data.inif", + "auditd.data.vm", + "auditd.data.flags", + "auditd.data.nlnk-fam", + "auditd.data.old-fs", + "auditd.data.old-ses", + "auditd.data.seqno", + "auditd.data.fver", + "auditd.data.qbytes", + "auditd.data.seuser", + "auditd.data.cap_fe", + "auditd.data.new-vcpu", + "auditd.data.old-level", + "auditd.data.old_pp", + "auditd.data.daddr", + "auditd.data.old-role", + "auditd.data.ioctlcmd", + "auditd.data.smac", + "auditd.data.apparmor", + "auditd.data.fe", + "auditd.data.perm_mask", + "auditd.data.ses", + "auditd.data.cap_fi", + "auditd.data.obj_uid", + "auditd.data.reason", + "auditd.data.list", + "auditd.data.old_lock", + "auditd.data.bus", + "auditd.data.old_pe", + "auditd.data.new-role", + "auditd.data.prom", + "auditd.data.uri", + "auditd.data.audit_enabled", + "auditd.data.old-log_passwd", + "auditd.data.old-seuser", + "auditd.data.per", + "auditd.data.scontext", + "auditd.data.tclass", + "auditd.data.ver", + "auditd.data.new", + "auditd.data.val", + "auditd.data.img-ctx", + "auditd.data.old-chardev", + "auditd.data.old_val", + "auditd.data.success", + "auditd.data.inode_uid", + "auditd.data.removed", + "auditd.data.socket.port", + "auditd.data.socket.saddr", + "auditd.data.socket.addr", + "auditd.data.socket.family", + "auditd.data.socket.path", + "geoip.continent_name", + "geoip.city_name", + "geoip.region_name", + "geoip.country_iso_code", + "hash.blake2b_256", + "hash.blake2b_384", + "hash.blake2b_512", + "hash.md5", + "hash.sha1", + "hash.sha224", + "hash.sha256", + "hash.sha384", + "hash.sha3_224", + "hash.sha3_256", + "hash.sha3_384", + "hash.sha3_512", + "hash.sha512", + "hash.sha512_224", + "hash.sha512_256", + "hash.xxh64", + "event.origin", + "user.entity_id", + "user.terminal", + "process.entity_id", + "process.hash.blake2b_256", + "process.hash.blake2b_384", + "process.hash.blake2b_512", + "process.hash.sha224", + "process.hash.sha384", + "process.hash.sha3_224", + "process.hash.sha3_256", + "process.hash.sha3_384", + "process.hash.sha3_512", + "process.hash.sha512_224", + "process.hash.sha512_256", + "process.hash.xxh64", + "socket.entity_id", + "system.audit.host.timezone.name", + "system.audit.host.hostname", + "system.audit.host.id", + "system.audit.host.architecture", + "system.audit.host.mac", + "system.audit.host.os.codename", + "system.audit.host.os.platform", + "system.audit.host.os.name", + "system.audit.host.os.family", + "system.audit.host.os.version", + "system.audit.host.os.kernel", + "system.audit.package.entity_id", + "system.audit.package.name", + "system.audit.package.version", + "system.audit.package.release", + "system.audit.package.arch", + "system.audit.package.license", + "system.audit.package.summary", + "system.audit.package.url", + "system.audit.user.name", + "system.audit.user.uid", + "system.audit.user.gid", + "system.audit.user.dir", + "system.audit.user.shell", + "system.audit.user.user_information", + "system.audit.user.password.type", + "fields.*" + ] + }, + "refresh_interval": "5s" + } + } + } +} \ No newline at end of file From 168239ca07d4a0d90e8fea04a7c4d63fa455cd45 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 16 Mar 2020 12:39:27 +0100 Subject: [PATCH 03/76] [Uptime] Index Status API to Rest (#59657) * gql to rest * update snap * fix api Co-authored-by: Elastic Machine --- .../plugins/uptime/common/constants/index.ts | 1 + .../uptime/common/constants/rest_api.ts | 9 ++++ .../plugins/uptime/common/graphql/types.ts | 12 +---- .../uptime/common/runtime_types/common.ts | 6 +++ .../connected/empty_state/empty_state.tsx | 30 ++++++++++++ .../public/components/connected/index.ts | 1 + .../kuerybar/kuery_bar_container.tsx | 2 +- .../connected/pages/overview_container.tsx | 2 +- .../__snapshots__/empty_state.test.tsx.snap | 37 +++++++-------- .../__tests__/empty_state.test.tsx | 24 ++++------ .../functional/empty_state/empty_state.tsx | 33 ++++++------- .../empty_state/empty_state_error.tsx | 7 ++- .../public/components/functional/index.ts | 1 - .../functional/kuery_bar/kuery_bar.tsx | 16 +++---- .../__tests__/monitor_list.test.tsx | 4 +- .../monitor_list_pagination.test.tsx | 4 +- .../monitor_list_drawer/__tests__/data.json | 2 +- .../public/hooks/update_kuery_string.ts | 2 +- .../plugins/uptime/public/pages/overview.tsx | 7 ++- .../uptime/public/queries/doc_count_query.ts | 22 --------- .../plugins/uptime/public/queries/index.ts | 1 - .../public/queries/monitor_states_query.ts | 4 +- .../uptime/public/state/actions/index.ts | 1 + .../public/state/actions/index_status.ts | 10 ++++ .../uptime/public/state/actions/types.ts | 8 ++++ .../uptime/public/state/actions/utils.ts | 16 +++++++ .../plugins/uptime/public/state/api/index.ts | 1 + .../uptime/public/state/api/index_status.ts | 31 +++++++++++++ .../public/state/effects/fetch_effect.ts | 4 -- .../uptime/public/state/effects/index.ts | 2 + .../public/state/effects/index_status.ts | 17 +++++++ .../uptime/public/state/reducers/index.ts | 2 + .../public/state/reducers/index_pattern.ts | 3 +- .../public/state/reducers/index_status.ts | 30 ++++++++++++ .../index.ts => state/reducers/types.ts} | 5 +- .../uptime/public/state/reducers/utils.ts | 33 +++++++++++++ .../state/selectors/__tests__/index.test.ts | 5 ++ .../uptime/public/state/selectors/index.ts | 6 ++- .../graphql/monitor_states/resolvers.ts | 16 +------ .../graphql/monitor_states/schema.gql.ts | 13 +----- .../server/lib/requests/get_index_status.ts | 6 +-- .../server/lib/requests/uptime_requests.ts | 7 +-- .../plugins/uptime/server/rest_api/index.ts | 3 +- .../get_index_pattern.ts | 0 .../rest_api/index_state/get_index_status.ts | 29 ++++++++++++ .../{index_pattern => index_state}/index.ts | 1 + .../apis/uptime/feature_controls.ts | 25 ++++------ .../apis/uptime/graphql/doc_count.js | 36 --------------- .../uptime/graphql/fixtures/doc_count.json | 8 ---- .../graphql/fixtures/monitor_states.json | 46 +++++-------------- .../fixtures/monitor_states_id_filtered.json | 10 ++-- .../fixtures/monitor_states_page_1.json | 46 +++++-------------- .../fixtures/monitor_states_page_10.json | 46 +++++-------------- .../monitor_states_page_10_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_2.json | 46 +++++-------------- .../monitor_states_page_2_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_3.json | 46 +++++-------------- .../monitor_states_page_3_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_4.json | 46 +++++-------------- .../monitor_states_page_4_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_5.json | 46 +++++-------------- .../monitor_states_page_5_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_6.json | 46 +++++-------------- .../monitor_states_page_6_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_7.json | 46 +++++-------------- .../monitor_states_page_7_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_8.json | 46 +++++-------------- .../monitor_states_page_8_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_9.json | 46 +++++-------------- .../monitor_states_page_9_previous.json | 46 +++++-------------- .../apis/uptime/graphql/index.js | 1 - .../apis/uptime/rest/doc_count.ts | 20 ++++++++ .../apis/uptime/rest/fixtures/doc_count.json | 4 ++ .../api_integration/apis/uptime/rest/index.ts | 1 + 74 files changed, 590 insertions(+), 911 deletions(-) create mode 100644 x-pack/legacy/plugins/uptime/common/constants/rest_api.ts create mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/actions/utils.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/api/index_status.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts rename x-pack/legacy/plugins/uptime/public/{components/functional/empty_state/index.ts => state/reducers/types.ts} (76%) create mode 100644 x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts rename x-pack/plugins/uptime/server/rest_api/{index_pattern => index_state}/get_index_pattern.ts (100%) create mode 100644 x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts rename x-pack/plugins/uptime/server/rest_api/{index_pattern => index_state}/index.ts (82%) delete mode 100644 x-pack/test/api_integration/apis/uptime/graphql/doc_count.js delete mode 100644 x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json create mode 100644 x-pack/test/api_integration/apis/uptime/rest/doc_count.ts create mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json diff --git a/x-pack/legacy/plugins/uptime/common/constants/index.ts b/x-pack/legacy/plugins/uptime/common/constants/index.ts index 9d5ad4607491c..0425fc19a7b45 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/index.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/index.ts @@ -12,3 +12,4 @@ export * from './capabilities'; export { PLUGIN } from './plugin'; export { QUERY, STATES } from './query'; export * from './ui'; +export * from './rest_api'; diff --git a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts new file mode 100644 index 0000000000000..f09c795977831 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export enum REST_API_URLS { + INDEX_STATUS = '/api/uptime/index_status', +} diff --git a/x-pack/legacy/plugins/uptime/common/graphql/types.ts b/x-pack/legacy/plugins/uptime/common/graphql/types.ts index a33a69c229873..1a37ce0b18c73 100644 --- a/x-pack/legacy/plugins/uptime/common/graphql/types.ts +++ b/x-pack/legacy/plugins/uptime/common/graphql/types.ts @@ -21,8 +21,6 @@ export interface Query { /** Fetches the current state of Uptime monitors for the given parameters. */ getMonitorStates?: MonitorSummaryResult | null; - /** Fetches details about the uptime index. */ - getStatesIndexStatus: StatesIndexStatus; } export interface PingResults { @@ -392,7 +390,7 @@ export interface MonitorSummaryResult { /** The objects representing the state of a series of heartbeat monitors. */ summaries?: MonitorSummary[] | null; /** The number of summaries. */ - totalSummaryCount: DocCount; + totalSummaryCount: number; } /** Represents the current state and associated data for an Uptime monitor. */ export interface MonitorSummary { @@ -525,13 +523,7 @@ export interface SummaryHistogramPoint { /** The number of _down_ documents. */ down: number; } -/** Represents the current status of the uptime index. */ -export interface StatesIndexStatus { - /** Flag denoting whether the index exists. */ - indexExists: boolean; - /** The number of documents in the index. */ - docCount?: DocCount | null; -} + export interface AllPingsQueryArgs { /** Optional: the direction to sort by. Accepts 'asc' and 'desc'. Defaults to 'desc'. */ diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts index 84e3ae33294f0..37101b5b46fd2 100644 --- a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts +++ b/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts @@ -22,6 +22,12 @@ export const SummaryType = t.partial({ geo: CheckGeoType, }); +export const StatesIndexStatusType = t.type({ + indexExists: t.boolean, + docCount: t.number, +}); + export type Summary = t.TypeOf; export type CheckGeo = t.TypeOf; export type Location = t.TypeOf; +export type StatesIndexStatus = t.TypeOf; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx new file mode 100644 index 0000000000000..cac7042ca5b5c --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { indexStatusAction } from '../../../state/actions'; +import { indexStatusSelector } from '../../../state/selectors'; +import { EmptyStateComponent } from '../../functional/empty_state/empty_state'; + +export const EmptyState: React.FC = ({ children }) => { + const { data, loading, errors } = useSelector(indexStatusSelector); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(indexStatusAction.get()); + }, [dispatch]); + + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts index 2e30e5c3cb24f..baa961ddc87d2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts @@ -13,3 +13,4 @@ export { MonitorStatusBar } from './monitor/status_bar_container'; export { MonitorListDrawer } from './monitor/list_drawer_container'; export { MonitorListActionsPopover } from './monitor/drawer_popover_container'; export { DurationChart } from './charts/monitor_duration'; +export { EmptyState } from './empty_state/empty_state'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx index d0f160b2c5540..a42f96962b95e 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx @@ -10,7 +10,7 @@ import { selectIndexPattern } from '../../../state/selectors'; import { getIndexPattern } from '../../../state/actions'; import { KueryBarComponent } from '../../functional'; -const mapStateToProps = (state: AppState) => ({ indexPattern: selectIndexPattern(state) }); +const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); const mapDispatchToProps = (dispatch: any) => ({ loadIndexPattern: () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx index cbd1fae77c518..79aaa071507e1 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx @@ -18,6 +18,6 @@ const mapDispatchToProps = (dispatch: any): DispatchProps => ({ setEsKueryFilters: (esFilters: string) => dispatch(setEsKueryString(esFilters)), }); -const mapStateToProps = (state: AppState) => ({ indexPattern: selectIndexPattern(state) }); +const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); export const OverviewPage = connect(mapStateToProps, mapDispatchToProps)(OverviewPageComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap index 472d9c2be59e4..a885cfe22ccd2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap @@ -2,16 +2,6 @@ exports[`EmptyState component does not render empty state with appropriate base path and no docs 1`] = ` `; -exports[`EmptyState component doesn't render child components when count is falsey 1`] = ` +exports[`EmptyState component doesn't render child components when count is falsy 1`] = ` { let statesIndexStatus: StatesIndexStatus; @@ -16,15 +16,13 @@ describe('EmptyState component', () => { beforeEach(() => { statesIndexStatus = { indexExists: true, - docCount: { - count: 1, - }, + docCount: 1, }; }); it('renders child components when count is truthy', () => { const component = shallowWithIntl( - +

Foo
Bar
Baz
@@ -33,9 +31,9 @@ describe('EmptyState component', () => { expect(component).toMatchSnapshot(); }); - it(`doesn't render child components when count is falsey`, () => { + it(`doesn't render child components when count is falsy`, () => { const component = mountWithIntl( - +
Shouldn't be rendered
); @@ -57,7 +55,7 @@ describe('EmptyState component', () => { }, ]; const component = mountWithIntl( - +
Shouldn't appear...
); @@ -66,7 +64,7 @@ describe('EmptyState component', () => { it('renders loading state if no errors or doc count', () => { const component = mountWithIntl( - +
Should appear even while loading...
); @@ -75,13 +73,11 @@ describe('EmptyState component', () => { it('does not render empty state with appropriate base path and no docs', () => { statesIndexStatus = { - docCount: { - count: 0, - }, + docCount: 0, indexExists: true, }; const component = mountWithIntl( - +
If this is in the snapshot the test should fail
); @@ -91,7 +87,7 @@ describe('EmptyState component', () => { it('notifies when index does not exist', () => { statesIndexStatus.indexExists = false; const component = mountWithIntl( - +
This text should not render
); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx index d2d46dff3b9f5..80afc2894ea44 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx @@ -6,29 +6,29 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; -import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../../higher_order'; -import { docCountQuery } from '../../../queries'; import { EmptyStateError } from './empty_state_error'; import { EmptyStateLoading } from './empty_state_loading'; -import { StatesIndexStatus } from '../../../../common/graphql/types'; import { DataMissing } from './data_missing'; - -interface EmptyStateQueryResult { - statesIndexStatus?: StatesIndexStatus; -} +import { StatesIndexStatus } from '../../../../common/runtime_types'; interface EmptyStateProps { children: JSX.Element[] | JSX.Element; + statesIndexStatus: StatesIndexStatus | null; + loading: boolean; + errors?: Error[]; } -type Props = UptimeGraphQLQueryProps & EmptyStateProps; - -export const EmptyStateComponent = ({ children, data, errors }: Props) => { - if (errors) { +export const EmptyStateComponent = ({ + children, + statesIndexStatus, + loading, + errors, +}: EmptyStateProps) => { + if (errors?.length) { return ; } - if (data && data.statesIndexStatus) { - const { indexExists, docCount } = data.statesIndexStatus; + if (!loading && statesIndexStatus) { + const { indexExists, docCount } = statesIndexStatus; if (!indexExists) { return ( { })} /> ); - } else if (indexExists && docCount && docCount.count === 0) { + } else if (indexExists && docCount === 0) { return ( { } return ; }; - -export const EmptyState = withUptimeGraphQL( - EmptyStateComponent, - docCountQuery -); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx index 745b185b57fac..c8e2bece1cb7f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx @@ -7,15 +7,14 @@ import { EuiEmptyPrompt, EuiPanel, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -import { GraphQLError } from 'graphql'; interface EmptyStateErrorProps { - errors: GraphQLError[]; + errors: Error[]; } export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { const unauthorized = errors.find( - (error: GraphQLError) => error.message && error.message.includes('unauthorized') + (error: Error) => error.message && error.message.includes('unauthorized') ); return ( @@ -46,7 +45,7 @@ export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { body={ {!unauthorized && - errors.map((error: GraphQLError) =>

{error.message}

)} + errors.map((error: Error) =>

{error.message}

)}
} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts index e86ba548fb5d9..daba13d8df641 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts @@ -5,7 +5,6 @@ */ export { DonutChart } from './charts/donut_chart'; -export { EmptyState } from './empty_state'; export { KueryBarComponent } from './kuery_bar/kuery_bar'; export { MonitorCharts } from './monitor_charts'; export { MonitorList } from './monitor_list'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx index 496e8d898df3c..2f5ccc2adf313 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx @@ -34,14 +34,16 @@ function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) { interface Props { autocomplete: DataPublicPluginSetup['autocomplete']; - loadIndexPattern: any; - indexPattern: any; + loadIndexPattern: () => void; + indexPattern: IIndexPattern | null; + loading: boolean; } export function KueryBarComponent({ autocomplete: autocompleteService, loadIndexPattern, indexPattern, + loading, }: Props) { useEffect(() => { if (!indexPattern) { @@ -53,19 +55,13 @@ export function KueryBarComponent({ suggestions: [], isLoadingIndexPattern: true, }); - const [isLoadingIndexPattern, setIsLoadingIndexPattern] = useState(true); const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); let currentRequestCheck: string; - useEffect(() => { - if (indexPattern !== undefined) { - setIsLoadingIndexPattern(false); - } - }, [indexPattern]); const [getUrlParams, updateUrlParams] = useUrlParams(); const { search: kuery } = getUrlParams(); - const indexPatternMissing = !isLoadingIndexPattern && !indexPattern; + const indexPatternMissing = loading && !indexPattern; async function onChange(inputValue: string, selectionStart: number) { if (!indexPattern) { @@ -124,7 +120,7 @@ export function KueryBarComponent({ { }, }, ], - totalSummaryCount: { - count: 2, - }, + totalSummaryCount: 2, }; }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx index ff54e61006156..1aef9281a3066 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx @@ -89,9 +89,7 @@ describe('MonitorListPagination component', () => { }, }, ], - totalSummaryCount: { - count: 2, - }, + totalSummaryCount: 2, }; }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json index a45e974685b9c..e8142f0480c4a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json @@ -3,7 +3,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": null, - "totalSummaryCount": { "count": 147428, "__typename": "DocCount" }, + "totalSummaryCount": 147428, "summaries": [ { "monitor_id": "andrewvc-com", diff --git a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts b/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts index 5fcacf8424660..ab4d6f75849e8 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts @@ -24,7 +24,7 @@ const getKueryString = (urlFilters: string): string => { }; export const useUpdateKueryString = ( - indexPattern: IIndexPattern, + indexPattern: IIndexPattern | null, filterQueryString = '', urlFilters: string ): [string?, Error?] => { diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index 15e31d5e44629..af9b8bf046416 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -9,7 +9,6 @@ import React, { useContext, useEffect } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { - EmptyState, MonitorList, OverviewPageParsingErrorCallout, StatusPanel, @@ -19,13 +18,13 @@ import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../../../plugins/observability/public'; import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public'; import { UptimeThemeContext } from '../contexts'; -import { FilterGroup, KueryBar } from '../components/connected'; +import { EmptyState, FilterGroup, KueryBar } from '../components/connected'; import { useUpdateKueryString } from '../hooks'; import { PageHeader } from './page_header'; interface OverviewPageProps { autocomplete: DataPublicPluginSetup['autocomplete']; - indexPattern: IIndexPattern; + indexPattern: IIndexPattern | null; setEsKueryFilters: (esFilters: string) => void; } @@ -81,7 +80,7 @@ export const OverviewPageComponent = ({ autocomplete, indexPattern, setEsKueryFi return ( <> - + diff --git a/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts b/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts deleted file mode 100644 index 3067a9d16f050..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const docCountQueryString = ` -query GetStateIndexStatus { - statesIndexStatus: getStatesIndexStatus { - docCount { - count - } - indexExists - } -} -`; - -export const docCountQuery = gql` - ${docCountQueryString} -`; diff --git a/x-pack/legacy/plugins/uptime/public/queries/index.ts b/x-pack/legacy/plugins/uptime/public/queries/index.ts index f2fff9bc506d0..283382ec1b7ba 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/index.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { docCountQuery, docCountQueryString } from './doc_count_query'; export { pingsQuery, pingsQueryString } from './pings_query'; diff --git a/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts b/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts index 76f62ad453bd9..9e609786094d5 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts @@ -17,9 +17,7 @@ query MonitorStates($dateRangeStart: String!, $dateRangeEnd: String!, $paginatio ) { prevPagePagination nextPagePagination - totalSummaryCount { - count - } + totalSummaryCount summaries { monitor_id histogram { diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts b/x-pack/legacy/plugins/uptime/public/state/actions/index.ts index dfcea64bf9c08..b2ab73879a4a7 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/index.ts @@ -11,3 +11,4 @@ export * from './monitor_status'; export * from './index_patternts'; export * from './ping'; export * from './monitor_duration'; +export * from './index_status'; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts new file mode 100644 index 0000000000000..336758a71ce60 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts @@ -0,0 +1,10 @@ +/* + * 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 { createAsyncAction } from './utils'; +import { StatesIndexStatus } from '../../../common/runtime_types'; + +export const indexStatusAction = createAsyncAction('GET INDEX STATUS'); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts b/x-pack/legacy/plugins/uptime/public/state/actions/types.ts index dba70ed839ac5..e9bf11256b0b8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/types.ts @@ -4,6 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Action } from 'redux-actions'; + +export interface AsyncAction { + get: (payload?: any) => Action; + success: (payload?: any) => Action; + fail: (payload?: any) => Action; +} + export interface QueryParams { monitorId: string; dateStart: string; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts b/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts new file mode 100644 index 0000000000000..337c4bfb2fa47 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts @@ -0,0 +1,16 @@ +/* + * 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 { createAction } from 'redux-actions'; +import { AsyncAction } from './types'; + +export function createAsyncAction(actionStr: string): AsyncAction { + return { + get: createAction(actionStr), + success: createAction(`${actionStr}_SUCCESS`), + fail: createAction(`${actionStr}_FAIL`), + }; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index.ts b/x-pack/legacy/plugins/uptime/public/state/api/index.ts index 7d42c6ee46bdc..518091cb36dde 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/index.ts @@ -9,5 +9,6 @@ export * from './overview_filters'; export * from './snapshot'; export * from './monitor_status'; export * from './index_pattern'; +export * from './index_status'; export * from './ping'; export * from './monitor_duration'; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts new file mode 100644 index 0000000000000..9c531b3406a7c --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PathReporter } from 'io-ts/lib/PathReporter'; +import { isRight } from 'fp-ts/lib/Either'; +import { getApiPath } from '../../lib/helper'; +import { REST_API_URLS } from '../../../common/constants/rest_api'; +import { StatesIndexStatus, StatesIndexStatusType } from '../../../common/runtime_types'; + +interface ApiRequest { + basePath: string; +} + +export const fetchIndexStatus = async ({ basePath }: ApiRequest): Promise => { + const url = getApiPath(REST_API_URLS.INDEX_STATUS, basePath); + + const response = await fetch(url); + if (!response.ok) { + throw new Error(response.statusText); + } + const responseData = await response.json(); + const decoded = StatesIndexStatusType.decode(responseData); + PathReporter.report(decoded); + if (isRight(decoded)) { + return decoded.right; + } + throw PathReporter.report(decoded); +}; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts index d293cdbe451b5..ea389ff0a6745 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts @@ -26,10 +26,6 @@ export function fetchEffectFactory( ) { return function*(action: Action) { try { - if (!action.payload) { - yield put(fail(new Error('Cannot fetch snapshot for undefined parameters.'))); - return; - } const { payload: { ...params }, } = action; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts index 43af88f4cc291..7c45aa142ecfd 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts @@ -12,6 +12,7 @@ import { fetchMonitorStatusEffect } from './monitor_status'; import { fetchIndexPatternEffect } from './index_pattern'; import { fetchPingHistogramEffect } from './ping'; import { fetchMonitorDurationEffect } from './monitor_duration'; +import { fetchIndexStatusEffect } from './index_status'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -21,4 +22,5 @@ export function* rootEffect() { yield fork(fetchIndexPatternEffect); yield fork(fetchPingHistogramEffect); yield fork(fetchMonitorDurationEffect); + yield fork(fetchIndexStatusEffect); } diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts new file mode 100644 index 0000000000000..793a671f5fed8 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts @@ -0,0 +1,17 @@ +/* + * 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 { takeLatest } from 'redux-saga/effects'; +import { indexStatusAction } from '../actions'; +import { fetchIndexStatus } from '../api'; +import { fetchEffectFactory } from './fetch_effect'; + +export function* fetchIndexStatusEffect() { + yield takeLatest( + indexStatusAction.get, + fetchEffectFactory(fetchIndexStatus, indexStatusAction.success, indexStatusAction.fail) + ); +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts index 32362afae42bc..4a83b54504ca8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts @@ -13,6 +13,7 @@ import { monitorStatusReducer } from './monitor_status'; import { indexPatternReducer } from './index_pattern'; import { pingReducer } from './ping'; import { monitorDurationReducer } from './monitor_duration'; +import { indexStatusReducer } from './index_status'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -23,4 +24,5 @@ export const rootReducer = combineReducers({ indexPattern: indexPatternReducer, ping: pingReducer, monitorDuration: monitorDurationReducer, + indexStatus: indexStatusReducer, }); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts index dff043f81b95c..bc482e2f35c45 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts @@ -5,9 +5,10 @@ */ import { handleActions, Action } from 'redux-actions'; import { getIndexPattern, getIndexPatternSuccess, getIndexPatternFail } from '../actions'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; export interface IndexPatternState { - index_pattern: any; + index_pattern: IIndexPattern | null; errors: any[]; loading: boolean; } diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts new file mode 100644 index 0000000000000..50a02210fb5d3 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { handleActions } from 'redux-actions'; +import { indexStatusAction } from '../actions'; +import { handleAsyncAction } from './utils'; +import { IReducerState } from './types'; +import { StatesIndexStatus } from '../../../common/runtime_types'; + +export interface IndexStatusState extends IReducerState { + data: StatesIndexStatus | null; +} + +const initialState: IndexStatusState = { + data: null, + loading: false, + errors: [], +}; + +type PayLoad = StatesIndexStatus & Error; + +export const indexStatusReducer = handleActions( + { + ...handleAsyncAction('data', indexStatusAction), + }, + initialState +); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts similarity index 76% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts rename to x-pack/legacy/plugins/uptime/public/state/reducers/types.ts index 8ee70bf51f006..40fe4bddbf172 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts @@ -4,4 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EmptyState } from './empty_state'; +export interface IReducerState { + errors: Error[]; + loading: boolean; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts new file mode 100644 index 0000000000000..773ec10686943 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Action } from 'redux-actions'; +import { AsyncAction } from '../actions/types'; +import { IReducerState } from './types'; + +export function handleAsyncAction( + storeKey: string, + asyncAction: AsyncAction +) { + return { + [String(asyncAction.get)]: (state: ReducerState) => ({ + ...state, + loading: true, + }), + + [String(asyncAction.success)]: (state: ReducerState, action: Action) => ({ + ...state, + loading: false, + [storeKey]: action.payload === null ? action.payload : { ...action.payload }, + }), + + [String(asyncAction.fail)]: (state: ReducerState, action: Action) => ({ + ...state, + errors: [...state.errors, action.payload], + loading: false, + }), + }; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts index 24d34b4d067cc..de446418632b8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -60,6 +60,11 @@ describe('state selectors', () => { loading: false, errors: [], }, + indexStatus: { + loading: false, + data: null, + errors: [], + }, }; it('selects base path from state', () => { diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index 0a914a14c372b..adba288b8b145 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -30,7 +30,7 @@ export const selectMonitorStatus = (state: AppState) => { }; export const selectIndexPattern = ({ indexPattern }: AppState) => { - return indexPattern.index_pattern; + return { indexPattern: indexPattern.index_pattern, loading: indexPattern.loading }; }; export const selectPingHistogram = ({ ping, ui }: AppState) => { @@ -45,3 +45,7 @@ export const selectPingHistogram = ({ ping, ui }: AppState) => { export const selectDurationLines = ({ monitorDuration }: AppState) => { return monitorDuration; }; + +export const indexStatusSelector = ({ indexStatus }: AppState) => { + return indexStatus; +}; diff --git a/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts b/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts index 6ac42f7717259..1560b78b3c050 100644 --- a/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts +++ b/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts @@ -10,9 +10,8 @@ import { UMResolver } from '../../../../../legacy/plugins/uptime/common/graphql/ import { GetMonitorStatesQueryArgs, MonitorSummaryResult, - StatesIndexStatus, } from '../../../../../legacy/plugins/uptime/common/graphql/types'; -import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants/context_defaults'; +import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; export type UMGetMonitorStatesResolver = UMResolver< MonitorSummaryResult | Promise, @@ -21,19 +20,11 @@ export type UMGetMonitorStatesResolver = UMResolver< UMContext >; -export type UMStatesIndexExistsResolver = UMResolver< - StatesIndexStatus | Promise, - any, - {}, - UMContext ->; - export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( libs: UMServerLibs ): { Query: { getMonitorStates: UMGetMonitorStatesResolver; - getStatesIndexStatus: UMStatesIndexExistsResolver; }; } => { return { @@ -64,7 +55,7 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( }), ]); - const totalSummaryCount = indexStatus?.docCount ?? { count: undefined }; + const totalSummaryCount = indexStatus?.docCount ?? 0; return { summaries, @@ -73,9 +64,6 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( totalSummaryCount, }; }, - async getStatesIndexStatus(_resolver, {}, { APICaller }): Promise { - return await libs.requests.getIndexStatus({ callES: APICaller }); - }, }, }; }; diff --git a/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts b/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts index 198f97eab9652..d088aed951204 100644 --- a/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts +++ b/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts @@ -156,15 +156,7 @@ export const monitorStatesSchema = gql` "The objects representing the state of a series of heartbeat monitors." summaries: [MonitorSummary!] "The number of summaries." - totalSummaryCount: DocCount! - } - - "Represents the current status of the uptime index." - type StatesIndexStatus { - "Flag denoting whether the index exists." - indexExists: Boolean! - "The number of documents in the index." - docCount: DocCount + totalSummaryCount: Int! } enum CursorDirection { @@ -186,8 +178,5 @@ export const monitorStatesSchema = gql` filters: String statusFilter: String ): MonitorSummaryResult - - "Fetches details about the uptime index." - getStatesIndexStatus: StatesIndexStatus! } `; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts index 95aa7eeef88e1..d8a05c08b1417 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts @@ -5,8 +5,8 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/graphql/types'; import { INDEX_NAMES } from '../../../../../legacy/plugins/uptime/common/constants'; +import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/runtime_types'; export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ callES }) => { const { @@ -15,8 +15,6 @@ export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = asy } = await callES('count', { index: INDEX_NAMES.HEARTBEAT }); return { indexExists: total > 0, - docCount: { - count, - }, + docCount: count, }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts index 6fd77afe711d4..7f192994bd075 100644 --- a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts +++ b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts @@ -5,11 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { - Ping, - PingResults, - StatesIndexStatus, -} from '../../../../../legacy/plugins/uptime/common/graphql/types'; +import { Ping, PingResults } from '../../../../../legacy/plugins/uptime/common/graphql/types'; import { GetFilterBarParams, GetLatestMonitorParams, @@ -26,6 +22,7 @@ import { MonitorDetails, MonitorLocations, Snapshot, + StatesIndexStatus, } from '../../../../../legacy/plugins/uptime/common/runtime_types'; import { GetMonitorStatesResult } from './get_monitor_states'; import { GetSnapshotCountParams } from './get_snapshot_counts'; diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 69981b7860d59..b0cc38ebfb4b6 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -6,7 +6,6 @@ import { createGetOverviewFilters } from './overview_filters'; import { createGetPingsRoute } from './pings'; -import { createGetIndexPatternRoute } from './index_pattern'; import { createLogMonitorPageRoute, createLogOverviewPageRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; import { UMRestApiRouteFactory } from './types'; @@ -18,6 +17,7 @@ import { } from './monitors'; import { createGetPingHistogramRoute } from './pings/get_ping_histogram'; import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; +import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -27,6 +27,7 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createGetOverviewFilters, createGetPingsRoute, createGetIndexPatternRoute, + createGetIndexStatusRoute, createGetMonitorRoute, createGetMonitorDetailsRoute, createGetMonitorLocationsRoute, diff --git a/x-pack/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts similarity index 100% rename from x-pack/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts rename to x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts new file mode 100644 index 0000000000000..44799aa19c140 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -0,0 +1,29 @@ +/* + * 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 { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; +import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; + +export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: REST_API_URLS.INDEX_STATUS, + validate: false, + options: { + tags: ['access:uptime'], + }, + handler: async ({ callES }, _context, _request, response): Promise => { + try { + return response.ok({ + body: { + ...(await libs.requests.getIndexStatus({ callES })), + }, + }); + } catch (e) { + return response.internalError({ body: { message: e.message } }); + } + }, +}); diff --git a/x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts b/x-pack/plugins/uptime/server/rest_api/index_state/index.ts similarity index 82% rename from x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts rename to x-pack/plugins/uptime/server/rest_api/index_state/index.ts index b35e2e7b65a29..ff44794bfe7d1 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/index.ts @@ -5,3 +5,4 @@ */ export { createGetIndexPatternRoute } from './get_index_pattern'; +export { createGetIndexStatusRoute } from './get_index_status'; diff --git a/x-pack/test/api_integration/apis/uptime/feature_controls.ts b/x-pack/test/api_integration/apis/uptime/feature_controls.ts index adbfacb014e9f..15666acab2335 100644 --- a/x-pack/test/api_integration/apis/uptime/feature_controls.ts +++ b/x-pack/test/api_integration/apis/uptime/feature_controls.ts @@ -5,9 +5,9 @@ */ import expect from '@kbn/expect'; -import { docCountQueryString } from '../../../../legacy/plugins/uptime/public/queries'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PINGS_DATE_RANGE_END, PINGS_DATE_RANGE_START } from './constants'; +import { REST_API_URLS } from '../../../../legacy/plugins/uptime/common/constants'; export default function featureControlsTests({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); @@ -26,22 +26,13 @@ export default function featureControlsTests({ getService }: FtrProviderContext) expect(result.response).to.have.property('statusCode', 200); }; - const executeGraphQLQuery = async (username: string, password: string, spaceId?: string) => { + const executeRESTAPIQuery = async (username: string, password: string, spaceId?: string) => { const basePath = spaceId ? `/s/${spaceId}` : ''; - const getDocCountQuery = { - operationName: null, - query: docCountQueryString, - variables: { - dateRangeStart: '2019-01-28T17:40:08.078Z', - dateRangeEnd: '2019-01-28T19:00:16.078Z', - }, - }; return await supertest - .post(`${basePath}/api/uptime/graphql`) + .get(basePath + REST_API_URLS.INDEX_STATUS) .auth(username, password) .set('kbn-xsrf', 'foo') - .send({ ...getDocCountQuery }) .then((response: any) => ({ error: undefined, response })) .catch((error: any) => ({ error, response: undefined })); }; @@ -82,7 +73,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -121,7 +112,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expectResponse(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -163,7 +154,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -232,7 +223,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }); it('user_1 can access APIs in space_1', async () => { - const graphQLResult = await executeGraphQLQuery(username, password, space1Id); + const graphQLResult = await executeRESTAPIQuery(username, password, space1Id); expectResponse(graphQLResult); const pingsResult = await executePingsRequest(username, password, space1Id); @@ -240,7 +231,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }); it(`user_1 can't access APIs in space_2`, async () => { - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); diff --git a/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js b/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js deleted file mode 100644 index 1aa69faaab736..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { docCountQueryString } from '../../../../../legacy/plugins/uptime/public/queries'; -import { expectFixtureEql } from './helpers/expect_fixture_eql'; - -export default function({ getService }) { - describe('docCount query', () => { - before('load heartbeat data', () => getService('esArchiver').load('uptime/full_heartbeat')); - after('unload heartbeat index', () => getService('esArchiver').unload('uptime/full_heartbeat')); - - const supertest = getService('supertest'); - - it(`will fetch the index's count`, async () => { - const getDocCountQuery = { - operationName: null, - query: docCountQueryString, - variables: { - dateRangeStart: '2019-01-28T17:40:08.078Z', - dateRangeEnd: '2025-01-28T19:00:16.078Z', - }, - }; - const { - body: { data }, - } = await supertest - .post('/api/uptime/graphql') - .set('kbn-xsrf', 'foo') - .send({ ...getDocCountQuery }); - - expectFixtureEql(data, 'doc_count'); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json deleted file mode 100644 index 4daf223a79a69..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "statesIndexStatus": { - "docCount": { - "count": 2000 - }, - "indexExists": true - } -} \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json index 59f5f95e7d840..05724f0716e8d 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0009-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json index 9a1363f00578a..6e62787069f40 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": null, - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0002-up", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -198,4 +194,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json index 59f5f95e7d840..05724f0716e8d 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0009-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json index 5c07b4daaf543..6cbe4ee3659a8 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0090-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": null, - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0090-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json index 71184093f4318..9a3f781735cb7 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0080-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0089-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0080-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json index 3b15ea3c24eeb..4f4af9c2c6012 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0010-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0019-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0010-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json index eb6512d2f75b3..fe48ad49d13ba 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0009-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json index aee4fa6946fc0..70ca665704a79 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0020-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0029-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0020-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json index 03164f794a4d5..3f09c951ec2fa 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0010-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0019-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0010-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json index 488fdab14f1e2..cdc0f32c9765e 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0030-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0039-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0030-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json index 79ce05d86f533..9f6d004380c16 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0020-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0029-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0020-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json index ef62cdd86c2a9..dedddb2a78ade 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0040-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0049-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0040-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json index 34e8269cb95d9..fabcf70404952 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0030-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0039-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0030-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json index d733467c3f9b6..943cc68249dc1 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0050-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0059-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0050-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json index 12e27106bd533..564f58f59f373 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0040-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0049-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0040-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json index d0f2b820f8327..cb94273e91fd8 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0060-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0069-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0060-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json index cf0c8641cc87b..7aac62bba84f7 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0050-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0059-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0050-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json index 2801e94e034c7..08cbd0d878b44 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0070-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0079-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0070-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json index 7fcbd4dd59659..8de639b705ee9 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0060-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0069-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0060-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json index 0adb7ad0b0dba..c38f5c801a267 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0080-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0089-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0080-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json index a796be38bd0d9..5c2ec8512e320 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0070-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0079-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0070-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/index.js b/x-pack/test/api_integration/apis/uptime/graphql/index.js index c2fdc57edede3..ee22974d47170 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/index.js +++ b/x-pack/test/api_integration/apis/uptime/graphql/index.js @@ -10,7 +10,6 @@ export default function({ loadTestFile }) { // the uptime app and runs it against the live HTTP server, // verifying the pre-loaded documents are returned in a way that // matches the snapshots contained in './fixtures' - loadTestFile(require.resolve('./doc_count')); loadTestFile(require.resolve('./monitor_states')); loadTestFile(require.resolve('./ping_list')); }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts new file mode 100644 index 0000000000000..1f5322f581b39 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { expectFixtureEql } from '../graphql/helpers/expect_fixture_eql'; +import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; + +export default function({ getService }: FtrProviderContext) { + describe('docCount query', () => { + const supertest = getService('supertest'); + + it(`will fetch the index's count`, async () => { + const apiResponse = await supertest.get(REST_API_URLS.INDEX_STATUS); + const data = apiResponse.body; + expectFixtureEql(data, 'doc_count'); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json new file mode 100644 index 0000000000000..41b9af392dded --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json @@ -0,0 +1,4 @@ +{ + "docCount": 2000, + "indexExists": true +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index 5e26cb9216f45..67b94f19c638f 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -21,6 +21,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./selected_monitor')); loadTestFile(require.resolve('./ping_histogram')); loadTestFile(require.resolve('./monitor_duration')); + loadTestFile(require.resolve('./doc_count')); }); }); } From a946adbf10b0148bf510b0588713dd7a2a04579f Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 16 Mar 2020 13:01:48 +0100 Subject: [PATCH 04/76] adds new test (#60064) --- .../cypress/integration/detections.spec.ts | 43 ++++++++++++++++++- .../siem/cypress/screens/detections.ts | 4 +- .../plugins/siem/cypress/tasks/detections.ts | 7 +++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts index 1624586d4ca14..c048510c50c36 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -5,12 +5,14 @@ */ import { NUMBER_OF_SIGNALS, + OPEN_CLOSE_SIGNALS_BTN, SELECTED_SIGNALS, SHOWING_SIGNALS, SIGNALS, } from '../screens/detections'; import { + closeFirstSignal, closeSignals, goToClosedSignals, goToOpenedSignals, @@ -26,7 +28,7 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Detections', () => { - before(() => { + beforeEach(() => { esArchiverLoad('signals'); loginAndWaitForPage(DETECTIONS); }); @@ -111,4 +113,43 @@ describe('Detections', () => { .should('eql', expectedNumberOfOpenedSignals.toString()); }); }); + + it('Closes one signal when more than one opened signals are selected', () => { + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + const numberOfSignalsToBeClosed = 1; + const numberOfSignalsToBeSelected = 3; + + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('have.attr', 'disabled'); + selectNumberOfSignals(numberOfSignalsToBeSelected); + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('not.have.attr', 'disabled'); + + closeFirstSignal(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + + const expectedNumberOfSignals = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignals.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignals.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signal`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8b5ba23578807..f388ac1215d01 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -12,7 +12,9 @@ export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; -export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; +export const OPEN_CLOSE_SIGNAL_BTN = '[data-test-subj="update-signal-status-button"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] button'; export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 21a0c136b90df..3416e3eb81de3 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -8,6 +8,7 @@ import { CLOSED_SIGNALS_BTN, LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNAL_BTN, OPEN_CLOSE_SIGNALS_BTN, OPENED_SIGNALS_BTN, SIGNALS, @@ -15,6 +16,12 @@ import { } from '../screens/detections'; import { REFRESH_BUTTON } from '../screens/siem_header'; +export const closeFirstSignal = () => { + cy.get(OPEN_CLOSE_SIGNAL_BTN) + .first() + .click({ force: true }); +}; + export const closeSignals = () => { cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); }; From 4be60e54261be05eda4c5b4530c93d9450398d8d Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 16 Mar 2020 15:05:41 +0300 Subject: [PATCH 05/76] [Step 1][App Arch] Saved object migrations from kibana plugin to new platform (#59044) * [App Arch] Saved object migrations from kibana plugin to new platform Part of #55946 * search type -> data plugin * remove migrations folder * visualization type -> visualizations plugin * src/legacy/core_plugins/data/mappings-> np * savedObjectsManagement -> management section * move code into save_objects folder Co-authored-by: Elastic Machine --- .../saved_objects/serialization/types.ts | 2 +- src/legacy/core_plugins/data/index.ts | 19 - src/legacy/core_plugins/kibana/index.js | 51 - src/legacy/core_plugins/kibana/mappings.json | 89 - .../kibana/migrations/migrations.js | 564 +----- .../kibana/migrations/migrations.test.js | 1574 ----------------- .../index_patterns/index_patterns_service.ts | 7 +- src/plugins/data/server/plugin.ts | 3 + .../data/server/query/index.ts} | 32 +- .../data/server/query/query_service.ts | 29 + .../data/server/saved_objects/index.ts | 22 + .../index_pattern_migrations.test.ts | 97 + .../saved_objects/index_pattern_migrations.ts | 60 + .../server/saved_objects/index_patterns.ts | 58 + .../data/server/saved_objects/query.ts | 52 + .../data/server/saved_objects/search.ts | 60 + .../saved_objects/search_migrations.test.ts | 299 ++++ .../server/saved_objects/search_migrations.ts | 92 + .../data/server/search/search_service.ts | 4 + src/plugins/data/server/server.api.md | 2 +- src/plugins/visualizations/kibana.json | 2 +- src/plugins/visualizations/server/index.ts | 30 + src/plugins/visualizations/server/plugin.ts | 54 + .../server/saved_objects/index.ts | 20 + .../server/saved_objects/visualization.ts | 56 + .../visualization_migrations.test.ts | 1356 ++++++++++++++ .../saved_objects/visualization_migrations.ts | 574 ++++++ src/plugins/visualizations/server/types.ts | 23 + 28 files changed, 2899 insertions(+), 2332 deletions(-) rename src/{legacy/core_plugins/data/mappings.ts => plugins/data/server/query/index.ts} (60%) create mode 100644 src/plugins/data/server/query/query_service.ts create mode 100644 src/plugins/data/server/saved_objects/index.ts create mode 100644 src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts create mode 100644 src/plugins/data/server/saved_objects/index_pattern_migrations.ts create mode 100644 src/plugins/data/server/saved_objects/index_patterns.ts create mode 100644 src/plugins/data/server/saved_objects/query.ts create mode 100644 src/plugins/data/server/saved_objects/search.ts create mode 100644 src/plugins/data/server/saved_objects/search_migrations.test.ts create mode 100644 src/plugins/data/server/saved_objects/search_migrations.ts create mode 100644 src/plugins/visualizations/server/index.ts create mode 100644 src/plugins/visualizations/server/plugin.ts create mode 100644 src/plugins/visualizations/server/saved_objects/index.ts create mode 100644 src/plugins/visualizations/server/saved_objects/visualization.ts create mode 100644 src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts create mode 100644 src/plugins/visualizations/server/saved_objects/visualization_migrations.ts create mode 100644 src/plugins/visualizations/server/types.ts diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts index 524c2c8ffae7a..dfaec127ba159 100644 --- a/src/core/server/saved_objects/serialization/types.ts +++ b/src/core/server/saved_objects/serialization/types.ts @@ -50,7 +50,7 @@ export interface SavedObjectsRawDocSource { * scenario out of the box. */ interface SavedObjectDoc { - attributes: unknown; + attributes: any; id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional type: string; namespace?: string; diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 813eab00f7258..10c8cf464b82d 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -19,8 +19,6 @@ import { resolve } from 'path'; import { Legacy } from '../../../../kibana'; -import { mappings } from './mappings'; -import { SavedQuery } from '../../../plugins/data/public'; // eslint-disable-next-line import/no-default-export export default function DataPlugin(kibana: any) { @@ -36,23 +34,6 @@ export default function DataPlugin(kibana: any) { init: (server: Legacy.Server) => ({}), uiExports: { injectDefaultVars: () => ({}), - mappings, - savedObjectsManagement: { - query: { - icon: 'search', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj: SavedQuery) { - return obj.attributes.title; - }, - getInAppUrl(obj: SavedQuery) { - return { - path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, - }, }, }; diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 092eed924f330..1d772536fa1ea 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -126,57 +126,6 @@ export default function(kibana) { ], savedObjectsManagement: { - 'index-pattern': { - icon: 'indexPatternApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'management.kibana.index_patterns', - }; - }, - }, - visualization: { - icon: 'visualizeApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/visualize/edit/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'visualize.show', - }; - }, - }, - search: { - icon: 'discoverApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/discover/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, dashboard: { icon: 'dashboardApp', defaultSearchField: 'title', diff --git a/src/legacy/core_plugins/kibana/mappings.json b/src/legacy/core_plugins/kibana/mappings.json index 4cf9ea1d301c0..af3f79588552b 100644 --- a/src/legacy/core_plugins/kibana/mappings.json +++ b/src/legacy/core_plugins/kibana/mappings.json @@ -1,93 +1,4 @@ { - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "dashboard": { "properties": { "description": { diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.js b/src/legacy/core_plugins/kibana/migrations/migrations.js index 29b6e632d19fd..d37887c640b90 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.js @@ -17,7 +17,7 @@ * under the License. */ -import { cloneDeep, get, omit, has, flow } from 'lodash'; +import { get } from 'lodash'; import { migrations730 as dashboardMigrations730 } from '../public/dashboard/migrations'; function migrateIndexPattern(doc) { @@ -58,559 +58,7 @@ function migrateIndexPattern(doc) { doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); } -// [TSVB] Migrate percentile-rank aggregation (value -> values) -const migratePercentileRankAggregation = doc => { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState && visState.type === 'metrics') { - const series = get(visState, 'params.series') || []; - - series.forEach(part => { - (part.metrics || []).forEach(metric => { - if (metric.type === 'percentile_rank' && has(metric, 'value')) { - metric.values = [metric.value]; - - delete metric.value; - } - }); - }); - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } - return doc; -}; - -// Migrate date histogram aggregation (remove customInterval) -const migrateDateHistogramAggregation = doc => { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type === 'date_histogram' && agg.params) { - if (agg.params.interval === 'custom') { - agg.params.interval = agg.params.customInterval; - } - delete agg.params.customInterval; - } - - if ( - get(agg, 'params.customBucket.type', null) === 'date_histogram' && - agg.params.customBucket.params - ) { - if (agg.params.customBucket.params.interval === 'custom') { - agg.params.customBucket.params.interval = agg.params.customBucket.params.customInterval; - } - delete agg.params.customBucket.params.customInterval; - } - }); - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } - return doc; -}; - -function removeDateHistogramTimeZones(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - // We're checking always for the existance of agg.params here. This should always exist, but better - // be safe then sorry during migrations. - if (agg.type === 'date_histogram' && agg.params) { - delete agg.params.time_zone; - } - - if ( - get(agg, 'params.customBucket.type', null) === 'date_histogram' && - agg.params.customBucket.params - ) { - delete agg.params.customBucket.params.time_zone; - } - }); - doc.attributes.visState = JSON.stringify(visState); - } - } - return doc; -} - -// migrate gauge verticalSplit to alignment -// https://github.com/elastic/kibana/issues/34636 -function migrateGaugeVerticalSplitToAlignment(doc, logger) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.type === 'gauge' && !visState.params.gauge.alignment) { - visState.params.gauge.alignment = visState.params.gauge.verticalSplit - ? 'vertical' - : 'horizontal'; - delete visState.params.gauge.verticalSplit; - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - logger.warning(`Exception @ migrateGaugeVerticalSplitToAlignment! ${e}`); - logger.warning(`Exception @ migrateGaugeVerticalSplitToAlignment! Payload: ${visStateJSON}`); - } - } - return doc; -} -// Migrate filters (string -> { query: string, language: lucene }) -/* - Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters. - In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself. - We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene. - For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object. - Path to the series array is thus: - attributes.visState. -*/ -function transformFilterStringToQueryObject(doc) { - // Migrate filters - // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly - const newDoc = cloneDeep(doc); - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const visType = get(visState, 'params.type'); - const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; - if (tsvbTypes.indexOf(visType) === -1) { - // skip - return doc; - } - // migrate the params fitler - const params = get(visState, 'params'); - if (params.filter && typeof params.filter === 'string') { - const paramsFilterObject = { - query: params.filter, - language: 'lucene', - }; - params.filter = paramsFilterObject; - } - - // migrate the annotations query string: - const annotations = get(visState, 'params.annotations') || []; - annotations.forEach(item => { - if (!item.query_string) { - // we don't need to transform anything if there isn't a filter at all - return; - } - if (typeof item.query_string === 'string') { - const itemQueryStringObject = { - query: item.query_string, - language: 'lucene', - }; - item.query_string = itemQueryStringObject; - } - }); - // migrate the series filters - const series = get(visState, 'params.series') || []; - series.forEach(item => { - if (!item.filter) { - // we don't need to transform anything if there isn't a filter at all - return; - } - // series item filter - if (typeof item.filter === 'string') { - const itemfilterObject = { - query: item.filter, - language: 'lucene', - }; - item.filter = itemfilterObject; - } - // series item split filters filter - if (item.split_filters) { - const splitFilters = get(item, 'split_filters') || []; - splitFilters.forEach(filter => { - if (!filter.filter) { - // we don't need to transform anything if there isn't a filter at all - return; - } - if (typeof filter.filter === 'string') { - const filterfilterObject = { - query: filter.filter, - language: 'lucene', - }; - filter.filter = filterfilterObject; - } - }); - } - }); - newDoc.attributes.visState = JSON.stringify(visState); - } - } - return newDoc; -} -function transformSplitFiltersStringToQueryObject(doc) { - // Migrate split_filters in TSVB objects that weren't migrated in 7.3 - // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly - const newDoc = cloneDeep(doc); - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const visType = get(visState, 'params.type'); - const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; - if (tsvbTypes.indexOf(visType) === -1) { - // skip - return doc; - } - // migrate the series split_filter filters - const series = get(visState, 'params.series') || []; - series.forEach(item => { - // series item split filters filter - if (item.split_filters) { - const splitFilters = get(item, 'split_filters') || []; - if (splitFilters.length > 0) { - // only transform split_filter filters if we have filters - splitFilters.forEach(filter => { - if (typeof filter.filter === 'string') { - const filterfilterObject = { - query: filter.filter, - language: 'lucene', - }; - filter.filter = filterfilterObject; - } - }); - } - } - }); - newDoc.attributes.visState = JSON.stringify(visState); - } - } - return newDoc; -} - -function migrateFiltersAggQuery(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type !== 'filters') return; - - agg.params.filters.forEach(filter => { - if (filter.input.language) return filter; - filter.input.language = 'lucene'; - }); - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - } - return doc; -} - -function replaceMovAvgToMovFn(doc, logger) { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - - if (visState && visState.type === 'metrics') { - const series = get(visState, 'params.series', []); - - series.forEach(part => { - if (part.metrics && Array.isArray(part.metrics)) { - part.metrics.forEach(metric => { - if (metric.type === 'moving_average') { - metric.model_type = metric.model; - metric.alpha = get(metric, 'settings.alpha', 0.3); - metric.beta = get(metric, 'settings.beta', 0.1); - metric.gamma = get(metric, 'settings.gamma', 0.3); - metric.period = get(metric, 'settings.period', 1); - metric.multiplicative = get(metric, 'settings.type') === 'mult'; - - delete metric.minimize; - delete metric.model; - delete metric.settings; - delete metric.predict; - } - }); - } - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - logger.warning(`Exception @ replaceMovAvgToMovFn! ${e}`); - logger.warning(`Exception @ replaceMovAvgToMovFn! Payload: ${visStateJSON}`); - } - } - - return doc; -} - -function migrateSearchSortToNestedArray(doc) { - const sort = get(doc, 'attributes.sort'); - if (!sort) return doc; - - // Don't do anything if we already have a two dimensional array - if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) { - return doc; - } - - return { - ...doc, - attributes: { - ...doc.attributes, - sort: [doc.attributes.sort], - }, - }; -} - -function migrateFiltersAggQueryStringQueries(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type !== 'filters') return doc; - - agg.params.filters.forEach(filter => { - if (filter.input.query.query_string) { - filter.input.query = filter.input.query.query_string.query; - } - }); - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - } - return doc; -} - -function migrateSubTypeAndParentFieldProperties(doc) { - if (!doc.attributes.fields) return doc; - - const fieldsString = doc.attributes.fields; - const fields = JSON.parse(fieldsString); - const migratedFields = fields.map(field => { - if (field.subType === 'multi') { - return { - ...omit(field, 'parent'), - subType: { multi: { parent: field.parent } }, - }; - } - - return field; - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - fields: JSON.stringify(migratedFields), - }, - }; -} - -const executeMigrations720 = flow( - migratePercentileRankAggregation, - migrateDateHistogramAggregation -); -const executeMigrations730 = flow( - migrateGaugeVerticalSplitToAlignment, - transformFilterStringToQueryObject, - migrateFiltersAggQuery, - replaceMovAvgToMovFn -); - -const executeVisualizationMigrations731 = flow(migrateFiltersAggQueryStringQueries); - -const executeSearchMigrations740 = flow(migrateSearchSortToNestedArray); - -const executeMigrations742 = flow(transformSplitFiltersStringToQueryObject); - export const migrations = { - 'index-pattern': { - '6.5.0': doc => { - doc.attributes.type = doc.attributes.type || undefined; - doc.attributes.typeMeta = doc.attributes.typeMeta || undefined; - return doc; - }, - '7.6.0': flow(migrateSubTypeAndParentFieldProperties), - }, - visualization: { - /** - * We need to have this migration twice, once with a version prior to 7.0.0 once with a version - * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already - * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, - * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we - * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects - * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced - * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 - * only contained the 6.7.2 migration and not the 7.0.1 migration. - */ - '6.7.2': removeDateHistogramTimeZones, - '7.0.0': doc => { - // Set new "references" attribute - doc.references = doc.references || []; - - // Migrate index pattern - migrateIndexPattern(doc); - - // Migrate saved search - const savedSearchId = get(doc, 'attributes.savedSearchId'); - if (savedSearchId) { - doc.references.push({ - type: 'search', - name: 'search_0', - id: savedSearchId, - }); - doc.attributes.savedSearchRefName = 'search_0'; - } - delete doc.attributes.savedSearchId; - - // Migrate controls - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const controls = get(visState, 'params.controls') || []; - controls.forEach((control, i) => { - if (!control.indexPattern) { - return; - } - control.indexPatternRefName = `control_${i}_index_pattern`; - doc.references.push({ - name: control.indexPatternRefName, - type: 'index-pattern', - id: control.indexPattern, - }); - delete control.indexPattern; - }); - doc.attributes.visState = JSON.stringify(visState); - } - } - - // Migrate table splits - try { - const visState = JSON.parse(doc.attributes.visState); - if (get(visState, 'type') !== 'table') { - return doc; // do nothing; we only want to touch tables - } - - let splitCount = 0; - visState.aggs = visState.aggs.map(agg => { - if (agg.schema !== 'split') { - return agg; - } - - splitCount++; - if (splitCount === 1) { - return agg; // leave the first split agg unchanged - } - agg.schema = 'bucket'; - // the `row` param is exclusively used by split aggs, so we remove it - agg.params = omit(agg.params, ['row']); - return agg; - }); - - if (splitCount <= 1) { - return doc; // do nothing; we only want to touch tables with multiple split aggs - } - - const newDoc = cloneDeep(doc); - newDoc.attributes.visState = JSON.stringify(visState); - return newDoc; - } catch (e) { - throw new Error( - `Failure attempting to migrate saved object '${doc.attributes.title}' - ${e}` - ); - } - }, - '7.0.1': removeDateHistogramTimeZones, - '7.2.0': doc => executeMigrations720(doc), - '7.3.0': executeMigrations730, - '7.3.1': executeVisualizationMigrations731, - // migrate split_filters that were not migrated in 7.3.0 (transformFilterStringToQueryObject). - '7.4.2': executeMigrations742, - }, dashboard: { '7.0.0': doc => { // Set new "references" attribute @@ -651,14 +99,4 @@ export const migrations = { }, '7.3.0': dashboardMigrations730, }, - search: { - '7.0.0': doc => { - // Set new "references" attribute - doc.references = doc.references || []; - // Migrate index pattern - migrateIndexPattern(doc); - return doc; - }, - '7.4.0': executeSearchMigrations740, - }, }; diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.test.js b/src/legacy/core_plugins/kibana/migrations/migrations.test.js index e39bc59201e7f..b02081128c858 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.test.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.test.js @@ -19,1312 +19,6 @@ import { migrations } from './migrations'; -describe('index-pattern', () => { - describe('6.5.0', () => { - const migrate = doc => migrations['index-pattern']['6.5.0'](doc); - - it('adds "type" and "typeMeta" properties to object when not declared', () => { - expect( - migrate({ - attributes: {}, - }) - ).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "type": undefined, - "typeMeta": undefined, - }, -} -`); - }); - - it('keeps "type" and "typeMeta" properties as is when declared', () => { - expect( - migrate({ - attributes: { - type: '123', - typeMeta: '123', - }, - }) - ).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "type": "123", - "typeMeta": "123", - }, -} -`); - }); - }); - - describe('7.6.0', function() { - const migrate = doc => migrations['index-pattern']['7.6.0'](doc); - - it('should remove the parent property and update the subType prop on every field that has them', () => { - const input = { - attributes: { - title: 'test', - fields: - '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":"multi","parent":"customer_name"}]', - }, - }; - const expected = { - attributes: { - title: 'test', - fields: - '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"customer_name"}}}]', - }, - }; - - expect(migrate(input)).toEqual(expected); - }); - }); -}); - -describe('visualization', () => { - describe('date histogram time zone removal', () => { - const migrate = doc => migrations.visualization['6.7.2'](doc); - let doc; - beforeEach(() => { - doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - enabled: true, - id: '1', - params: { - // Doesn't make much sense but we want to test it's not removing it from anything else - time_zone: 'Europe/Berlin', - }, - schema: 'metric', - type: 'count', - }, - { - enabled: true, - id: '2', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - time_zone: 'Europe/Berlin', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '4', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '3', - params: { - customBucket: { - enabled: true, - id: '1-bucket', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - time_zone: 'Europe/Berlin', - useNormalizedEsInterval: true, - }, - type: 'date_histogram', - }, - customMetric: { - enabled: true, - id: '1-metric', - params: {}, - type: 'count', - }, - }, - schema: 'metric', - type: 'max_bucket', - }, - ], - }), - }, - }; - }); - - it('should remove time_zone from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.time_zone'); - }); - - it('should not remove time_zone from non date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[0]).toHaveProperty('params.time_zone'); - }); - - it('should remove time_zone from nested aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); - }); - - it('should not fail on date histograms without a time_zone', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[2]).not.toHaveProperty('params.time_zone'); - }); - - it('should be able to apply the migration twice, since we need it for 6.7.2 and 7.0.1', () => { - const migratedDoc = migrate(migrate(doc)); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.time_zone'); - expect(aggs[0]).toHaveProperty('params.time_zone'); - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); - expect(aggs[2]).not.toHaveProperty('params.time_zone'); - }); - }); - - describe('7.0.0', () => { - const migrate = doc => migrations.visualization['7.0.0'](doc); - const generateDoc = ({ type, aggs }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ type, aggs }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - - it('does not throw error on empty object', () => { - const migratedDoc = migrate({ - attributes: { - visState: '{}', - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{}", - }, - "references": Array [], -} -`); - }); - - it('skips errors when searchSourceJSON is null', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: null, - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips errors when searchSourceJSON is undefined', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: undefined, - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when searchSourceJSON is not a string', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: 123, - }, - savedSearchId: '123', - }, - }; - expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when searchSourceJSON is invalid json', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{abc123}', - }, - savedSearchId: '123', - }, - }; - expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts "index" attribute from doc', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts index patterns from the filter', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - bar: true, - filter: [ - { - meta: { index: 'my-index', foo: true }, - }, - ], - }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts index patterns from controls', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - foo: true, - visState: JSON.stringify({ - bar: false, - params: { - controls: [ - { - bar: true, - indexPattern: 'pattern*', - }, - { - foo: true, - }, - ], - }, - }), - }, - }; - const migratedDoc = migrate(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "control_0_index_pattern", - "type": "index-pattern", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips extracting savedSearchId when missing', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); - }); - - it('extract savedSearchId from doc', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], -} -`); - }); - - it('delete savedSearchId when empty string in doc', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - savedSearchId: '', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); - }); - - it('should return a new object if vis is table and has multiple split aggs', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - ]; - const tableDoc = generateDoc({ type: 'table', aggs }); - const expected = tableDoc; - const actual = migrate(tableDoc); - expect(actual).not.toBe(expected); - }); - - it('should not touch any vis that is not table', () => { - const aggs = []; - const pieDoc = generateDoc({ type: 'pie', aggs }); - const expected = pieDoc; - const actual = migrate(pieDoc); - expect(actual).toBe(expected); - }); - - it('should not change values in any vis that is not table', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'segment', - params: { hey: 'ya' }, - }, - ]; - const pieDoc = generateDoc({ type: 'pie', aggs }); - const expected = pieDoc; - const actual = migrate(pieDoc); - expect(actual).toEqual(expected); - }); - - it('should not touch table vis if there are not multiple split aggs', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - ]; - const tableDoc = generateDoc({ type: 'table', aggs }); - const expected = tableDoc; - const actual = migrate(tableDoc); - expect(actual).toBe(expected); - }); - - it('should change all split aggs to `bucket` except the first', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - { - id: '4', - schema: 'bucket', - params: { heyyy: 'yaaa' }, - }, - ]; - const expected = ['metric', 'split', 'bucket', 'bucket']; - const migrated = migrate(generateDoc({ type: 'table', aggs })); - const actual = JSON.parse(migrated.attributes.visState); - expect(actual.aggs.map(agg => agg.schema)).toEqual(expected); - }); - - it('should remove `rows` param from any aggs that are not `split`', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - ]; - const expected = [{}, { foo: 'bar', row: true }, { hey: 'ya' }]; - const migrated = migrate(generateDoc({ type: 'table', aggs })); - const actual = JSON.parse(migrated.attributes.visState); - expect(actual.aggs.map(agg => agg.params)).toEqual(expected); - }); - - it('should throw with a reference to the doc name if something goes wrong', () => { - const doc = { - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: '!/// Intentionally malformed JSON ///!', - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }; - expect(() => migrate(doc)).toThrowError(/My Vis/); - }); - }); - - describe('date histogram custom interval removal', () => { - const migrate = doc => migrations.visualization['7.2.0'](doc); - let doc; - beforeEach(() => { - doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - enabled: true, - id: '1', - params: { - customInterval: '1h', - }, - schema: 'metric', - type: 'count', - }, - { - enabled: true, - id: '2', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '4', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'custom', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '3', - params: { - customBucket: { - enabled: true, - id: '1-bucket', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'custom', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - type: 'date_histogram', - }, - customMetric: { - enabled: true, - id: '1-metric', - params: {}, - type: 'count', - }, - }, - schema: 'metric', - type: 'max_bucket', - }, - ], - }), - }, - }; - }); - - it('should remove customInterval from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.customInterval'); - }); - - it('should not change interval from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1].params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[1].params.interval - ); - }); - - it('should not remove customInterval from non date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[0]).toHaveProperty('params.customInterval'); - }); - - it('should set interval with customInterval value and remove customInterval when interval equals "custom"', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[2].params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[2].params.customInterval - ); - expect(aggs[2]).not.toHaveProperty('params.customInterval'); - }); - - it('should remove customInterval from nested aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); - }); - - it('should remove customInterval from nested aggregations and set interval with customInterval value', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3].params.customBucket.params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[3].params.customBucket.params.customInterval - ); - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); - }); - - it('should not fail on date histograms without a customInterval', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customInterval'); - }); - }); - describe('7.3.0', () => { - const logMsgArr = []; - const logger = { - warning: msg => logMsgArr.push(msg), - }; - const migrate = doc => migrations.visualization['7.3.0'](doc, logger); - - it('migrates type = gauge verticalSplit: false to alignment: vertical', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: false } } }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", - }, -} -`); - }); - - it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: true } } }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", - }, -} -`); - }); - - it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge' }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\"}", - }, -} -`); - expect(logMsgArr).toMatchInlineSnapshot(` -Array [ - "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", - "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", -] -`); - }); - - describe('filters agg query migration', () => { - const doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - type: 'filters', - params: { - filters: [ - { - input: { - query: 'response:200', - }, - label: '', - }, - { - input: { - query: 'response:404', - }, - label: 'bad response', - }, - { - input: { - query: { - exists: { - field: 'phpmemory', - }, - }, - }, - label: '', - }, - ], - }, - }, - ], - }), - }, - }; - - it('should add language property to filters without one, assuming lucene', () => { - const migrationResult = migrate(doc); - expect(migrationResult).toEqual({ - attributes: { - visState: JSON.stringify({ - aggs: [ - { - type: 'filters', - params: { - filters: [ - { - input: { - query: 'response:200', - language: 'lucene', - }, - label: '', - }, - { - input: { - query: 'response:404', - language: 'lucene', - }, - label: 'bad response', - }, - { - input: { - query: { - exists: { - field: 'phpmemory', - }, - }, - language: 'lucene', - }, - label: '', - }, - ], - }, - }, - ], - }), - }, - }); - }); - }); - - describe('replaceMovAvgToMovFn()', () => { - let doc; - - beforeEach(() => { - doc = { - attributes: { - title: 'VIS', - visState: `{"title":"VIS","type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417", - "type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(0,156,224,1)", - "split_mode":"terms","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count", - "numerator":"FlightDelay:true"},{"settings":"","minimize":0,"window":5,"model": - "holt_winters","id":"23054fe0-8915-11e9-9b86-d3f94982620f","type":"moving_average","field": - "61ca57f2-469d-11e7-af02-69e470af7417","predict":1}],"separate_axis":0,"axis_position":"right", - "formatter":"number","chart_type":"line","line_width":"2","point_size":"0","fill":0.5,"stacked":"none", - "label":"Percent Delays","terms_size":"2","terms_field":"OriginCityName"}],"time_field":"timestamp", - "index_pattern":"kibana_sample_data_flights","interval":">=12h","axis_position":"left","axis_formatter": - "number","show_legend":1,"show_grid":1,"annotations":[{"fields":"FlightDelay,Cancelled,Carrier", - "template":"{{Carrier}}: Flight Delayed and Cancelled!","index_pattern":"kibana_sample_data_flights", - "query_string":"FlightDelay:true AND Cancelled:true","id":"53b7dff0-4c89-11e8-a66a-6989ad5a0a39", - "color":"rgba(0,98,177,1)","time_field":"timestamp","icon":"fa-exclamation-triangle", - "ignore_global_filters":1,"ignore_panel_filters":1,"hidden":true}],"legend_position":"bottom", - "axis_scale":"normal","default_index_pattern":"kibana_sample_data_flights","default_timefield":"timestamp"}, - "aggs":[]}`, - }, - migrationVersion: { - visualization: '7.2.0', - }, - type: 'visualization', - }; - }); - - test('should add some necessary moving_fn fields', () => { - const migratedDoc = migrate(doc); - const visState = JSON.parse(migratedDoc.attributes.visState); - const metric = visState.params.series[0].metrics[1]; - - expect(metric).toHaveProperty('model_type'); - expect(metric).toHaveProperty('alpha'); - expect(metric).toHaveProperty('beta'); - expect(metric).toHaveProperty('gamma'); - expect(metric).toHaveProperty('period'); - expect(metric).toHaveProperty('multiplicative'); - }); - }); - }); - describe('7.3.0 tsvb', () => { - const migrate = doc => migrations.visualization['7.3.0'](doc); - const generateDoc = ({ params }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ params }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - it('should change series item filters from a string into an object', () => { - const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] }; - const testDoc1 = generateDoc({ params }); - const migratedTestDoc1 = migrate(testDoc1); - const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; - expect(series[0].filter).toHaveProperty('query'); - expect(series[0].filter).toHaveProperty('language'); - }); - it('should not change a series item filter string in the object after migration', () => { - const markdownParams = { - type: 'markdown', - series: [ - { - filter: 'Filter Bytes Test:>1000', - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - }; - const markdownDoc = generateDoc({ params: markdownParams }); - const migratedMarkdownDoc = migrate(markdownDoc); - const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series; - expect(markdownSeries[0].filter.query).toBe( - JSON.parse(markdownDoc.attributes.visState).params.series[0].filter - ); - expect(markdownSeries[0].split_filters[0].filter.query).toBe( - JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter - ); - }); - it('should change series item filters from a string into an object for all filters', () => { - const params = { - type: 'timeseries', - filter: 'bytes:>1000', - series: [ - { - filter: 'Filter Bytes Test:>1000', - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - annotations: [{ query_string: 'bytes:>1000' }], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - }); - it('should not fail on a metric visualization without a filter in a series item', () => { - const params = { type: 'metric', series: [{}, {}, {}] }; - const testDoc1 = generateDoc({ params }); - const migratedTestDoc1 = migrate(testDoc1); - const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; - expect(series[2]).not.toHaveProperty('filter.query'); - }); - it('should not migrate a visualization of unknown type', () => { - const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] }; - const doc = generateDoc({ params }); - const migratedDoc = migrate(doc); - const series = JSON.parse(migratedDoc.attributes.visState).params.series; - expect(series[0].filter).toEqual(params.series[0].filter); - }); - }); - - describe('7.3.1', () => { - const migrate = migrations.visualization['7.3.1']; - - it('should migrate filters agg query string queries', () => { - const state = { - aggs: [ - { type: 'count', params: {} }, - { - type: 'filters', - params: { - filters: [ - { - input: { - query: { - query_string: { query: 'machine.os.keyword:"win 8"' }, - }, - }, - }, - ], - }, - }, - ], - }; - const expected = { - aggs: [ - { type: 'count', params: {} }, - { - type: 'filters', - params: { - filters: [{ input: { query: 'machine.os.keyword:"win 8"' } }], - }, - }, - ], - }; - const migratedDoc = migrate({ attributes: { visState: JSON.stringify(state) } }); - expect(migratedDoc).toEqual({ attributes: { visState: JSON.stringify(expected) } }); - }); - }); - describe('7.4.2 tsvb split_filters migration', () => { - const migrate = doc => migrations.visualization['7.4.2'](doc); - const generateDoc = ({ params }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ params }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - it('should change series item filters from a string into an object for all filters', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(Object.keys(timeSeriesParams.filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ - query: 'bytes:>1000', - language: 'lucene', - }); - }); - it('should change series item split filters when there is no filter item', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - annotations: [ - { - query_string: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ - query: 'bytes:>1000', - language: 'lucene', - }); - }); - it('should not convert split_filters to objects if there are no split filter filters', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [], - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters).not.toHaveProperty('query'); - }); - it('should do nothing if a split_filter is already a query:language object', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [ - { - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }, - ], - annotations: [ - { - query_string: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters[0].filter.query).toEqual('bytes:>1000'); - expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); - }); - }); -}); - describe('dashboard', () => { describe('7.0.0', () => { const migration = migrations.dashboard['7.0.0']; @@ -1751,271 +445,3 @@ Object { }); }); }); - -describe('search', () => { - describe('7.0.0', () => { - const migration = migrations.search['7.0.0']; - - test('skips errors when searchSourceJSON is null', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: null, - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips errors when searchSourceJSON is undefined', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: undefined, - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when searchSourceJSON is not a string', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: 123, - }, - }, - }; - expect(migration(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when searchSourceJSON is invalid json', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: '{abc123}', - }, - }, - }; - expect(migration(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when "index" and "filter" is missing from searchSourceJSON', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true }), - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('extracts "index" attribute from doc', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - }, - "id": "123", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - ], - "type": "search", -} -`); - }); - - test('extracts index patterns from filter', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - bar: true, - filter: [ - { - meta: { - foo: true, - index: 'my-index', - }, - }, - ], - }), - }, - }, - }; - const migratedDoc = migration(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - }, - "id": "123", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - ], - "type": "search", -} -`); - }); - }); - - describe('7.4.0', function() { - const migration = migrations.search['7.4.0']; - - test('transforms one dimensional sort arrays into two dimensional arrays', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - sort: ['bytes', 'desc'], - }, - }; - - const expected = { - id: '123', - type: 'search', - attributes: { - sort: [['bytes', 'desc']], - }, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(expected); - }); - - test("doesn't modify search docs that already have two dimensional sort arrays", () => { - const doc = { - id: '123', - type: 'search', - attributes: { - sort: [['bytes', 'desc']], - }, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(doc); - }); - - test("doesn't modify search docs that have no sort array", () => { - const doc = { - id: '123', - type: 'search', - attributes: {}, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(doc); - }); - }); -}); diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index 78f34e21b9e41..58e8fbae9f9e2 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -19,10 +19,13 @@ import { CoreSetup, Plugin } from 'kibana/server'; import { registerRoutes } from './routes'; +import { indexPatternSavedObjectType } from '../saved_objects'; export class IndexPatternsService implements Plugin { - public setup({ http }: CoreSetup) { - registerRoutes(http); + public setup(core: CoreSetup) { + core.savedObjects.registerType(indexPatternSavedObjectType); + + registerRoutes(core.http); } public start() {} diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 616e65ad872ab..efb8759e7bead 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -21,6 +21,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../.. import { IndexPatternsService } from './index_patterns'; import { ISearchSetup } from './search'; import { SearchService } from './search/search_service'; +import { QueryService } from './query/query_service'; import { ScriptsService } from './scripts'; import { KqlTelemetryService } from './kql_telemetry'; import { UsageCollectionSetup } from '../../usage_collection/server'; @@ -47,6 +48,7 @@ export class DataServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.savedObjects.registerType(querySavedObjectType); + } + + public start() {} +} diff --git a/src/plugins/data/server/saved_objects/index.ts b/src/plugins/data/server/saved_objects/index.ts new file mode 100644 index 0000000000000..5d980974474de --- /dev/null +++ b/src/plugins/data/server/saved_objects/index.ts @@ -0,0 +1,22 @@ +/* + * 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. + */ + +export { searchSavedObjectType } from './search'; +export { querySavedObjectType } from './query'; +export { indexPatternSavedObjectType } from './index_patterns'; diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts new file mode 100644 index 0000000000000..b1410e2498667 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts @@ -0,0 +1,97 @@ +/* + * 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 { SavedObjectMigrationContext } from 'kibana/server'; +import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration index-pattern', () => { + describe('6.5.0', () => { + const migrationFn = indexPatternSavedObjectTypeMigrations['6.5.0']; + + test('adds "type" and "typeMeta" properties to object when not declared', () => { + expect( + migrationFn( + { + type: 'index-pattern', + attributes: {}, + }, + savedObjectMigrationContext + ) + ).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "type": undefined, + "typeMeta": undefined, + }, + "type": "index-pattern", +} +`); + }); + + test('keeps "type" and "typeMeta" properties as is when declared', () => { + expect( + migrationFn( + { + type: 'index-pattern', + attributes: { + type: '123', + typeMeta: '123', + }, + }, + savedObjectMigrationContext + ) + ).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "type": "123", + "typeMeta": "123", + }, + "type": "index-pattern", +} +`); + }); + }); + + describe('7.6.0', () => { + const migrationFn = indexPatternSavedObjectTypeMigrations['7.6.0']; + + test('should remove the parent property and update the subType prop on every field that has them', () => { + const input = { + type: 'index-pattern', + attributes: { + title: 'test', + fields: + '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":"multi","parent":"customer_name"}]', + }, + }; + const expected = { + type: 'index-pattern', + attributes: { + title: 'test', + fields: + '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"customer_name"}}}]', + }, + }; + + expect(migrationFn(input, savedObjectMigrationContext)).toEqual(expected); + }); + }); +}); diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts new file mode 100644 index 0000000000000..7a16386ea484c --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts @@ -0,0 +1,60 @@ +/* + * 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 { flow, omit } from 'lodash'; +import { SavedObjectMigrationFn } from 'kibana/server'; + +const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ + ...doc, + attributes: { + ...doc.attributes, + type: doc.attributes.type || undefined, + typeMeta: doc.attributes.typeMeta || undefined, + }, +}); + +const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { + if (!doc.attributes.fields) return doc; + + const fieldsString = doc.attributes.fields; + const fields = JSON.parse(fieldsString) as any[]; + const migratedFields = fields.map(field => { + if (field.subType === 'multi') { + return { + ...omit(field, 'parent'), + subType: { multi: { parent: field.parent } }, + }; + } + + return field; + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + fields: JSON.stringify(migratedFields), + }, + }; +}; + +export const indexPatternSavedObjectTypeMigrations = { + '6.5.0': flow(migrateAttributeTypeAndAttributeTypeMeta), + '7.6.0': flow(migrateSubTypeAndParentFieldProperties), +}; diff --git a/src/plugins/data/server/saved_objects/index_patterns.ts b/src/plugins/data/server/saved_objects/index_patterns.ts new file mode 100644 index 0000000000000..9838071eee5a4 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_patterns.ts @@ -0,0 +1,58 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; + +export const indexPatternSavedObjectType: SavedObjectsType = { + name: 'index-pattern', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'indexPatternApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'management.kibana.index_patterns', + }; + }, + }, + mappings: { + properties: { + fieldFormatMap: { type: 'text' }, + fields: { type: 'text' }, + intervalName: { type: 'keyword' }, + notExpandable: { type: 'boolean' }, + sourceFilters: { type: 'text' }, + timeFieldName: { type: 'keyword' }, + title: { type: 'text' }, + type: { type: 'keyword' }, + typeMeta: { type: 'keyword' }, + }, + }, + migrations: indexPatternSavedObjectTypeMigrations, +}; diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts new file mode 100644 index 0000000000000..ff0a6cfde8113 --- /dev/null +++ b/src/plugins/data/server/saved_objects/query.ts @@ -0,0 +1,52 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; + +export const querySavedObjectType: SavedObjectsType = { + name: 'query', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'search', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + mappings: { + properties: { + title: { type: 'text' }, + description: { type: 'text' }, + query: { + properties: { language: { type: 'keyword' }, query: { type: 'keyword', index: false } }, + }, + filters: { type: 'object', enabled: false }, + timefilter: { type: 'object', enabled: false }, + }, + }, + migrations: {}, +}; diff --git a/src/plugins/data/server/saved_objects/search.ts b/src/plugins/data/server/saved_objects/search.ts new file mode 100644 index 0000000000000..8b30ff7d08201 --- /dev/null +++ b/src/plugins/data/server/saved_objects/search.ts @@ -0,0 +1,60 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +import { searchSavedObjectTypeMigrations } from './search_migrations'; + +export const searchSavedObjectType: SavedObjectsType = { + name: 'search', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'discoverApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/discover/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + mappings: { + properties: { + columns: { type: 'keyword' }, + description: { type: 'text' }, + hits: { type: 'integer' }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { type: 'text' }, + }, + }, + sort: { type: 'keyword' }, + title: { type: 'text' }, + version: { type: 'integer' }, + }, + }, + migrations: searchSavedObjectTypeMigrations, +}; diff --git a/src/plugins/data/server/saved_objects/search_migrations.test.ts b/src/plugins/data/server/saved_objects/search_migrations.test.ts new file mode 100644 index 0000000000000..7fdf2e14aefed --- /dev/null +++ b/src/plugins/data/server/saved_objects/search_migrations.test.ts @@ -0,0 +1,299 @@ +/* + * 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 { SavedObjectMigrationContext } from 'kibana/server'; +import { searchSavedObjectTypeMigrations } from './search_migrations'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration search', () => { + describe('7.0.0', () => { + const migrationFn = searchSavedObjectTypeMigrations['7.0.0']; + + test('skips errors when searchSourceJSON is null', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips errors when searchSourceJSON is undefined', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: undefined, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when searchSourceJSON is not a string', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: 123, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when searchSourceJSON is invalid json', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: '{abc123}', + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when "index" and "filter" is missing from searchSourceJSON', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('extracts "index" attribute from doc', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + }, + "id": "123", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + ], + "type": "search", +} +`); + }); + + test('extracts index patterns from filter', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + bar: true, + filter: [ + { + meta: { + foo: true, + index: 'my-index', + }, + }, + ], + }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + }, + "id": "123", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + ], + "type": "search", +} +`); + }); + }); + + describe('7.4.0', function() { + const migrationFn = searchSavedObjectTypeMigrations['7.4.0']; + + test('transforms one dimensional sort arrays into two dimensional arrays', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + sort: ['bytes', 'desc'], + }, + }; + + const expected = { + id: '123', + type: 'search', + attributes: { + sort: [['bytes', 'desc']], + }, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(expected); + }); + + test("doesn't modify search docs that already have two dimensional sort arrays", () => { + const doc = { + id: '123', + type: 'search', + attributes: { + sort: [['bytes', 'desc']], + }, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(doc); + }); + + test("doesn't modify search docs that have no sort array", () => { + const doc = { + id: '123', + type: 'search', + attributes: {}, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(doc); + }); + }); +}); diff --git a/src/plugins/data/server/saved_objects/search_migrations.ts b/src/plugins/data/server/saved_objects/search_migrations.ts new file mode 100644 index 0000000000000..db545e52ce170 --- /dev/null +++ b/src/plugins/data/server/saved_objects/search_migrations.ts @@ -0,0 +1,92 @@ +/* + * 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 { flow, get } from 'lodash'; +import { SavedObjectMigrationFn } from 'kibana/server'; + +const migrateIndexPattern: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return doc; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc; + } + + if (searchSource.index && Array.isArray(doc.references)) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: 'index-pattern', + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index || !Array.isArray(doc.references)) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + + return doc; +}; + +const setNewReferences: SavedObjectMigrationFn = (doc, context) => { + doc.references = doc.references || []; + // Migrate index pattern + return migrateIndexPattern(doc, context); +}; + +const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { + const sort = get(doc, 'attributes.sort'); + if (!sort) return doc; + + // Don't do anything if we already have a two dimensional array + if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) { + return doc; + } + + return { + ...doc, + attributes: { + ...doc.attributes, + sort: [doc.attributes.sort], + }, + }; +}; + +export const searchSavedObjectTypeMigrations = { + '7.0.0': flow(setNewReferences), + '7.4.0': flow(migrateSearchSortToNestedArray), +}; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 46f90e3c6fc62..5ee19cd3df19f 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -34,6 +34,8 @@ import { import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; +import { searchSavedObjectType } from '../saved_objects'; + declare module 'kibana/server' { interface RequestHandlerContext { search?: IRouteHandlerSearchContext; @@ -53,6 +55,8 @@ export class SearchService implements Plugin { this.contextContainer = core.context.createContextContainer(); + core.savedObjects.registerType(searchSavedObjectType); + core.http.registerRouteHandlerContext<'search'>('search', context => { return createApi({ caller: context.core.elasticsearch.dataClient.callAsCurrentUser, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 666df2900c2c3..2a2d9bb414c14 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -733,7 +733,7 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:62:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:64:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index cf79ce17293d6..8e63ea7833327 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -1,7 +1,7 @@ { "id": "visualizations", "version": "kibana", - "server": false, + "server": true, "ui": true, "requiredPlugins": [ "expressions" diff --git a/src/plugins/visualizations/server/index.ts b/src/plugins/visualizations/server/index.ts new file mode 100644 index 0000000000000..80c10c3945d4c --- /dev/null +++ b/src/plugins/visualizations/server/index.ts @@ -0,0 +1,30 @@ +/* + * 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 { PluginInitializerContext } from '../../../core/server'; +import { VisualizationsPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new VisualizationsPlugin(initializerContext); +} + +export { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; diff --git a/src/plugins/visualizations/server/plugin.ts b/src/plugins/visualizations/server/plugin.ts new file mode 100644 index 0000000000000..79cce6b5867a1 --- /dev/null +++ b/src/plugins/visualizations/server/plugin.ts @@ -0,0 +1,54 @@ +/* + * 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 { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, +} from '../../../core/server'; + +import { visualizationSavedObjectType } from './saved_objects'; + +import { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; + +export class VisualizationsPlugin + implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('visualizations: Setup'); + + core.savedObjects.registerType(visualizationSavedObjectType); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('visualizations: Started'); + return {}; + } + + public stop() {} +} diff --git a/src/plugins/visualizations/server/saved_objects/index.ts b/src/plugins/visualizations/server/saved_objects/index.ts new file mode 100644 index 0000000000000..be75f63582450 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export { visualizationSavedObjectType } from './visualization'; diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts new file mode 100644 index 0000000000000..9f4782f3ec730 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -0,0 +1,56 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +import { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; + +export const visualizationSavedObjectType: SavedObjectsType = { + name: 'visualization', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'visualizeApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/visualize/edit/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'visualize.show', + }; + }, + }, + mappings: { + properties: { + description: { type: 'text' }, + kibanaSavedObjectMeta: { properties: { searchSourceJSON: { type: 'text' } } }, + savedSearchRefName: { type: 'keyword' }, + title: { type: 'text' }, + uiStateJSON: { type: 'text' }, + version: { type: 'integer' }, + visState: { type: 'text' }, + }, + }, + migrations: visualizationSavedObjectTypeMigrations, +}; diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts new file mode 100644 index 0000000000000..02c114bad4e72 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts @@ -0,0 +1,1356 @@ +/* + * 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 { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; +import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'kibana/server'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration visualization', () => { + describe('6.7.2', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['6.7.2']( + doc as Parameters[0], + savedObjectMigrationContext + ); + let doc: any; + + describe('date histogram time zone removal', () => { + beforeEach(() => { + doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + enabled: true, + id: '1', + params: { + // Doesn't make much sense but we want to test it's not removing it from anything else + time_zone: 'Europe/Berlin', + }, + schema: 'metric', + type: 'count', + }, + { + enabled: true, + id: '2', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + time_zone: 'Europe/Berlin', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '4', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '3', + params: { + customBucket: { + enabled: true, + id: '1-bucket', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + time_zone: 'Europe/Berlin', + useNormalizedEsInterval: true, + }, + type: 'date_histogram', + }, + customMetric: { + enabled: true, + id: '1-metric', + params: {}, + type: 'count', + }, + }, + schema: 'metric', + type: 'max_bucket', + }, + ], + }), + }, + } as Parameters[0]; + }); + + it('should remove time_zone from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[1]).not.toHaveProperty('params.time_zone'); + }); + + it('should not remove time_zone from non date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[0]).toHaveProperty('params.time_zone'); + }); + + it('should remove time_zone from nested aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); + }); + + it('should not fail on date histograms without a time_zone', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[2]).not.toHaveProperty('params.time_zone'); + }); + + it('should be able to apply the migration twice, since we need it for 6.7.2 and 7.0.1', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[1]).not.toHaveProperty('params.time_zone'); + expect(aggs[0]).toHaveProperty('params.time_zone'); + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); + expect(aggs[2]).not.toHaveProperty('params.time_zone'); + }); + }); + }); + + describe('7.0.0', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.0.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (type: any, aggs: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ type, aggs }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + references: [], + }); + + it('does not throw error on empty object', () => { + const migratedDoc = migrate({ + attributes: { + visState: '{}', + }, + }); + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{}", + }, + "references": Array [], +} +`); + }); + + it('skips errors when searchSourceJSON is null', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips errors when searchSourceJSON is undefined', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: undefined, + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when searchSourceJSON is not a string', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: 123, + }, + savedSearchId: '123', + }, + }; + + expect(migrate(doc)).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when searchSourceJSON is invalid json', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{abc123}', + }, + savedSearchId: '123', + }, + }; + + expect(migrate(doc)).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts "index" attribute from doc', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts index patterns from the filter', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + bar: true, + filter: [ + { + meta: { index: 'my-index', foo: true }, + }, + ], + }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts index patterns from controls', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + foo: true, + visState: JSON.stringify({ + bar: false, + params: { + controls: [ + { + bar: true, + indexPattern: 'pattern*', + }, + { + foo: true, + }, + ], + }, + }), + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "control_0_index_pattern", + "type": "index-pattern", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips extracting savedSearchId when missing', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], +} +`); + }); + + it('extract savedSearchId from doc', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], +} +`); + }); + + it('delete savedSearchId when empty string in doc', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + savedSearchId: '', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], +} +`); + }); + + it('should return a new object if vis is table and has multiple split aggs', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + ]; + const tableDoc = generateDoc('table', aggs); + const expected = tableDoc; + const actual = migrate(tableDoc); + + expect(actual).not.toEqual(expected); + }); + + it('should not touch any vis that is not table', () => { + const pieDoc = generateDoc('pie', []); + const expected = pieDoc; + const actual = migrate(pieDoc); + + expect(actual).toEqual(expected); + }); + + it('should not change values in any vis that is not table', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'segment', + params: { hey: 'ya' }, + }, + ]; + const pieDoc = generateDoc('pie', aggs); + const expected = pieDoc; + const actual = migrate(pieDoc); + + expect(actual).toEqual(expected); + }); + + it('should not touch table vis if there are not multiple split aggs', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + ]; + const tableDoc = generateDoc('table', aggs); + const expected = tableDoc; + const actual = migrate(tableDoc); + + expect(actual).toEqual(expected); + }); + + it('should change all split aggs to `bucket` except the first', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + { + id: '4', + schema: 'bucket', + params: { heyyy: 'yaaa' }, + }, + ]; + const expected = ['metric', 'split', 'bucket', 'bucket']; + const migrated = migrate(generateDoc('table', aggs)); + const actual = JSON.parse(migrated.attributes.visState); + + expect(actual.aggs.map((agg: any) => agg.schema)).toEqual(expected); + }); + + it('should remove `rows` param from any aggs that are not `split`', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + ]; + const expected = [{}, { foo: 'bar', row: true }, { hey: 'ya' }]; + const migrated = migrate(generateDoc('table', aggs)); + const actual = JSON.parse(migrated.attributes.visState); + + expect(actual.aggs.map((agg: any) => agg.params)).toEqual(expected); + }); + + it('should throw with a reference to the doc name if something goes wrong', () => { + const doc = { + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: '!/// Intentionally malformed JSON ///!', + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }; + expect(() => migrate(doc)).toThrowError(/My Vis/); + }); + }); + + describe('7.2.0', () => { + describe('date histogram custom interval removal', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.2.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + let doc: any; + + beforeEach(() => { + doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + enabled: true, + id: '1', + params: { + customInterval: '1h', + }, + schema: 'metric', + type: 'count', + }, + { + enabled: true, + id: '2', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '4', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'custom', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '3', + params: { + customBucket: { + enabled: true, + id: '1-bucket', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'custom', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + type: 'date_histogram', + }, + customMetric: { + enabled: true, + id: '1-metric', + params: {}, + type: 'count', + }, + }, + schema: 'metric', + type: 'max_bucket', + }, + ], + }), + }, + }; + }); + + it('should remove customInterval from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[1]).not.toHaveProperty('params.customInterval'); + }); + + it('should not change interval from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[1].params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[1].params.interval + ); + }); + + it('should not remove customInterval from non date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[0]).toHaveProperty('params.customInterval'); + }); + + it('should set interval with customInterval value and remove customInterval when interval equals "custom"', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[2].params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[2].params.customInterval + ); + expect(aggs[2]).not.toHaveProperty('params.customInterval'); + }); + + it('should remove customInterval from nested aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); + }); + + it('should remove customInterval from nested aggregations and set interval with customInterval value', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3].params.customBucket.params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[3].params.customBucket.params.customInterval + ); + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); + }); + + it('should not fail on date histograms without a customInterval', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3]).not.toHaveProperty('params.customInterval'); + }); + }); + }); + + describe('7.3.0', () => { + const logMsgArr: string[] = []; + const logger = ({ + log: { + warn: (msg: string) => logMsgArr.push(msg), + }, + } as unknown) as SavedObjectMigrationContext; + + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.0']( + doc as Parameters[0], + logger + ); + + it('migrates type = gauge verticalSplit: false to alignment: vertical', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: false } } }), + }, + }); + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", + }, +} +`); + }); + + it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: true } } }), + }, + }); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", + }, +} +`); + }); + + it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge' }), + }, + }); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\"}", + }, +} +`); + expect(logMsgArr).toMatchInlineSnapshot(` +Array [ + "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", + "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", +] +`); + }); + + describe('filters agg query migration', () => { + const doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + type: 'filters', + params: { + filters: [ + { + input: { + query: 'response:200', + }, + label: '', + }, + { + input: { + query: 'response:404', + }, + label: 'bad response', + }, + { + input: { + query: { + exists: { + field: 'phpmemory', + }, + }, + }, + label: '', + }, + ], + }, + }, + ], + }), + }, + }; + + it('should add language property to filters without one, assuming lucene', () => { + const migrationResult = migrate(doc); + + expect(migrationResult).toEqual({ + attributes: { + visState: JSON.stringify({ + aggs: [ + { + type: 'filters', + params: { + filters: [ + { + input: { + query: 'response:200', + language: 'lucene', + }, + label: '', + }, + { + input: { + query: 'response:404', + language: 'lucene', + }, + label: 'bad response', + }, + { + input: { + query: { + exists: { + field: 'phpmemory', + }, + }, + language: 'lucene', + }, + label: '', + }, + ], + }, + }, + ], + }), + }, + }); + }); + }); + + describe('replaceMovAvgToMovFn()', () => { + let doc: any; + + beforeEach(() => { + doc = { + attributes: { + title: 'VIS', + visState: `{"title":"VIS","type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417", + "type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(0,156,224,1)", + "split_mode":"terms","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count", + "numerator":"FlightDelay:true"},{"settings":"","minimize":0,"window":5,"model": + "holt_winters","id":"23054fe0-8915-11e9-9b86-d3f94982620f","type":"moving_average","field": + "61ca57f2-469d-11e7-af02-69e470af7417","predict":1}],"separate_axis":0,"axis_position":"right", + "formatter":"number","chart_type":"line","line_width":"2","point_size":"0","fill":0.5,"stacked":"none", + "label":"Percent Delays","terms_size":"2","terms_field":"OriginCityName"}],"time_field":"timestamp", + "index_pattern":"kibana_sample_data_flights","interval":">=12h","axis_position":"left","axis_formatter": + "number","show_legend":1,"show_grid":1,"annotations":[{"fields":"FlightDelay,Cancelled,Carrier", + "template":"{{Carrier}}: Flight Delayed and Cancelled!","index_pattern":"kibana_sample_data_flights", + "query_string":"FlightDelay:true AND Cancelled:true","id":"53b7dff0-4c89-11e8-a66a-6989ad5a0a39", + "color":"rgba(0,98,177,1)","time_field":"timestamp","icon":"fa-exclamation-triangle", + "ignore_global_filters":1,"ignore_panel_filters":1,"hidden":true}],"legend_position":"bottom", + "axis_scale":"normal","default_index_pattern":"kibana_sample_data_flights","default_timefield":"timestamp"}, + "aggs":[]}`, + }, + migrationVersion: { + visualization: '7.2.0', + }, + type: 'visualization', + }; + }); + + test('should add some necessary moving_fn fields', () => { + const migratedDoc = migrate(doc); + const visState = JSON.parse(migratedDoc.attributes.visState); + const metric = visState.params.series[0].metrics[1]; + + expect(metric).toHaveProperty('model_type'); + expect(metric).toHaveProperty('alpha'); + expect(metric).toHaveProperty('beta'); + expect(metric).toHaveProperty('gamma'); + expect(metric).toHaveProperty('period'); + expect(metric).toHaveProperty('multiplicative'); + }); + }); + }); + + describe('7.3.0 tsvb', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + it('should change series item filters from a string into an object', () => { + const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[0].filter).toHaveProperty('query'); + expect(series[0].filter).toHaveProperty('language'); + }); + it('should not change a series item filter string in the object after migration', () => { + const markdownParams = { + type: 'markdown', + series: [ + { + filter: 'Filter Bytes Test:>1000', + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + }; + const markdownDoc = generateDoc(markdownParams); + const migratedMarkdownDoc = migrate(markdownDoc); + const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series; + + expect(markdownSeries[0].filter.query).toBe( + JSON.parse(markdownDoc.attributes.visState).params.series[0].filter + ); + expect(markdownSeries[0].split_filters[0].filter.query).toBe( + JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter + ); + }); + + it('should change series item filters from a string into an object for all filters', () => { + const params = { + type: 'timeseries', + filter: 'bytes:>1000', + series: [ + { + filter: 'Filter Bytes Test:>1000', + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + annotations: [{ query_string: 'bytes:>1000' }], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + }); + + it('should not fail on a metric visualization without a filter in a series item', () => { + const params = { type: 'metric', series: [{}, {}, {}] }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[2]).not.toHaveProperty('filter.query'); + }); + + it('should not migrate a visualization of unknown type', () => { + const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] }; + const doc = generateDoc(params); + const migratedDoc = migrate(doc); + const series = JSON.parse(migratedDoc.attributes.visState).params.series; + + expect(series[0].filter).toEqual(params.series[0].filter); + }); + }); + + describe('7.3.1', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.1']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + it('should migrate filters agg query string queries', () => { + const state = { + aggs: [ + { type: 'count', params: {} }, + { + type: 'filters', + params: { + filters: [ + { + input: { + query: { + query_string: { query: 'machine.os.keyword:"win 8"' }, + }, + }, + }, + ], + }, + }, + ], + }; + const expected = { + aggs: [ + { type: 'count', params: {} }, + { + type: 'filters', + params: { + filters: [{ input: { query: 'machine.os.keyword:"win 8"' } }], + }, + }, + ], + }; + const migratedDoc = migrate({ attributes: { visState: JSON.stringify(state) } }); + + expect(migratedDoc).toEqual({ attributes: { visState: JSON.stringify(expected) } }); + }); + }); + + describe('7.4.2 tsvb split_filters migration', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.4.2']( + doc as Parameters[0], + savedObjectMigrationContext + ); + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + + it('should change series item filters from a string into an object for all filters', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(Object.keys(timeSeriesParams.filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ + query: 'bytes:>1000', + language: 'lucene', + }); + }); + + it('should change series item split filters when there is no filter item', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + annotations: [ + { + query_string: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ + query: 'bytes:>1000', + language: 'lucene', + }); + }); + + it('should not convert split_filters to objects if there are no split filter filters', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [], + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters).not.toHaveProperty('query'); + }); + + it('should do nothing if a split_filter is already a query:language object', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [ + { + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }, + ], + annotations: [ + { + query_string: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters[0].filter.query).toEqual('bytes:>1000'); + expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); + }); + }); +}); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts new file mode 100644 index 0000000000000..9ee355cbb23cf --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -0,0 +1,574 @@ +/* + * 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 { SavedObjectMigrationFn } from 'kibana/server'; +import { cloneDeep, get, omit, has, flow } from 'lodash'; + +const migrateIndexPattern: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return doc; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc; + } + + if (searchSource.index && Array.isArray(doc.references)) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: 'index-pattern', + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index || !Array.isArray(doc.references)) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + + return doc; +}; + +// [TSVB] Migrate percentile-rank aggregation (value -> values) +const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series') || []; + + series.forEach(part => { + (part.metrics || []).forEach((metric: any) => { + if (metric.type === 'percentile_rank' && has(metric, 'value')) { + metric.values = [metric.value]; + + delete metric.value; + } + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + +// Migrate date histogram aggregation (remove customInterval) +const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type === 'date_histogram' && agg.params) { + if (agg.params.interval === 'custom') { + agg.params.interval = agg.params.customInterval; + } + delete agg.params.customInterval; + } + + if ( + get(agg, 'params.customBucket.type', null) === 'date_histogram' && + agg.params.customBucket.params + ) { + if (agg.params.customBucket.params.interval === 'custom') { + agg.params.customBucket.params.interval = agg.params.customBucket.params.customInterval; + } + delete agg.params.customBucket.params.customInterval; + } + }); + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + +const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + // We're checking always for the existance of agg.params here. This should always exist, but better + // be safe then sorry during migrations. + if (agg.type === 'date_histogram' && agg.params) { + delete agg.params.time_zone; + } + + if ( + get(agg, 'params.customBucket.type', null) === 'date_histogram' && + agg.params.customBucket.params + ) { + delete agg.params.customBucket.params.time_zone; + } + }); + doc.attributes.visState = JSON.stringify(visState); + } + } + return doc; +}; + +// migrate gauge verticalSplit to alignment +// https://github.com/elastic/kibana/issues/34636 +const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + if (visState && visState.type === 'gauge' && !visState.params.gauge.alignment) { + visState.params.gauge.alignment = visState.params.gauge.verticalSplit + ? 'vertical' + : 'horizontal'; + delete visState.params.gauge.verticalSplit; + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + logger.log.warn(`Exception @ migrateGaugeVerticalSplitToAlignment! ${e}`); + logger.log.warn(`Exception @ migrateGaugeVerticalSplitToAlignment! Payload: ${visStateJSON}`); + } + } + return doc; +}; +// Migrate filters (string -> { query: string, language: lucene }) +/* + Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters. + In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself. + We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene. + For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object. + Path to the series array is thus: + attributes.visState. +*/ +const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { + // Migrate filters + // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly + const newDoc = cloneDeep(doc); + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const visType = get(visState, 'params.type'); + const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; + if (tsvbTypes.indexOf(visType) === -1) { + // skip + return doc; + } + // migrate the params fitler + const params: any = get(visState, 'params'); + if (params.filter && typeof params.filter === 'string') { + const paramsFilterObject = { + query: params.filter, + language: 'lucene', + }; + params.filter = paramsFilterObject; + } + + // migrate the annotations query string: + const annotations: any[] = get(visState, 'params.annotations') || []; + annotations.forEach(item => { + if (!item.query_string) { + // we don't need to transform anything if there isn't a filter at all + return; + } + if (typeof item.query_string === 'string') { + const itemQueryStringObject = { + query: item.query_string, + language: 'lucene', + }; + item.query_string = itemQueryStringObject; + } + }); + // migrate the series filters + const series: any[] = get(visState, 'params.series') || []; + + series.forEach(item => { + if (!item.filter) { + // we don't need to transform anything if there isn't a filter at all + return; + } + // series item filter + if (typeof item.filter === 'string') { + const itemfilterObject = { + query: item.filter, + language: 'lucene', + }; + item.filter = itemfilterObject; + } + // series item split filters filter + if (item.split_filters) { + const splitFilters: any[] = get(item, 'split_filters') || []; + splitFilters.forEach(filter => { + if (!filter.filter) { + // we don't need to transform anything if there isn't a filter at all + return; + } + if (typeof filter.filter === 'string') { + const filterfilterObject = { + query: filter.filter, + language: 'lucene', + }; + filter.filter = filterfilterObject; + } + }); + } + }); + newDoc.attributes.visState = JSON.stringify(visState); + } + } + return newDoc; +}; + +const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { + // Migrate split_filters in TSVB objects that weren't migrated in 7.3 + // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly + const newDoc = cloneDeep(doc); + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const visType = get(visState, 'params.type'); + const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; + if (tsvbTypes.indexOf(visType) === -1) { + // skip + return doc; + } + // migrate the series split_filter filters + const series: any[] = get(visState, 'params.series') || []; + series.forEach(item => { + // series item split filters filter + if (item.split_filters) { + const splitFilters: any[] = get(item, 'split_filters') || []; + if (splitFilters.length > 0) { + // only transform split_filter filters if we have filters + splitFilters.forEach(filter => { + if (typeof filter.filter === 'string') { + const filterfilterObject = { + query: filter.filter, + language: 'lucene', + }; + filter.filter = filterfilterObject; + } + }); + } + } + }); + newDoc.attributes.visState = JSON.stringify(visState); + } + } + return newDoc; +}; + +const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type !== 'filters') return; + + agg.params.filters.forEach((filter: any) => { + if (filter.input.language) return filter; + filter.input.language = 'lucene'; + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + } + return doc; +}; + +const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series', []); + + series.forEach(part => { + if (part.metrics && Array.isArray(part.metrics)) { + part.metrics.forEach((metric: any) => { + if (metric.type === 'moving_average') { + metric.model_type = metric.model; + metric.alpha = get(metric, 'settings.alpha', 0.3); + metric.beta = get(metric, 'settings.beta', 0.1); + metric.gamma = get(metric, 'settings.gamma', 0.3); + metric.period = get(metric, 'settings.period', 1); + metric.multiplicative = get(metric, 'settings.type') === 'mult'; + + delete metric.minimize; + delete metric.model; + delete metric.settings; + delete metric.predict; + } + }); + } + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + logger.log.warn(`Exception @ replaceMovAvgToMovFn! ${e}`); + logger.log.warn(`Exception @ replaceMovAvgToMovFn! Payload: ${visStateJSON}`); + } + } + + return doc; +}; + +const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type !== 'filters') return doc; + + agg.params.filters.forEach((filter: any) => { + if (filter.input.query.query_string) { + filter.input.query = filter.input.query.query_string.query; + } + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + } + return doc; +}; + +const addDocReferences: SavedObjectMigrationFn = doc => ({ + ...doc, + references: doc.references || [], +}); + +const migrateSavedSearch: SavedObjectMigrationFn = doc => { + const savedSearchId = get(doc, 'attributes.savedSearchId'); + + if (savedSearchId && doc.references) { + doc.references.push({ + type: 'search', + name: 'search_0', + id: savedSearchId, + }); + doc.attributes.savedSearchRefName = 'search_0'; + } + + delete doc.attributes.savedSearchId; + + return doc; +}; + +const migrateControls: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const controls: any[] = get(visState, 'params.controls') || []; + controls.forEach((control, i) => { + if (!control.indexPattern || !doc.references) { + return; + } + control.indexPatternRefName = `control_${i}_index_pattern`; + doc.references.push({ + name: control.indexPatternRefName, + type: 'index-pattern', + id: control.indexPattern, + }); + delete control.indexPattern; + }); + doc.attributes.visState = JSON.stringify(visState); + } + } + + return doc; +}; + +const migrateTableSplits: SavedObjectMigrationFn = doc => { + try { + const visState = JSON.parse(doc.attributes.visState); + if (get(visState, 'type') !== 'table') { + return doc; // do nothing; we only want to touch tables + } + + let splitCount = 0; + visState.aggs = visState.aggs.map((agg: any) => { + if (agg.schema !== 'split') { + return agg; + } + + splitCount++; + if (splitCount === 1) { + return agg; // leave the first split agg unchanged + } + agg.schema = 'bucket'; + // the `row` param is exclusively used by split aggs, so we remove it + agg.params = omit(agg.params, ['row']); + return agg; + }); + + if (splitCount <= 1) { + return doc; // do nothing; we only want to touch tables with multiple split aggs + } + + const newDoc = cloneDeep(doc); + newDoc.attributes.visState = JSON.stringify(visState); + + return newDoc; + } catch (e) { + throw new Error(`Failure attempting to migrate saved object '${doc.attributes.title}' - ${e}`); + } +}; + +export const visualizationSavedObjectTypeMigrations = { + /** + * We need to have this migration twice, once with a version prior to 7.0.0 once with a version + * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already + * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, + * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we + * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects + * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced + * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 + * only contained the 6.7.2 migration and not the 7.0.1 migration. + */ + '6.7.2': flow(removeDateHistogramTimeZones), + '7.0.0': flow( + addDocReferences, + migrateIndexPattern, + migrateSavedSearch, + migrateControls, + migrateTableSplits + ), + '7.0.1': flow(removeDateHistogramTimeZones), + '7.2.0': flow( + migratePercentileRankAggregation, + migrateDateHistogramAggregation + ), + '7.3.0': flow( + migrateGaugeVerticalSplitToAlignment, + transformFilterStringToQueryObject, + migrateFiltersAggQuery, + replaceMovAvgToMovFn + ), + '7.3.1': flow(migrateFiltersAggQueryStringQueries), + '7.4.2': flow(transformSplitFiltersStringToQueryObject), +}; diff --git a/src/plugins/visualizations/server/types.ts b/src/plugins/visualizations/server/types.ts new file mode 100644 index 0000000000000..6924edb29627d --- /dev/null +++ b/src/plugins/visualizations/server/types.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsPluginStart {} From 271c9597bed0487a899821538d183d91342e7461 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 16 Mar 2020 14:33:03 +0200 Subject: [PATCH 06/76] [SIEM][CASE] Change configuration button (#60229) * Change button * Make URLs constants --- .../pages/case/components/all_cases/index.tsx | 18 +++++++++--------- .../siem/public/pages/case/translations.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx index 1349246494ec8..7b655999ace09 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx @@ -8,7 +8,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiBasicTable, EuiButton, - EuiButtonIcon, EuiContextMenuPanel, EuiEmptyPrompt, EuiFlexGroup, @@ -45,6 +44,9 @@ import { OpenClosedStats } from '../open_closed_stats'; import { getActions } from './actions'; import { CasesTableFilters } from './table_filters'; +const CONFIGURE_CASES_URL = getConfigureCasesUrl(); +const CREATE_CASE_URL = getCreateCaseUrl(); + const Div = styled.div` margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; `; @@ -259,16 +261,14 @@ export const AllCases = React.memo(() => { /> - - {i18n.CREATE_TITLE} + + {i18n.CONFIGURE_CASES_BUTTON} - + + {i18n.CREATE_TITLE} + @@ -325,7 +325,7 @@ export const AllCases = React.memo(() => { titleSize="xs" body={i18n.NO_CASES_BODY} actions={ - + {i18n.ADD_NEW_CASE} } diff --git a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts index 9c0287a56ccbc..6ef412d408ae5 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts @@ -120,7 +120,7 @@ export const CONFIGURE_CASES_PAGE_TITLE = i18n.translate( ); export const CONFIGURE_CASES_BUTTON = i18n.translate('xpack.siem.case.configureCasesButton', { - defaultMessage: 'Configure cases', + defaultMessage: 'Edit third-party connection', }); export const ADD_COMMENT = i18n.translate('xpack.siem.case.caseView.comment.addComment', { From dd7531deb4c83a2711b0d4bc4871acea425eb8cf Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 16 Mar 2020 14:30:20 +0100 Subject: [PATCH 07/76] Add UiSettings validation & Kibana default route redirection (#59694) * add schema to ui settings params * add validation for defaults and overrides * validate in ui settings client * ui settings routes validation * clean up tests * use schema for defaultRoutes * move URL redirection to NP * fix spaces test * update docs * update kbn pm * fix karma test * fix tests * address comments * get rid of getDEfaultRoute * regen docs * fix tests * fix enter-spaces test * validate on relative url format * update i18n * fix enter-spoace test * move relative url validation to utils * add CoreApp containing application logic * extract public uiSettings params in a separate type * make schema required * update docs --- ...in-core-public.iuisettingsclient.getall.md | 2 +- ...na-plugin-core-public.iuisettingsclient.md | 2 +- .../core/public/kibana-plugin-core-public.md | 1 + ...ugin-core-public.publicuisettingsparams.md | 13 ++ ...ana-plugin-core-public.uisettingsparams.md | 5 +- ...gin-core-public.uisettingsparams.schema.md | 11 ++ ...ugin-core-public.uisettingsparams.value.md | 2 +- ...-server.iuisettingsclient.getregistered.md | 2 +- ...na-plugin-core-server.iuisettingsclient.md | 2 +- .../core/server/kibana-plugin-core-server.md | 1 + ...ugin-core-server.publicuisettingsparams.md | 13 ++ ...ana-plugin-core-server.uisettingsparams.md | 5 +- ...gin-core-server.uisettingsparams.schema.md | 11 ++ ...ugin-core-server.uisettingsparams.value.md | 2 +- .../src/errors/validation_error.ts | 3 + .../src/kbn_client/kbn_client_ui_settings.ts | 4 +- packages/kbn-pm/dist/index.js | 4 +- src/core/public/index.ts | 2 +- src/core/public/public.api.md | 16 +- src/core/public/types.ts | 1 + .../ui_settings_api.test.ts.snap | 8 +- src/core/public/ui_settings/types.ts | 6 +- .../ui_settings/ui_settings_api.test.ts | 2 +- .../public/ui_settings/ui_settings_api.ts | 10 +- .../public/ui_settings/ui_settings_client.ts | 8 +- src/core/server/core_app/core_app.ts | 52 ++++++ src/core/server/core_app/index.ts | 20 +++ .../default_route_provider_config.test.ts | 90 ++++++++++ src/core/server/index.ts | 1 + src/core/server/server.api.md | 11 +- src/core/server/server.ts | 15 +- src/core/server/ui_settings/index.ts | 1 + .../integration_tests/routes.test.ts | 66 ++++++++ src/core/server/ui_settings/routes/set.ts | 4 +- .../server/ui_settings/routes/set_many.ts | 4 +- src/core/server/ui_settings/types.ts | 5 +- .../ui_settings/ui_settings_client.test.ts | 159 ++++++++++++++++-- .../server/ui_settings/ui_settings_client.ts | 73 +++++--- .../ui_settings/ui_settings_service.test.ts | 42 +++++ .../server/ui_settings/ui_settings_service.ts | 20 +++ src/core/types/ui_settings.ts | 19 ++- src/core/utils/url.test.ts | 16 +- src/core/utils/url.ts | 16 ++ .../kibana/ui_setting_defaults.js | 24 ++- .../tests_bundle/tests_entry_template.js | 11 +- src/legacy/server/http/index.js | 11 -- .../default_route_provider.test.ts | 87 ---------- .../default_route_provider_config.test.ts | 54 ------ .../http/setup_default_route_provider.ts | 74 -------- src/legacy/server/kbn_server.d.ts | 1 - .../management_app/advanced_settings.test.tsx | 10 +- .../lib/to_editable_config.test.ts | 6 +- .../management_app/lib/to_editable_config.ts | 4 +- .../public/management_app/types.ts | 11 +- src/plugins/data/public/public.api.md | 2 +- src/test_utils/kbn_server.ts | 1 + test/api_integration/apis/index.js | 1 - .../ui_settings_plugin/server/plugin.ts | 3 +- .../spaces/server/routes/views/enter_space.ts | 8 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../functional/apps/spaces/enter_space.ts | 32 ++-- .../es_archives/spaces/enter_space/data.json | 4 +- 63 files changed, 724 insertions(+), 372 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md create mode 100644 src/core/server/core_app/core_app.ts create mode 100644 src/core/server/core_app/index.ts create mode 100644 src/core/server/core_app/integration_tests/default_route_provider_config.test.ts create mode 100644 src/core/server/ui_settings/integration_tests/routes.test.ts delete mode 100644 src/legacy/server/http/integration_tests/default_route_provider.test.ts delete mode 100644 src/legacy/server/http/integration_tests/default_route_provider_config.test.ts delete mode 100644 src/legacy/server/http/setup_default_route_provider.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md index 805ac57b2fb9a..004979977376e 100644 --- a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md +++ b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md @@ -9,5 +9,5 @@ Gets the metadata about all uiSettings, including the type, default value, and u Signature: ```typescript -getAll: () => Readonly>; +getAll: () => Readonly>; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md index da566ed25cff5..87ef5784a6c6d 100644 --- a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md +++ b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md @@ -18,7 +18,7 @@ export interface IUiSettingsClient | --- | --- | --- | | [get](./kibana-plugin-core-public.iuisettingsclient.get.md) | <T = any>(key: string, defaultOverride?: T) => T | Gets the value for a specific uiSetting. If this setting has no user-defined value then the defaultOverride parameter is returned (and parsed if setting is of type "json" or "number). If the parameter is not defined and the key is not registered by any plugin then an error is thrown, otherwise reads the default value defined by a plugin. | | [get$](./kibana-plugin-core-public.iuisettingsclient.get_.md) | <T = any>(key: string, defaultOverride?: T) => Observable<T> | Gets an observable of the current value for a config key, and all updates to that config key in the future. Providing a defaultOverride argument behaves the same as it does in \#get() | -| [getAll](./kibana-plugin-core-public.iuisettingsclient.getall.md) | () => Readonly<Record<string, UiSettingsParams & UserProvidedValues>> | Gets the metadata about all uiSettings, including the type, default value, and user value for each key. | +| [getAll](./kibana-plugin-core-public.iuisettingsclient.getall.md) | () => Readonly<Record<string, PublicUiSettingsParams & UserProvidedValues>> | Gets the metadata about all uiSettings, including the type, default value, and user value for each key. | | [getSaved$](./kibana-plugin-core-public.iuisettingsclient.getsaved_.md) | <T = any>() => Observable<{
key: string;
newValue: T;
oldValue: T;
}> | Returns an Observable that notifies subscribers of each update to the uiSettings, including the key, newValue, and oldValue of the setting that changed. | | [getUpdate$](./kibana-plugin-core-public.iuisettingsclient.getupdate_.md) | <T = any>() => Observable<{
key: string;
newValue: T;
oldValue: T;
}> | Returns an Observable that notifies subscribers of each update to the uiSettings, including the key, newValue, and oldValue of the setting that changed. | | [getUpdateErrors$](./kibana-plugin-core-public.iuisettingsclient.getupdateerrors_.md) | () => Observable<Error> | Returns an Observable that notifies subscribers of each error while trying to update the settings, containing the actual Error class. | diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index bafc2eb3a4bc9..a9fbaa25ea150 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -147,6 +147,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [MountPoint](./kibana-plugin-core-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. | | [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The plugin export at the root of a plugin's public directory should conform to this interface. | | [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | | +| [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. | | [RecursiveReadonly](./kibana-plugin-core-public.recursivereadonly.md) | | | [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value | | [SavedObjectAttributeSingle](./kibana-plugin-core-public.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | diff --git a/docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md b/docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md new file mode 100644 index 0000000000000..678a69289ff23 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) + +## PublicUiSettingsParams type + +A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. + +Signature: + +```typescript +export declare type PublicUiSettingsParams = Omit; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md index 00f1c0f0deca5..e7facb4a109cd 100644 --- a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md @@ -9,7 +9,7 @@ UiSettings parameters defined by the plugins. Signature: ```typescript -export interface UiSettingsParams +export interface UiSettingsParams ``` ## Properties @@ -24,7 +24,8 @@ export interface UiSettingsParams | [options](./kibana-plugin-core-public.uisettingsparams.options.md) | string[] | array of permitted values for this setting | | [readonly](./kibana-plugin-core-public.uisettingsparams.readonly.md) | boolean | a flag indicating that value cannot be changed | | [requiresPageReload](./kibana-plugin-core-public.uisettingsparams.requirespagereload.md) | boolean | a flag indicating whether new value applying requires page reloading | +| [schema](./kibana-plugin-core-public.uisettingsparams.schema.md) | Type<T> | | | [type](./kibana-plugin-core-public.uisettingsparams.type.md) | UiSettingsType | defines a type of UI element [UiSettingsType](./kibana-plugin-core-public.uisettingstype.md) | | [validation](./kibana-plugin-core-public.uisettingsparams.validation.md) | ImageValidation | StringValidation | | -| [value](./kibana-plugin-core-public.uisettingsparams.value.md) | SavedObjectAttribute | default value to fall back to if a user doesn't provide any | +| [value](./kibana-plugin-core-public.uisettingsparams.value.md) | T | default value to fall back to if a user doesn't provide any | diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md new file mode 100644 index 0000000000000..f90d5161f96a9 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) > [schema](./kibana-plugin-core-public.uisettingsparams.schema.md) + +## UiSettingsParams.schema property + +Signature: + +```typescript +schema: Type; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md index 8775588290d70..2740f169eeecb 100644 --- a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md @@ -9,5 +9,5 @@ default value to fall back to if a user doesn't provide any Signature: ```typescript -value?: SavedObjectAttribute; +value?: T; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md index 2ca6b4cbe1589..71a2bbf88472e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md +++ b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md @@ -9,5 +9,5 @@ Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-ser Signature: ```typescript -getRegistered: () => Readonly>; +getRegistered: () => Readonly>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md index 42fcc81419cbe..af99b5e5bb215 100644 --- a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md @@ -18,7 +18,7 @@ export interface IUiSettingsClient | --- | --- | --- | | [get](./kibana-plugin-core-server.iuisettingsclient.get.md) | <T = any>(key: string) => Promise<T> | Retrieves uiSettings values set by the user with fallbacks to default values if not specified. | | [getAll](./kibana-plugin-core-server.iuisettingsclient.getall.md) | <T = any>() => Promise<Record<string, T>> | Retrieves a set of all uiSettings values set by the user with fallbacks to default values if not specified. | -| [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | () => Readonly<Record<string, UiSettingsParams>> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | +| [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | () => Readonly<Record<string, PublicUiSettingsParams>> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | | [getUserProvided](./kibana-plugin-core-server.iuisettingsclient.getuserprovided.md) | <T = any>() => Promise<Record<string, UserProvidedValues<T>>> | Retrieves a set of all uiSettings values set by the user. | | [isOverridden](./kibana-plugin-core-server.iuisettingsclient.isoverridden.md) | (key: string) => boolean | Shows whether the uiSettings value set by the user. | | [remove](./kibana-plugin-core-server.iuisettingsclient.remove.md) | (key: string) => Promise<void> | Removes uiSettings value by key. | diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 8a88329031e1f..54cf496b2d6af 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -232,6 +232,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PluginInitializer](./kibana-plugin-core-server.plugininitializer.md) | The plugin export at the root of a plugin's server directory should conform to this interface. | | [PluginName](./kibana-plugin-core-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. | | [PluginOpaqueId](./kibana-plugin-core-server.pluginopaqueid.md) | | +| [PublicUiSettingsParams](./kibana-plugin-core-server.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) exposed to the client-side. | | [RecursiveReadonly](./kibana-plugin-core-server.recursivereadonly.md) | | | [RedirectResponseOptions](./kibana-plugin-core-server.redirectresponseoptions.md) | HTTP response parameters for redirection response | | [RequestHandler](./kibana-plugin-core-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) functions. | diff --git a/docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md b/docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md new file mode 100644 index 0000000000000..4ccc91fbe1f74 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PublicUiSettingsParams](./kibana-plugin-core-server.publicuisettingsparams.md) + +## PublicUiSettingsParams type + +A sub-set of [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) exposed to the client-side. + +Signature: + +```typescript +export declare type PublicUiSettingsParams = Omit; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md index fe6e5d956f3e2..f134decb5102b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md @@ -9,7 +9,7 @@ UiSettings parameters defined by the plugins. Signature: ```typescript -export interface UiSettingsParams +export interface UiSettingsParams ``` ## Properties @@ -24,7 +24,8 @@ export interface UiSettingsParams | [options](./kibana-plugin-core-server.uisettingsparams.options.md) | string[] | array of permitted values for this setting | | [readonly](./kibana-plugin-core-server.uisettingsparams.readonly.md) | boolean | a flag indicating that value cannot be changed | | [requiresPageReload](./kibana-plugin-core-server.uisettingsparams.requirespagereload.md) | boolean | a flag indicating whether new value applying requires page reloading | +| [schema](./kibana-plugin-core-server.uisettingsparams.schema.md) | Type<T> | | | [type](./kibana-plugin-core-server.uisettingsparams.type.md) | UiSettingsType | defines a type of UI element [UiSettingsType](./kibana-plugin-core-server.uisettingstype.md) | | [validation](./kibana-plugin-core-server.uisettingsparams.validation.md) | ImageValidation | StringValidation | | -| [value](./kibana-plugin-core-server.uisettingsparams.value.md) | SavedObjectAttribute | default value to fall back to if a user doesn't provide any | +| [value](./kibana-plugin-core-server.uisettingsparams.value.md) | T | default value to fall back to if a user doesn't provide any | diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md new file mode 100644 index 0000000000000..f181fbd309b7f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) > [schema](./kibana-plugin-core-server.uisettingsparams.schema.md) + +## UiSettingsParams.schema property + +Signature: + +```typescript +schema: Type; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md index ca00cd0cd6396..78c8f0c8fcf8d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md @@ -9,5 +9,5 @@ default value to fall back to if a user doesn't provide any Signature: ```typescript -value?: SavedObjectAttribute; +value?: T; ``` diff --git a/packages/kbn-config-schema/src/errors/validation_error.ts b/packages/kbn-config-schema/src/errors/validation_error.ts index d688d022da85c..2a4f887bc4349 100644 --- a/packages/kbn-config-schema/src/errors/validation_error.ts +++ b/packages/kbn-config-schema/src/errors/validation_error.ts @@ -44,5 +44,8 @@ export class ValidationError extends SchemaError { constructor(error: SchemaTypeError, namespace?: string) { super(ValidationError.extractMessage(error, namespace), error); + + // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, ValidationError.prototype); } } diff --git a/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts b/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts index ad01dea624c3c..dbfa87e70032b 100644 --- a/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts +++ b/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts @@ -67,7 +67,7 @@ export class KbnClientUiSettings { * Replace all uiSettings with the `doc` values, `doc` is merged * with some defaults */ - async replace(doc: UiSettingValues) { + async replace(doc: UiSettingValues, { retries = 5 }: { retries?: number } = {}) { this.log.debug('replacing kibana config doc: %j', doc); const changes: Record = { @@ -85,7 +85,7 @@ export class KbnClientUiSettings { method: 'POST', path: '/api/kibana/settings', body: { changes }, - retries: 5, + retries, }); } diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index fe0491870e4bd..338d489300526 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -43920,7 +43920,7 @@ class KbnClientUiSettings { * Replace all uiSettings with the `doc` values, `doc` is merged * with some defaults */ - async replace(doc) { + async replace(doc, { retries = 5 } = {}) { this.log.debug('replacing kibana config doc: %j', doc); const changes = { ...this.defaults, @@ -43935,7 +43935,7 @@ class KbnClientUiSettings { method: 'POST', path: '/api/kibana/settings', body: { changes }, - retries: 5, + retries, }); } /** diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 483d4dbfdf7c5..0ff044878afa9 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -171,7 +171,7 @@ export { ErrorToastOptions, } from './notifications'; -export { MountPoint, UnmountCallback } from './types'; +export { MountPoint, UnmountCallback, PublicUiSettingsParams } from './types'; /** * Core services exposed to the `Plugin` setup lifecycle diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 69668176a397e..46667230edc3b 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -16,10 +16,11 @@ import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; +import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types'; import React from 'react'; import * as Rx from 'rxjs'; import { ShallowPromise } from '@kbn/utility-types'; -import { UiSettingsParams as UiSettingsParams_2 } from 'src/core/server/types'; +import { Type } from '@kbn/config-schema'; import { UnregisterCallback } from 'history'; import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types'; @@ -784,7 +785,7 @@ export type IToasts = Pick(key: string, defaultOverride?: T) => Observable; get: (key: string, defaultOverride?: T) => T; - getAll: () => Readonly>; + getAll: () => Readonly>; getSaved$: () => Observable<{ key: string; newValue: T; @@ -933,6 +934,9 @@ export interface PluginInitializerContext // @public (undocumented) export type PluginOpaqueId = symbol; +// @public +export type PublicUiSettingsParams = Omit; + // Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -1291,7 +1295,7 @@ export type ToastsSetup = IToasts; export type ToastsStart = IToasts; // @public -export interface UiSettingsParams { +export interface UiSettingsParams { category?: string[]; // Warning: (ae-forgotten-export) The symbol "DeprecationSettings" needs to be exported by the entry point index.d.ts deprecation?: DeprecationSettings; @@ -1301,16 +1305,18 @@ export interface UiSettingsParams { options?: string[]; readonly?: boolean; requiresPageReload?: boolean; + // (undocumented) + schema: Type; type?: UiSettingsType; // (undocumented) validation?: ImageValidation | StringValidation; - value?: SavedObjectAttribute; + value?: T; } // @public (undocumented) export interface UiSettingsState { // (undocumented) - [key: string]: UiSettingsParams_2 & UserProvidedValues_2; + [key: string]: PublicUiSettingsParams_2 & UserProvidedValues_2; } // @public diff --git a/src/core/public/types.ts b/src/core/public/types.ts index 267a9e9f7e014..26f1e46836378 100644 --- a/src/core/public/types.ts +++ b/src/core/public/types.ts @@ -19,6 +19,7 @@ export { UiSettingsParams, + PublicUiSettingsParams, UserProvidedValues, UiSettingsType, ImageValidation, diff --git a/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap b/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap index cd55c77526d52..b737c04a5f269 100644 --- a/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap +++ b/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap @@ -84,21 +84,21 @@ Array [ exports[`#batchSet rejects all promises for batched requests that fail: promise rejections 1`] = ` Array [ Object { - "error": [Error: Request failed with status code: 400], + "error": [Error: invalid], "isRejected": true, }, Object { - "error": [Error: Request failed with status code: 400], + "error": [Error: invalid], "isRejected": true, }, Object { - "error": [Error: Request failed with status code: 400], + "error": [Error: invalid], "isRejected": true, }, ] `; -exports[`#batchSet rejects on 301 1`] = `"Request failed with status code: 301"`; +exports[`#batchSet rejects on 301 1`] = `"Moved Permanently"`; exports[`#batchSet rejects on 404 response 1`] = `"Request failed with status code: 404"`; diff --git a/src/core/public/ui_settings/types.ts b/src/core/public/ui_settings/types.ts index 19fd91924f247..d92c033ae8c8c 100644 --- a/src/core/public/ui_settings/types.ts +++ b/src/core/public/ui_settings/types.ts @@ -18,11 +18,11 @@ */ import { Observable } from 'rxjs'; -import { UiSettingsParams, UserProvidedValues } from 'src/core/server/types'; +import { PublicUiSettingsParams, UserProvidedValues } from 'src/core/server/types'; /** @public */ export interface UiSettingsState { - [key: string]: UiSettingsParams & UserProvidedValues; + [key: string]: PublicUiSettingsParams & UserProvidedValues; } /** @@ -53,7 +53,7 @@ export interface IUiSettingsClient { * Gets the metadata about all uiSettings, including the type, default value, and user value * for each key. */ - getAll: () => Readonly>; + getAll: () => Readonly>; /** * Sets the value for a uiSetting. If the setting is not registered by any plugin diff --git a/src/core/public/ui_settings/ui_settings_api.test.ts b/src/core/public/ui_settings/ui_settings_api.test.ts index 1170c42cea704..9a462e0541347 100644 --- a/src/core/public/ui_settings/ui_settings_api.test.ts +++ b/src/core/public/ui_settings/ui_settings_api.test.ts @@ -148,7 +148,7 @@ describe('#batchSet', () => { '*', { status: 400, - body: 'invalid', + body: { message: 'invalid' }, }, { overwriteRoutes: false, diff --git a/src/core/public/ui_settings/ui_settings_api.ts b/src/core/public/ui_settings/ui_settings_api.ts index 33b43107acf1b..c5efced0a41e3 100644 --- a/src/core/public/ui_settings/ui_settings_api.ts +++ b/src/core/public/ui_settings/ui_settings_api.ts @@ -152,10 +152,14 @@ export class UiSettingsApi { }, }); } catch (err) { - if (err.response && err.response.status >= 300) { - throw new Error(`Request failed with status code: ${err.response.status}`); + if (err.response) { + if (err.response.status === 400) { + throw new Error(err.body.message); + } + if (err.response.status > 400) { + throw new Error(`Request failed with status code: ${err.response.status}`); + } } - throw err; } finally { this.loadingCount$.next(this.loadingCount$.getValue() - 1); diff --git a/src/core/public/ui_settings/ui_settings_client.ts b/src/core/public/ui_settings/ui_settings_client.ts index f0071ed08435c..f5596b1bc34fc 100644 --- a/src/core/public/ui_settings/ui_settings_client.ts +++ b/src/core/public/ui_settings/ui_settings_client.ts @@ -21,14 +21,14 @@ import { cloneDeep, defaultsDeep } from 'lodash'; import { Observable, Subject, concat, defer, of } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { UiSettingsParams, UserProvidedValues } from 'src/core/server/types'; +import { UserProvidedValues, PublicUiSettingsParams } from 'src/core/server/types'; import { IUiSettingsClient, UiSettingsState } from './types'; import { UiSettingsApi } from './ui_settings_api'; interface UiSettingsClientParams { api: UiSettingsApi; - defaults: Record; + defaults: Record; initialSettings?: UiSettingsState; done$: Observable; } @@ -39,8 +39,8 @@ export class UiSettingsClient implements IUiSettingsClient { private readonly updateErrors$ = new Subject(); private readonly api: UiSettingsApi; - private readonly defaults: Record; - private cache: Record; + private readonly defaults: Record; + private cache: Record; constructor(params: UiSettingsClientParams) { this.api = params.api; diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts new file mode 100644 index 0000000000000..2f8c85f47a76e --- /dev/null +++ b/src/core/server/core_app/core_app.ts @@ -0,0 +1,52 @@ +/* + * 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 { InternalCoreSetup } from '../internal_types'; +import { CoreContext } from '../core_context'; +import { Logger } from '../logging'; + +/** @internal */ +export class CoreApp { + private readonly logger: Logger; + constructor(core: CoreContext) { + this.logger = core.logger.get('core-app'); + } + setup(coreSetup: InternalCoreSetup) { + this.logger.debug('Setting up core app.'); + this.registerDefaultRoutes(coreSetup); + } + + private registerDefaultRoutes(coreSetup: InternalCoreSetup) { + const httpSetup = coreSetup.http; + const router = httpSetup.createRouter('/'); + router.get({ path: '/', validate: false }, async (context, req, res) => { + const defaultRoute = await context.core.uiSettings.client.get('defaultRoute'); + const basePath = httpSetup.basePath.get(req); + const url = `${basePath}${defaultRoute}`; + + return res.redirected({ + headers: { + location: url, + }, + }); + }); + router.get({ path: '/core', validate: false }, async (context, req, res) => + res.ok({ body: { version: '0.0.1' } }) + ); + } +} diff --git a/src/core/server/core_app/index.ts b/src/core/server/core_app/index.ts new file mode 100644 index 0000000000000..342ed43f1ff8a --- /dev/null +++ b/src/core/server/core_app/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export { CoreApp } from './core_app'; diff --git a/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts b/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts new file mode 100644 index 0000000000000..221e6fa42471c --- /dev/null +++ b/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts @@ -0,0 +1,90 @@ +/* + * 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 * as kbnTestServer from '../../../../test_utils/kbn_server'; +import { Root } from '../../root'; + +const { startES } = kbnTestServer.createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), +}); +let esServer: kbnTestServer.TestElasticsearchUtils; + +describe('default route provider', () => { + let root: Root; + + beforeAll(async () => { + esServer = await startES(); + root = kbnTestServer.createRootWithCorePlugins({ + server: { + basePath: '/hello', + }, + }); + + await root.setup(); + await root.start(); + }); + + afterAll(async () => { + await esServer.stop(); + await root.shutdown(); + }); + + it('redirects to the configured default route respecting basePath', async function() { + const { status, header } = await kbnTestServer.request.get(root, '/'); + + expect(status).toEqual(302); + expect(header).toMatchObject({ + location: '/hello/app/kibana', + }); + }); + + it('ignores invalid values', async function() { + const invalidRoutes = [ + 'http://not-your-kibana.com', + '///example.com', + '//example.com', + ' //example.com', + ]; + + for (const url of invalidRoutes) { + await kbnTestServer.request + .post(root, '/api/kibana/settings/defaultRoute') + .send({ value: url }) + .expect(400); + } + + const { status, header } = await kbnTestServer.request.get(root, '/'); + expect(status).toEqual(302); + expect(header).toMatchObject({ + location: '/hello/app/kibana', + }); + }); + + it('consumes valid values', async function() { + await kbnTestServer.request + .post(root, '/api/kibana/settings/defaultRoute') + .send({ value: '/valid' }) + .expect(200); + + const { status, header } = await kbnTestServer.request.get(root, '/'); + expect(status).toEqual(302); + expect(header).toMatchObject({ + location: '/hello/valid', + }); + }); +}); diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 4a1ac8988e4e5..89fee92a7ef02 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -250,6 +250,7 @@ export { export { IUiSettingsClient, UiSettingsParams, + PublicUiSettingsParams, UiSettingsType, UiSettingsServiceSetup, UiSettingsServiceStart, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 9cd0c26ea2497..84fe081adaae6 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -998,7 +998,7 @@ export interface IScopedRenderingClient { export interface IUiSettingsClient { get: (key: string) => Promise; getAll: () => Promise>; - getRegistered: () => Readonly>; + getRegistered: () => Readonly>; getUserProvided: () => Promise>>; isOverridden: (key: string) => boolean; remove: (key: string) => Promise; @@ -1443,6 +1443,9 @@ export interface PluginsServiceStart { contracts: Map; } +// @public +export type PublicUiSettingsParams = Omit; + // Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -2284,7 +2287,7 @@ export interface StringValidationRegexString { } // @public -export interface UiSettingsParams { +export interface UiSettingsParams { category?: string[]; deprecation?: DeprecationSettings; description?: string; @@ -2293,10 +2296,12 @@ export interface UiSettingsParams { options?: string[]; readonly?: boolean; requiresPageReload?: boolean; + // (undocumented) + schema: Type; type?: UiSettingsType; // (undocumented) validation?: ImageValidation | StringValidation; - value?: SavedObjectAttribute; + value?: T; } // @public (undocumented) diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 2402504f717ca..09a1328f346d8 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -26,8 +26,9 @@ import { RawConfigurationProvider, coreDeprecationProvider, } from './config'; +import { CoreApp } from './core_app'; import { ElasticsearchService } from './elasticsearch'; -import { HttpService, InternalHttpServiceSetup } from './http'; +import { HttpService } from './http'; import { RenderingService, RenderingServiceSetup } from './rendering'; import { LegacyService, ensureValidConfiguration } from './legacy'; import { Logger, LoggerFactory } from './logging'; @@ -69,6 +70,7 @@ export class Server { private readonly uiSettings: UiSettingsService; private readonly uuid: UuidService; private readonly metrics: MetricsService; + private readonly coreApp: CoreApp; private coreStart?: InternalCoreStart; @@ -92,6 +94,7 @@ export class Server { this.capabilities = new CapabilitiesService(core); this.uuid = new UuidService(core); this.metrics = new MetricsService(core); + this.coreApp = new CoreApp(core); } public async setup() { @@ -122,8 +125,6 @@ export class Server { context: contextServiceSetup, }); - this.registerDefaultRoute(httpSetup); - const capabilitiesSetup = this.capabilities.setup({ http: httpSetup }); const elasticsearchServiceSetup = await this.elasticsearch.setup({ @@ -168,6 +169,7 @@ export class Server { }); this.registerCoreContext(coreSetup, renderingSetup); + this.coreApp.setup(coreSetup); return coreSetup; } @@ -216,13 +218,6 @@ export class Server { await this.metrics.stop(); } - private registerDefaultRoute(httpSetup: InternalHttpServiceSetup) { - const router = httpSetup.createRouter('/core'); - router.get({ path: '/', validate: false }, async (context, req, res) => - res.ok({ body: { version: '0.0.1' } }) - ); - } - private registerCoreContext(coreSetup: InternalCoreSetup, rendering: RenderingServiceSetup) { coreSetup.http.registerRouteHandlerContext( coreId, diff --git a/src/core/server/ui_settings/index.ts b/src/core/server/ui_settings/index.ts index ddb66df3ffcbe..b11f398d59fa8 100644 --- a/src/core/server/ui_settings/index.ts +++ b/src/core/server/ui_settings/index.ts @@ -27,6 +27,7 @@ export { UiSettingsServiceStart, IUiSettingsClient, UiSettingsParams, + PublicUiSettingsParams, InternalUiSettingsServiceSetup, InternalUiSettingsServiceStart, UiSettingsType, diff --git a/src/core/server/ui_settings/integration_tests/routes.test.ts b/src/core/server/ui_settings/integration_tests/routes.test.ts new file mode 100644 index 0000000000000..c1261bc7c1350 --- /dev/null +++ b/src/core/server/ui_settings/integration_tests/routes.test.ts @@ -0,0 +1,66 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import * as kbnTestServer from '../../../../test_utils/kbn_server'; + +describe('ui settings service', () => { + describe('routes', () => { + let root: ReturnType; + beforeAll(async () => { + root = kbnTestServer.createRoot(); + + const { uiSettings } = await root.setup(); + uiSettings.register({ + custom: { + value: '42', + schema: schema.string(), + }, + }); + + await root.start(); + }, 30000); + afterAll(async () => await root.shutdown()); + + describe('set', () => { + it('validates value', async () => { + const response = await kbnTestServer.request + .post(root, '/api/kibana/settings/custom') + .send({ value: 100 }) + .expect(400); + + expect(response.body.message).toBe( + '[validation [custom]]: expected value of type [string] but got [number]' + ); + }); + }); + describe('set many', () => { + it('validates value', async () => { + const response = await kbnTestServer.request + .post(root, '/api/kibana/settings') + .send({ changes: { custom: 100, foo: 'bar' } }) + .expect(400); + + expect(response.body.message).toBe( + '[validation [custom]]: expected value of type [string] but got [number]' + ); + }); + }); + }); +}); diff --git a/src/core/server/ui_settings/routes/set.ts b/src/core/server/ui_settings/routes/set.ts index 51ad256b51335..e5158e274245c 100644 --- a/src/core/server/ui_settings/routes/set.ts +++ b/src/core/server/ui_settings/routes/set.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { schema } from '@kbn/config-schema'; +import { schema, ValidationError } from '@kbn/config-schema'; import { IRouter } from '../../http'; import { SavedObjectsErrorHelpers } from '../../saved_objects'; @@ -56,7 +56,7 @@ export function registerSetRoute(router: IRouter) { }); } - if (error instanceof CannotOverrideError) { + if (error instanceof CannotOverrideError || error instanceof ValidationError) { return response.badRequest({ body: error }); } diff --git a/src/core/server/ui_settings/routes/set_many.ts b/src/core/server/ui_settings/routes/set_many.ts index 3794eba004bee..5623c3fe11b80 100644 --- a/src/core/server/ui_settings/routes/set_many.ts +++ b/src/core/server/ui_settings/routes/set_many.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { schema } from '@kbn/config-schema'; +import { schema, ValidationError } from '@kbn/config-schema'; import { IRouter } from '../../http'; import { SavedObjectsErrorHelpers } from '../../saved_objects'; @@ -50,7 +50,7 @@ export function registerSetManyRoute(router: IRouter) { }); } - if (error instanceof CannotOverrideError) { + if (error instanceof CannotOverrideError || error instanceof ValidationError) { return response.badRequest({ body: error }); } diff --git a/src/core/server/ui_settings/types.ts b/src/core/server/ui_settings/types.ts index f3eb1f5a6859c..076e1de4458d7 100644 --- a/src/core/server/ui_settings/types.ts +++ b/src/core/server/ui_settings/types.ts @@ -17,9 +17,10 @@ * under the License. */ import { SavedObjectsClientContract } from '../saved_objects/types'; -import { UiSettingsParams, UserProvidedValues } from '../../types'; +import { UiSettingsParams, UserProvidedValues, PublicUiSettingsParams } from '../../types'; export { UiSettingsParams, + PublicUiSettingsParams, StringValidationRegexString, StringValidationRegex, StringValidation, @@ -41,7 +42,7 @@ export interface IUiSettingsClient { /** * Returns registered uiSettings values {@link UiSettingsParams} */ - getRegistered: () => Readonly>; + getRegistered: () => Readonly>; /** * Retrieves uiSettings values set by the user with fallbacks to default values if not specified. */ diff --git a/src/core/server/ui_settings/ui_settings_client.test.ts b/src/core/server/ui_settings/ui_settings_client.test.ts index b8aa57291dccf..4ce33eed267a3 100644 --- a/src/core/server/ui_settings/ui_settings_client.test.ts +++ b/src/core/server/ui_settings/ui_settings_client.test.ts @@ -18,6 +18,7 @@ */ import Chance from 'chance'; +import { schema } from '@kbn/config-schema'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { createOrUpgradeSavedConfigMock } from './create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.mock'; @@ -145,6 +146,22 @@ describe('ui settings', () => { expect(error.message).toBe('Unable to update "foo" because it is overridden'); } }); + + it('validates value if a schema presents', async () => { + const defaults = { foo: { schema: schema.string() } }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await expect( + uiSettings.setMany({ + bar: 2, + foo: 1, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: [validation [foo]]: expected value of type [string] but got [number]]` + ); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(0); + }); }); describe('#set()', () => { @@ -163,6 +180,17 @@ describe('ui settings', () => { }); }); + it('validates value if a schema presents', async () => { + const defaults = { foo: { schema: schema.string() } }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await expect(uiSettings.set('foo', 1)).rejects.toMatchInlineSnapshot( + `[Error: [validation [foo]]: expected value of type [string] but got [number]]` + ); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(0); + }); + it('throws CannotOverrideError if the key is overridden', async () => { const { uiSettings } = setup({ overrides: { @@ -193,6 +221,20 @@ describe('ui settings', () => { expect(savedObjectsClient.update).toHaveBeenCalledWith(TYPE, ID, { one: null }); }); + it('does not fail validation', async () => { + const defaults = { + foo: { + schema: schema.string(), + value: '1', + }, + }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await uiSettings.remove('foo'); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(1); + }); + it('throws CannotOverrideError if the key is overridden', async () => { const { uiSettings } = setup({ overrides: { @@ -235,6 +277,20 @@ describe('ui settings', () => { }); }); + it('does not fail validation', async () => { + const defaults = { + foo: { + schema: schema.string(), + value: '1', + }, + }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await uiSettings.removeMany(['foo', 'bar']); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(1); + }); + it('throws CannotOverrideError if any key is overridden', async () => { const { uiSettings } = setup({ overrides: { @@ -256,7 +312,13 @@ describe('ui settings', () => { const value = chance.word(); const defaults = { key: { value } }; const { uiSettings } = setup({ defaults }); - expect(uiSettings.getRegistered()).toBe(defaults); + expect(uiSettings.getRegistered()).toEqual(defaults); + }); + it('does not leak validation schema outside', () => { + const value = chance.word(); + const defaults = { key: { value, schema: schema.string() } }; + const { uiSettings } = setup({ defaults }); + expect(uiSettings.getRegistered()).toStrictEqual({ key: { value } }); }); }); @@ -274,7 +336,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource }); const result = await uiSettings.getUserProvided(); - expect(result).toEqual({ + expect(result).toStrictEqual({ user: { userValue: 'customized', }, @@ -286,7 +348,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource }); const result = await uiSettings.getUserProvided(); - expect(result).toEqual({ + expect(result).toStrictEqual({ user: { userValue: 'customized', }, @@ -296,6 +358,32 @@ describe('ui settings', () => { }); }); + it('ignores user-configured value if it fails validation', async () => { + const esDocSource = { user: 'foo', id: 'bar' }; + const defaults = { + id: { + value: 42, + schema: schema.number(), + }, + }; + const { uiSettings } = setup({ esDocSource, defaults }); + const result = await uiSettings.getUserProvided(); + + expect(result).toStrictEqual({ + user: { + userValue: 'foo', + }, + }); + + expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + ], + ] + `); + }); + it('automatically creates the savedConfig if it is missing and returns empty object', async () => { const { uiSettings, savedObjectsClient, createOrUpgradeSavedConfig } = setup(); savedObjectsClient.get = jest @@ -303,7 +391,7 @@ describe('ui settings', () => { .mockRejectedValueOnce(SavedObjectsClient.errors.createGenericNotFoundError()) .mockResolvedValueOnce({ attributes: {} }); - expect(await uiSettings.getUserProvided()).toEqual({}); + expect(await uiSettings.getUserProvided()).toStrictEqual({}); expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); @@ -320,7 +408,7 @@ describe('ui settings', () => { SavedObjectsClient.errors.createGenericNotFoundError() ); - expect(await uiSettings.getUserProvided()).toEqual({ foo: { userValue: 'bar ' } }); + expect(await uiSettings.getUserProvided()).toStrictEqual({ foo: { userValue: 'bar ' } }); }); it('returns an empty object on Forbidden responses', async () => { @@ -329,7 +417,7 @@ describe('ui settings', () => { const error = SavedObjectsClient.errors.decorateForbiddenError(new Error()); savedObjectsClient.get.mockRejectedValue(error); - expect(await uiSettings.getUserProvided()).toEqual({}); + expect(await uiSettings.getUserProvided()).toStrictEqual({}); expect(createOrUpgradeSavedConfig).toHaveBeenCalledTimes(0); }); @@ -339,7 +427,7 @@ describe('ui settings', () => { const error = SavedObjectsClient.errors.decorateEsUnavailableError(new Error()); savedObjectsClient.get.mockRejectedValue(error); - expect(await uiSettings.getUserProvided()).toEqual({}); + expect(await uiSettings.getUserProvided()).toStrictEqual({}); expect(createOrUpgradeSavedConfig).toHaveBeenCalledTimes(0); }); @@ -382,7 +470,7 @@ describe('ui settings', () => { }; const { uiSettings } = setup({ esDocSource, overrides }); - expect(await uiSettings.getUserProvided()).toEqual({ + expect(await uiSettings.getUserProvided()).toStrictEqual({ user: { userValue: 'customized', }, @@ -404,15 +492,40 @@ describe('ui settings', () => { expect(savedObjectsClient.get).toHaveBeenCalledWith(TYPE, ID); }); - it(`returns defaults when es doc is empty`, async () => { + it('returns defaults when es doc is empty', async () => { const esDocSource = {}; const defaults = { foo: { value: 'bar' } }; const { uiSettings } = setup({ esDocSource, defaults }); - expect(await uiSettings.getAll()).toEqual({ + expect(await uiSettings.getAll()).toStrictEqual({ foo: 'bar', }); }); + it('ignores user-configured value if it fails validation', async () => { + const esDocSource = { user: 'foo', id: 'bar' }; + const defaults = { + id: { + value: 42, + schema: schema.number(), + }, + }; + const { uiSettings } = setup({ esDocSource, defaults }); + const result = await uiSettings.getAll(); + + expect(result).toStrictEqual({ + id: 42, + user: 'foo', + }); + + expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + ], + ] + `); + }); + it(`merges user values, including ones without defaults, into key value pairs`, async () => { const esDocSource = { foo: 'user-override', @@ -427,7 +540,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource, defaults }); - expect(await uiSettings.getAll()).toEqual({ + expect(await uiSettings.getAll()).toStrictEqual({ foo: 'user-override', bar: 'user-provided', }); @@ -451,7 +564,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource, defaults, overrides }); - expect(await uiSettings.getAll()).toEqual({ + expect(await uiSettings.getAll()).toStrictEqual({ foo: 'bax', bar: 'user-provided', }); @@ -518,6 +631,28 @@ describe('ui settings', () => { expect(await uiSettings.get('dateFormat')).toBe('foo'); }); + + it('returns the default value if user-configured value fails validation', async () => { + const esDocSource = { id: 'bar' }; + const defaults = { + id: { + value: 42, + schema: schema.number(), + }, + }; + + const { uiSettings } = setup({ esDocSource, defaults }); + + expect(await uiSettings.get('id')).toBe(42); + + expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + ], + ] + `); + }); }); describe('#isOverridden()', () => { diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts index a7e55d2b2da65..76c8284175f11 100644 --- a/src/core/server/ui_settings/ui_settings_client.ts +++ b/src/core/server/ui_settings/ui_settings_client.ts @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { defaultsDeep } from 'lodash'; +import { defaultsDeep, omit } from 'lodash'; import { SavedObjectsErrorHelpers } from '../saved_objects'; import { SavedObjectsClientContract } from '../saved_objects/types'; import { Logger } from '../logging'; import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config'; -import { IUiSettingsClient, UiSettingsParams } from './types'; +import { IUiSettingsClient, UiSettingsParams, PublicUiSettingsParams } from './types'; import { CannotOverrideError } from './ui_settings_errors'; export interface UiSettingsServiceOptions { @@ -40,14 +40,14 @@ interface ReadOptions { autoCreateOrUpgradeIfMissing?: boolean; } -interface UserProvidedValue { +interface UserProvidedValue { userValue?: T; isOverridden?: boolean; } type UiSettingsRawValue = UiSettingsParams & UserProvidedValue; -type UserProvided = Record>; +type UserProvided = Record>; type UiSettingsRaw = Record; export class UiSettingsClient implements IUiSettingsClient { @@ -72,7 +72,11 @@ export class UiSettingsClient implements IUiSettingsClient { } getRegistered() { - return this.defaults; + const copiedDefaults: Record = {}; + for (const [key, value] of Object.entries(this.defaults)) { + copiedDefaults[key] = omit(value, 'schema'); + } + return copiedDefaults; } async get(key: string): Promise { @@ -90,29 +94,21 @@ export class UiSettingsClient implements IUiSettingsClient { }, {} as Record); } - async getUserProvided(): Promise> { - const userProvided: UserProvided = {}; - - // write the userValue for each key stored in the saved object that is not overridden - for (const [key, userValue] of Object.entries(await this.read())) { - if (userValue !== null && !this.isOverridden(key)) { - userProvided[key] = { - userValue, - }; - } - } + async getUserProvided(): Promise> { + const userProvided: UserProvided = this.onReadHook(await this.read()); // write all overridden keys, dropping the userValue is override is null and // adding keys for overrides that are not in saved object - for (const [key, userValue] of Object.entries(this.overrides)) { + for (const [key, value] of Object.entries(this.overrides)) { userProvided[key] = - userValue === null ? { isOverridden: true } : { isOverridden: true, userValue }; + value === null ? { isOverridden: true } : { isOverridden: true, userValue: value }; } return userProvided; } async setMany(changes: Record) { + this.onWriteHook(changes); await this.write({ changes }); } @@ -147,6 +143,43 @@ export class UiSettingsClient implements IUiSettingsClient { return defaultsDeep(userProvided, this.defaults); } + private validateKey(key: string, value: unknown) { + const definition = this.defaults[key]; + if (value === null || definition === undefined) return; + if (definition.schema) { + definition.schema.validate(value, {}, `validation [${key}]`); + } + } + + private onWriteHook(changes: Record) { + for (const key of Object.keys(changes)) { + this.assertUpdateAllowed(key); + } + + for (const [key, value] of Object.entries(changes)) { + this.validateKey(key, value); + } + } + + private onReadHook(values: Record) { + // write the userValue for each key stored in the saved object that is not overridden + // validate value read from saved objects as it can be changed via SO API + const filteredValues: UserProvided = {}; + for (const [key, userValue] of Object.entries(values)) { + if (userValue === null || this.isOverridden(key)) continue; + try { + this.validateKey(key, userValue); + filteredValues[key] = { + userValue: userValue as T, + }; + } catch (error) { + this.log.warn(`Ignore invalid UiSettings value. ${error}.`); + } + } + + return filteredValues; + } + private async write({ changes, autoCreateOrUpgradeIfMissing = true, @@ -154,10 +187,6 @@ export class UiSettingsClient implements IUiSettingsClient { changes: Record; autoCreateOrUpgradeIfMissing?: boolean; }) { - for (const key of Object.keys(changes)) { - this.assertUpdateAllowed(key); - } - try { await this.savedObjectsClient.update(this.type, this.id, changes); } catch (error) { diff --git a/src/core/server/ui_settings/ui_settings_service.test.ts b/src/core/server/ui_settings/ui_settings_service.test.ts index 11766713b3be0..08400f56ad281 100644 --- a/src/core/server/ui_settings/ui_settings_service.test.ts +++ b/src/core/server/ui_settings/ui_settings_service.test.ts @@ -17,6 +17,8 @@ * under the License. */ import { BehaviorSubject } from 'rxjs'; +import { schema } from '@kbn/config-schema'; + import { MockUiSettingsClientConstructor } from './ui_settings_service.test.mock'; import { UiSettingsService, SetupDeps } from './ui_settings_service'; import { httpServiceMock } from '../http/http_service.mock'; @@ -35,6 +37,7 @@ const defaults = { value: 'bar', category: [], description: '', + schema: schema.string(), }, }; @@ -104,6 +107,45 @@ describe('uiSettings', () => { }); describe('#start', () => { + describe('validation', () => { + it('validates registered definitions', async () => { + const { register } = await service.setup(setupDeps); + register({ + custom: { + value: 42, + schema: schema.string(), + }, + }); + + await expect(service.start()).rejects.toMatchInlineSnapshot( + `[Error: [ui settings defaults [custom]]: expected value of type [string] but got [number]]` + ); + }); + + it('validates overrides', async () => { + const coreContext = mockCoreContext.create(); + coreContext.configService.atPath.mockReturnValueOnce( + new BehaviorSubject({ + overrides: { + custom: 42, + }, + }) + ); + const customizedService = new UiSettingsService(coreContext); + const { register } = await customizedService.setup(setupDeps); + register({ + custom: { + value: '42', + schema: schema.string(), + }, + }); + + await expect(customizedService.start()).rejects.toMatchInlineSnapshot( + `[Error: [ui settings overrides [custom]]: expected value of type [string] but got [number]]` + ); + }); + }); + describe('#asScopedToClient', () => { it('passes saved object type "config" to UiSettingsClient', async () => { await service.setup(setupDeps); diff --git a/src/core/server/ui_settings/ui_settings_service.ts b/src/core/server/ui_settings/ui_settings_service.ts index de2cc9d510e0c..83e66cf6dd06d 100644 --- a/src/core/server/ui_settings/ui_settings_service.ts +++ b/src/core/server/ui_settings/ui_settings_service.ts @@ -70,6 +70,9 @@ export class UiSettingsService } public async start(): Promise { + this.validatesDefinitions(); + this.validatesOverrides(); + return { asScopedToClient: this.getScopedClientFactory(), }; @@ -101,4 +104,21 @@ export class UiSettingsService this.uiSettingsDefaults.set(key, value); }); } + + private validatesDefinitions() { + for (const [key, definition] of this.uiSettingsDefaults) { + if (definition.schema) { + definition.schema.validate(definition.value, {}, `ui settings defaults [${key}]`); + } + } + } + + private validatesOverrides() { + for (const [key, value] of Object.entries(this.overrides)) { + const definition = this.uiSettingsDefaults.get(key); + if (definition?.schema) { + definition.schema.validate(value, {}, `ui settings overrides [${key}]`); + } + } + } } diff --git a/src/core/types/ui_settings.ts b/src/core/types/ui_settings.ts index eccd3f9616af0..ed1076b571960 100644 --- a/src/core/types/ui_settings.ts +++ b/src/core/types/ui_settings.ts @@ -16,8 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - -import { SavedObjectAttribute } from './saved_objects'; +import { Type } from '@kbn/config-schema'; /** * UI element type to represent the settings. @@ -49,11 +48,11 @@ export interface DeprecationSettings { * UiSettings parameters defined by the plugins. * @public * */ -export interface UiSettingsParams { +export interface UiSettingsParams { /** title in the UI */ name?: string; /** default value to fall back to if a user doesn't provide any */ - value?: SavedObjectAttribute; + value?: T; /** description provided to a user in UI */ description?: string; /** used to group the configured setting in the UI */ @@ -73,10 +72,22 @@ export interface UiSettingsParams { /* * Allows defining a custom validation applicable to value change on the client. * @deprecated + * Use schema instead. */ validation?: ImageValidation | StringValidation; + /* + * Value validation schema + * Used to validate value on write and read. + */ + schema: Type; } +/** + * A sub-set of {@link UiSettingsParams} exposed to the client-side. + * @public + * */ +export type PublicUiSettingsParams = Omit; + /** * Allows regex objects or a regex string * @public diff --git a/src/core/utils/url.test.ts b/src/core/utils/url.test.ts index 3c35ba44455bc..419c0cda2b8cb 100644 --- a/src/core/utils/url.test.ts +++ b/src/core/utils/url.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { modifyUrl } from './url'; +import { modifyUrl, isRelativeUrl } from './url'; describe('modifyUrl()', () => { test('throws an error with invalid input', () => { @@ -69,3 +69,17 @@ describe('modifyUrl()', () => { ).toEqual('mail:localhost'); }); }); + +describe('isRelativeUrl()', () => { + test('returns "true" for a relative URL', () => { + expect(isRelativeUrl('good')).toBe(true); + expect(isRelativeUrl('/good')).toBe(true); + expect(isRelativeUrl('/good/even/better')).toBe(true); + }); + test('returns "false" for a non-relative URL', () => { + expect(isRelativeUrl('http://evil.com')).toBe(false); + expect(isRelativeUrl('//evil.com')).toBe(false); + expect(isRelativeUrl('///evil.com')).toBe(false); + expect(isRelativeUrl(' //evil.com')).toBe(false); + }); +}); diff --git a/src/core/utils/url.ts b/src/core/utils/url.ts index 31de7e1814038..c2bf80ce3f86f 100644 --- a/src/core/utils/url.ts +++ b/src/core/utils/url.ts @@ -99,3 +99,19 @@ export function modifyUrl( slashes: modifiedParts.slashes, } as UrlObject); } + +export function isRelativeUrl(candidatePath: string) { + // validate that `candidatePath` is not attempting a redirect to somewhere + // outside of this Kibana install + const all = parseUrl(candidatePath, false /* parseQueryString */, true /* slashesDenoteHost */); + const { protocol, hostname, port } = all; + // We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not + // detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but + // browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser + // hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`) + // and the first slash that belongs to path. + if (protocol !== null || hostname !== null || port !== null) { + return false; + } + return true; +} diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index c0628b72c2ce7..85b1956f45333 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -16,11 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - import moment from 'moment-timezone'; import numeralLanguages from '@elastic/numeral/languages'; import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + import { DEFAULT_QUERY_LANGUAGE } from '../../../plugins/data/common'; +import { isRelativeUrl } from '../../../core/utils'; export function getUiSettingDefaults() { const weekdays = moment.weekdays().slice(); @@ -67,17 +69,23 @@ export function getUiSettingDefaults() { defaultMessage: 'Default route', }), value: '/app/kibana', - validation: { - regexString: '^/', - message: i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage', { - defaultMessage: 'The route must start with a slash ("/")', - }), - }, + schema: schema.string({ + validate(value) { + if (!value.startsWith('/') || !isRelativeUrl(value)) { + return i18n.translate( + 'kbn.advancedSettings.defaultRoute.defaultRouteIsRelativeValidationMessage', + { + defaultMessage: 'Must be a relative URL.', + } + ); + } + }, + }), description: i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteText', { defaultMessage: 'This setting specifies the default route when opening Kibana. ' + 'You can use this setting to modify the landing page when opening Kibana. ' + - 'The route must start with a slash ("/").', + 'The route must be a relative URL.', }), }, 'query:queryString:options': { diff --git a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js index 57adf730f3dd9..3e3dc284671da 100644 --- a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js +++ b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import { Type } from '@kbn/config-schema'; import pkg from '../../../../package.json'; export const createTestEntryTemplate = defaultUiSettings => bundle => ` @@ -87,7 +87,14 @@ const coreSystem = new CoreSystem({ buildNum: 1234, devMode: true, uiSettings: { - defaults: ${JSON.stringify(defaultUiSettings, null, 2) + defaults: ${JSON.stringify( + defaultUiSettings, + (key, value) => { + if (value instanceof Type) return null; + return value; + }, + 2 + ) .split('\n') .join('\n ')}, user: {} diff --git a/src/legacy/server/http/index.js b/src/legacy/server/http/index.js index 265d71e95b301..d616afb533d0a 100644 --- a/src/legacy/server/http/index.js +++ b/src/legacy/server/http/index.js @@ -24,15 +24,12 @@ import Boom from 'boom'; import { registerHapiPlugins } from './register_hapi_plugins'; import { setupBasePathProvider } from './setup_base_path_provider'; -import { setupDefaultRouteProvider } from './setup_default_route_provider'; export default async function(kbnServer, server, config) { server = kbnServer.server; setupBasePathProvider(kbnServer); - setupDefaultRouteProvider(server); - await registerHapiPlugins(server); // provide a simple way to expose static directories @@ -60,14 +57,6 @@ export default async function(kbnServer, server, config) { }); }); - server.route({ - path: '/', - method: 'GET', - async handler(req, h) { - return h.redirect(await req.getDefaultRoute()); - }, - }); - server.route({ method: 'GET', path: '/{p*}', diff --git a/src/legacy/server/http/integration_tests/default_route_provider.test.ts b/src/legacy/server/http/integration_tests/default_route_provider.test.ts deleted file mode 100644 index d91438d904558..0000000000000 --- a/src/legacy/server/http/integration_tests/default_route_provider.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - */ -jest.mock('../../../ui/ui_settings/ui_settings_mixin', () => { - return jest.fn(); -}); - -import * as kbnTestServer from '../../../../test_utils/kbn_server'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Root } from '../../../../core/server/root'; - -let mockDefaultRouteSetting: any = ''; - -describe('default route provider', () => { - let root: Root; - beforeAll(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); - - await root.setup(); - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - - kbnServer.server.decorate('request', 'getUiSettingsService', function() { - return { - get: (key: string) => { - if (key === 'defaultRoute') { - return Promise.resolve(mockDefaultRouteSetting); - } - throw Error(`unsupported ui setting: ${key}`); - }, - getRegistered: () => { - return { - defaultRoute: { - value: '/app/kibana', - }, - }; - }, - }; - }); - }, 30000); - - afterAll(async () => await root.shutdown()); - - it('redirects to the configured default route', async function() { - mockDefaultRouteSetting = '/app/some/default/route'; - - const { status, header } = await kbnTestServer.request.get(root, '/'); - expect(status).toEqual(302); - expect(header).toMatchObject({ - location: '/app/some/default/route', - }); - }); - - const invalidRoutes = [ - 'http://not-your-kibana.com', - '///example.com', - '//example.com', - ' //example.com', - ]; - for (const route of invalidRoutes) { - it(`falls back to /app/kibana when the configured route (${route}) is not a valid relative path`, async function() { - mockDefaultRouteSetting = route; - - const { status, header } = await kbnTestServer.request.get(root, '/'); - expect(status).toEqual(302); - expect(header).toMatchObject({ - location: '/app/kibana', - }); - }); - } -}); diff --git a/src/legacy/server/http/integration_tests/default_route_provider_config.test.ts b/src/legacy/server/http/integration_tests/default_route_provider_config.test.ts deleted file mode 100644 index 8365941cbeb10..0000000000000 --- a/src/legacy/server/http/integration_tests/default_route_provider_config.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 * as kbnTestServer from '../../../../test_utils/kbn_server'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Root } from '../../../../core/server/root'; - -describe('default route provider', () => { - let root: Root; - - afterEach(async () => await root.shutdown()); - - it('redirects to the configured default route', async function() { - root = kbnTestServer.createRoot({ - server: { - defaultRoute: '/app/some/default/route', - }, - migrations: { skip: true }, - }); - - await root.setup(); - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - - kbnServer.server.decorate('request', 'getSavedObjectsClient', function() { - return { - get: (type: string, id: string) => ({ attributes: {} }), - }; - }); - - const { status, header } = await kbnTestServer.request.get(root, '/'); - - expect(status).toEqual(302); - expect(header).toMatchObject({ - location: '/app/some/default/route', - }); - }); -}); diff --git a/src/legacy/server/http/setup_default_route_provider.ts b/src/legacy/server/http/setup_default_route_provider.ts deleted file mode 100644 index 9a580dd1c59bd..0000000000000 --- a/src/legacy/server/http/setup_default_route_provider.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 { Legacy } from 'kibana'; -import { parse } from 'url'; - -export function setupDefaultRouteProvider(server: Legacy.Server) { - server.decorate('request', 'getDefaultRoute', async function() { - // @ts-ignore - const request: Legacy.Request = this; - - const serverBasePath: string = server.config().get('server.basePath'); - - const uiSettings = request.getUiSettingsService(); - - const defaultRoute = await uiSettings.get('defaultRoute'); - const qualifiedDefaultRoute = `${request.getBasePath()}${defaultRoute}`; - - if (isRelativePath(qualifiedDefaultRoute, serverBasePath)) { - return qualifiedDefaultRoute; - } else { - server.log( - ['http', 'warn'], - `Ignoring configured default route of '${defaultRoute}', as it is malformed.` - ); - - const fallbackRoute = uiSettings.getRegistered().defaultRoute.value; - - const qualifiedFallbackRoute = `${request.getBasePath()}${fallbackRoute}`; - return qualifiedFallbackRoute; - } - }); - - function isRelativePath(candidatePath: string, basePath = '') { - // validate that `candidatePath` is not attempting a redirect to somewhere - // outside of this Kibana install - const { protocol, hostname, port, pathname } = parse( - candidatePath, - false /* parseQueryString */, - true /* slashesDenoteHost */ - ); - - // We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not - // detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but - // browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser - // hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`) - // and the first slash that belongs to path. - if (protocol !== null || hostname !== null || port !== null) { - return false; - } - - if (!String(pathname).startsWith(basePath)) { - return false; - } - - return true; - } -} diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 68b5a63871372..9952b345fa06f 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -92,7 +92,6 @@ declare module 'hapi' { interface Request { getSavedObjectsClient(options?: SavedObjectsClientProviderOptions): SavedObjectsClientContract; getBasePath(): string; - getDefaultRoute(): Promise; getUiSettingsService(): IUiSettingsClient; } diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx index 7a2ab648ec258..6103041cf0a4c 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx @@ -22,7 +22,11 @@ import { Observable } from 'rxjs'; import { ReactWrapper } from 'enzyme'; import { mountWithI18nProvider } from 'test_utils/enzyme_helpers'; import dedent from 'dedent'; -import { UiSettingsParams, UserProvidedValues, UiSettingsType } from '../../../../core/public'; +import { + PublicUiSettingsParams, + UserProvidedValues, + UiSettingsType, +} from '../../../../core/public'; import { FieldSetting } from './types'; import { AdvancedSettingsComponent } from './advanced_settings'; import { notificationServiceMock, docLinksServiceMock } from '../../../../core/public/mocks'; @@ -68,7 +72,7 @@ function mockConfig() { remove: (key: string) => Promise.resolve(true), isCustom: (key: string) => false, isOverridden: (key: string) => Boolean(config.getAll()[key].isOverridden), - getRegistered: () => ({} as Readonly>), + getRegistered: () => ({} as Readonly>), overrideLocalDefault: (key: string, value: any) => {}, getUpdate$: () => new Observable<{ @@ -89,7 +93,7 @@ function mockConfig() { getUpdateErrors$: () => new Observable(), get: (key: string, defaultOverride?: any): any => config.getAll()[key] || defaultOverride, get$: (key: string) => new Observable(config.get(key)), - getAll: (): Readonly> => { + getAll: (): Readonly> => { return { 'test:array:setting': { ...defaultConfig, diff --git a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts index 881a2eb003cc8..7ac9b281eb99a 100644 --- a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts +++ b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { UiSettingsParams, StringValidationRegex } from 'src/core/public'; +import { PublicUiSettingsParams, StringValidationRegex } from 'src/core/public'; import expect from '@kbn/expect'; import { toEditableConfig } from './to_editable_config'; @@ -30,7 +30,7 @@ function invoke({ name = 'woah', value = 'forreal', }: { - def?: UiSettingsParams & { isOverridden?: boolean }; + def?: PublicUiSettingsParams & { isOverridden?: boolean }; name?: string; value?: any; }) { @@ -55,7 +55,7 @@ describe('Settings', function() { }); describe('when given a setting definition object', function() { - let def: UiSettingsParams & { isOverridden?: boolean }; + let def: PublicUiSettingsParams & { isOverridden?: boolean }; beforeEach(function() { def = { value: 'the original', diff --git a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts index 2c27d72f7f645..406bc35f826e8 100644 --- a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts +++ b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts @@ -18,7 +18,7 @@ */ import { - UiSettingsParams, + PublicUiSettingsParams, UserProvidedValues, StringValidationRegexString, SavedObjectAttribute, @@ -40,7 +40,7 @@ export function toEditableConfig({ isCustom, isOverridden, }: { - def: UiSettingsParams & UserProvidedValues; + def: PublicUiSettingsParams & UserProvidedValues; name: string; value: SavedObjectAttribute; isCustom: boolean; diff --git a/src/plugins/advanced_settings/public/management_app/types.ts b/src/plugins/advanced_settings/public/management_app/types.ts index d44a05ce36f5d..ee9b9b0535b79 100644 --- a/src/plugins/advanced_settings/public/management_app/types.ts +++ b/src/plugins/advanced_settings/public/management_app/types.ts @@ -17,17 +17,12 @@ * under the License. */ -import { - UiSettingsType, - StringValidation, - ImageValidation, - SavedObjectAttribute, -} from '../../../../core/public'; +import { UiSettingsType, StringValidation, ImageValidation } from '../../../../core/public'; export interface FieldSetting { displayName: string; name: string; - value: SavedObjectAttribute; + value: unknown; description?: string; options?: string[]; optionLabels?: Record; @@ -36,7 +31,7 @@ export interface FieldSetting { category: string[]; ariaName: string; isOverridden: boolean; - defVal: SavedObjectAttribute; + defVal: unknown; isCustom: boolean; validation?: StringValidation | ImageValidation; readOnly?: boolean; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 333b13eedc17a..783411bbf27e2 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -38,6 +38,7 @@ import { Observable } from 'rxjs'; import { Plugin as Plugin_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import { PopoverAnchorPosition } from '@elastic/eui'; +import { PublicUiSettingsParams } from 'src/core/server/types'; import React from 'react'; import * as React_2 from 'react'; import { Required } from '@kbn/utility-types'; @@ -49,7 +50,6 @@ import { SearchResponse as SearchResponse_2 } from 'elasticsearch'; import { SimpleSavedObject } from 'src/core/public'; import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; -import { UiSettingsParams } from 'src/core/server/types'; import { Unit } from '@elastic/datemath'; import { UnregisterCallback } from 'history'; import { UserProvidedValues } from 'src/core/server/types'; diff --git a/src/test_utils/kbn_server.ts b/src/test_utils/kbn_server.ts index f4c3ecd8243ce..12f7eb5a0a043 100644 --- a/src/test_utils/kbn_server.ts +++ b/src/test_utils/kbn_server.ts @@ -50,6 +50,7 @@ const DEFAULTS_SETTINGS = { logging: { silent: true }, plugins: {}, optimize: { enabled: false }, + migrations: { skip: true }, }; const DEFAULT_SETTINGS_WITH_CORE_PLUGINS = { diff --git a/test/api_integration/apis/index.js b/test/api_integration/apis/index.js index 8cdbbf8e74a3d..57e9120773f33 100644 --- a/test/api_integration/apis/index.js +++ b/test/api_integration/apis/index.js @@ -33,6 +33,5 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./status')); loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./ui_metric')); - loadTestFile(require.resolve('./core')); }); } diff --git a/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts b/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts index c32e8a75d95da..3801d3bbce055 100644 --- a/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import { schema } from '@kbn/config-schema'; import { Plugin, CoreSetup } from 'kibana/server'; export class UiSettingsPlugin implements Plugin { @@ -27,6 +27,7 @@ export class UiSettingsPlugin implements Plugin { description: 'just for testing', value: '2', category: ['any'], + schema: schema.string(), }, }); diff --git a/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts b/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts index 337faa2a18fb6..60ae3a1fa77bb 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts @@ -14,7 +14,13 @@ export function initEnterSpaceView(server: Legacy.Server) { path: ENTER_SPACE_PATH, async handler(request, h) { try { - return h.redirect(await request.getDefaultRoute()); + const uiSettings = request.getUiSettingsService(); + const defaultRoute = await uiSettings.get('defaultRoute'); + + const basePath = server.newPlatform.setup.core.http.basePath.get(request); + const url = `${basePath}${defaultRoute}`; + + return h.redirect(url); } catch (e) { server.log(['spaces', 'error'], `Error navigating to space: ${e}`); return wrapError(e); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fb0eb6e4bf804..3763020a0b692 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -833,7 +833,6 @@ "kbn.advancedSettings.defaultIndexTitle": "デフォルトのインデックス", "kbn.advancedSettings.defaultRoute.defaultRouteText": "この設定は、Kibana 起動時のデフォルトのルートを設定します。この設定で、Kibana 起動時のランディングページを変更できます。ルートはスラッシュ (\"/\") で始まる必要があります。", "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "デフォルトのルート", - "kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage": "ルートはスラッシュ (\"/\") で始まる必要があります。", "kbn.advancedSettings.disableAnimationsText": "Kibana UI の不要なアニメーションをオフにします。変更を適用するにはページを更新してください。", "kbn.advancedSettings.disableAnimationsTitle": "アニメーションを無効にする", "kbn.advancedSettings.discover.aggsTermsSizeText": "「可視化」ボタンをクリックした際に、フィールドドロップダウンやディスカバリサイドバーに可視化される用語の数を設定します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0a9c82afaec1d..0477470a4b8a1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -833,7 +833,6 @@ "kbn.advancedSettings.defaultIndexTitle": "默认索引", "kbn.advancedSettings.defaultRoute.defaultRouteText": "此设置指定打开 Kibana 时的默认路由。您可以使用此设置修改打开 Kibana 时的登陆页面。路由必须以正斜杠(“/”)开头。", "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "默认路由", - "kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage": "路由必须以正斜杠(“/”)开头", "kbn.advancedSettings.disableAnimationsText": "在 Kibana UI 中关闭所有没有必要的动画。刷新页面以应用更改。", "kbn.advancedSettings.disableAnimationsTitle": "禁用动画", "kbn.advancedSettings.discover.aggsTermsSizeText": "确定在单击“可视化”按钮时将在发现侧边栏的字段下拉列表中可视化多少个词。", diff --git a/x-pack/test/functional/apps/spaces/enter_space.ts b/x-pack/test/functional/apps/spaces/enter_space.ts index e0b1ec544d460..38220c15cb266 100644 --- a/x-pack/test/functional/apps/spaces/enter_space.ts +++ b/x-pack/test/functional/apps/spaces/enter_space.ts @@ -10,7 +10,6 @@ export default function enterSpaceFunctonalTests({ getPageObjects, }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['security', 'spaceSelector']); describe('Enter Space', function() { @@ -25,8 +24,8 @@ export default function enterSpaceFunctonalTests({ await PageObjects.security.forceLogout(); }); - it('allows user to navigate to different spaces, respecting the configured default route', async () => { - const spaceId = 'another-space'; + it('falls back to the default home page when the configured default route is malformed', async () => { + const spaceId = 'default'; await PageObjects.security.login(null, null, { expectSpaceSelector: true, @@ -34,22 +33,11 @@ export default function enterSpaceFunctonalTests({ await PageObjects.spaceSelector.clickSpaceCard(spaceId); - await PageObjects.spaceSelector.expectRoute(spaceId, '/app/kibana/#/dashboard'); - - await PageObjects.spaceSelector.openSpacesNav(); - - // change spaces - - await PageObjects.spaceSelector.clickSpaceAvatar('default'); - - await PageObjects.spaceSelector.expectRoute('default', '/app/canvas'); + await PageObjects.spaceSelector.expectHomePage(spaceId); }); - it('falls back to the default home page when the configured default route is malformed', async () => { - await kibanaServer.uiSettings.replace({ defaultRoute: 'http://example.com/evil' }); - - // This test only works with the default space, as other spaces have an enforced relative url of `${serverBasePath}/s/space-id/${defaultRoute}` - const spaceId = 'default'; + it('allows user to navigate to different spaces, respecting the configured default route', async () => { + const spaceId = 'another-space'; await PageObjects.security.login(null, null, { expectSpaceSelector: true, @@ -57,7 +45,15 @@ export default function enterSpaceFunctonalTests({ await PageObjects.spaceSelector.clickSpaceCard(spaceId); - await PageObjects.spaceSelector.expectHomePage(spaceId); + await PageObjects.spaceSelector.expectRoute(spaceId, '/app/canvas'); + + await PageObjects.spaceSelector.openSpacesNav(); + + // change spaces + const newSpaceId = 'default'; + await PageObjects.spaceSelector.clickSpaceAvatar(newSpaceId); + + await PageObjects.spaceSelector.expectHomePage(newSpaceId); }); }); } diff --git a/x-pack/test/functional/es_archives/spaces/enter_space/data.json b/x-pack/test/functional/es_archives/spaces/enter_space/data.json index 462a2a1ee38fe..475fc14e96e6a 100644 --- a/x-pack/test/functional/es_archives/spaces/enter_space/data.json +++ b/x-pack/test/functional/es_archives/spaces/enter_space/data.json @@ -7,7 +7,7 @@ "config": { "buildNum": 8467, "dateFormat:tz": "UTC", - "defaultRoute": "/app/canvas" + "defaultRoute": "http://example.com/evil" }, "type": "config" } @@ -24,7 +24,7 @@ "config": { "buildNum": 8467, "dateFormat:tz": "UTC", - "defaultRoute": "/app/kibana/#dashboard" + "defaultRoute": "/app/canvas" }, "type": "config" } From 1f8e938b9c9b996e438df8cae11da62c59ec743b Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 16 Mar 2020 14:38:31 +0100 Subject: [PATCH 08/76] [Searchprofiler] Spacing between rendered shards (#60238) * Added unique key and some spacing to rendered shards * Give key to React.Fragment --- .../components/profile_tree/profile_tree.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx b/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx index 87a73cdefba31..1dec8f0161c52 100644 --- a/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx +++ b/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo } from 'react'; +import React, { memo, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { IndexDetails } from './index_details'; @@ -53,13 +53,11 @@ export const ProfileTree = memo(({ data, target, onHighlight }: Props) => { - {index.shards.map(shard => ( - + {index.shards.map((shard, idx) => ( + + + {idx < index.shards.length - 1 ? : undefined} + ))} From 6598298d016f6e7ca4feab4cd74f734fbe9c404f Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 16 Mar 2020 15:13:03 +0100 Subject: [PATCH 09/76] skips 'config_open.ts' files from linter check (#60248) --- .eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.js b/.eslintrc.js index 7af162f349b5c..3d6a5c262c453 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -322,6 +322,7 @@ module.exports = { 'x-pack/test/functional/apps/**/*.js', 'x-pack/legacy/plugins/apm/**/*.js', 'test/*/config.ts', + 'test/*/config_open.ts', 'test/*/{tests,test_suites,apis,apps}/**/*', 'test/visual_regression/tests/**/*', 'x-pack/test/*/{tests,test_suites,apis,apps}/**/*', From 56006534af1ccb7437e7ae0d60204760dd4b2110 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Mon, 16 Mar 2020 09:58:16 -0500 Subject: [PATCH 10/76] [DOCS] Removed below references (#60159) --- docs/apm/advanced-queries.asciidoc | 2 +- docs/apm/spans.asciidoc | 6 +- docs/apm/transactions.asciidoc | 8 +- .../canvas/canvas-tinymath-functions.asciidoc | 207 +++++++++--------- .../searchprofiler/more-complicated.asciidoc | 4 +- .../development-functional-tests.asciidoc | 4 +- ...pment-plugin-feature-registration.asciidoc | 4 +- .../development-plugin-localization.asciidoc | 2 +- docs/discover/kuery.asciidoc | 26 +-- docs/discover/search.asciidoc | 15 +- docs/epm/index.asciidoc | 10 +- docs/management/numeral.asciidoc | 2 +- .../create_and_manage_rollups.asciidoc | 10 +- docs/maps/geojson-upload.asciidoc | 2 +- .../indexing-geojson-data-tutorial.asciidoc | 4 +- docs/maps/vector-style.asciidoc | 8 +- docs/migration/migrate_8_0.asciidoc | 14 +- docs/settings/apm-settings.asciidoc | 2 +- docs/settings/ssl-settings.asciidoc | 6 +- docs/uptime-guide/install.asciidoc | 2 +- docs/uptime-guide/security.asciidoc | 5 +- docs/user/graph/getting-started.asciidoc | 68 +++--- docs/user/monitoring/beats-details.asciidoc | 2 +- docs/user/security/rbac_tutorial.asciidoc | 47 ++-- docs/visualize/vega.asciidoc | 2 +- 25 files changed, 227 insertions(+), 235 deletions(-) diff --git a/docs/apm/advanced-queries.asciidoc b/docs/apm/advanced-queries.asciidoc index ed77ebb4c4930..add6f601489e1 100644 --- a/docs/apm/advanced-queries.asciidoc +++ b/docs/apm/advanced-queries.asciidoc @@ -5,7 +5,7 @@ When querying in the APM app, you're simply searching and selecting data from fi Queries entered into the query bar are also added as parameters to the URL, so it's easy to share a specific query or view with others. -In the screenshot below, you can begin to see some of the transaction fields available for filtering on: +You can begin to see some of the transaction fields available for filtering: [role="screenshot"] image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM app in Kibana] diff --git a/docs/apm/spans.asciidoc b/docs/apm/spans.asciidoc index b1d54ce49c7cd..b09de576f2d4a 100644 --- a/docs/apm/spans.asciidoc +++ b/docs/apm/spans.asciidoc @@ -12,12 +12,12 @@ This makes it useful for visualizing where the selected transaction spent most o image::apm/images/apm-transaction-sample.png[Example of distributed trace colors in the APM app in Kibana] View a span in detail by clicking on it in the timeline waterfall. -For example, in the below screenshot we've clicked on an SQL Select database query. -The information displayed includes the actual SQL that was executed, how long it took, +When you click on an SQL Select database query, +the information displayed includes the actual SQL that was executed, how long it took, and the percentage of the trace's total time. You also get a stack trace, which shows the SQL query in your code. Finally, APM knows which files are your code and which are just modules or libraries that you've installed. -These library frames will be minimized by default in order to show you the most relevant stack trace. +These library frames will be minimized by default in order to show you the most relevant stack trace. [role="screenshot"] image::apm/images/apm-span-detail.png[Example view of a span detail in the APM app in Kibana] diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc index 9c21a569f152c..536ab2ec29c80 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -50,7 +50,7 @@ If there's a particular endpoint you're worried about, you can click on it to vi [IMPORTANT] ==== If you only see one route in the Transactions table, or if you have transactions named "unknown route", -it could be a symptom that the agent either wasn't installed correctly or doesn't support your framework. +it could be a symptom that the agent either wasn't installed correctly or doesn't support your framework. For further details, including troubleshooting and custom implementation instructions, refer to the documentation for each {apm-agents-ref}[APM Agent] you've implemented. @@ -103,9 +103,7 @@ The number of requests per bucket is displayed when hovering over the graph, and [role="screenshot"] image::apm/images/apm-transaction-duration-dist.png[Example view of transactions duration distribution graph] -Let's look at an example. -In the screenshot below, -you'll notice most of the requests fall into buckets on the left side of the graph, +Most of the requests fall into buckets on the left side of the graph, with a long tail of smaller buckets to the right. This is a typical distribution, and indicates most of our requests were served quickly - awesome! It's the requests on the right, the ones taking longer than average, that we probably want to focus on. @@ -133,4 +131,4 @@ For a particular transaction sample, we can get even more information in the *me * Custom - You can configure your agent to add custom contextual information on transactions. TIP: All of this data is stored in documents in Elasticsearch. -This means you can select "Actions - View sample document" to see the actual Elasticsearch document under the discover tab. \ No newline at end of file +This means you can select "Actions - View sample document" to see the actual Elasticsearch document under the discover tab. diff --git a/docs/canvas/canvas-tinymath-functions.asciidoc b/docs/canvas/canvas-tinymath-functions.asciidoc index 8c9f445b052a3..73808fc6625d1 100644 --- a/docs/canvas/canvas-tinymath-functions.asciidoc +++ b/docs/canvas/canvas-tinymath-functions.asciidoc @@ -3,21 +3,21 @@ === TinyMath functions TinyMath provides a set of functions that can be used with the Canvas expression -language to perform complex math calculations. Read on for detailed information about -the functions available in TinyMath, including what parameters each function accepts, +language to perform complex math calculations. Read on for detailed information about +the functions available in TinyMath, including what parameters each function accepts, the return value of that function, and examples of how each function behaves. -Most of the functions below accept arrays and apply JavaScript Math methods to -each element of that array. For the functions that accept multiple arrays as -parameters, the function generally does the calculation index by index. +Most of the functions accept arrays and apply JavaScript Math methods to +each element of that array. For the functions that accept multiple arrays as +parameters, the function generally does the calculation index by index. -Any function below can be wrapped by another function as long as the return type +Any function can be wrapped by another function as long as the return type of the inner function matches the acceptable parameter type of the outer function. [float] === abs( a ) -Calculates the absolute value of a number. For arrays, the function will be +Calculates the absolute value of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -29,7 +29,7 @@ applied index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The absolute value of `a`. Returns +*Returns*: `number` | `Array.`. The absolute value of `a`. Returns an array with the absolute values of each element if `a` is an array. *Example* @@ -43,7 +43,7 @@ abs([-1 , -2, 3, -4]) // returns [1, 2, 3, 4] [float] === add( ...args ) -Calculates the sum of one or more numbers/arrays passed into the function. If at +Calculates the sum of one or more numbers/arrays passed into the function. If at least one array of numbers is passed into the function, the function will calculate the sum by index. [cols="3*^<"] @@ -55,9 +55,9 @@ least one array of numbers is passed into the function, the function will calcul |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The sum of all numbers in `args` if `args` -contains only numbers. Returns an array of sums of the elements at each index, -including all scalar numbers in `args` in the calculation at each index if `args` +*Returns*: `number` | `Array.`. The sum of all numbers in `args` if `args` +contains only numbers. Returns an array of sums of the elements at each index, +including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Throws*: `'Array length mismatch'` if `args` contains arrays of different lengths @@ -73,7 +73,7 @@ add([1, 2], 3, [4, 5], 6) // returns [(1 + 3 + 4 + 6), (2 + 3 + 5 + 6)] = [14, 1 [float] === cbrt( a ) -Calculates the cube root of a number. For arrays, the function will be applied +Calculates the cube root of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -85,7 +85,7 @@ index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The cube root of `a`. Returns an array with +*Returns*: `number` | `Array.`. The cube root of `a`. Returns an array with the cube roots of each element if `a` is an array. *Example* @@ -99,7 +99,7 @@ cbrt([27, 64, 125]) // returns [3, 4, 5] [float] === ceil( a ) -Calculates the ceiling of a number, i.e., rounds a number towards positive infinity. +Calculates the ceiling of a number, i.e., rounds a number towards positive infinity. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -111,7 +111,7 @@ For arrays, the function will be applied index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The ceiling of `a`. Returns an array with +*Returns*: `number` | `Array.`. The ceiling of `a`. Returns an array with the ceilings of each element if `a` is an array. *Example* @@ -125,7 +125,7 @@ ceil([1.1, 2.2, 3.3]) // returns [2, 3, 4] [float] === clamp( ...a, min, max ) -Restricts value to a given range and returns closed available value. If only `min` +Restricts value to a given range and returns closed available value. If only `min` is provided, values are restricted to only a lower bound. [cols="3*^<"] @@ -145,11 +145,11 @@ is provided, values are restricted to only a lower bound. |(optional) The maximum value this function will return. |=== -*Returns*: `number` | `Array.`. The closest value between `min` (inclusive) -and `max` (inclusive). Returns an array with values greater than or equal to `min` +*Returns*: `number` | `Array.`. The closest value between `min` (inclusive) +and `max` (inclusive). Returns an array with values greater than or equal to `min` and less than or equal to `max` (if provided) at each index. -*Throws*: +*Throws*: - `'Array length mismatch'` if a `min` and/or `max` are arrays of different lengths @@ -194,7 +194,7 @@ count(100) // returns 1 [float] === cube( a ) -Calculates the cube of a number. For arrays, the function will be applied +Calculates the cube of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -206,7 +206,7 @@ index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The cube of `a`. Returns an array +*Returns*: `number` | `Array.`. The cube of `a`. Returns an array with the cubes of each element if `a` is an array. *Example* @@ -219,7 +219,7 @@ cube([3, 4, 5]) // returns [27, 64, 125] [float] === divide( a, b ) -Divides two numbers. If at least one array of numbers is passed into the function, +Divides two numbers. If at least one array of numbers is passed into the function, the function will be applied index-wise to each element. [cols="3*^<"] @@ -235,8 +235,8 @@ the function will be applied index-wise to each element. |divisor, a number or an array of numbers, b != 0 |=== -*Returns*: `number` | `Array.`. Returns the quotient of `a` and `b` -if both are numbers. Returns an array with the quotients applied index-wise to +*Returns*: `number` | `Array.`. Returns the quotient of `a` and `b` +if both are numbers. Returns an array with the quotients applied index-wise to each element if `a` or `b` is an array. *Throws*: @@ -257,7 +257,7 @@ divide([14, 42, 65, 108], [2, 7, 5, 12]) // returns [7, 6, 13, 9] [float] === exp( a ) -Calculates _e^x_ where _e_ is Euler's number. For arrays, the function will be applied +Calculates _e^x_ where _e_ is Euler's number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -269,7 +269,7 @@ index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. Returns an array with the values of +*Returns*: `number` | `Array.`. Returns an array with the values of `e^x` evaluated where `x` is each element of `a` if `a` is an array. *Example* @@ -282,7 +282,7 @@ exp([1, 2, 3]) // returns [e^1, e^2, e^3] = [2.718281828459045, 7.38905609893064 [float] === first( a ) -Returns the first element of an array. If anything other than an array is passed +Returns the first element of an array. If anything other than an array is passed in, the input is returned. [cols="3*^<"] @@ -306,7 +306,7 @@ first([1, 2, 3]) // returns 1 [float] === fix( a ) -Calculates the fix of a number, i.e., rounds a number towards 0. For arrays, the +Calculates the fix of a number, i.e., rounds a number towards 0. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -318,7 +318,7 @@ function will be applied index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The fix of `a`. Returns an array with +*Returns*: `number` | `Array.`. The fix of `a`. Returns an array with the fixes for each element if `a` is an array. *Example* @@ -332,7 +332,7 @@ fix([1.8, 2.9, -3.7, -4.6]) // returns [1, 2, -3, -4] [float] === floor( a ) -Calculates the floor of a number, i.e., rounds a number towards negative infinity. +Calculates the floor of a number, i.e., rounds a number towards negative infinity. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -344,7 +344,7 @@ For arrays, the function will be applied index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The floor of `a`. Returns an array +*Returns*: `number` | `Array.`. The floor of `a`. Returns an array with the floor of each element if `a` is an array. *Example* @@ -358,7 +358,7 @@ floor([1.7, 2.8, 3.9]) // returns [1, 2, 3] [float] === last( a ) -Returns the last element of an array. If anything other than an array is passed +Returns the last element of an array. If anything other than an array is passed in, the input is returned. [cols="3*^<"] @@ -382,7 +382,7 @@ last([1, 2, 3]) // returns 3 [float] === log( a, b ) -Calculates the logarithm of a number. For arrays, the function will be applied +Calculates the logarithm of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -398,7 +398,7 @@ index-wise to each element. |(optional) base for the logarithm. If not provided a value, the default base is e, and the natural log is calculated. |=== -*Returns*: `number` | `Array.`. The logarithm of `a`. Returns an array +*Returns*: `number` | `Array.`. The logarithm of `a`. Returns an array with the the logarithms of each element if `a` is an array. *Throws*: @@ -419,7 +419,7 @@ log([2, 4, 8, 16, 32], 2) // returns [1, 2, 3, 4, 5] [float] === log10( a ) -Calculates the logarithm base 10 of a number. For arrays, the function will be +Calculates the logarithm base 10 of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -431,7 +431,7 @@ applied index-wise to each element. |a number or an array of numbers, `a` must be greater than 0 |=== -*Returns*: `number` | `Array.`. The logarithm of `a`. Returns an array +*Returns*: `number` | `Array.`. The logarithm of `a`. Returns an array with the the logarithms base 10 of each element if `a` is an array. *Throws*: `'Must be greater than 0'` if `a` < 0 @@ -448,8 +448,8 @@ log([10, 100, 1000, 10000, 100000]) // returns [1, 2, 3, 4, 5] [float] === max( ...args ) -Finds the maximum value of one of more numbers/arrays of numbers passed into the function. -If at least one array of numbers is passed into the function, the function will +Finds the maximum value of one of more numbers/arrays of numbers passed into the function. +If at least one array of numbers is passed into the function, the function will find the maximum by index. [cols="3*^<"] @@ -461,9 +461,9 @@ find the maximum by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The maximum value of all numbers if -`args` contains only numbers. Returns an array with the the maximum values at each -index, including all scalar numbers in `args` in the calculation at each index if +*Returns*: `number` | `Array.`. The maximum value of all numbers if +`args` contains only numbers. Returns an array with the the maximum values at each +index, including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Throws*: `'Array length mismatch'` if `args` contains arrays of different lengths @@ -479,8 +479,8 @@ max([1, 9], 4, [3, 5]) // returns [max([1, 4, 3]), max([9, 4, 5])] = [4, 9] [float] === mean( ...args ) -Finds the mean value of one of more numbers/arrays of numbers passed into the function. -If at least one array of numbers is passed into the function, the function will +Finds the mean value of one of more numbers/arrays of numbers passed into the function. +If at least one array of numbers is passed into the function, the function will find the mean by index. [cols="3*^<"] @@ -492,9 +492,9 @@ find the mean by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The maximum value of all numbers if -`args` contains only numbers. Returns an array with the the maximum values at each -index, including all scalar numbers in `args` in the calculation at each index if +*Returns*: `number` | `Array.`. The maximum value of all numbers if +`args` contains only numbers. Returns an array with the the maximum values at each +index, including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Throws*: `'Array length mismatch'` if `args` contains arrays of different lengths @@ -510,8 +510,8 @@ max([1, 9], 4, [3, 5]) // returns [max([1, 4, 3]), max([9, 4, 5])] = [4, 9] [float] === mean( ...args ) -Finds the mean value of one of more numbers/arrays of numbers passed into the function. -If at least one array of numbers is passed into the function, the function will +Finds the mean value of one of more numbers/arrays of numbers passed into the function. +If at least one array of numbers is passed into the function, the function will find the mean by index. [cols="3*^<"] @@ -523,9 +523,9 @@ find the mean by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The mean value of all numbers if `args` -contains only numbers. Returns an array with the the mean values of each index, -including all scalar numbers in `args` in the calculation at each index if `args` +*Returns*: `number` | `Array.`. The mean value of all numbers if `args` +contains only numbers. Returns an array with the the mean values of each index, +including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Example* @@ -539,8 +539,8 @@ mean([1, 9], 5, [3, 4]) // returns [mean([1, 5, 3]), mean([9, 5, 4])] = [3, 6] [float] === median( ...args ) -Finds the median value(s) of one of more numbers/arrays of numbers passed into the function. -If at least one array of numbers is passed into the function, the function will +Finds the median value(s) of one of more numbers/arrays of numbers passed into the function. +If at least one array of numbers is passed into the function, the function will find the median by index. [cols="3*^<"] @@ -552,9 +552,9 @@ find the median by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The median value of all numbers if `args` -contains only numbers. Returns an array with the the median values of each index, -including all scalar numbers in `args` in the calculation at each index if `args` +*Returns*: `number` | `Array.`. The median value of all numbers if `args` +contains only numbers. Returns an array with the the median values of each index, +including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Example* @@ -569,8 +569,8 @@ median([1, 9], 2, 4, [3, 5]) // returns [median([1, 2, 4, 3]), median([9, 2, 4, [float] === min( ...args ) -Finds the minimum value of one of more numbers/arrays of numbers passed into the function. -If at least one array of numbers is passed into the function, the function will +Finds the minimum value of one of more numbers/arrays of numbers passed into the function. +If at least one array of numbers is passed into the function, the function will find the minimum by index. [cols="3*^<"] @@ -582,9 +582,9 @@ find the minimum by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The minimum value of all numbers if -`args` contains only numbers. Returns an array with the the minimum values of each -index, including all scalar numbers in `args` in the calculation at each index if `a` +*Returns*: `number` | `Array.`. The minimum value of all numbers if +`args` contains only numbers. Returns an array with the the minimum values of each +index, including all scalar numbers in `args` in the calculation at each index if `a` is an array. *Throws*: `'Array length mismatch'` if `args` contains arrays of different lengths. @@ -600,7 +600,7 @@ min([1, 9], 4, [3, 5]) // returns [min([1, 4, 3]), min([9, 4, 5])] = [1, 4] [float] === mod( a, b ) -Remainder after dividing two numbers. If at least one array of numbers is passed +Remainder after dividing two numbers. If at least one array of numbers is passed into the function, the function will be applied index-wise to each element. [cols="3*^<"] @@ -616,8 +616,8 @@ into the function, the function will be applied index-wise to each element. |divisor, a number or an array of numbers, b != 0 |=== -*Returns*: `number` | `Array.`. The remainder of `a` divided by `b` if -both are numbers. Returns an array with the the remainders applied index-wise to +*Returns*: `number` | `Array.`. The remainder of `a` divided by `b` if +both are numbers. Returns an array with the the remainders applied index-wise to each element if `a` or `b` is an array. *Throws*: @@ -638,8 +638,8 @@ mod([14, 42, 65, 108], [5, 4, 14, 2]) // returns [5, 2, 9, 0] [float] === mode( ...args ) -Finds the mode value(s) of one of more numbers/arrays of numbers passed into the function. -If at least one array of numbers is passed into the function, the function will +Finds the mode value(s) of one of more numbers/arrays of numbers passed into the function. +If at least one array of numbers is passed into the function, the function will find the mode by index. [cols="3*^<"] @@ -651,9 +651,9 @@ find the mode by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.>`. An array of mode value(s) of all -numbers if `args` contains only numbers. Returns an array of arrays with mode value(s) -of each index, including all scalar numbers in `args` in the calculation at each index +*Returns*: `number` | `Array.>`. An array of mode value(s) of all +numbers if `args` contains only numbers. Returns an array of arrays with mode value(s) +of each index, including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Example* @@ -668,7 +668,7 @@ mode([1, 9], 1, 4, [3, 5]) // returns [mode([1, 1, 4, 3]), mode([9, 1, 4, 5])] = [float] === multiply( a, b ) -Multiplies two numbers. If at least one array of numbers is passed into the function, +Multiplies two numbers. If at least one array of numbers is passed into the function, the function will be applied index-wise to each element. [cols="3*^<"] @@ -684,11 +684,11 @@ the function will be applied index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The product of `a` and `b` if both are -numbers. Returns an array with the the products applied index-wise to each element +*Returns*: `number` | `Array.`. The product of `a` and `b` if both are +numbers. Returns an array with the the products applied index-wise to each element if `a` or `b` is an array. -*Throws*: `'Array length mismatch'` if `a` and `b` are arrays with different lengths +*Throws*: `'Array length mismatch'` if `a` and `b` are arrays with different lengths *Example* [source, js] @@ -702,7 +702,7 @@ multiply([1, 2, 3, 4], [2, 7, 5, 12]) // returns [2, 14, 15, 48] [float] === pow( a, b ) -Calculates the cube root of a number. For arrays, the function will be applied +Calculates the cube root of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -718,7 +718,7 @@ index-wise to each element. |the power that `a` is raised to |=== -*Returns*: `number` | `Array.`. `a` raised to the power of `b`. Returns +*Returns*: `number` | `Array.`. `a` raised to the power of `b`. Returns an array with the each element raised to the power of `b` if `a` is an array. *Throws*: `'Missing exponent'` if `b` is not provided @@ -733,8 +733,8 @@ pow([1, 2, 3], 4) // returns [1, 16, 81] [float] === random( a, b ) -Generates a random number within the given range where the lower bound is inclusive -and the upper bound is exclusive. If no numbers are passed in, it will return a +Generates a random number within the given range where the lower bound is inclusive +and the upper bound is exclusive. If no numbers are passed in, it will return a number between 0 and 1. If only one number is passed in, it will return a number between 0 and the number passed in. @@ -751,11 +751,11 @@ between 0 and the number passed in. |(optional) must be greater than `a` |=== -*Returns*: `number`. A random number between 0 and 1 if no numbers are passed in. -Returns a random number between 0 and `a` if only one number is passed in. Returns +*Returns*: `number`. A random number between 0 and 1 if no numbers are passed in. +Returns a random number between 0 and `a` if only one number is passed in. Returns a random number between `a` and `b` if two numbers are passed in. -*Throws*: `'Min must be greater than max'` if `a` < 0 when only `a` is passed in +*Throws*: `'Min must be greater than max'` if `a` < 0 when only `a` is passed in or if `a` > `b` when both `a` and `b` are passed in *Example* @@ -769,8 +769,8 @@ random(-10,10) // returns a random number between -10 (inclusive) and 10 (exclus [float] === range( ...args ) -Finds the range of one of more numbers/arrays of numbers passed into the function. If at -least one array of numbers is passed into the function, the function will find +Finds the range of one of more numbers/arrays of numbers passed into the function. If at +least one array of numbers is passed into the function, the function will find the range by index. [cols="3*^<"] @@ -782,9 +782,9 @@ the range by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The range value of all numbers if `args` -contains only numbers. Returns an array with the range values at each index, -including all scalar numbers in `args` in the calculation at each index if `args` +*Returns*: `number` | `Array.`. The range value of all numbers if `args` +contains only numbers. Returns an array with the range values at each index, +including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Example* @@ -798,8 +798,8 @@ range([1, 9], 4, [3, 5]) // returns [range([1, 4, 3]), range([9, 4, 5])] = [3, 5 [float] === range( ...args ) -Finds the range of one of more numbers/arrays of numbers into the function. If at -least one array of numbers is passed into the function, the function will find +Finds the range of one of more numbers/arrays of numbers into the function. If at +least one array of numbers is passed into the function, the function will find the range by index. [cols="3*^<"] @@ -811,9 +811,9 @@ the range by index. |one or more numbers or arrays of numbers |=== -*Returns*: `number` | `Array.`. The range value of all numbers if `args` -contains only numbers. Returns an array with the the range values at each index, -including all scalar numbers in `args` in the calculation at each index if `args` +*Returns*: `number` | `Array.`. The range value of all numbers if `args` +contains only numbers. Returns an array with the the range values at each index, +including all scalar numbers in `args` in the calculation at each index if `args` contains at least one array. *Example* @@ -827,7 +827,7 @@ range([1, 9], 4, [3, 5]) // returns [range([1, 4, 3]), range([9, 4, 5])] = [3, 5 [float] === round( a, b ) -Rounds a number towards the nearest integer by default, or decimal place (if passed in as `b`). +Rounds a number towards the nearest integer by default, or decimal place (if passed in as `b`). For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -843,7 +843,7 @@ For arrays, the function will be applied index-wise to each element. |(optional) number of decimal places, default value: 0 |=== -*Returns*: `number` | `Array.`. The rounded value of `a`. Returns an +*Returns*: `number` | `Array.`. The rounded value of `a`. Returns an array with the the rounded values of each element if `a` is an array. *Example* @@ -885,7 +885,7 @@ size(100) // returns 1 [float] === sqrt( a ) -Calculates the square root of a number. For arrays, the function will be applied +Calculates the square root of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -897,7 +897,7 @@ index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The square root of `a`. Returns an array +*Returns*: `number` | `Array.`. The square root of `a`. Returns an array with the the square roots of each element if `a` is an array. *Throws*: `'Unable find the square root of a negative number'` if `a` < 0 @@ -913,7 +913,7 @@ sqrt([9, 16, 25]) // returns [3, 4, 5] [float] === square( a ) -Calculates the square of a number. For arrays, the function will be applied +Calculates the square of a number. For arrays, the function will be applied index-wise to each element. [cols="3*^<"] @@ -925,7 +925,7 @@ index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The square of `a`. Returns an array +*Returns*: `number` | `Array.`. The square of `a`. Returns an array with the the squares of each element if `a` is an array. *Example* @@ -938,7 +938,7 @@ square([3, 4, 5]) // returns [9, 16, 25] [float] === subtract( a, b ) -Subtracts two numbers. If at least one array of numbers is passed into the function, +Subtracts two numbers. If at least one array of numbers is passed into the function, the function will be applied index-wise to each element. [cols="3*^<"] @@ -954,7 +954,7 @@ the function will be applied index-wise to each element. |a number or an array of numbers |=== -*Returns*: `number` | `Array.`. The difference of `a` and `b` if both are +*Returns*: `number` | `Array.`. The difference of `a` and `b` if both are numbers, or an array of differences applied index-wise to each element. *Throws*: `'Array length mismatch'` if `a` and `b` are arrays with different lengths @@ -971,11 +971,11 @@ subtract([14, 42, 65, 108], [2, 7, 5, 12]) // returns [12, 35, 52, 96] [float] === sum( ...args ) -Calculates the sum of one or more numbers/arrays passed into the function. If at -least one array is passed, the function will sum up one or more numbers/arrays of +Calculates the sum of one or more numbers/arrays passed into the function. If at +least one array is passed, the function will sum up one or more numbers/arrays of numbers and distinct values of an array. Sum accepts arrays of different lengths. -*Returns*: `number`. The sum of one or more numbers/arrays of numbers including +*Returns*: `number`. The sum of one or more numbers/arrays of numbers including distinct values in arrays *Example* @@ -992,7 +992,7 @@ sum([10, 20, 30, 40], 10, [1, 2, 3], 22) // returns sum(10, 20, 30, 40, 10, 1, 2 Counts the number of unique values in an array. -*Returns*: `number`. The number of unique values in the array. Returns 1 if `a` +*Returns*: `number`. The number of unique values in the array. Returns 1 if `a` is not an array. *Example* @@ -1003,4 +1003,3 @@ unique([]) // returns 0 unique([1, 2, 3, 4]) // returns 4 unique([1, 2, 3, 4, 2, 2, 2, 3, 4, 2, 4, 5, 2, 1, 4, 2]) // returns 5 ------------ - diff --git a/docs/dev-tools/searchprofiler/more-complicated.asciidoc b/docs/dev-tools/searchprofiler/more-complicated.asciidoc index a0771f4a0f240..338341d65924d 100644 --- a/docs/dev-tools/searchprofiler/more-complicated.asciidoc +++ b/docs/dev-tools/searchprofiler/more-complicated.asciidoc @@ -25,11 +25,11 @@ POST test/_bulk // CONSOLE -- -. From the {searchprofiler}, enter "test" in the *Index* field to restrict profiled +. From the {searchprofiler}, enter "test" in the *Index* field to restrict profiled queries to the `test` index. . Replace the default `match_all` query in the query editor with a query that has two sub-query -components and includes a simple aggregation, like the example below. +components and includes a simple aggregation: + -- [source,js] diff --git a/docs/developer/core/development-functional-tests.asciidoc b/docs/developer/core/development-functional-tests.asciidoc index 77a2bfe77b4ab..dcb3d65b8b83f 100644 --- a/docs/developer/core/development-functional-tests.asciidoc +++ b/docs/developer/core/development-functional-tests.asciidoc @@ -69,7 +69,7 @@ node scripts/functional_tests_server.js node ../scripts/functional_test_runner.js ---------- -** Selenium tests are run in headless mode on CI. Locally the same tests will be executed in a real browser. You can activate headless mode by setting the environment variable below: +** Selenium tests are run in headless mode on CI. Locally the same tests will be executed in a real browser. You can activate headless mode by setting the environment variable: + ["source", "shell"] ---------- @@ -181,7 +181,7 @@ node scripts/functional_test_runner --config test/functional/config.firefox.js [float] ===== Anatomy of a test file -The annotated example file below shows the basic structure every test suite uses. It starts by importing https://github.com/elastic/kibana/tree/master/packages/kbn-expect[`@kbn/expect`] and defining its default export: an anonymous Test Provider. The test provider then destructures the Provider API for the `getService()` and `getPageObjects()` functions. It uses these functions to collect the dependencies of this suite. The rest of the test file will look pretty normal to mocha.js users. `describe()`, `it()`, `before()` and the lot are used to define suites that happen to automate a browser via services and objects of type `PageObject`. +This annotated example file shows the basic structure every test suite uses. It starts by importing https://github.com/elastic/kibana/tree/master/packages/kbn-expect[`@kbn/expect`] and defining its default export: an anonymous Test Provider. The test provider then destructures the Provider API for the `getService()` and `getPageObjects()` functions. It uses these functions to collect the dependencies of this suite. The rest of the test file will look pretty normal to mocha.js users. `describe()`, `it()`, `before()` and the lot are used to define suites that happen to automate a browser via services and objects of type `PageObject`. ["source","js"] ---- diff --git a/docs/developer/plugin/development-plugin-feature-registration.asciidoc b/docs/developer/plugin/development-plugin-feature-registration.asciidoc index 2c686964d369a..ca61e5309ce85 100644 --- a/docs/developer/plugin/development-plugin-feature-registration.asciidoc +++ b/docs/developer/plugin/development-plugin-feature-registration.asciidoc @@ -46,7 +46,7 @@ Registering a feature consists of the following fields. For more information, co |`privileges` (required) |{repo}blob/{branch}/x-pack/plugins/features/server/feature.ts[`FeatureWithAllOrReadPrivileges`]. -|see examples below +|See <> and <> |The set of privileges this feature requires to function. |`icon` @@ -80,6 +80,7 @@ if (canUserSave) { } ----------- +[[example-1-canvas]] ==== Example 1: Canvas Application ["source","javascript"] ----------- @@ -134,6 +135,7 @@ if (canUserSave) { Because the `read` privilege does not define the `save` capability, users with read-only access will have their `uiCapabilities.canvas.save` flag set to `false`. +[[example-2-dev-tools]] ==== Example 2: Dev Tools ["source","javascript"] diff --git a/docs/developer/plugin/development-plugin-localization.asciidoc b/docs/developer/plugin/development-plugin-localization.asciidoc index 78ee933f681f4..1fb8b6aa0cbde 100644 --- a/docs/developer/plugin/development-plugin-localization.asciidoc +++ b/docs/developer/plugin/development-plugin-localization.asciidoc @@ -161,7 +161,7 @@ Full details are {repo}tree/master/packages/kbn-i18n#angularjs[here]. To learn more about i18n tooling, see {blob}src/dev/i18n/README.md[i18n dev tooling]. -To learn more about implementing i18n in the UI, follow the links below: +To learn more about implementing i18n in the UI, use the following links: * {blob}packages/kbn-i18n/README.md[i18n plugin] * {blob}packages/kbn-i18n/GUIDELINE.md[i18n guidelines] diff --git a/docs/discover/kuery.asciidoc b/docs/discover/kuery.asciidoc index c835c15028074..48a7c65bdbf15 100644 --- a/docs/discover/kuery.asciidoc +++ b/docs/discover/kuery.asciidoc @@ -2,13 +2,13 @@ === Kibana Query Language In Kibana 6.3, we introduced a number of exciting experimental query language enhancements. These -features are now available by default in 7.0. Out of the box, Kibana's query language now includes scripted field support and a -simplified, easier to use syntax. If you have a Basic license or above, autocomplete functionality will also be enabled. +features are now available by default in 7.0. Out of the box, Kibana's query language now includes scripted field support and a +simplified, easier to use syntax. If you have a Basic license or above, autocomplete functionality will also be enabled. ==== Language Syntax -If you're familiar with Kibana's old lucene query syntax, you should feel right at home with the new syntax. The basics -stay the same, we've simply refined things to make the query language easier to use. Read about the changes below. +If you're familiar with Kibana's old Lucene query syntax, you should feel right at home with the new syntax. The basics +stay the same, we've simply refined things to make the query language easier to use. `response:200` will match documents where the response field matches the value 200. @@ -19,8 +19,8 @@ they appear. This means documents with "quick brown fox" will match, but so will to search for a phrase. The query parser will no longer split on whitespace. Multiple search terms must be separated by explicit -boolean operators. Lucene will combine search terms with an `or` by default, so `response:200 extension:php` would -become `response:200 or extension:php` in KQL. This will match documents where response matches 200, extension matches php, or both. +boolean operators. Lucene will combine search terms with an `or` by default, so `response:200 extension:php` would +become `response:200 or extension:php` in KQL. This will match documents where response matches 200, extension matches php, or both. Note that boolean operators are not case sensitive. We can make terms required by using `and`. @@ -48,9 +48,9 @@ Entire groups can also be inverted. `response:200 and not (extension:php or extension:css)` -Ranges are similar to lucene with a small syntactical difference. +Ranges are similar to lucene with a small syntactical difference. -Instead of `bytes:>1000`, we omit the colon: `bytes > 1000`. +Instead of `bytes:>1000`, we omit the colon: `bytes > 1000`. `>, >=, <, <=` are all valid range operators. @@ -76,15 +76,15 @@ in the response field, but a query for just `200` will search for 200 across all KQL supports querying on {ref}/nested.html[nested fields] through a special syntax. You can query nested fields in subtly different ways, depending on the results you want, so crafting nested queries requires extra thought. - + One main consideration is how to match parts of the nested query to the individual nested documents. There are two main approaches to take: * *Parts of the query may only match a single nested document.* This is what most users want when querying on a nested field. -* *Parts of the query can match different nested documents.* This is how a regular object field works. +* *Parts of the query can match different nested documents.* This is how a regular object field works. Although generally less useful, there might be occasions where you want to query a nested field in this way. -Let's take a look at the first approach. In the following document, `items` is a nested field. Each document in the nested +Let's take a look at the first approach. In the following document, `items` is a nested field. Each document in the nested field contains a name, stock, and category. [source,json] @@ -122,7 +122,7 @@ To find stores that have more than 10 bananas in stock, you would write a query `items:{ name:banana and stock > 10 }` -`items` is the "nested path". Everything inside the curly braces (the "nested group") must match a single nested document. +`items` is the "nested path". Everything inside the curly braces (the "nested group") must match a single nested document. The following example returns no matches because no single nested document has bananas with a stock of 9. @@ -138,7 +138,7 @@ The subqueries in this example are in separate nested groups and can match diffe ==== Combine approaches -You can combine these two approaches to create complex queries. What if you wanted to find a store with more than 10 +You can combine these two approaches to create complex queries. What if you wanted to find a store with more than 10 bananas that *also* stocks vegetables? You could do this: `items:{ name:banana and stock > 10 } and items:{ category:vegetable }` diff --git a/docs/discover/search.asciidoc b/docs/discover/search.asciidoc index 9c4e406455c27..21ae4560fba94 100644 --- a/docs/discover/search.asciidoc +++ b/docs/discover/search.asciidoc @@ -56,7 +56,7 @@ query language you can also submit queries using the {ref}/query-dsl.html[Elasti [[save-open-search]] === Saving searches -A saved search persists your current view of Discover for later retrieval and reuse. You can reload a saved search into Discover, add it to a dashboard, and use it as the basis for a <>. +A saved search persists your current view of Discover for later retrieval and reuse. You can reload a saved search into Discover, add it to a dashboard, and use it as the basis for a <>. A saved search includes the query text, filters, and optionally, the time filter. A saved search also includes the selected columns in the document table, the sort order, and the current index pattern. @@ -164,12 +164,9 @@ You can import, export, and delete saved queries from <>. +index pattern are searched. +To change the indices you are searching, click the index pattern and select a +different <>. [[autorefresh]] === Refresh the search results @@ -180,7 +177,7 @@ retrieve the latest results. . Click image:images/time-filter-calendar.png[]. -. In the *Refresh every* field, enter the refresh rate, then select the interval +. In the *Refresh every* field, enter the refresh rate, then select the interval from the dropdown. . Click *Start*. @@ -189,5 +186,5 @@ image::images/autorefresh-intervals.png[] To disable auto refresh, click *Stop*. -If auto refresh is not enabled, click *Refresh* to manually refresh the search +If auto refresh is not enabled, click *Refresh* to manually refresh the search results. diff --git a/docs/epm/index.asciidoc b/docs/epm/index.asciidoc index 46d45b85690e3..d2ebe003afd6b 100644 --- a/docs/epm/index.asciidoc +++ b/docs/epm/index.asciidoc @@ -47,12 +47,12 @@ A user-specified string that will be used to part of the index name in Elasticse ==== Package -A package contains all the assets for the Elastic Stack. A more detailed definition of a package can be found under https://github.com/elastic/package-registry . +A package contains all the assets for the Elastic Stack. A more detailed definition of a package can be found under https://github.com/elastic/package-registry. == Indexing Strategy -Ingest Management enforces an indexing strategy to allow the system to automically detect indices and run queries on it. In short the indexing strategy looks as following: +Ingest Management enforces an indexing strategy to allow the system to automatically detect indices and run queries on it. In short the indexing strategy looks as following: ``` {type}-{dataset}-{namespace} @@ -85,7 +85,7 @@ The version is included in each pipeline to allow upgrades. The pipeline itself === Templates & ILM Policies -To make the above strategy possible, alias templates are required. For each type there is a basic alias template with a default ILM policy. These default templates apply to all indices which follow the indexing strategy and do not have a more specific dataset alias template. +To make the above strategy possible, alias templates are required. For each type there is a basic alias template with a default ILM policy. These default templates apply to all indices which follow the indexing strategy and do not have a more specific dataset alias template. The `metrics` and `logs` alias template contain all the basic fields from ECS. @@ -109,7 +109,7 @@ Filtering for data in queries for example in visualizations or dashboards should === Security permissions -Security permissions can be set on different levels. To set special permissions for the access on the prod namespace an index pattern as below can be used: +Security permissions can be set on different levels. To set special permissions for the access on the prod namespace, use the following index pattern: ``` /(logs|metrics)-[^-]+-prod-$/ @@ -142,5 +142,3 @@ The new ingest pipeline is expected to still work with the data coming from olde In case of a breaking change in the data structure, the new ingest pipeline is also expected to deal with this change. In case there are breaking changes which cannot be dealt with in an ingest pipeline, a new package has to be created. Each package lists its minimal required agent version. In case there are agents enrolled with an older version, the user is notified to upgrade these agents as otherwise the new configs cannot be rolled out. - - diff --git a/docs/management/numeral.asciidoc b/docs/management/numeral.asciidoc index 65dfdab3abd3c..5d4d48ca785e1 100644 --- a/docs/management/numeral.asciidoc +++ b/docs/management/numeral.asciidoc @@ -19,7 +19,7 @@ The numeral pattern syntax expresses: Number of decimal places:: The `.` character turns on the option to show decimal places using a locale-specific decimal separator, most often `.` or `,`. To add trailing zeroes such as `5.00`, use a pattern like `0.00`. -To have optional zeroes, use the `[]` characters. Examples below. +To have optional zeroes, use the `[]` characters. Thousands separator:: The thousands separator `,` turns on the option to group thousands using a locale-specific separator. The separator is most often `,` or `.`, and sometimes ` `. diff --git a/docs/management/rollups/create_and_manage_rollups.asciidoc b/docs/management/rollups/create_and_manage_rollups.asciidoc index 565c179b741f1..6a56970687fd6 100644 --- a/docs/management/rollups/create_and_manage_rollups.asciidoc +++ b/docs/management/rollups/create_and_manage_rollups.asciidoc @@ -70,11 +70,7 @@ This allows for more granular queries, such as 2h and 12h. [float] ==== Create the rollup job -As you walk through the *Create rollup job* UI, enter the data shown in -the table below. The terms, histogram, and metrics fields reflect -the key information to retain in the rolled up data: where visitors are from (geo.src), -what operating system they are using (machine.os.keyword), -and how much data is being sent (bytes). +As you walk through the *Create rollup job* UI, enter the data: |=== |*Field* |*Value* @@ -118,6 +114,10 @@ and how much data is being sent (bytes). |bytes (average) |=== +The terms, histogram, and metrics fields reflect +the key information to retain in the rolled up data: where visitors are from (geo.src), +what operating system they are using (machine.os.keyword), +and how much data is being sent (bytes). You can now use the rolled up data for analysis at a fraction of the storage cost of the original index. The original data can live side by side with the new diff --git a/docs/maps/geojson-upload.asciidoc b/docs/maps/geojson-upload.asciidoc index 8c3cb371b6add..ad20264f56138 100644 --- a/docs/maps/geojson-upload.asciidoc +++ b/docs/maps/geojson-upload.asciidoc @@ -14,7 +14,7 @@ GeoJSON is the most commonly used and flexible option. [float] === Upload a GeoJSON file -Follow the instructions below to upload a GeoJSON data file, or try the +Follow these instructions to upload a GeoJSON data file, or try the <>. . Open *Elastic Maps*, and then click *Add layer*. diff --git a/docs/maps/indexing-geojson-data-tutorial.asciidoc b/docs/maps/indexing-geojson-data-tutorial.asciidoc index 22b736032cb79..a94e5757d5dfa 100644 --- a/docs/maps/indexing-geojson-data-tutorial.asciidoc +++ b/docs/maps/indexing-geojson-data-tutorial.asciidoc @@ -46,7 +46,7 @@ image::maps/images/fu_gs_new_england_map.png[] === Upload and index GeoJSON files For each GeoJSON file you downloaded, complete the following steps: -. Below the map legend, click *Add layer*. +. Click *Add layer*. . From the list of layer types, click *Uploaded GeoJSON*. . Using the File Picker, upload the GeoJSON file. + @@ -86,7 +86,7 @@ hot spots are. An advantage of having indexed {ref}/geo-point.html[geo_point] data for the lightning strikes is that you can perform aggregations on the data. -. Below the map legend, click *Add layer*. +. Click *Add layer*. . From the list of layer types, click *Grid aggregation*. + Because you indexed `lightning_detected.geojson` using the index name and diff --git a/docs/maps/vector-style.asciidoc b/docs/maps/vector-style.asciidoc index 509b1fae4066a..80e4c4ed5f844 100644 --- a/docs/maps/vector-style.asciidoc +++ b/docs/maps/vector-style.asciidoc @@ -12,7 +12,7 @@ For each property, you can specify whether to use a constant or data driven valu Use static styling to specificy a constant value for a style property. -The image below shows an example of static styling using the <> data set. +This image shows an example of static styling using the <> data set. The *kibana_sample_data_logs* layer uses static styling for all properties. [role="screenshot"] @@ -26,7 +26,7 @@ image::maps/images/vector_style_static.png[] Use data driven styling to symbolize features by property values. To enable data driven styling for a style property, change the selected value from *Fixed* or *Solid* to *By value*. -The image below shows an example of data driven styling using the <> data set. +This image shows an example of data driven styling using the <> data set. The *kibana_sample_data_logs* layer uses data driven styling for fill color and symbol size style properties. * The `hour_of_day` property determines the fill color for each feature based on where the value fits on a linear scale. @@ -87,7 +87,7 @@ Qualitative data driven styling is available for the following styling propertie Qualitative data driven styling uses a {ref}/search-aggregations-bucket-terms-aggregation.html[terms aggregation] to retrieve the top nine categories for the property. Feature values within the top categories are assigned a unique color. Feature values outside of the top categories are grouped into the *Other* category. A feature is assigned the *Other* category when the property value is undefined. -The image below shows an example of quantitative data driven styling using the <> data set. +This image shows an example of quantitative data driven styling using the <> data set. The `machine.os.keyword` property determines the color of each symbol based on category. [role="screenshot"] @@ -101,7 +101,7 @@ image::maps/images/quantitative_data_driven_styling.png[] Class styling symbolizes features by class and requires multiple layers. Use <> to define the class for each layer, and <> to symbolize each class. -The image below shows an example of class styling using the <> data set. +This image shows an example of class styling using the <> data set. * The *Mac OS requests* layer applies the filter `machine.os : osx` so the layer only contains Mac OS requests. The fill color is a static value of green. diff --git a/docs/migration/migrate_8_0.asciidoc b/docs/migration/migrate_8_0.asciidoc index a34f956ace263..ce4c97391f1b5 100644 --- a/docs/migration/migrate_8_0.asciidoc +++ b/docs/migration/migrate_8_0.asciidoc @@ -19,16 +19,16 @@ See also <> and <>. [float] [[breaking_80_index_pattern_changes]] -=== Index pattern changes +=== Index pattern changes [float] ==== Removed support for time-based internal index patterns -*Details:* Time-based interval index patterns were deprecated in 5.x. In 6.x, -you could no longer create time-based interval index patterns, but they continued +*Details:* Time-based interval index patterns were deprecated in 5.x. In 6.x, +you could no longer create time-based interval index patterns, but they continued to function as expected. Support for these index patterns has been removed in 8.0. -*Impact:* You must migrate your time_based index patterns to a wildcard pattern, -for example, `logstash-*`. +*Impact:* You must migrate your time_based index patterns to a wildcard pattern, +for example, `logstash-*`. [float] @@ -76,7 +76,7 @@ specified explicitly. [float] ==== `/api/security/v1/saml` endpoint is no longer supported -*Details:* The deprecated `/api/security/v1/saml` endpoint is no longer supported. +*Details:* The deprecated `/api/security/v1/saml` endpoint is no longer supported. *Impact:* Rely on `/api/security/saml/callback` endpoint when using SAML instead. This change should be reflected in Kibana `server.xsrf.whitelist` config as well as in Elasticsearch and Identity Provider SAML settings. @@ -108,7 +108,7 @@ access level. [float] ==== Legacy job parameters are no longer supported -*Details:* POST URL snippets that were copied in Kibana 6.2 or below are no longer supported. These logs have +*Details:* POST URL snippets that were copied in Kibana 6.2 or earlier are no longer supported. These logs have been deprecated with warnings that have been logged throughout 7.x. Please use Kibana UI to re-generate the POST URL snippets if you depend on these for automated PDF reports. diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc index a6eeffec51cb0..91bbef5690fd5 100644 --- a/docs/settings/apm-settings.asciidoc +++ b/docs/settings/apm-settings.asciidoc @@ -32,7 +32,7 @@ image::settings/images/apm-settings.png[APM app settings in Kibana] // tag::general-apm-settings[] If you'd like to change any of the default values, -copy and paste the relevant settings below into your `kibana.yml` configuration file. +copy and paste the relevant settings into your `kibana.yml` configuration file. xpack.apm.enabled:: Set to `false` to disabled the APM plugin {kib}. Defaults to `true`. diff --git a/docs/settings/ssl-settings.asciidoc b/docs/settings/ssl-settings.asciidoc index 5341d3543e7c6..3a0a474d9d597 100644 --- a/docs/settings/ssl-settings.asciidoc +++ b/docs/settings/ssl-settings.asciidoc @@ -44,7 +44,7 @@ Java Cryptography Architecture documentation]. Defaults to the value of The following settings are used to specify a private key, certificate, and the trusted certificates that should be used when communicating over an SSL/TLS connection. -If none of the settings below are specified, the default values are used. +If none of the settings are specified, the default values are used. See {ref}/security-settings.html[Default TLS/SSL settings]. ifdef::server[] @@ -54,8 +54,8 @@ ifndef::server[] A private key and certificate are optional and would be used if the server requires client authentication for PKI authentication. endif::server[] -If none of the settings below are specified, the defaults values are used. -See {ref}/security-settings.html[Default TLS/SSL settings]. +If none of the settings bare specified, the defaults values are used. +See {ref}/security-settings.html[Default TLS/SSL settings]. [float] ===== PEM encoded files diff --git a/docs/uptime-guide/install.asciidoc b/docs/uptime-guide/install.asciidoc index 5d32a26529f86..e7c50bb7604ce 100644 --- a/docs/uptime-guide/install.asciidoc +++ b/docs/uptime-guide/install.asciidoc @@ -20,7 +20,7 @@ then jump straight to <>. === Install the stack yourself If you'd rather install the stack yourself, -first see the https://www.elastic.co/support/matrix[Elastic Support Matrix] for information about supported operating systems and product compatibility. Then, follow the steps below. +first see the https://www.elastic.co/support/matrix[Elastic Support Matrix] for information about supported operating systems and product compatibility. * <> * <> diff --git a/docs/uptime-guide/security.asciidoc b/docs/uptime-guide/security.asciidoc index 6651b33ea0e0e..0c6fa4c6c4f56 100644 --- a/docs/uptime-guide/security.asciidoc +++ b/docs/uptime-guide/security.asciidoc @@ -1,9 +1,8 @@ [[uptime-security]] == Elasticsearch Security -If you use Elasticsearch security, you'll need to enable certain privileges for users -that would like to access the Uptime app. Below is an example of creating -a user and support role to implement those privileges. +If you use Elasticsearch security, you'll need to enable certain privileges for users +that would like to access the Uptime app. For example, create user and support roles to implement the privileges: [float] === Create a role diff --git a/docs/user/graph/getting-started.asciidoc b/docs/user/graph/getting-started.asciidoc index 7b3bd10147966..1749678ace9e3 100644 --- a/docs/user/graph/getting-started.asciidoc +++ b/docs/user/graph/getting-started.asciidoc @@ -2,7 +2,7 @@ [[graph-getting-started]] == Using Graph -You must index data into {es} before you can create a graph. +You must index data into {es} before you can create a graph. <> or get started with a <>. [float] @@ -11,24 +11,24 @@ You must index data into {es} before you can create a graph. . From the side navigation, open *Graph*. + -If this is your first graph, follow the prompts to create it. +If this is your first graph, follow the prompts to create it. For subsequent graphs, click *New*. . Select a data source to explore. . Add one or more multi-value fields that contain the terms you want to -graph. +graph. + The vertices in the graph are selected from these terms. . Enter a search query to discover relationships between terms in the selected -fields. +fields. + -For example, if you are using the {kib} sample web logs data set, and you want +For example, if you are using the {kib} sample web logs data set, and you want to generate a graph of the successful requests to particular pages from different locations, you could search for the 200 response code. The weight of the connection between two vertices indicates how strongly they -are related. +are related. + [role="screenshot"] image::user/graph/images/graph-url-connections.png["URL connections"] @@ -45,11 +45,11 @@ additional connections: image:user/graph/images/graph-expand-button.png[Expand Selection]. * To display additional connections between the displayed vertices, click the link icon -image:user/graph/images/graph-link-button.png[Add links to existing terms]. +image:user/graph/images/graph-link-button.png[Add links to existing terms]. * To explore a particular area of the graph, select the vertices you are interested in, and then click expand or link. * To step back through your changes to the graph, click undo -image:user/graph/images/graph-undo-button.png[Undo] and redo +image:user/graph/images/graph-undo-button.png[Undo] and redo image:user/graph/images/graph-redo-button.png[Redo]. . To see more relationships in your data, submit additional queries. @@ -63,61 +63,61 @@ image::user/graph/images/graph-add-query.png["Adding networks"] [[style-vertex-properties]] === Style vertex properties -Each vertex has a color, icon, and label. To change -the color or icon of all vertices -of a certain field, click the field badge below the search bar, and then +Each vertex has a color, icon, and label. To change +the color or icon of all vertices +of a certain field, click it's badge, and then select *Edit settings*. -To change the color and label of selected vertices, +To change the color and label of selected vertices, click the style icon image:user/graph/images/graph-style-button.png[Style] -in the control bar on the right. +in the control bar on the right. [float] [[edit-graph-settings]] === Edit graph settings -By default, *Graph* is configured to tune out noise in your data. +By default, *Graph* is configured to tune out noise in your data. If this isn't a good fit for your data, use *Settings > Advanced settings* -to adjust the way *Graph* queries your data. You can tune the graph to show -only the results relevant to you and to improve performance. -For more information, see <>. +to adjust the way *Graph* queries your data. You can tune the graph to show +only the results relevant to you and to improve performance. +For more information, see <>. -You can configure the number of vertices that a search or +You can configure the number of vertices that a search or expand operation adds to the graph. -By default, only the five most relevant terms for any given field are added -at a time. This keeps the graph from overflowing. To increase this number, click -a field below the search bar, select *Edit Settings*, and change *Terms per hop*. +By default, only the five most relevant terms for any given field are added +at a time. This keeps the graph from overflowing. To increase this number, click +a field, select *Edit Settings*, and change *Terms per hop*. [float] [[graph-block-terms]] === Block terms from the graph -Documents that match a blocked term are not allowed in the graph. -To block a term, select its vertex and click +Documents that match a blocked term are not allowed in the graph. +To block a term, select its vertex and click the block icon image:user/graph/images/graph-block-button.png[Block selection] -in the control panel. +in the control panel. For a list of blocked terms, go to *Settings > Blocked terms*. [float] [[graph-drill-down]] === Drill down into raw documents -With drilldowns, you can display additional information about a -selected vertex in a new browser window. For example, you might -configure a drilldown URL to perform a web search for the selected vertex term. +With drilldowns, you can display additional information about a +selected vertex in a new browser window. For example, you might +configure a drilldown URL to perform a web search for the selected vertex term. -Use the drilldown icon image:user/graph/images/graph-info-icon.png[Drilldown selection] +Use the drilldown icon image:user/graph/images/graph-info-icon.png[Drilldown selection] in the control panel to show the drilldown buttons for the selected vertices. -To configure drilldowns, go to *Settings > Drilldowns*. See also +To configure drilldowns, go to *Settings > Drilldowns*. See also <>. [float] [[graph-run-layout]] === Run and pause layout -Graph uses a "force layout", where vertices behave like magnets, -pushing off of one another. By default, when you add a new vertex to -the graph, all vertices begin moving. In some cases, the movement might -go on for some time. To freeze the current vertex position, +Graph uses a "force layout", where vertices behave like magnets, +pushing off of one another. By default, when you add a new vertex to +the graph, all vertices begin moving. In some cases, the movement might +go on for some time. To freeze the current vertex position, click the pause icon image:user/graph/images/graph-pause-button.png[Block selection] -in the control panel. +in the control panel. diff --git a/docs/user/monitoring/beats-details.asciidoc b/docs/user/monitoring/beats-details.asciidoc index 672ed6226e427..0b2be4dd9e3d9 100644 --- a/docs/user/monitoring/beats-details.asciidoc +++ b/docs/user/monitoring/beats-details.asciidoc @@ -13,7 +13,7 @@ image::user/monitoring/images/monitoring-beats.jpg["Monitoring Beats",link="imag To view an overview of the Beats data in the cluster, click *Overview*. The overview page has a section for activity in the last day, which is a real-time -sample of data. Below that, a summary bar and charts follow the typical paradigm +sample of data. The summary bar and charts follow the typical paradigm of data in the Monitoring UI, which is bound to the span of the time filter in the top right corner of the page. This overview page can therefore show up-to-date or historical information. diff --git a/docs/user/security/rbac_tutorial.asciidoc b/docs/user/security/rbac_tutorial.asciidoc index e4dbdc2483f70..d45aae86a9ccb 100644 --- a/docs/user/security/rbac_tutorial.asciidoc +++ b/docs/user/security/rbac_tutorial.asciidoc @@ -10,10 +10,10 @@ Kibana spaces. ==== Scenario Our user is a web developer working on a bank's -online mortgage service. The web developer has these +online mortgage service. The web developer has these three requirements: -* Have access to the data for that service +* Have access to the data for that service * Build visualizations and dashboards * Monitor the performance of the system @@ -24,28 +24,28 @@ You'll provide the web developer with the access and privileges to get the job d To complete this tutorial, you'll need the following: -* **Administrative privileges**: You must have a role that grants privileges to create a space, role, and user. This is any role which grants the `manage_security` cluster privilege. By default, the `superuser` role provides this access. See the {ref}/built-in-roles.html[built-in] roles. -* **A space**: In this tutorial, use `Dev Mortgage` as the space +* **Administrative privileges**: You must have a role that grants privileges to create a space, role, and user. This is any role which grants the `manage_security` cluster privilege. By default, the `superuser` role provides this access. See the {ref}/built-in-roles.html[built-in] roles. +* **A space**: In this tutorial, use `Dev Mortgage` as the space name. See <> for details on creating a space. -* **Data**: You can use <> or -live data. In the steps below, Filebeat and Metricbeat data are used. +* **Data**: You can use <> or +live data. In the following steps, Filebeat and Metricbeat data are used. [float] ==== Steps -With the requirements in mind, here are the steps that you will work +With the requirements in mind, here are the steps that you will work through in this tutorial: * Create a role named `mortgage-developer` * Give the role permission to access the data in the relevant indices -* Give the role permission to create visualizations and dashboards +* Give the role permission to create visualizations and dashboards * Create the web developer's user account with the proper roles [float] ==== Create a role -Go to **Management > Roles** +Go to **Management > Roles** for an overview of your roles. This view provides actions for you to create, edit, and delete roles. @@ -53,21 +53,21 @@ for you to create, edit, and delete roles. image::security/images/role-management.png["Role management"] -You can create as many roles as you like. Click *Create role* and -provide a name. Use `dev-mortgage` because this role is for a developer +You can create as many roles as you like. Click *Create role* and +provide a name. Use `dev-mortgage` because this role is for a developer working on the bank's mortgage application. [float] ==== Give the role permission to access the data -Access to data in indices is an index-level privilege, so in -*Index privileges*, add lines for the indices that contain the -data for this role. Two privileges are required: `read` and -`view_index_metadata`. All privileges are detailed in the +Access to data in indices is an index-level privilege, so in +*Index privileges*, add lines for the indices that contain the +data for this role. Two privileges are required: `read` and +`view_index_metadata`. All privileges are detailed in the https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html[security privileges] documentation. -In the screenshots, Filebeat and Metricbeat data is used, but you +In the screenshots, Filebeat and Metricbeat data is used, but you should use the index patterns for your indices. [role="screenshot"] @@ -76,12 +76,12 @@ image::security/images/role-index-privilege.png["Index privilege"] [float] ==== Give the role permission to create visualizations and dashboards -By default, roles do not give Kibana privileges. Click **Add space +By default, roles do not give Kibana privileges. Click **Add space privilege** and associate this role with the `Dev Mortgage` space. -To enable users with the `dev-mortgage` role to create visualizations -and dashboards, click *All* for *Visualize* and *Dashboard*. Also -assign *All* for *Discover* because it is common for developers +To enable users with the `dev-mortgage` role to create visualizations +and dashboards, click *All* for *Visualize* and *Dashboard*. Also +assign *All* for *Discover* because it is common for developers to create saved searches while designing visualizations. [role="screenshot"] @@ -90,15 +90,14 @@ image::security/images/role-space-visualization.png["Associate space"] [float] ==== Create the developer's user account with the proper roles -Go to **Management > Users** and click on **Create user** to create a -user. Give the user the `dev-mortgage` role +Go to **Management > Users** and click on **Create user** to create a +user. Give the user the `dev-mortgage` role and the `monitoring-user` role, which is required for users of **Stack Monitoring**. [role="screenshot"] image::security/images/role-new-user.png["Developer user"] -Finally, have the developer log in and access the Dev Mortgage space +Finally, have the developer log in and access the Dev Mortgage space and create a new visualization. NOTE: If the user is assigned to only one space, they will automatically enter that space on login. - diff --git a/docs/visualize/vega.asciidoc b/docs/visualize/vega.asciidoc index c9cf1e7aeb820..b8c0d1dbe3dda 100644 --- a/docs/visualize/vega.asciidoc +++ b/docs/visualize/vega.asciidoc @@ -324,7 +324,7 @@ replace `"url": "data/world-110m.json"` with `"url": "https://vega.github.io/editor/data/world-110m.json"`. Also, regular Vega examples use `"autosize": "pad"` layout model, whereas Kibana uses `fit`. Remove all `autosize`, `width`, and `height` -values. See link:#sizing-and-positioning[sizing and positioning] below. +values. See link:#sizing-and-positioning[sizing and positioning]. [[vega-additional-configuration-options]] ==== Additional configuration options From 7fa5c2face8211328417d7915dc05134e6fed805 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Mon, 16 Mar 2020 09:23:58 -0600 Subject: [PATCH 11/76] [skip-ci] Service Status RFC (#59621) --- rfcs/text/0010_service_status.md | 373 +++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 rfcs/text/0010_service_status.md diff --git a/rfcs/text/0010_service_status.md b/rfcs/text/0010_service_status.md new file mode 100644 index 0000000000000..ded594930a367 --- /dev/null +++ b/rfcs/text/0010_service_status.md @@ -0,0 +1,373 @@ +- Start Date: 2020-03-07 +- RFC PR: https://github.com/elastic/kibana/pull/59621 +- Kibana Issue: https://github.com/elastic/kibana/issues/41983 + +# Summary + +A set API for describing the current status of a system (Core service or plugin) +in Kibana. + +# Basic example + +```ts +// Override default behavior and only elevate severity when elasticsearch is not available +core.status.set( + core.status.core$.pipe(core => core.elasticsearch); +) +``` + +# Motivation + +Kibana should do as much possible to help users keep their installation in a working state. This includes providing as much detail about components that are not working as well as ensuring that failures in one part of the application do not block using other portions of the application. + +In order to provide the user with as much detail as possible about any systems that are not working correctly, the status mechanism should provide excellent defaults in terms of expressing relationships between services and presenting detailed information to the user. + +# Detailed design + +## Failure Guidelines + +While this RFC primarily describes how status information is signaled from individual services and plugins to Core, it's first important to define how Core expects these services and plugins to behave in the face of failure more broadly. + +Core is designed to be resilient and adaptive to change. When at all possible, Kibana should automatically recover from failure, rather than requiring any kind of intervention by the user or administrator. + +Given this goal, Core expects the following from plugins: +- During initialization, `setup`, and `start` plugins should only throw an exception if a truly unrecoverable issue is encountered. Examples: HTTP port is unavailable, server does not have the appropriate file permissions. +- Temporary error conditions should always be retried automatically. A user should not have to restart Kibana in order to resolve a problem when avoidable. This means all initialization code should include error handling and automated retries. Examples: creating an Elasticsearch index, connecting to an external service. + - It's important to note that some issues do require manual intervention in _other services_ (eg. Elasticsearch). Kibana should still recover without restarting once that external issue is resolved. +- Unhandled promise rejections are not permitted. In the future, Node.js will crash on unhandled promise rejections. It is impossible for Core to be able to properly handle and retry these situations, so all services and plugins should handle all rejected promises and retry when necessary. +- Plugins should only crash the Kibana server when absolutely necessary. Some features are considered "mission-critical" to customers and may need to halt Kibana if they are not functioning correctly. Example: audit logging. + +## API Design + +### Types + +```ts +/** + * The current status of a service at a point in time. + * + * @typeParam Meta - JSON-serializable object. Plugins should export this type to allow other plugins to read the `meta` + * field in a type-safe way. + */ +type ServiceStatus = unknown> = { + /** + * The current availability level of the service. + */ + level: ServiceStatusLevel.available; + /** + * A high-level summary of the service status. + */ + summary?: string; + /** + * A more detailed description of the service status. + */ + detail?: string; + /** + * A URL to open in a new tab about how to resolve or troubleshoot the problem. + */ + documentationUrl?: string; + /** + * Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained, + * machine-readable information about the service status. May include status information for underlying features. + */ + meta?: Meta; +} | { + level: ServiceStatusLevel; + summary: string; // required when level !== available + detail?: string; + documentationUrl?: string; + meta?: Meta; +} + +/** + * The current "level" of availability of a service. + */ +enum ServiceStatusLevel { + /** + * Everything is working! + */ + available, + /** + * Some features may not be working. + */ + degraded, + /** + * The service is unavailable, but other functions that do not depend on this service should work. + */ + unavailable, + /** + * Block all user functions and display the status page, reserved for Core services only. + * Note: In the real implementation, this will be split out to a different type. Kept as a single type here to make + * the RFC easier to follow. + */ + critical +} + +/** + * Status of core services. Only contains entries for backend services that could have a non-available `status`. + * For example, `context` cannot possibly be broken, so it is not included. + */ +interface CoreStatus { + elasticsearch: ServiceStatus; + http: ServiceStatus; + savedObjects: ServiceStatus; + uiSettings: ServiceStatus; + metrics: ServiceStatus; +} +``` + +### Plugin API + +```ts +/** + * The API exposed to plugins on CoreSetup.status + */ +interface StatusSetup { + /** + * Allows a plugin to specify a custom status dependent on its own criteria. + * Completely overrides the default inherited status. + */ + set(status$: Observable): void; + + /** + * Current status for all Core services. + */ + core$: Observable; + + /** + * Current status for all dependencies of the current plugin. + * Each key of the `Record` is a plugin id. + */ + plugins$: Observable>; + + /** + * The status of this plugin as derived from its dependencies. + * + * @remarks + * By default, plugins inherit this derived status from their dependencies. + * Calling {@link StatusSetup.set} overrides this default status. + */ + derivedStatus$: Observable; +} +``` + +### HTTP API + +The HTTP endpoint should return basic information about the Kibana node as well as the overall system status and the status of each individual system. + +This API does not need to include UI-specific details like the existing API such as `uiColor` and `icon`. + +```ts +/** + * Response type for the endpoint: GET /api/status + */ +interface StatusResponse { + /** server.name */ + name: string; + /** server.uuid */ + uuid: string; + /** Currently exposed by existing status API */ + version: { + number: string; + build_hash: string; + build_number: number; + build_snapshot: boolean; + }; + /** Similar format to existing API, but slightly different shape */ + status: { + /** See "Overall status calculation" section below */ + overall: ServiceStatus; + core: CoreStatus; + plugins: Record; + } +} +``` + +## Behaviors + +### Levels + +Each member of the `ServiceStatusLevel` enum has specific behaviors associated with it: +- **`available`**: + - All endpoints and apps associated with the service are accessible +- **`degraded`**: + - All endpoints and apps are available by default + - Some APIs may return `503 Unavailable` responses. This is not automatic, must be implemented directly by the service. + - Some plugin contract APIs may throw errors. This is not automatic, must be implemented directly by the service. +- **`unavailable`**: + - All endpoints (with some exceptions in Core) in Kibana return a `503 Unavailable` responses by default. This is automatic. + - When trying to access any app associated with the unavailable service, the user is presented with an error UI with detail about the outage. + - Some plugin contract APIs may throw errors. This is not automatic, must be implemented directly by the service. +- **`critical`**: + - All endpoints (with some exceptions in Core) in Kibana return a `503 Unavailable` response by default. This is automatic. + - All applications redirect to the system-wide status page with detail about which services are down and any relevant detail. This is automatic. + - Some plugin contract APIs may throw errors. This is not automatic, must be implemented directly by the service. + - This level is reserved for Core services only. + +### Overall status calculation + +The status level of the overall system is calculated to be the highest severity status of all core services and plugins. + +The `summary` property is calculated as follows: +- If the overall status level is `available`, the `summary` is `"Kibana is operating normally"` +- If a single core service or plugin is not `available`, the `summary` is `Kibana is ${level} due to ${serviceName}. See ${statusPageUrl} for more information.` +- If multiple core services or plugins are not `available`, the `summary` is `Kibana is ${level} due to multiple components. See ${statusPageUrl} for more information.` + +### Status inheritance + +By default, plugins inherit their status from all Core services and their dependencies on other plugins. + +This can be summarized by the following matrix: + +| core | required | optional | inherited | +|----------------|----------------|----------------|-------------| +| critical | _any_ | _any_ | critical | +| unavailable | <= unavailable | <= unavailable | unavailable | +| degraded | <= degraded | <= degraded | degraded | +| <= unavailable | unavailable | <= unavailable | unavailable | +| <= degraded | degraded | <= degraded | degraded | +| <= degraded | <= degraded | unavailable | degraded | +| <= degraded | <= degraded | degraded | degraded | +| available | available | available | available | + +If a plugin calls the `StatusSetup#set` API, the inherited status is completely overridden. They status the plugin specifies is the source of truth. If a plugin wishes to "merge" its custom status with the inherited status calculated by Core, it may do so by using the `StatusSetup#inherited$` property in its calculated status. + +If a plugin never calls the `StatusSetup#set` API, the plugin's status defaults to the inherited status. + +_Disabled_ plugins, that is plugins that are explicitly disabled in Kibana's configuration, do not have any status. They are not present in any status APIs and are **not** considered `unavailable`. Disabled plugins are excluded from the status inheritance calculation, even if a plugin has a optional dependency on a disabled plugin. In summary, if a plugin has an optional dependency on a disabled plugin, the plugin will not be considered `degraded` just because that optional dependency is disabled. + +### HTTP responses + +As specified in the [_Levels section_](#levels), a service's HTTP endpoints will respond with `503 Unavailable` responses in some status levels. + +In both the `critical` and `unavailable` levels, all of a service's endpoints will return 503s. However, in the `degraded` level, it is up to service authors to decide which endpoints should return a 503. This may be implemented directly in the route handler logic or by using any of the [utilities provided](#status-utilities). + +When a 503 is returned either via the default behavior or behavior implemented using the [provided utilities](#status-utilities), the HTTP response will include the following: +- `Retry-After` header, set to `60` seconds +- A body with mime type `application/json` containing the status of the service the HTTP route belongs to: + ```json5 + { + "error": "Unavailable", + // `ServiceStatus#summary` + "message": "Newsfeed API cannot be reached", + "attributes": { + "status": { + // Human readable form of `ServiceStatus#level` + "level": "critical", + // `ServiceStatus#summary` + "summary": "Newsfeed API cannot be reached", + // `ServiceStatus#detail` or null + "detail": null, + // `ServiceStatus#documentationUrl` or null + "documentationUrl": null, + // JSON-serialized from `ServiceStatus#meta` or null + "meta": {} + } + }, + "statusCode": 503 + } + ``` + +## Status Utilities + +Though many plugins should be able to rely on the default status inheritance and associated behaviors, there are common patterns and overrides that some plugins will need. The status service should provide some utilities for these common patterns out-of-the-box. + +```ts +/** + * Extension of the main Status API + */ +interface StatusSetup { + /** + * Helpers for expressing status in HTTP routes. + */ + http: { + /** + * High-order route handler function for wrapping routes with 503 logic based + * on a predicate. + * + * @remarks + * When a 503 is returned, it also includes detailed information from the service's + * current `ServiceStatus` including `meta` information. + * + * @example + * ```ts + * router.get( + * { path: '/my-api' } + * unavailableWhen( + * ServiceStatusLevel.degraded, + * async (context, req, res) => { + * return res.ok({ body: 'done' }); + * } + * ) + * ) + * ``` + * + * @param predicate When a level is specified, if the plugin's current status + * level is >= to the severity of the specified level, route + * returns a 503. When a function is specified, if that + * function returns `true`, a 503 is returned. + * @param handler The route handler to execute when a 503 is not returned. + * @param options.retryAfter Number of seconds to set the `Retry-After` + * header to when the endpoint is unavailable. + * Defaults to `60`. + */ + unavailableWhen( + predicate: ServiceStatusLevel | + (self: ServiceStatus, core: CoreStatus, plugins: Record) => boolean, + handler: RouteHandler, + options?: { retryAfter?: number } + ): RouteHandler; + } +} +``` + +## Additional Examples + +### Combine inherited status with check against external dependency +```ts +const getExternalDepHealth = async () => { + const resp = await window.fetch('https://myexternaldep.com/_healthz'); + return resp.json(); +} + +// Create an observable that checks the status of an external service every every 10s +const myExternalDependency$: Observable = interval(10000).pipe( + mergeMap(() => of(getExternalDepHealth())), + map(health => health.ok ? ServiceStatusLevel.available : ServiceStatusLevel.unavailable), + catchError(() => of(ServiceStatusLevel.unavailable)) +); + +// Merge the inherited status with the external check +core.status.set( + combineLatest( + core.status.inherited$, + myExternalDependency$ + ).pipe( + map(([inherited, external]) => ({ + level: Math.max(inherited.level, external) + })) + ) +); +``` + +# Drawbacks + +1. **The default behaviors and inheritance of statuses may appear to be "magic" to developers who do not read the documentation about how this works.** Compared to the legacy status mechanism, these defaults are much more opinionated and the resulting status is less explicit in plugin code compared to the legacy `mirrorPluginStatus` mechanism. +2. **The default behaviors and inheritance may not fit real-world status very well.** If many plugins must customize their status in order to opt-out of the defaults, this would be a step backwards from the legacy mechanism. + +# Alternatives + +We could somewhat reduce the complexity of the status inheritance by leveraging the dependencies between plugins to enable and disable plugins based on whether or not their upstream dependencies are available. This may simplify plugin code but would greatly complicate how Kibana fundamentally operates, requiring that plugins may get stopped and started multiple times within a single Kibana server process. We would be trading simplicity in one area for complexity in another. + +# Adoption strategy + +By default, most plugins would not need to do much at all. Today, very few plugins leverage the legacy status system. The majority of ones that do, simply call the `mirrorPluginStatus` utility to follow the status of the legacy elasticsearch plugin. + +Plugins that wish to expose more detail about their availability will easily be able to do so, including providing detailed information such as links to documentation to resolve the problem. + +# How we teach this + +This largely follows the same patterns we have used for other Core APIs: Observables, composable utilties, etc. + +This should be taught using the same channels we've leveraged for other Kibana Platform APIs: API documentation, additions to the [Migration Guide](../../src/core/MIGRATION.md) and [Migration Examples](../../src/core/MIGRATION_EXMAPLES.md). + +# Unresolved questions From 6a648658ce5d331bc781da707a5e66e8899e98ee Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Mon, 16 Mar 2020 16:58:17 +0100 Subject: [PATCH 12/76] Bump acorn sub-dependency to version ^7.1.1 (#60239) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5b13c8bd37aed..ae55508cee886 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5921,9 +5921,9 @@ acorn@^6.2.1: integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== acorn@^7.0.0, acorn@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" - integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== address@1.1.0: version "1.1.0" From 3a4683ce70cbcae7a0c382c55c33b7d43a4db426 Mon Sep 17 00:00:00 2001 From: Chris Queen Date: Mon, 16 Mar 2020 12:37:51 -0400 Subject: [PATCH 13/76] Passing in a null value as a param to compareFilters no longer throws an exception (#59609) * The 'filter' parameter passed into the mapFilter function is now checked for nullish-ness via optional chaining. * Added tests in compare_filters.test.ts for when compareFilters accepts a null value for it's 'filter' parameter * Removed recently added optional chaining to the filters param in the mapFilter function. Instead, the compareFilters function now performs a null check on both filter params, and returns false if either are null * Updated null check in compareFilters function to use negation null check instead of checking strictly for a null value * fix tests types Co-authored-by: Elastic Machine Co-authored-by: Liza K --- .../filter_manager/lib/compare_filters.test.ts | 16 ++++++++++++++++ .../query/filter_manager/lib/compare_filters.ts | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts index 5d6c25b0d96c1..da8f5b3564948 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts @@ -48,6 +48,22 @@ describe('filter manager utilities', () => { expect(compareFilters(f1, f2)).toBeTruthy(); }); + test('should compare filters, where one filter is null', () => { + const f1 = buildQueryFilter( + { _type: { match: { query: 'apache', type: 'phrase' } } }, + 'index', + '' + ); + const f2 = null; + expect(compareFilters(f1, f2 as any)).toBeFalsy(); + }); + + test('should compare a null filter with an empty filter', () => { + const f1 = null; + const f2 = buildEmptyFilter(true); + expect(compareFilters(f1 as any, f2)).toBeFalsy(); + }); + test('should compare duplicates, ignoring meta attributes', () => { const f1 = buildQueryFilter( { _type: { match: { query: 'apache', type: 'phrase' } } }, diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts index b4402885bc0be..a2105fdc1d3ef 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts @@ -74,6 +74,8 @@ export const compareFilters = ( second: Filter | Filter[], comparatorOptions: FilterCompareOptions = {} ) => { + if (!first || !second) return false; + let comparators: FilterCompareOptions = {}; const excludedAttributes: string[] = ['$$hashKey', 'meta']; From 8a578960c05f04879963927d4f80075736e73e8a Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 16 Mar 2020 17:28:07 +0000 Subject: [PATCH 14/76] [ML] Use real datafeed ID for datafeed preview (#60275) --- .../jobs_list/components/job_details/datafeed_preview_tab.js | 5 +++-- x-pack/plugins/ml/public/application/services/job_service.js | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js index 7a98ec5e5ce4a..216c416f30a6b 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/datafeed_preview_tab.js @@ -57,7 +57,8 @@ export class DatafeedPreviewPane extends Component { } componentDidMount() { - const canPreviewDatafeed = checkPermission('canPreviewDatafeed'); + const canPreviewDatafeed = + checkPermission('canPreviewDatafeed') && this.props.job.datafeed_config !== undefined; this.setState({ canPreviewDatafeed }); updateDatafeedPreview(this.props.job, canPreviewDatafeed) @@ -87,7 +88,7 @@ function updateDatafeedPreview(job, canPreviewDatafeed) { return new Promise((resolve, reject) => { if (canPreviewDatafeed) { mlJobService - .getDatafeedPreview(job.job_id) + .getDatafeedPreview(job.datafeed_config.datafeed_id) .then(resp => { if (Array.isArray(resp)) { resolve(JSON.stringify(resp.slice(0, ML_DATA_PREVIEW_COUNT), null, 2)); diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index fe3663d6a3ddb..f092e85bef5ce 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -747,8 +747,7 @@ class JobService { return datafeedId; } - getDatafeedPreview(jobId) { - const datafeedId = this.getDatafeedId(jobId); + getDatafeedPreview(datafeedId) { return ml.datafeedPreview({ datafeedId }); } From dfff4fd6fa8a8063c2d30a8ebb16228da702f12d Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 16 Mar 2020 12:18:27 -0600 Subject: [PATCH 15/76] [SIEM][Detection Engine] Refactors signal rule alert type into smaller code by creating functions Refactors signal rule alert type into a smaller executor ## Summary * Breaks out the schema into its own file and function * Breaks out the action group into its own file and function * Moves misc types being added to this into the `./types` file * Breaks out all the writing of errors and success into their own functions * Uses destructuring to pull data out of some of the data types * Tweaks the gap detection to accept a date instead of moment to ease "ergonomics" * Updates unit tests for the gap detection ### Checklist Delete any items that are not applicable to this PR. - [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 --- .../get_current_status_saved_object.ts | 56 ++++ .../signals/get_rule_status_saved_objects.ts | 31 ++ .../signals/siem_rule_action_groups.ts | 16 + .../signals/signal_params_schema.ts | 40 +++ .../signals/signal_rule_alert_type.ts | 300 +++++------------- .../lib/detection_engine/signals/types.ts | 12 + .../detection_engine/signals/utils.test.ts | 48 ++- .../lib/detection_engine/signals/utils.ts | 2 +- .../signals/write_current_status_succeeded.ts | 30 ++ .../write_gap_error_to_saved_object.ts | 61 ++++ ...e_signal_rule_exception_to_saved_object.ts | 58 ++++ 11 files changed, 416 insertions(+), 238 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/siem_rule_action_groups.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts new file mode 100644 index 0000000000000..e5057b6b68997 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_current_status_saved_object.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsFindResponse, SavedObject } from 'src/core/server'; + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; +import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; + +interface CurrentStatusSavedObjectParams { + alertId: string; + services: AlertServices; + ruleStatusSavedObjects: SavedObjectsFindResponse; +} + +export const getCurrentStatusSavedObject = async ({ + alertId, + services, + ruleStatusSavedObjects, +}: CurrentStatusSavedObjectParams): Promise> => { + if (ruleStatusSavedObjects.saved_objects.length === 0) { + // create + const date = new Date().toISOString(); + const currentStatusSavedObject = await services.savedObjectsClient.create< + IRuleSavedAttributesSavedObjectAttributes + >(ruleStatusSavedObjectType, { + alertId, // do a search for this id. + statusDate: date, + status: 'going to run', + lastFailureAt: null, + lastSuccessAt: null, + lastFailureMessage: null, + lastSuccessMessage: null, + }); + return currentStatusSavedObject; + } else { + // update 0th to executing. + const currentStatusSavedObject = ruleStatusSavedObjects.saved_objects[0]; + const sDate = new Date().toISOString(); + currentStatusSavedObject.attributes.status = 'going to run'; + currentStatusSavedObject.attributes.statusDate = sDate; + await services.savedObjectsClient.update( + ruleStatusSavedObjectType, + currentStatusSavedObject.id, + { + ...currentStatusSavedObject.attributes, + } + ); + return currentStatusSavedObject; + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts new file mode 100644 index 0000000000000..5a59d0413cfb9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsFindResponse } from 'kibana/server'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; +import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; + +interface GetRuleStatusSavedObject { + alertId: string; + services: AlertServices; +} + +export const getRuleStatusSavedObjects = async ({ + alertId, + services, +}: GetRuleStatusSavedObject): Promise> => { + return services.savedObjectsClient.find({ + type: ruleStatusSavedObjectType, + perPage: 6, // 0th element is current status, 1-5 is last 5 failures. + sortField: 'statusDate', + sortOrder: 'desc', + search: `${alertId}`, + searchFields: ['alertId'], + }); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/siem_rule_action_groups.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/siem_rule_action_groups.ts new file mode 100644 index 0000000000000..50c63df14996b --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/siem_rule_action_groups.ts @@ -0,0 +1,16 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const siemRuleActionGroups = [ + { + id: 'default', + name: i18n.translate('xpack.siem.detectionEngine.signalRuleAlert.actionGroups.default', { + defaultMessage: 'Default', + }), + }, +]; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts new file mode 100644 index 0000000000000..d1726f93108c7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts @@ -0,0 +1,40 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants'; + +/** + * This is the schema for the Alert Rule that represents the SIEM alert for signals + * that index into the .siem-signals-${space-id} + */ +export const signalParamsSchema = () => + schema.object({ + description: schema.string(), + note: schema.nullable(schema.string()), + falsePositives: schema.arrayOf(schema.string(), { defaultValue: [] }), + from: schema.string(), + ruleId: schema.string(), + immutable: schema.boolean({ defaultValue: false }), + index: schema.nullable(schema.arrayOf(schema.string())), + language: schema.nullable(schema.string()), + outputIndex: schema.nullable(schema.string()), + savedId: schema.nullable(schema.string()), + timelineId: schema.nullable(schema.string()), + timelineTitle: schema.nullable(schema.string()), + meta: schema.nullable(schema.object({}, { allowUnknowns: true })), + query: schema.nullable(schema.string()), + filters: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + maxSignals: schema.number({ defaultValue: DEFAULT_MAX_SIGNALS }), + riskScore: schema.number(), + severity: schema.string(), + threat: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + to: schema.string(), + type: schema.string(), + references: schema.arrayOf(schema.string(), { defaultValue: [] }), + version: schema.number({ defaultValue: 1 }), + }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts index b467dfdaff305..e3ea121a9ebb1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -4,35 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; -import moment from 'moment'; -import { i18n } from '@kbn/i18n'; -import { - SIGNALS_ID, - DEFAULT_MAX_SIGNALS, - DEFAULT_SEARCH_AFTER_PAGE_SIZE, -} from '../../../../common/constants'; +import { SIGNALS_ID, DEFAULT_SEARCH_AFTER_PAGE_SIZE } from '../../../../common/constants'; import { buildEventsSearchQuery } from './build_events_query'; import { getInputIndex } from './get_input_output_index'; import { searchAfterAndBulkCreate } from './search_after_bulk_create'; import { getFilter } from './get_filter'; -import { SignalRuleAlertTypeDefinition } from './types'; +import { SignalRuleAlertTypeDefinition, AlertAttributes } from './types'; import { getGapBetweenRuns } from './utils'; -import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; -import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; -interface AlertAttributes { - enabled: boolean; - name: string; - tags: string[]; - createdBy: string; - createdAt: string; - updatedBy: string; - schedule: { - interval: string; - }; -} +import { writeSignalRuleExceptionToSavedObject } from './write_signal_rule_exception_to_saved_object'; +import { signalParamsSchema } from './signal_params_schema'; +import { siemRuleActionGroups } from './siem_rule_action_groups'; +import { writeGapErrorToSavedObject } from './write_gap_error_to_saved_object'; +import { getRuleStatusSavedObjects } from './get_rule_status_saved_objects'; +import { getCurrentStatusSavedObject } from './get_current_status_saved_object'; +import { writeCurrentStatusSucceeded } from './write_current_status_succeeded'; + export const signalRulesAlertType = ({ logger, version, @@ -43,43 +31,11 @@ export const signalRulesAlertType = ({ return { id: SIGNALS_ID, name: 'SIEM Signals', - actionGroups: [ - { - id: 'default', - name: i18n.translate('xpack.siem.detectionEngine.signalRuleAlert.actionGroups.default', { - defaultMessage: 'Default', - }), - }, - ], + actionGroups: siemRuleActionGroups, defaultActionGroupId: 'default', validate: { - params: schema.object({ - description: schema.string(), - note: schema.nullable(schema.string()), - falsePositives: schema.arrayOf(schema.string(), { defaultValue: [] }), - from: schema.string(), - ruleId: schema.string(), - immutable: schema.boolean({ defaultValue: false }), - index: schema.nullable(schema.arrayOf(schema.string())), - language: schema.nullable(schema.string()), - outputIndex: schema.nullable(schema.string()), - savedId: schema.nullable(schema.string()), - timelineId: schema.nullable(schema.string()), - timelineTitle: schema.nullable(schema.string()), - meta: schema.nullable(schema.object({}, { allowUnknowns: true })), - query: schema.nullable(schema.string()), - filters: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), - maxSignals: schema.number({ defaultValue: DEFAULT_MAX_SIGNALS }), - riskScore: schema.number(), - severity: schema.string(), - threat: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), - to: schema.string(), - type: schema.string(), - references: schema.arrayOf(schema.string(), { defaultValue: [] }), - version: schema.number({ defaultValue: 1 }), - }), + params: signalParamsSchema(), }, - // fun fact: previousStartedAt is not actually a Date but a String of a date async executor({ previousStartedAt, alertId, services, params }) { const { from, @@ -93,89 +49,43 @@ export const signalRulesAlertType = ({ to, type, } = params; - // TODO: Remove this hard extraction of name once this is fixed: https://github.com/elastic/kibana/issues/50522 const savedObject = await services.savedObjectsClient.get('alert', alertId); - const ruleStatusSavedObjects = await services.savedObjectsClient.find< - IRuleSavedAttributesSavedObjectAttributes - >({ - type: ruleStatusSavedObjectType, - perPage: 6, // 0th element is current status, 1-5 is last 5 failures. - sortField: 'statusDate', - sortOrder: 'desc', - search: `${alertId}`, - searchFields: ['alertId'], + + const ruleStatusSavedObjects = await getRuleStatusSavedObjects({ + alertId, + services, }); - let currentStatusSavedObject; - if (ruleStatusSavedObjects.saved_objects.length === 0) { - // create - const date = new Date().toISOString(); - currentStatusSavedObject = await services.savedObjectsClient.create< - IRuleSavedAttributesSavedObjectAttributes - >(ruleStatusSavedObjectType, { - alertId, // do a search for this id. - statusDate: date, - status: 'going to run', - lastFailureAt: null, - lastSuccessAt: null, - lastFailureMessage: null, - lastSuccessMessage: null, - }); - } else { - // update 0th to executing. - currentStatusSavedObject = ruleStatusSavedObjects.saved_objects[0]; - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'going to run'; - currentStatusSavedObject.attributes.statusDate = sDate; - await services.savedObjectsClient.update( - ruleStatusSavedObjectType, - currentStatusSavedObject.id, - { - ...currentStatusSavedObject.attributes, - } - ); - } - const name = savedObject.attributes.name; - const tags = savedObject.attributes.tags; + const currentStatusSavedObject = await getCurrentStatusSavedObject({ + alertId, + services, + ruleStatusSavedObjects, + }); + + const { + name, + tags, + createdAt, + createdBy, + updatedBy, + enabled, + schedule: { interval }, + } = savedObject.attributes; - const createdBy = savedObject.attributes.createdBy; - const createdAt = savedObject.attributes.createdAt; - const updatedBy = savedObject.attributes.updatedBy; const updatedAt = savedObject.updated_at ?? ''; - const interval = savedObject.attributes.schedule.interval; - const enabled = savedObject.attributes.enabled; - const gap = getGapBetweenRuns({ - previousStartedAt: previousStartedAt != null ? moment(previousStartedAt) : null, // TODO: Remove this once previousStartedAt is no longer a string - interval, - from, - to, - }); - if (gap != null && gap.asMilliseconds() > 0) { - logger.warn( - `Signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" has a time gap of ${gap.humanize()} (${gap.asMilliseconds()}ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.` - ); - // write a failure status whenever we have a time gap - // this is a temporary solution until general activity - // monitoring is developed as a feature - const gapDate = new Date().toISOString(); - await services.savedObjectsClient.create(ruleStatusSavedObjectType, { - alertId, - statusDate: gapDate, - status: 'failed', - lastFailureAt: gapDate, - lastSuccessAt: currentStatusSavedObject.attributes.lastSuccessAt, - lastFailureMessage: `Signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" has a time gap of ${gap.humanize()} (${gap.asMilliseconds()}ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.`, - lastSuccessMessage: currentStatusSavedObject.attributes.lastSuccessMessage, - }); - if (ruleStatusSavedObjects.saved_objects.length >= 6) { - // delete fifth status and prepare to insert a newer one. - const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); - await toDelete.forEach(async item => - services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) - ); - } - } + const gap = getGapBetweenRuns({ previousStartedAt, interval, from, to }); + + await writeGapErrorToSavedObject({ + alertId, + logger, + ruleId: ruleId ?? '(unknown rule id)', + currentStatusSavedObject, + services, + gap, + ruleStatusSavedObjects, + name, + }); // set searchAfter page size to be the lesser of default page size or maxSignals. const searchAfterSize = DEFAULT_SEARCH_AFTER_PAGE_SIZE <= params.maxSignals @@ -243,107 +153,45 @@ export const signalRulesAlertType = ({ logger.debug( `Finished signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}"` ); - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'succeeded'; - currentStatusSavedObject.attributes.statusDate = sDate; - currentStatusSavedObject.attributes.lastSuccessAt = sDate; - currentStatusSavedObject.attributes.lastSuccessMessage = 'succeeded'; - await services.savedObjectsClient.update( - ruleStatusSavedObjectType, - currentStatusSavedObject.id, - { - ...currentStatusSavedObject.attributes, - } - ); + await writeCurrentStatusSucceeded({ + services, + currentStatusSavedObject, + }); } else { - logger.error( - `Error processing signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}"` - ); - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'failed'; - currentStatusSavedObject.attributes.statusDate = sDate; - currentStatusSavedObject.attributes.lastFailureAt = sDate; - currentStatusSavedObject.attributes.lastFailureMessage = `Bulk Indexing signals failed. Check logs for further details \nRule name: "${name}"\nid: "${alertId}"\nrule_id: "${ruleId}"\n`; - // current status is failing - await services.savedObjectsClient.update( - ruleStatusSavedObjectType, - currentStatusSavedObject.id, - { - ...currentStatusSavedObject.attributes, - } - ); - // create new status for historical purposes - await services.savedObjectsClient.create(ruleStatusSavedObjectType, { - ...currentStatusSavedObject.attributes, + await writeSignalRuleExceptionToSavedObject({ + name, + alertId, + currentStatusSavedObject, + logger, + message: `Bulk Indexing signals failed. Check logs for further details \nRule name: "${name}"\nid: "${alertId}"\nrule_id: "${ruleId}"\n`, + services, + ruleStatusSavedObjects, + ruleId: ruleId ?? '(unknown rule id)', }); - - if (ruleStatusSavedObjects.saved_objects.length >= 6) { - // delete fifth status and prepare to insert a newer one. - const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); - await toDelete.forEach(async item => - services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) - ); - } } } catch (err) { - logger.error( - `Error from signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}", ${err.message}` - ); - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'failed'; - currentStatusSavedObject.attributes.statusDate = sDate; - currentStatusSavedObject.attributes.lastFailureAt = sDate; - currentStatusSavedObject.attributes.lastFailureMessage = err.message; - // current status is failing - await services.savedObjectsClient.update( - ruleStatusSavedObjectType, - currentStatusSavedObject.id, - { - ...currentStatusSavedObject.attributes, - } - ); - // create new status for historical purposes - await services.savedObjectsClient.create(ruleStatusSavedObjectType, { - ...currentStatusSavedObject.attributes, + await writeSignalRuleExceptionToSavedObject({ + name, + alertId, + currentStatusSavedObject, + logger, + message: err?.message ?? '(no error message given)', + services, + ruleStatusSavedObjects, + ruleId: ruleId ?? '(unknown rule id)', }); - - if (ruleStatusSavedObjects.saved_objects.length >= 6) { - // delete fifth status and prepare to insert a newer one. - const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); - await toDelete.forEach(async item => - services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) - ); - } } } catch (exception) { - logger.error( - `Error from signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" message: ${exception.message}` - ); - const sDate = new Date().toISOString(); - currentStatusSavedObject.attributes.status = 'failed'; - currentStatusSavedObject.attributes.statusDate = sDate; - currentStatusSavedObject.attributes.lastFailureAt = sDate; - currentStatusSavedObject.attributes.lastFailureMessage = exception.message; - // current status is failing - await services.savedObjectsClient.update( - ruleStatusSavedObjectType, - currentStatusSavedObject.id, - { - ...currentStatusSavedObject.attributes, - } - ); - // create new status for historical purposes - await services.savedObjectsClient.create(ruleStatusSavedObjectType, { - ...currentStatusSavedObject.attributes, + await writeSignalRuleExceptionToSavedObject({ + name, + alertId, + currentStatusSavedObject, + logger, + message: exception?.message ?? '(no error message given)', + services, + ruleStatusSavedObjects, + ruleId: ruleId ?? '(unknown rule id)', }); - - if (ruleStatusSavedObjects.saved_objects.length >= 6) { - // delete fifth status and prepare to insert a newer one. - const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); - await toDelete.forEach(async item => - services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) - ); - } } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts index 7442545117310..eaed3f2ead3a5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts @@ -145,3 +145,15 @@ export interface SignalHit { event: object; signal: Partial; } + +export interface AlertAttributes { + enabled: boolean; + name: string; + tags: string[]; + createdBy: string; + createdAt: string; + updatedBy: string; + schedule: { + interval: string; + }; +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts index bf25ab8bfd7ea..873e06fcbb44e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.test.ts @@ -179,7 +179,10 @@ describe('utils', () => { describe('getGapBetweenRuns', () => { test('it returns a gap of 0 when "from" and interval match each other and the previous started was from the previous interval time', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(5, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(5, 'minutes') + .toDate(), interval: '5m', from: 'now-5m', to: 'now', @@ -191,7 +194,10 @@ describe('utils', () => { test('it returns a negative gap of 1 minute when "from" overlaps to by 1 minute and the previousStartedAt was 5 minutes ago', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(5, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(5, 'minutes') + .toDate(), interval: '5m', from: 'now-6m', to: 'now', @@ -203,7 +209,10 @@ describe('utils', () => { test('it returns a negative gap of 5 minutes when "from" overlaps to by 1 minute and the previousStartedAt was 5 minutes ago', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(5, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(5, 'minutes') + .toDate(), interval: '5m', from: 'now-10m', to: 'now', @@ -215,7 +224,10 @@ describe('utils', () => { test('it returns a negative gap of 1 minute when "from" overlaps to by 1 minute and the previousStartedAt was 10 minutes ago and so was the interval', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(10, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(10, 'minutes') + .toDate(), interval: '10m', from: 'now-11m', to: 'now', @@ -230,7 +242,8 @@ describe('utils', () => { previousStartedAt: nowDate .clone() .subtract(5, 'minutes') - .subtract(30, 'seconds'), + .subtract(30, 'seconds') + .toDate(), interval: '5m', from: 'now-6m', to: 'now', @@ -242,7 +255,10 @@ describe('utils', () => { test('it returns an exact 0 gap when the from overlaps with now by 1 minute, the interval is 5 minutes but the previous started is one minute late', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(6, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(6, 'minutes') + .toDate(), interval: '5m', from: 'now-6m', to: 'now', @@ -257,7 +273,8 @@ describe('utils', () => { previousStartedAt: nowDate .clone() .subtract(6, 'minutes') - .subtract(30, 'seconds'), + .subtract(30, 'seconds') + .toDate(), interval: '5m', from: 'now-6m', to: 'now', @@ -269,7 +286,10 @@ describe('utils', () => { test('it returns a gap of 1 minute when the from overlaps with now by 1 minute, the interval is 5 minutes but the previous started is two minutes late', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(7, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(7, 'minutes') + .toDate(), interval: '5m', from: 'now-6m', to: 'now', @@ -292,7 +312,7 @@ describe('utils', () => { test('it returns null if the interval is an invalid string such as "invalid"', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone(), + previousStartedAt: nowDate.clone().toDate(), interval: 'invalid', // if not set to "x" where x is an interval such as 6m from: 'now-5m', to: 'now', @@ -303,7 +323,10 @@ describe('utils', () => { test('it returns the expected result when "from" is an invalid string such as "invalid"', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(7, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(7, 'minutes') + .toDate(), interval: '5m', from: 'invalid', to: 'now', @@ -315,7 +338,10 @@ describe('utils', () => { test('it returns the expected result when "to" is an invalid string such as "invalid"', () => { const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(7, 'minutes'), + previousStartedAt: nowDate + .clone() + .subtract(7, 'minutes') + .toDate(), interval: '5m', from: 'now-6m', to: 'invalid', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts index 016aed9fabcd6..8e7fb9c38d658 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/utils.ts @@ -68,7 +68,7 @@ export const getGapBetweenRuns = ({ to, now = moment(), }: { - previousStartedAt: moment.Moment | undefined | null; + previousStartedAt: Date | undefined | null; interval: string; from: string; to: string; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.ts new file mode 100644 index 0000000000000..6b06235b29063 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_current_status_succeeded.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObject } from 'src/core/server'; +import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; + +interface GetRuleStatusSavedObject { + services: AlertServices; + currentStatusSavedObject: SavedObject; +} + +export const writeCurrentStatusSucceeded = async ({ + services, + currentStatusSavedObject, +}: GetRuleStatusSavedObject): Promise => { + const sDate = new Date().toISOString(); + currentStatusSavedObject.attributes.status = 'succeeded'; + currentStatusSavedObject.attributes.statusDate = sDate; + currentStatusSavedObject.attributes.lastSuccessAt = sDate; + currentStatusSavedObject.attributes.lastSuccessMessage = 'succeeded'; + await services.savedObjectsClient.update(ruleStatusSavedObjectType, currentStatusSavedObject.id, { + ...currentStatusSavedObject.attributes, + }); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts new file mode 100644 index 0000000000000..3650548c80ad5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_gap_error_to_saved_object.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment'; +import { Logger, SavedObject, SavedObjectsFindResponse } from 'src/core/server'; + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; +import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; + +interface WriteGapErrorToSavedObjectParams { + logger: Logger; + alertId: string; + ruleId: string; + currentStatusSavedObject: SavedObject; + ruleStatusSavedObjects: SavedObjectsFindResponse; + services: AlertServices; + gap: moment.Duration | null | undefined; + name: string; +} + +export const writeGapErrorToSavedObject = async ({ + alertId, + currentStatusSavedObject, + logger, + services, + ruleStatusSavedObjects, + ruleId, + gap, + name, +}: WriteGapErrorToSavedObjectParams): Promise => { + if (gap != null && gap.asMilliseconds() > 0) { + logger.warn( + `Signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" has a time gap of ${gap.humanize()} (${gap.asMilliseconds()}ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.` + ); + // write a failure status whenever we have a time gap + // this is a temporary solution until general activity + // monitoring is developed as a feature + const gapDate = new Date().toISOString(); + await services.savedObjectsClient.create(ruleStatusSavedObjectType, { + alertId, + statusDate: gapDate, + status: 'failed', + lastFailureAt: gapDate, + lastSuccessAt: currentStatusSavedObject.attributes.lastSuccessAt, + lastFailureMessage: `Signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" has a time gap of ${gap.humanize()} (${gap.asMilliseconds()}ms), and could be missing signals within that time. Consider increasing your look behind time or adding more Kibana instances.`, + lastSuccessMessage: currentStatusSavedObject.attributes.lastSuccessMessage, + }); + + if (ruleStatusSavedObjects.saved_objects.length >= 6) { + // delete fifth status and prepare to insert a newer one. + const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); + await toDelete.forEach(async item => + services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) + ); + } + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts new file mode 100644 index 0000000000000..5ca0808902a52 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/write_signal_rule_exception_to_saved_object.ts @@ -0,0 +1,58 @@ +/* + * 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 { Logger, SavedObject, SavedObjectsFindResponse } from 'src/core/server'; + +import { AlertServices } from '../../../../../../../plugins/alerting/server'; +import { IRuleSavedAttributesSavedObjectAttributes } from '../rules/types'; +import { ruleStatusSavedObjectType } from '../rules/saved_object_mappings'; + +interface SignalRuleExceptionParams { + logger: Logger; + alertId: string; + ruleId: string; + currentStatusSavedObject: SavedObject; + ruleStatusSavedObjects: SavedObjectsFindResponse; + message: string; + services: AlertServices; + name: string; +} + +export const writeSignalRuleExceptionToSavedObject = async ({ + alertId, + currentStatusSavedObject, + logger, + message, + services, + ruleStatusSavedObjects, + ruleId, + name, +}: SignalRuleExceptionParams): Promise => { + logger.error( + `Error from signal rule name: "${name}", id: "${alertId}", rule_id: "${ruleId}" message: ${message}` + ); + const sDate = new Date().toISOString(); + currentStatusSavedObject.attributes.status = 'failed'; + currentStatusSavedObject.attributes.statusDate = sDate; + currentStatusSavedObject.attributes.lastFailureAt = sDate; + currentStatusSavedObject.attributes.lastFailureMessage = message; + // current status is failing + await services.savedObjectsClient.update(ruleStatusSavedObjectType, currentStatusSavedObject.id, { + ...currentStatusSavedObject.attributes, + }); + // create new status for historical purposes + await services.savedObjectsClient.create(ruleStatusSavedObjectType, { + ...currentStatusSavedObject.attributes, + }); + + if (ruleStatusSavedObjects.saved_objects.length >= 6) { + // delete fifth status and prepare to insert a newer one. + const toDelete = ruleStatusSavedObjects.saved_objects.slice(5); + await toDelete.forEach(async item => + services.savedObjectsClient.delete(ruleStatusSavedObjectType, item.id) + ); + } +}; From 6cd888f75fefc6d3ec4405f7b937069e5ea9bf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 16 Mar 2020 19:47:56 +0100 Subject: [PATCH 16/76] [Logs UI] Fix log rate table row expansion (#60096) This fixes the log rate table row expansion button, which broke in #54586 during a refactoring. --- .../sections/anomalies/table.tsx | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx index 6eaa5de900080..5cb5f3a993d48 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/table.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBasicTable } from '@elastic/eui'; +import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import { i18n } from '@kbn/i18n'; import React, { useCallback, useMemo, useState } from 'react'; @@ -20,8 +20,8 @@ import { LogEntryRateResults } from '../../use_log_entry_rate_results'; import { AnomaliesTableExpandedRow } from './expanded_row'; interface TableItem { - id: string; - partition: string; + partitionName: string; + partitionId: string; topAnomalyScore: number; } @@ -55,11 +55,10 @@ export const AnomaliesTable: React.FunctionComponent<{ const tableItems: TableItem[] = useMemo(() => { return Object.entries(results.partitionBuckets).map(([key, value]) => { return { - // Note: EUI's table expanded rows won't work with a key of '' in itemIdToExpandedRowMap, so we have to use the friendly name here - id: getFriendlyNameForPartitionId(key), // The real ID partitionId: key, - partition: getFriendlyNameForPartitionId(key), + // Note: EUI's table expanded rows won't work with a key of '' in itemIdToExpandedRowMap, so we have to use the friendly name here + partitionName: getFriendlyNameForPartitionId(key), topAnomalyScore: formatAnomalyScore(value.topAnomalyScore), }; }); @@ -91,8 +90,8 @@ export const AnomaliesTable: React.FunctionComponent<{ const sortedTableItems = useMemo(() => { let sortedItems: TableItem[] = []; - if (sorting.sort.field === 'partition') { - sortedItems = tableItems.sort((a, b) => (a.partition > b.partition ? 1 : -1)); + if (sorting.sort.field === 'partitionName') { + sortedItems = tableItems.sort((a, b) => (a.partitionId > b.partitionId ? 1 : -1)); } else if (sorting.sort.field === 'topAnomalyScore') { sortedItems = tableItems.sort((a, b) => a.topAnomalyScore - b.topAnomalyScore); } @@ -100,10 +99,10 @@ export const AnomaliesTable: React.FunctionComponent<{ }, [tableItems, sorting]); const expandItem = useCallback( - item => { + (item: TableItem) => { const newItemIdToExpandedRowMap = { ...itemIdToExpandedRowMap, - [item.id]: ( + [item.partitionName]: ( { - if (itemIdToExpandedRowMap[item.id]) { - const { [item.id]: toggledItem, ...remainingExpandedRowMap } = itemIdToExpandedRowMap; + (item: TableItem) => { + if (itemIdToExpandedRowMap[item.partitionName]) { + const { + [item.partitionName]: toggledItem, + ...remainingExpandedRowMap + } = itemIdToExpandedRowMap; setItemIdToExpandedRowMap(remainingExpandedRowMap); } }, [itemIdToExpandedRowMap] ); - const columns = [ + const columns: Array> = [ { - field: 'partition', + field: 'partitionName', name: partitionColumnName, sortable: true, truncateText: true, @@ -149,8 +151,8 @@ export const AnomaliesTable: React.FunctionComponent<{ isExpander: true, render: (item: TableItem) => ( @@ -161,7 +163,7 @@ export const AnomaliesTable: React.FunctionComponent<{ return ( Date: Mon, 16 Mar 2020 13:33:40 -0600 Subject: [PATCH 17/76] [Maps] add draw control to create distance filter (#58163) * [Maps] add distance filter to draw controls * create distance filter * update jest snapshot * remove duplicated code * reset circle draw when user hits escape * i18n cleanup * ts MultiIndexGeoFieldSelect * ts DistanceFilterForm * remove unused prop * make interface a type * move geo_field_with_index to components folder * convert draw_circle to TS Co-authored-by: Elastic Machine --- .../geometry_filter_form.test.js.snap | 248 ++++++------------ .../components/distance_filter_form.tsx | 99 +++++++ .../public/components/geo_field_with_index.ts | 17 ++ .../public/components/geometry_filter_form.js | 95 ++----- .../multi_index_geo_field_select.tsx | 78 ++++++ .../map/mb/draw_control/draw_circle.ts | 137 ++++++++++ .../map/mb/draw_control/draw_control.js | 51 ++-- .../map/mb/draw_control/draw_tooltip.js | 26 +- .../__snapshots__/tools_control.test.js.snap | 46 ++++ .../tools_control/tools_control.js | 68 ++++- .../maps/public/elasticsearch_geo_utils.js | 33 +++ x-pack/package.json | 1 + x-pack/plugins/maps/common/constants.ts | 1 + .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - yarn.lock | 16 ++ 16 files changed, 636 insertions(+), 282 deletions(-) create mode 100644 x-pack/legacy/plugins/maps/public/components/distance_filter_form.tsx create mode 100644 x-pack/legacy/plugins/maps/public/components/geo_field_with_index.ts create mode 100644 x-pack/legacy/plugins/maps/public/components/multi_index_geo_field_select.tsx create mode 100644 x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap b/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap index c62b07a89e7a3..85a073c8d9ace 100644 --- a/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap @@ -17,49 +17,27 @@ exports[`should not render relation select when geo field is geo_point 1`] = ` value="My shape" /> - - - - - My index - - -
- my geo field - , - "value": "My index/my geo field", - }, - ] + -
+ } + /> @@ -95,49 +73,27 @@ exports[`should not show "within" relation when filter geometry is not closed 1` value="My shape" /> - - - - - My index - - -
- my geo field - , - "value": "My index/my geo field", - }, - ] + -
+ } + /> - - - - - My index - - -
- my geo field - , - "value": "My index/my geo field", - }, - ] + -
+ } + /> @@ -281,49 +215,27 @@ exports[`should render relation select when geo field is geo_shape 1`] = ` value="My shape" /> - - - - - My index - - -
- my geo field - , - "value": "My index/my geo field", - }, - ] + -
+ } + /> void; +} + +interface State { + selectedField: GeoFieldWithIndex | undefined; + filterLabel: string; +} + +export class DistanceFilterForm extends Component { + state = { + selectedField: this.props.geoFields.length ? this.props.geoFields[0] : undefined, + filterLabel: '', + }; + + _onGeoFieldChange = (selectedField: GeoFieldWithIndex | undefined) => { + this.setState({ selectedField }); + }; + + _onFilterLabelChange = (e: ChangeEvent) => { + this.setState({ + filterLabel: e.target.value, + }); + }; + + _onSubmit = () => { + if (!this.state.selectedField) { + return; + } + this.props.onSubmit({ + filterLabel: this.state.filterLabel, + indexPatternId: this.state.selectedField.indexPatternId, + geoFieldName: this.state.selectedField.geoFieldName, + }); + }; + + render() { + return ( + + + + + + + + + + + + {this.props.buttonLabel} + + + + ); + } +} diff --git a/x-pack/legacy/plugins/maps/public/components/geo_field_with_index.ts b/x-pack/legacy/plugins/maps/public/components/geo_field_with_index.ts new file mode 100644 index 0000000000000..863e0adda8fb2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/components/geo_field_with_index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +// Maps can contain geo fields from multiple index patterns. GeoFieldWithIndex is used to: +// 1) Combine the geo field along with associated index pattern state. +// 2) Package asynchronously looked up state via indexPatternService to avoid +// PITA of looking up async state in downstream react consumers. +export type GeoFieldWithIndex = { + geoFieldName: string; + geoFieldType: string; + indexPatternTitle: string; + indexPatternId: string; +}; diff --git a/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js b/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js index 3308155caa3e4..ac6461345e8bf 100644 --- a/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js +++ b/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js @@ -9,9 +9,6 @@ import PropTypes from 'prop-types'; import { EuiForm, EuiFormRow, - EuiSuperSelect, - EuiTextColor, - EuiText, EuiFieldText, EuiButton, EuiSelect, @@ -22,20 +19,7 @@ import { import { i18n } from '@kbn/i18n'; import { ES_GEO_FIELD_TYPE, ES_SPATIAL_RELATIONS } from '../../common/constants'; import { getEsSpatialRelationLabel } from '../../common/i18n_getters'; - -const GEO_FIELD_VALUE_DELIMITER = '/'; // `/` is not allowed in index pattern name so should not have collisions - -function createIndexGeoFieldName({ indexPatternTitle, geoFieldName }) { - return `${indexPatternTitle}${GEO_FIELD_VALUE_DELIMITER}${geoFieldName}`; -} - -function splitIndexGeoFieldName(value) { - const split = value.split(GEO_FIELD_VALUE_DELIMITER); - return { - indexPatternTitle: split[0], - geoFieldName: split[1], - }; -} +import { MultiIndexGeoFieldSelect } from './multi_index_geo_field_select'; export class GeometryFilterForm extends Component { static propTypes = { @@ -52,27 +36,13 @@ export class GeometryFilterForm extends Component { }; state = { - geoFieldTag: this.props.geoFields.length - ? createIndexGeoFieldName(this.props.geoFields[0]) - : '', + selectedField: this.props.geoFields.length ? this.props.geoFields[0] : undefined, geometryLabel: this.props.intitialGeometryLabel, relation: ES_SPATIAL_RELATIONS.INTERSECTS, }; - _getSelectedGeoField = () => { - if (!this.state.geoFieldTag) { - return null; - } - - const { indexPatternTitle, geoFieldName } = splitIndexGeoFieldName(this.state.geoFieldTag); - - return this.props.geoFields.find(option => { - return option.indexPatternTitle === indexPatternTitle && option.geoFieldName === geoFieldName; - }); - }; - - _onGeoFieldChange = selectedValue => { - this.setState({ geoFieldTag: selectedValue }); + _onGeoFieldChange = selectedField => { + this.setState({ selectedField }); }; _onGeometryLabelChange = e => { @@ -88,25 +58,21 @@ export class GeometryFilterForm extends Component { }; _onSubmit = () => { - const geoField = this._getSelectedGeoField(); this.props.onSubmit({ geometryLabel: this.state.geometryLabel, - indexPatternId: geoField.indexPatternId, - geoFieldName: geoField.geoFieldName, - geoFieldType: geoField.geoFieldType, + indexPatternId: this.state.selectedField.indexPatternId, + geoFieldName: this.state.selectedField.geoFieldName, + geoFieldType: this.state.selectedField.geoFieldType, relation: this.state.relation, }); }; _renderRelationInput() { - if (!this.state.geoFieldTag) { - return null; - } - - const { geoFieldType } = this._getSelectedGeoField(); - // relationship only used when filtering geo_shape fields - if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT) { + if ( + !this.state.selectedField || + this.state.selectedField.geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT + ) { return null; } @@ -141,20 +107,6 @@ export class GeometryFilterForm extends Component { } render() { - const options = this.props.geoFields.map(({ indexPatternTitle, geoFieldName }) => { - return { - inputDisplay: ( - - - {indexPatternTitle} - -
- {geoFieldName} -
- ), - value: createIndexGeoFieldName({ indexPatternTitle, geoFieldName }), - }; - }); let error; if (this.props.errorMsg) { error = {this.props.errorMsg}; @@ -174,24 +126,11 @@ export class GeometryFilterForm extends Component { />
- - - + {this._renderRelationInput()} @@ -204,7 +143,7 @@ export class GeometryFilterForm extends Component { size="s" fill onClick={this._onSubmit} - isDisabled={!this.state.geometryLabel || !this.state.geoFieldTag} + isDisabled={!this.state.geometryLabel || !this.state.selectedField} isLoading={this.props.isLoading} > {this.props.buttonLabel} diff --git a/x-pack/legacy/plugins/maps/public/components/multi_index_geo_field_select.tsx b/x-pack/legacy/plugins/maps/public/components/multi_index_geo_field_select.tsx new file mode 100644 index 0000000000000..0e5b94f0c6427 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/components/multi_index_geo_field_select.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFormRow, EuiSuperSelect, EuiTextColor, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { GeoFieldWithIndex } from './geo_field_with_index'; + +const OPTION_ID_DELIMITER = '/'; + +function createOptionId(geoField: GeoFieldWithIndex): string { + // Namespace field with indexPatterId to avoid collisions between field names + return `${geoField.indexPatternId}${OPTION_ID_DELIMITER}${geoField.geoFieldName}`; +} + +function splitOptionId(optionId: string) { + const split = optionId.split(OPTION_ID_DELIMITER); + return { + indexPatternId: split[0], + geoFieldName: split[1], + }; +} + +interface Props { + fields: GeoFieldWithIndex[]; + onChange: (newSelectedField: GeoFieldWithIndex | undefined) => void; + selectedField: GeoFieldWithIndex | undefined; +} + +export function MultiIndexGeoFieldSelect({ fields, onChange, selectedField }: Props) { + function onFieldSelect(selectedOptionId: string) { + const { indexPatternId, geoFieldName } = splitOptionId(selectedOptionId); + + const newSelectedField = fields.find(field => { + return field.indexPatternId === indexPatternId && field.geoFieldName === geoFieldName; + }); + onChange(newSelectedField); + } + + const options = fields.map((geoField: GeoFieldWithIndex) => { + return { + inputDisplay: ( + + + {geoField.indexPatternTitle} + +
+ {geoField.geoFieldName} +
+ ), + value: createOptionId(geoField), + }; + }); + + return ( + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts new file mode 100644 index 0000000000000..f2ceb8685d43e --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +// @ts-ignore +import turf from 'turf'; +// @ts-ignore +import turfCircle from '@turf/circle'; + +type DrawCircleState = { + circle: { + properties: { + center: {} | null; + radiusKm: number; + }; + id: string | number; + incomingCoords: (coords: unknown[]) => void; + toGeoJSON: () => unknown; + }; +}; + +type MouseEvent = { + lngLat: { + lng: number; + lat: number; + }; +}; + +export const DrawCircle = { + onSetup() { + // @ts-ignore + const circle: unknown = this.newFeature({ + type: 'Feature', + properties: { + center: null, + radiusKm: 0, + }, + geometry: { + type: 'Polygon', + coordinates: [[]], + }, + }); + + // @ts-ignore + this.addFeature(circle); + // @ts-ignore + this.clearSelectedFeatures(); + // @ts-ignore + this.updateUIClasses({ mouse: 'add' }); + // @ts-ignore + this.setActionableState({ + trash: true, + }); + return { + circle, + }; + }, + onKeyUp(state: DrawCircleState, e: { keyCode: number }) { + if (e.keyCode === 27) { + // clear point when user hits escape + state.circle.properties.center = null; + state.circle.properties.radiusKm = 0; + state.circle.incomingCoords([[]]); + } + }, + onClick(state: DrawCircleState, e: MouseEvent) { + if (!state.circle.properties.center) { + // first click, start circle + state.circle.properties.center = [e.lngLat.lng, e.lngLat.lat]; + } else { + // second click, finish draw + // @ts-ignore + this.updateUIClasses({ mouse: 'pointer' }); + state.circle.properties.radiusKm = turf.distance(state.circle.properties.center, [ + e.lngLat.lng, + e.lngLat.lat, + ]); + // @ts-ignore + this.changeMode('simple_select', { featuresId: state.circle.id }); + } + }, + onMouseMove(state: DrawCircleState, e: MouseEvent) { + if (!state.circle.properties.center) { + // circle not started, nothing to update + return; + } + + const mouseLocation = [e.lngLat.lng, e.lngLat.lat]; + state.circle.properties.radiusKm = turf.distance(state.circle.properties.center, mouseLocation); + const newCircleFeature = turfCircle( + state.circle.properties.center, + state.circle.properties.radiusKm + ); + state.circle.incomingCoords(newCircleFeature.geometry.coordinates); + }, + onStop(state: DrawCircleState) { + // @ts-ignore + this.updateUIClasses({ mouse: 'none' }); + // @ts-ignore + this.activateUIButton(); + + // @ts-ignore + if (this.getFeature(state.circle.id) === undefined) return; + + if (state.circle.properties.center && state.circle.properties.radiusKm > 0) { + // @ts-ignore + this.map.fire('draw.create', { + features: [state.circle.toGeoJSON()], + }); + } else { + // @ts-ignore + this.deleteFeature([state.circle.id], { silent: true }); + // @ts-ignore + this.changeMode('simple_select', {}, { silent: true }); + } + }, + toDisplayFeatures( + state: DrawCircleState, + geojson: { properties: { active: string } }, + display: (geojson: unknown) => unknown + ) { + if (state.circle.properties.center) { + geojson.properties.active = 'true'; + return display(geojson); + } + }, + onTrash(state: DrawCircleState) { + // @ts-ignore + this.deleteFeature([state.circle.id], { silent: true }); + // @ts-ignore + this.changeMode('simple_select'); + }, +}; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js index f1b4fe2aad1f7..99abe5d108b5a 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js @@ -9,7 +9,9 @@ import React from 'react'; import { DRAW_TYPE } from '../../../../../common/constants'; import MapboxDraw from '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw-unminified'; import DrawRectangle from 'mapbox-gl-draw-rectangle-mode'; +import { DrawCircle } from './draw_circle'; import { + createDistanceFilterWithMeta, createSpatialFilterWithBoundingBox, createSpatialFilterWithGeometry, getBoundingBoxGeometry, @@ -19,6 +21,7 @@ import { DrawTooltip } from './draw_tooltip'; const mbDrawModes = MapboxDraw.modes; mbDrawModes.draw_rectangle = DrawRectangle; +mbDrawModes.draw_circle = DrawCircle; export class DrawControl extends React.Component { constructor() { @@ -60,7 +63,21 @@ export class DrawControl extends React.Component { return; } - const isBoundingBox = this.props.drawState.drawType === DRAW_TYPE.BOUNDS; + if (this.props.drawState.drawType === DRAW_TYPE.DISTANCE) { + const circle = e.features[0]; + roundCoordinates(circle.properties.center); + const filter = createDistanceFilterWithMeta({ + alias: this.props.drawState.filterLabel, + distanceKm: _.round(circle.properties.radiusKm, circle.properties.radiusKm > 10 ? 0 : 2), + geoFieldName: this.props.drawState.geoFieldName, + indexPatternId: this.props.drawState.indexPatternId, + point: circle.properties.center, + }); + this.props.addFilters([filter]); + this.props.disableDrawState(); + return; + } + const geometry = e.features[0].geometry; // MapboxDraw returns coordinates with 12 decimals. Round to a more reasonable number roundCoordinates(geometry.coordinates); @@ -73,15 +90,16 @@ export class DrawControl extends React.Component { geometryLabel: this.props.drawState.geometryLabel, relation: this.props.drawState.relation, }; - const filter = isBoundingBox - ? createSpatialFilterWithBoundingBox({ - ...options, - geometry: getBoundingBoxGeometry(geometry), - }) - : createSpatialFilterWithGeometry({ - ...options, - geometry, - }); + const filter = + this.props.drawState.drawType === DRAW_TYPE.BOUNDS + ? createSpatialFilterWithBoundingBox({ + ...options, + geometry: getBoundingBoxGeometry(geometry), + }) + : createSpatialFilterWithGeometry({ + ...options, + geometry, + }); this.props.addFilters([filter]); } catch (error) { // TODO notify user why filter was not created @@ -109,11 +127,14 @@ export class DrawControl extends React.Component { this.props.mbMap.getCanvas().style.cursor = 'crosshair'; this.props.mbMap.on('draw.create', this._onDraw); } - const mbDrawMode = - this.props.drawState.drawType === DRAW_TYPE.POLYGON - ? this._mbDrawControl.modes.DRAW_POLYGON - : 'draw_rectangle'; - this._mbDrawControl.changeMode(mbDrawMode); + + if (this.props.drawState.drawType === DRAW_TYPE.BOUNDS) { + this._mbDrawControl.changeMode('draw_rectangle'); + } else if (this.props.drawState.drawType === DRAW_TYPE.DISTANCE) { + this._mbDrawControl.changeMode('draw_circle'); + } else if (this.props.drawState.drawType === DRAW_TYPE.POLYGON) { + this._mbDrawControl.changeMode(this._mbDrawControl.modes.DRAW_POLYGON); + } } render() { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js index 463fe52981410..c8bde29b94fb6 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js @@ -42,14 +42,24 @@ export class DrawTooltip extends Component { } render() { - const instructions = - this.props.drawState.drawType === DRAW_TYPE.BOUNDS - ? i18n.translate('xpack.maps.drawTooltip.boundsInstructions', { - defaultMessage: 'Click to start rectangle. Click again to finish.', - }) - : i18n.translate('xpack.maps.drawTooltip.polygonInstructions', { - defaultMessage: 'Click to add vertex. Double click to finish.', - }); + let instructions; + if (this.props.drawState.drawType === DRAW_TYPE.BOUNDS) { + instructions = i18n.translate('xpack.maps.drawTooltip.boundsInstructions', { + defaultMessage: + 'Click to start rectangle. Move mouse to adjust rectangle size. Click again to finish.', + }); + } else if (this.props.drawState.drawType === DRAW_TYPE.DISTANCE) { + instructions = i18n.translate('xpack.maps.drawTooltip.distanceInstructions', { + defaultMessage: 'Click to set point. Move mouse to adjust distance. Click to finish.', + }); + } else if (this.props.drawState.drawType === DRAW_TYPE.POLYGON) { + instructions = i18n.translate('xpack.maps.drawTooltip.polygonInstructions', { + defaultMessage: 'Click to start shape. Click to add vertex. Double click to finish.', + }); + } else { + // unknown draw type, tooltip not needed + return null; + } const tooltipAnchor = (
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap b/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap index 681c3f0fbfd61..d7fa099fe9dbe 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap @@ -41,6 +41,10 @@ exports[`Should render cancel button when drawing 1`] = ` "name": "Draw bounds to filter data", "panel": 2, }, + Object { + "name": "Draw distance to filter data", + "panel": 3, + }, ], "title": "Tools", }, @@ -86,6 +90,25 @@ exports[`Should render cancel button when drawing 1`] = ` "id": 2, "title": "Draw bounds", }, + Object { + "content": , + "id": 3, + "title": "Draw distance", + }, ] } /> @@ -144,6 +167,10 @@ exports[`renders 1`] = ` "name": "Draw bounds to filter data", "panel": 2, }, + Object { + "name": "Draw distance to filter data", + "panel": 3, + }, ], "title": "Tools", }, @@ -189,6 +216,25 @@ exports[`renders 1`] = ` "id": 2, "title": "Draw bounds", }, + Object { + "content": , + "id": 3, + "title": "Draw distance", + }, ] } /> diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js b/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js index ea6ffe3ba1435..e7c125abe70c7 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js @@ -14,9 +14,10 @@ import { EuiButton, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { DRAW_TYPE } from '../../../../common/constants'; +import { DRAW_TYPE, ES_GEO_FIELD_TYPE } from '../../../../common/constants'; import { FormattedMessage } from '@kbn/i18n/react'; import { GeometryFilterForm } from '../../../components/geometry_filter_form'; +import { DistanceFilterForm } from '../../../components/distance_filter_form'; const DRAW_SHAPE_LABEL = i18n.translate('xpack.maps.toolbarOverlay.drawShapeLabel', { defaultMessage: 'Draw shape to filter data', @@ -26,6 +27,10 @@ const DRAW_BOUNDS_LABEL = i18n.translate('xpack.maps.toolbarOverlay.drawBoundsLa defaultMessage: 'Draw bounds to filter data', }); +const DRAW_DISTANCE_LABEL = i18n.translate('xpack.maps.toolbarOverlay.drawDistanceLabel', { + defaultMessage: 'Draw distance to filter data', +}); + const DRAW_SHAPE_LABEL_SHORT = i18n.translate('xpack.maps.toolbarOverlay.drawShapeLabelShort', { defaultMessage: 'Draw shape', }); @@ -34,6 +39,13 @@ const DRAW_BOUNDS_LABEL_SHORT = i18n.translate('xpack.maps.toolbarOverlay.drawBo defaultMessage: 'Draw bounds', }); +const DRAW_DISTANCE_LABEL_SHORT = i18n.translate( + 'xpack.maps.toolbarOverlay.drawDistanceLabelShort', + { + defaultMessage: 'Draw distance', + } +); + export class ToolsControl extends Component { state = { isPopoverOpen: false, @@ -65,23 +77,43 @@ export class ToolsControl extends Component { this._closePopover(); }; + _initiateDistanceDraw = options => { + this.props.initiateDraw({ + drawType: DRAW_TYPE.DISTANCE, + ...options, + }); + this._closePopover(); + }; + _getDrawPanels() { + const tools = [ + { + name: DRAW_SHAPE_LABEL, + panel: 1, + }, + { + name: DRAW_BOUNDS_LABEL, + panel: 2, + }, + ]; + + const hasGeoPoints = this.props.geoFields.some(({ geoFieldType }) => { + return geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT; + }); + if (hasGeoPoints) { + tools.push({ + name: DRAW_DISTANCE_LABEL, + panel: 3, + }); + } + return [ { id: 0, title: i18n.translate('xpack.maps.toolbarOverlay.tools.toolbarTitle', { defaultMessage: 'Tools', }), - items: [ - { - name: DRAW_SHAPE_LABEL, - panel: 1, - }, - { - name: DRAW_BOUNDS_LABEL, - panel: 2, - }, - ], + items: tools, }, { id: 1, @@ -119,6 +151,20 @@ export class ToolsControl extends Component { /> ), }, + { + id: 3, + title: DRAW_DISTANCE_LABEL_SHORT, + content: ( + { + return geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT; + })} + onSubmit={this._initiateDistanceDraw} + /> + ), + }, ]; } diff --git a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js index 9b33d3036785c..79467e26ec3fa 100644 --- a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js +++ b/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js @@ -344,6 +344,39 @@ function createGeometryFilterWithMeta({ return createGeoPolygonFilter(geometry.coordinates, geoFieldName, { meta }); } +export function createDistanceFilterWithMeta({ + alias, + distanceKm, + geoFieldName, + indexPatternId, + point, +}) { + const meta = { + type: SPATIAL_FILTER_TYPE, + negate: false, + index: indexPatternId, + key: geoFieldName, + alias: alias + ? alias + : i18n.translate('xpack.maps.es_geo_utils.distanceFilterAlias', { + defaultMessage: '{geoFieldName} within {distanceKm}km of {pointLabel}', + values: { + distanceKm, + geoFieldName, + pointLabel: point.join(','), + }, + }), + }; + + return { + geo_distance: { + distance: `${distanceKm}km`, + [geoFieldName]: point, + }, + meta, + }; +} + export function roundCoordinates(coordinates) { for (let i = 0; i < coordinates.length; i++) { const value = coordinates[i]; diff --git a/x-pack/package.json b/x-pack/package.json index b9c4f7c554e95..3c8aa435c3e43 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -196,6 +196,7 @@ "@scant/router": "^0.1.0", "@slack/webhook": "^5.0.0", "@turf/boolean-contains": "6.0.1", + "@turf/circle": "6.0.1", "angular": "^1.7.9", "angular-resource": "1.7.9", "angular-sanitize": "1.7.9", diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index ae3e164ffb2bc..b1483cefa43bc 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -120,6 +120,7 @@ export const EMPTY_FEATURE_COLLECTION = { export const DRAW_TYPE = { BOUNDS: 'BOUNDS', + DISTANCE: 'DISTANCE', POLYGON: 'POLYGON', }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3763020a0b692..ad49f0242e8e6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7073,7 +7073,6 @@ "xpack.maps.feature.appDescription": "Elasticsearch と Elastic Maps Service の地理空間データを閲覧します", "xpack.maps.featureRegistry.mapsFeatureName": "マップ", "xpack.maps.geoGrid.resolutionLabel": "グリッド解像度", - "xpack.maps.geometryFilterForm.geoFieldLabel": "フィルタリングされたフィールド", "xpack.maps.geometryFilterForm.geometryLabelLabel": "ジオメトリラベル", "xpack.maps.geometryFilterForm.relationLabel": "空間関係", "xpack.maps.heatmap.colorRampLabel": "色の範囲", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0477470a4b8a1..76ecf333eb10a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7073,7 +7073,6 @@ "xpack.maps.feature.appDescription": "从 Elasticsearch 和 Elastic 地图服务浏览地理空间数据", "xpack.maps.featureRegistry.mapsFeatureName": "Maps", "xpack.maps.geoGrid.resolutionLabel": "网格分辨率", - "xpack.maps.geometryFilterForm.geoFieldLabel": "已筛选字段", "xpack.maps.geometryFilterForm.geometryLabelLabel": "几何标签", "xpack.maps.geometryFilterForm.relationLabel": "空间关系", "xpack.maps.heatmap.colorRampLabel": "颜色范围", diff --git a/yarn.lock b/yarn.lock index ae55508cee886..dcb360587e4ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4086,6 +4086,22 @@ "@turf/helpers" "6.x" "@turf/invariant" "6.x" +"@turf/circle@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/circle/-/circle-6.0.1.tgz#0ab72083373ae3c76b700c17a504ab1b5c0910b9" + integrity sha512-pF9XsYtCvY9ZyNqJ3hFYem9VaiGdVNQb0SFq/zzDMwH3iWZPPJQHnnDB/3e8RD1VDtBBov9p5uO2k7otsfezjw== + dependencies: + "@turf/destination" "6.x" + "@turf/helpers" "6.x" + +"@turf/destination@6.x": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@turf/destination/-/destination-6.0.1.tgz#5275887fa96ec463f44864a2c17f0b712361794a" + integrity sha512-MroK4nRdp7as174miCAugp8Uvorhe6rZ7MJiC9Hb4+hZR7gNFJyVKmkdDDXIoCYs6MJQsx0buI+gsCpKwgww0Q== + dependencies: + "@turf/helpers" "6.x" + "@turf/invariant" "6.x" + "@turf/helpers@6.x": version "6.1.4" resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.1.4.tgz#d6fd7ebe6782dd9c87dca5559bda5c48ae4c3836" From dccfa593dc3c0e2d95dc77709647e6313ac9c1b2 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Mon, 16 Mar 2020 15:37:42 -0400 Subject: [PATCH 18/76] Embeddable API cleanup (#60207) * wip * Remove test in legacy functional plugin --- .../hello_world_embeddable_factory.ts | 2 +- .../public/list_container/list_container.tsx | 7 +- .../list_container/list_container_factory.ts | 13 ++-- .../multi_task_todo_embeddable_factory.ts | 2 +- examples/embeddable_examples/public/plugin.ts | 47 +++++++------ .../searchable_list_container.tsx | 7 +- .../searchable_list_container_factory.ts | 13 ++-- .../public/todo/todo_embeddable_factory.tsx | 11 ++- examples/embeddable_explorer/public/app.tsx | 4 +- .../public/embeddable_panel_example.tsx | 7 +- .../public/hello_world_embeddable_example.tsx | 4 +- .../public/list_container_example.tsx | 7 +- .../embeddable_explorer/public/plugin.tsx | 4 +- .../public/todo_embeddable_example.tsx | 4 +- .../public/dashboard/np_ready/application.ts | 4 +- .../kibana/public/dashboard/plugin.ts | 6 +- .../embeddable/search_embeddable_factory.ts | 19 +++-- .../kibana/public/discover/plugin.ts | 31 ++++---- .../public/visualize/kibana_services.ts | 4 +- .../public/visualize/np_ready/types.d.ts | 4 +- .../kibana/public/visualize/plugin.ts | 6 +- .../visualize_embeddable_factory.tsx | 5 +- .../public/np_ready/public/mocks.ts | 2 +- .../public/np_ready/public/plugin.ts | 4 +- .../ui/public/new_platform/new_platform.ts | 6 +- .../actions/expand_panel_action.test.tsx | 3 +- .../actions/open_replace_panel_flyout.tsx | 4 +- .../actions/replace_panel_action.test.tsx | 3 +- .../public/actions/replace_panel_action.tsx | 4 +- .../public/actions/replace_panel_flyout.tsx | 4 +- .../public/embeddable/dashboard_container.tsx | 4 +- .../dashboard_container_factory.tsx | 48 ++++++------- .../embeddable/grid/dashboard_grid.test.tsx | 4 +- src/plugins/dashboard/public/plugin.tsx | 70 +++++++++++-------- .../public/api/get_embeddable_factories.ts | 26 ------- .../public/api/get_embeddable_factory.ts | 34 --------- src/plugins/embeddable/public/api/index.ts | 47 ------------- .../public/api/register_embeddable_factory.ts | 32 --------- .../embeddable/public/api/tests/helpers.ts | 27 ------- src/plugins/embeddable/public/api/types.ts | 43 ------------ src/plugins/embeddable/public/index.ts | 4 +- .../lib/actions/edit_panel_action.test.tsx | 12 ++-- .../public/lib/actions/edit_panel_action.ts | 5 +- .../public/lib/containers/container.ts | 4 +- .../embeddable_child_panel.test.tsx | 5 +- .../lib/containers/embeddable_child_panel.tsx | 6 +- .../lib/embeddables/embeddable_factory.ts | 6 +- .../embeddable_factory_renderer.test.tsx | 8 +-- .../embeddable_factory_renderer.tsx | 4 +- .../lib/panel/embeddable_panel.test.tsx | 4 +- .../public/lib/panel/embeddable_panel.tsx | 7 +- .../add_panel/add_panel_action.test.tsx | 6 +- .../add_panel/add_panel_action.ts | 7 +- .../add_panel/add_panel_flyout.test.tsx | 9 +-- .../add_panel/add_panel_flyout.tsx | 6 +- .../add_panel/open_add_panel_flyout.tsx | 6 +- .../customize_panel_action.test.ts | 3 +- .../inspect_panel_action.test.tsx | 6 +- .../remove_panel_action.test.tsx | 7 +- .../contact_card_embeddable_factory.tsx | 2 +- .../slow_contact_card_embeddable_factory.ts | 2 +- .../embeddables/filterable_container.tsx | 4 +- .../filterable_container_factory.ts | 6 +- .../filterable_embeddable_factory.ts | 2 +- .../embeddables/hello_world_container.tsx | 6 +- .../hello_world_container_component.tsx | 6 +- src/plugins/embeddable/public/lib/types.ts | 4 -- src/plugins/embeddable/public/mocks.ts | 7 +- .../tests/registry.test.ts => plugin.test.ts} | 16 +++-- src/plugins/embeddable/public/plugin.ts | 67 +++++++++++++----- .../public/tests/apply_filter_action.test.ts | 14 ++-- .../embeddable/public/tests/container.test.ts | 12 ++-- .../tests/customize_panel_modal.test.tsx | 8 +-- .../embeddable/public/tests/test_plugin.ts | 6 +- .../public/np_ready/public/app/app.tsx | 9 +-- .../app/dashboard_container_example.tsx | 16 +++-- .../hello_world_embeddable_factory.ts | 28 -------- .../public/np_ready/public/plugin.tsx | 30 ++------ .../dashboard_container.js | 14 ---- .../embeddable/embeddable_factory.ts | 42 ++++++----- .../editor_frame_service/service.test.tsx | 11 ++- .../public/editor_frame_service/service.tsx | 40 ++++++----- x-pack/legacy/plugins/lens/public/plugin.tsx | 6 +- .../embeddables/embedded_map_helpers.tsx | 11 +-- .../public/components/embeddables/types.ts | 6 -- x-pack/legacy/plugins/siem/public/plugin.tsx | 4 +- .../advanced_ui_actions/public/plugin.ts | 8 +-- .../test_helpers/time_range_container.ts | 4 +- .../time_range_embeddable_factory.ts | 2 +- .../public/embeddables/resolver/factory.ts | 2 +- x-pack/plugins/endpoint/public/plugin.ts | 4 +- .../plugins/resolver_test/public/plugin.ts | 8 ++- 92 files changed, 451 insertions(+), 647 deletions(-) delete mode 100644 src/plugins/embeddable/public/api/get_embeddable_factories.ts delete mode 100644 src/plugins/embeddable/public/api/get_embeddable_factory.ts delete mode 100644 src/plugins/embeddable/public/api/index.ts delete mode 100644 src/plugins/embeddable/public/api/register_embeddable_factory.ts delete mode 100644 src/plugins/embeddable/public/api/tests/helpers.ts delete mode 100644 src/plugins/embeddable/public/api/types.ts rename src/plugins/embeddable/public/{api/tests/registry.test.ts => plugin.test.ts} (70%) delete mode 100644 test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddables/hello_world_embeddable_factory.ts diff --git a/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts b/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts index de5a3d9380def..2995c99ac9e58 100644 --- a/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts +++ b/examples/embeddable_examples/public/hello_world/hello_world_embeddable_factory.ts @@ -33,7 +33,7 @@ export class HelloWorldEmbeddableFactory extends EmbeddableFactory { * embeddables should check the UI Capabilities service to be sure of * the right permissions. */ - public isEditable() { + public async isEditable() { return true; } diff --git a/examples/embeddable_examples/public/list_container/list_container.tsx b/examples/embeddable_examples/public/list_container/list_container.tsx index 35a674a03573a..bbbd0d6e32304 100644 --- a/examples/embeddable_examples/public/list_container/list_container.tsx +++ b/examples/embeddable_examples/public/list_container/list_container.tsx @@ -21,7 +21,7 @@ import ReactDOM from 'react-dom'; import { Container, ContainerInput, - GetEmbeddableFactory, + EmbeddableStart, } from '../../../../src/plugins/embeddable/public'; import { ListContainerComponent } from './list_container_component'; @@ -31,7 +31,10 @@ export class ListContainer extends Container<{}, ContainerInput> { public readonly type = LIST_CONTAINER; private node?: HTMLElement; - constructor(input: ContainerInput, getEmbeddableFactory: GetEmbeddableFactory) { + constructor( + input: ContainerInput, + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'] + ) { super(input, { embeddableLoaded: {} }, getEmbeddableFactory); } diff --git a/examples/embeddable_examples/public/list_container/list_container_factory.ts b/examples/embeddable_examples/public/list_container/list_container_factory.ts index de6b7d5f5e503..247cf48b41bde 100644 --- a/examples/embeddable_examples/public/list_container/list_container_factory.ts +++ b/examples/embeddable_examples/public/list_container/list_container_factory.ts @@ -20,25 +20,30 @@ import { i18n } from '@kbn/i18n'; import { EmbeddableFactory, - GetEmbeddableFactory, ContainerInput, + EmbeddableStart, } from '../../../../src/plugins/embeddable/public'; import { LIST_CONTAINER, ListContainer } from './list_container'; +interface StartServices { + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; +} + export class ListContainerFactory extends EmbeddableFactory { public readonly type = LIST_CONTAINER; public readonly isContainerType = true; - constructor(private getEmbeddableFactory: GetEmbeddableFactory) { + constructor(private getStartServices: () => Promise) { super(); } - public isEditable() { + public async isEditable() { return true; } public async create(initialInput: ContainerInput) { - return new ListContainer(initialInput, this.getEmbeddableFactory); + const { getEmbeddableFactory } = await this.getStartServices(); + return new ListContainer(initialInput, getEmbeddableFactory); } public getDisplayName() { diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts index a54201b157a6c..9afdeabaee765 100644 --- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts +++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable_factory.ts @@ -32,7 +32,7 @@ export class MultiTaskTodoEmbeddableFactory extends EmbeddableFactory< > { public readonly type = MULTI_TASK_TODO_EMBEDDABLE; - public isEditable() { + public async isEditable() { return true; } diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index b7a4f5c078d54..3663af68ae2c7 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -17,20 +17,11 @@ * under the License. */ -import { - IEmbeddableSetup, - IEmbeddableStart, - EmbeddableFactory, -} from '../../../src/plugins/embeddable/public'; +import { EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public'; import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world'; import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo'; -import { - MULTI_TASK_TODO_EMBEDDABLE, - MultiTaskTodoEmbeddableFactory, - MultiTaskTodoOutput, - MultiTaskTodoInput, -} from './multi_task_todo'; +import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo'; import { SEARCHABLE_LIST_CONTAINER, SearchableListContainerFactory, @@ -38,46 +29,56 @@ import { import { LIST_CONTAINER, ListContainerFactory } from './list_container'; interface EmbeddableExamplesSetupDependencies { - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; } interface EmbeddableExamplesStartDependencies { - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; } export class EmbeddableExamplesPlugin implements Plugin { - public setup(core: CoreSetup, deps: EmbeddableExamplesSetupDependencies) { + public setup( + core: CoreSetup, + deps: EmbeddableExamplesSetupDependencies + ) { deps.embeddable.registerEmbeddableFactory( HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory() ); - deps.embeddable.registerEmbeddableFactory< - EmbeddableFactory - >(MULTI_TASK_TODO_EMBEDDABLE, new MultiTaskTodoEmbeddableFactory()); - } + deps.embeddable.registerEmbeddableFactory( + MULTI_TASK_TODO_EMBEDDABLE, + new MultiTaskTodoEmbeddableFactory() + ); - public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) { // These are registered in the start method because `getEmbeddableFactory ` // is only available in start. We could reconsider this I think and make it // available in both. deps.embeddable.registerEmbeddableFactory( SEARCHABLE_LIST_CONTAINER, - new SearchableListContainerFactory(deps.embeddable.getEmbeddableFactory) + new SearchableListContainerFactory(async () => ({ + getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory, + })) ); deps.embeddable.registerEmbeddableFactory( LIST_CONTAINER, - new ListContainerFactory(deps.embeddable.getEmbeddableFactory) + new ListContainerFactory(async () => ({ + getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory, + })) ); - deps.embeddable.registerEmbeddableFactory>( + deps.embeddable.registerEmbeddableFactory( TODO_EMBEDDABLE, - new TodoEmbeddableFactory(core.overlays.openModal) + new TodoEmbeddableFactory(async () => ({ + openModal: (await core.getStartServices())[0].overlays.openModal, + })) ); } + public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {} + public stop() {} } diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx index 3079abb867c38..06462937c768d 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx @@ -21,7 +21,7 @@ import ReactDOM from 'react-dom'; import { Container, ContainerInput, - GetEmbeddableFactory, + EmbeddableStart, EmbeddableInput, } from '../../../../src/plugins/embeddable/public'; import { SearchableListContainerComponent } from './searchable_list_container_component'; @@ -40,7 +40,10 @@ export class SearchableListContainer extends Container Promise) { super(); } - public isEditable() { + public async isEditable() { return true; } public async create(initialInput: SearchableContainerInput) { - return new SearchableListContainer(initialInput, this.getEmbeddableFactory); + const { getEmbeddableFactory } = await this.getStartServices(); + return new SearchableListContainer(initialInput, getEmbeddableFactory); } public getDisplayName() { diff --git a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx index dd2168bb39eee..d7be436905382 100644 --- a/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx +++ b/examples/embeddable_examples/public/todo/todo_embeddable_factory.tsx @@ -43,6 +43,10 @@ function TaskInput({ onSave }: { onSave: (task: string) => void }) { ); } +interface StartServices { + openModal: OverlayStart['openModal']; +} + export class TodoEmbeddableFactory extends EmbeddableFactory< TodoInput, TodoOutput, @@ -50,11 +54,11 @@ export class TodoEmbeddableFactory extends EmbeddableFactory< > { public readonly type = TODO_EMBEDDABLE; - constructor(private openModal: OverlayStart['openModal']) { + constructor(private getStartServices: () => Promise) { super(); } - public isEditable() { + public async isEditable() { return true; } @@ -69,9 +73,10 @@ export class TodoEmbeddableFactory extends EmbeddableFactory< * in this case, the task string. */ public async getExplicitInput() { + const { openModal } = await this.getStartServices(); return new Promise<{ task: string }>(resolve => { const onSave = (task: string) => resolve({ task }); - const overlay = this.openModal( + const overlay = openModal( toMountPoint( { diff --git a/examples/embeddable_explorer/public/app.tsx b/examples/embeddable_explorer/public/app.tsx index da7e8cc188e31..9c8568454855d 100644 --- a/examples/embeddable_explorer/public/app.tsx +++ b/examples/embeddable_explorer/public/app.tsx @@ -23,7 +23,7 @@ import { BrowserRouter as Router, Route, withRouter, RouteComponentProps } from import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui'; -import { IEmbeddableStart } from '../../../src/plugins/embeddable/public'; +import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public'; import { @@ -74,7 +74,7 @@ const Nav = withRouter(({ history, navigateToApp, pages }: NavProps) => { interface Props { basename: string; navigateToApp: CoreStart['application']['navigateToApp']; - embeddableApi: IEmbeddableStart; + embeddableApi: EmbeddableStart; uiActionsApi: UiActionsStart; overlays: OverlayStart; notifications: CoreStart['notifications']; diff --git a/examples/embeddable_explorer/public/embeddable_panel_example.tsx b/examples/embeddable_explorer/public/embeddable_panel_example.tsx index e6687d8563f59..b26111bed7ff2 100644 --- a/examples/embeddable_explorer/public/embeddable_panel_example.tsx +++ b/examples/embeddable_explorer/public/embeddable_panel_example.tsx @@ -31,9 +31,8 @@ import { import { EuiSpacer } from '@elastic/eui'; import { OverlayStart, CoreStart, SavedObjectsStart, IUiSettingsClient } from 'kibana/public'; import { - GetEmbeddableFactory, EmbeddablePanel, - IEmbeddableStart, + EmbeddableStart, IEmbeddable, } from '../../../src/plugins/embeddable/public'; import { @@ -47,8 +46,8 @@ import { Start as InspectorStartContract } from '../../../src/plugins/inspector/ import { getSavedObjectFinder } from '../../../src/plugins/saved_objects/public'; interface Props { - getAllEmbeddableFactories: IEmbeddableStart['getEmbeddableFactories']; - getEmbeddableFactory: GetEmbeddableFactory; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; uiActionsApi: UiActionsStart; overlays: OverlayStart; notifications: CoreStart['notifications']; diff --git a/examples/embeddable_explorer/public/hello_world_embeddable_example.tsx b/examples/embeddable_explorer/public/hello_world_embeddable_example.tsx index 74a6766a1b5ee..ea1c3d781ebfd 100644 --- a/examples/embeddable_explorer/public/hello_world_embeddable_example.tsx +++ b/examples/embeddable_explorer/public/hello_world_embeddable_example.tsx @@ -29,14 +29,14 @@ import { EuiText, } from '@elastic/eui'; import { - GetEmbeddableFactory, + EmbeddableStart, EmbeddableFactoryRenderer, EmbeddableRoot, } from '../../../src/plugins/embeddable/public'; import { HelloWorldEmbeddable, HELLO_WORLD_EMBEDDABLE } from '../../embeddable_examples/public'; interface Props { - getEmbeddableFactory: GetEmbeddableFactory; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; } export function HelloWorldEmbeddableExample({ getEmbeddableFactory }: Props) { diff --git a/examples/embeddable_explorer/public/list_container_example.tsx b/examples/embeddable_explorer/public/list_container_example.tsx index 2c7b12a27d963..969fdb0ca46db 100644 --- a/examples/embeddable_explorer/public/list_container_example.tsx +++ b/examples/embeddable_explorer/public/list_container_example.tsx @@ -29,10 +29,7 @@ import { EuiText, } from '@elastic/eui'; import { EuiSpacer } from '@elastic/eui'; -import { - GetEmbeddableFactory, - EmbeddableFactoryRenderer, -} from '../../../src/plugins/embeddable/public'; +import { EmbeddableFactoryRenderer, EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { HELLO_WORLD_EMBEDDABLE, TODO_EMBEDDABLE, @@ -42,7 +39,7 @@ import { } from '../../embeddable_examples/public'; interface Props { - getEmbeddableFactory: GetEmbeddableFactory; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; } export function ListContainerExample({ getEmbeddableFactory }: Props) { diff --git a/examples/embeddable_explorer/public/plugin.tsx b/examples/embeddable_explorer/public/plugin.tsx index 1294e0c89c9e7..7c75b108d9912 100644 --- a/examples/embeddable_explorer/public/plugin.tsx +++ b/examples/embeddable_explorer/public/plugin.tsx @@ -19,12 +19,12 @@ import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; import { UiActionsService } from '../../../src/plugins/ui_actions/public'; -import { IEmbeddableStart } from '../../../src/plugins/embeddable/public'; +import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; interface StartDeps { uiActions: UiActionsService; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; inspector: InspectorStart; } diff --git a/examples/embeddable_explorer/public/todo_embeddable_example.tsx b/examples/embeddable_explorer/public/todo_embeddable_example.tsx index b1c93087faf83..ce92301236c2b 100644 --- a/examples/embeddable_explorer/public/todo_embeddable_example.tsx +++ b/examples/embeddable_explorer/public/todo_embeddable_example.tsx @@ -39,10 +39,10 @@ import { TODO_EMBEDDABLE, TodoEmbeddableFactory, } from '../../../examples/embeddable_examples/public/todo'; -import { GetEmbeddableFactory, EmbeddableRoot } from '../../../src/plugins/embeddable/public'; +import { EmbeddableStart, EmbeddableRoot } from '../../../src/plugins/embeddable/public'; interface Props { - getEmbeddableFactory: GetEmbeddableFactory; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; } interface State { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts index 9ca84735cac16..fe0e7a1d3e6d0 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts @@ -39,7 +39,7 @@ import { } from '../legacy_imports'; // @ts-ignore import { initDashboardApp } from './legacy_app'; -import { IEmbeddableStart } from '../../../../../../plugins/embeddable/public'; +import { EmbeddableStart } from '../../../../../../plugins/embeddable/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../plugins/navigation/public'; import { DataPublicPluginStart } from '../../../../../../plugins/data/public'; import { SharePluginStart } from '../../../../../../plugins/share/public'; @@ -67,7 +67,7 @@ export interface RenderDeps { chrome: ChromeStart; addBasePath: (path: string) => string; savedQueryService: DataPublicPluginStart['query']['savedQueries']; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; localStorage: Storage; share: SharePluginStart; config: KibanaLegacyStart['config']; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts index d94612225782d..a9ee77921ed4a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/plugin.ts @@ -35,7 +35,7 @@ import { DataPublicPluginSetup, esFilters, } from '../../../../../plugins/data/public'; -import { IEmbeddableStart } from '../../../../../plugins/embeddable/public'; +import { EmbeddableStart } from '../../../../../plugins/embeddable/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { DashboardConstants } from './np_ready/dashboard_constants'; @@ -54,7 +54,7 @@ import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public' export interface DashboardPluginStartDependencies { data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; navigation: NavigationStart; share: SharePluginStart; kibanaLegacy: KibanaLegacyStart; @@ -70,7 +70,7 @@ export class DashboardPlugin implements Plugin { private startDependencies: { data: DataPublicPluginStart; savedObjectsClient: SavedObjectsClientContract; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; navigation: NavigationStart; share: SharePluginStart; dashboardConfig: KibanaLegacyStart['dashboardConfig']; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts index 90f1549c9f369..6f3adc1f4fcce 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/search_embeddable_factory.ts @@ -32,6 +32,11 @@ import { SearchEmbeddable } from './search_embeddable'; import { SearchInput, SearchOutput } from './types'; import { SEARCH_EMBEDDABLE_TYPE } from './constants'; +interface StartServices { + executeTriggerActions: UiActionsStart['executeTriggerActions']; + isEditable: () => boolean; +} + export class SearchEmbeddableFactory extends EmbeddableFactory< SearchInput, SearchOutput, @@ -40,12 +45,10 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< public readonly type = SEARCH_EMBEDDABLE_TYPE; private $injector: auto.IInjectorService | null; private getInjector: () => Promise | null; - public isEditable: () => boolean; constructor( - private readonly executeTriggerActions: UiActionsStart['executeTriggerActions'], - getInjector: () => Promise, - isEditable: () => boolean + private getStartServices: () => Promise, + getInjector: () => Promise ) { super({ savedObjectMetaData: { @@ -58,13 +61,16 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< }); this.$injector = null; this.getInjector = getInjector; - this.isEditable = isEditable; } public canCreateNew() { return false; } + public async isEditable() { + return (await this.getStartServices()).isEditable(); + } + public getDisplayName() { return i18n.translate('kbn.embeddable.search.displayName', { defaultMessage: 'search', @@ -90,6 +96,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< try { const savedObject = await getServices().getSavedSearchById(savedObjectId); const indexPattern = savedObject.searchSource.getField('index'); + const { executeTriggerActions } = await this.getStartServices(); return new SearchEmbeddable( { savedSearch: savedObject, @@ -101,7 +108,7 @@ export class SearchEmbeddableFactory extends EmbeddableFactory< indexPatterns: indexPattern ? [indexPattern] : [], }, input, - this.executeTriggerActions, + executeTriggerActions, parent ); } catch (e) { diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts index 3ba0418d35f71..ba671a64592a5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts @@ -30,7 +30,7 @@ import { } from '../../../../../plugins/data/public'; import { registerFeature } from './np_ready/register_feature'; import './kibana_services'; -import { IEmbeddableStart, IEmbeddableSetup } from '../../../../../plugins/embeddable/public'; +import { EmbeddableStart, EmbeddableSetup } from '../../../../../plugins/embeddable/public'; import { getInnerAngularModule, getInnerAngularModuleEmbeddable } from './get_inner_angular'; import { setAngularModule, setServices } from './kibana_services'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; @@ -63,7 +63,7 @@ export interface DiscoverSetup { export type DiscoverStart = void; export interface DiscoverSetupPlugins { uiActions: UiActionsSetup; - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; kibanaLegacy: KibanaLegacySetup; home: HomePublicPluginSetup; visualizations: VisualizationsSetup; @@ -71,7 +71,7 @@ export interface DiscoverSetupPlugins { } export interface DiscoverStartPlugins { uiActions: UiActionsStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; navigation: NavigationStart; charts: ChartsPluginStart; data: DataPublicPluginStart; @@ -103,7 +103,7 @@ export class DiscoverPlugin implements Plugin { public initializeInnerAngular?: () => void; public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>; - setup(core: CoreSetup, plugins: DiscoverSetupPlugins): DiscoverSetup { + setup(core: CoreSetup, plugins: DiscoverSetupPlugins): DiscoverSetup { const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({ baseUrl: core.http.basePath.prepend('/app/kibana'), defaultSubUrl: '#/discover', @@ -173,6 +173,7 @@ export class DiscoverPlugin implements Plugin { }); registerFeature(plugins.home); + this.registerEmbeddable(core, plugins); return { addDocView: this.docViewsRegistry.addDocView.bind(this.docViewsRegistry), }; @@ -203,8 +204,6 @@ export class DiscoverPlugin implements Plugin { return { core, plugins }; }; - - this.registerEmbeddable(core, plugins); } stop() { @@ -216,19 +215,25 @@ export class DiscoverPlugin implements Plugin { /** * register embeddable with a slimmer embeddable version of inner angular */ - private async registerEmbeddable(core: CoreStart, plugins: DiscoverStartPlugins) { + private async registerEmbeddable( + core: CoreSetup, + plugins: DiscoverSetupPlugins + ) { const { SearchEmbeddableFactory } = await import('./np_ready/embeddable'); - const isEditable = () => core.application.capabilities.discover.save as boolean; if (!this.getEmbeddableInjector) { throw Error('Discover plugin method getEmbeddableInjector is undefined'); } - const factory = new SearchEmbeddableFactory( - plugins.uiActions.executeTriggerActions, - this.getEmbeddableInjector, - isEditable - ); + const getStartServices = async () => { + const [coreStart, deps] = await core.getStartServices(); + return { + executeTriggerActions: deps.uiActions.executeTriggerActions, + isEditable: () => coreStart.application.capabilities.discover.save as boolean, + }; + }; + + const factory = new SearchEmbeddableFactory(getStartServices, this.getEmbeddableInjector); plugins.embeddable.registerEmbeddableFactory(factory.type, factory); } diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index cfd12b3283459..7e96d7bde6e13 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -29,7 +29,7 @@ import { import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; -import { IEmbeddableStart } from '../../../../../plugins/embeddable/public'; +import { EmbeddableStart } from '../../../../../plugins/embeddable/public'; import { SharePluginStart } from '../../../../../plugins/share/public'; import { DataPublicPluginStart, IndexPatternsContract } from '../../../../../plugins/data/public'; import { VisualizationsStart } from '../../../visualizations/public'; @@ -44,7 +44,7 @@ export interface VisualizeKibanaServices { chrome: ChromeStart; core: CoreStart; data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; getBasePath: () => string; indexPatterns: IndexPatternsContract; localStorage: Storage; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts index ccb3b3ddbb1da..01ce872aeb679 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/types.d.ts @@ -24,7 +24,7 @@ import { DataPublicPluginStart, SavedQuery, } from 'src/plugins/data/public'; -import { IEmbeddableStart } from 'src/plugins/embeddable/public'; +import { EmbeddableStart } from 'src/plugins/embeddable/public'; import { PersistedState } from 'src/plugins/visualizations/public'; import { LegacyCoreStart } from 'kibana/public'; import { Vis } from 'src/legacy/core_plugins/visualizations/public'; @@ -61,7 +61,7 @@ export interface EditorRenderProps { appState: { save(): void }; core: LegacyCoreStart; data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; filters: Filter[]; uiState: PersistedState; timeRange: TimeRange; diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index b9e4487ae84fb..9d88152c59aa7 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -36,7 +36,7 @@ import { DataPublicPluginSetup, esFilters, } from '../../../../../plugins/data/public'; -import { IEmbeddableStart } from '../../../../../plugins/embeddable/public'; +import { EmbeddableStart } from '../../../../../plugins/embeddable/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { SharePluginStart } from '../../../../../plugins/share/public'; import { @@ -55,7 +55,7 @@ import { DefaultEditorController } from '../../../vis_default_editor/public'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; navigation: NavigationStart; share: SharePluginStart; visualizations: VisualizationsStart; @@ -71,7 +71,7 @@ export interface VisualizePluginSetupDependencies { export class VisualizePlugin implements Plugin { private startDependencies: { data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; navigation: NavigationStart; savedObjectsClient: SavedObjectsClientContract; share: SharePluginStart; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx index 237eeff42d9b2..1cd97115ee10e 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx @@ -83,7 +83,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< }); } - public isEditable() { + public async isEditable() { return getCapabilities().visualize.save as boolean; } @@ -114,13 +114,14 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< const indexPattern = await getIndexPattern(savedObject); const indexPatterns = indexPattern ? [indexPattern] : []; + const editable = await this.isEditable(); return new VisualizeEmbeddable( getTimeFilter(), { savedVisualization: savedObject, indexPatterns, editUrl, - editable: this.isEditable(), + editable, appState: input.appState, uiState: input.uiState, }, diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts index 2785247296ff4..4ee727e46f4d6 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts @@ -49,7 +49,7 @@ const createInstance = async () => { const setup = plugin.setup(coreMock.createSetup(), { data: dataPluginMock.createSetupContract(), expressions: expressionsPluginMock.createSetupContract(), - embeddable: embeddablePluginMock.createStartContract(), + embeddable: embeddablePluginMock.createSetupContract(), usageCollection: usageCollectionPluginMock.createSetupContract(), }); const doStart = () => diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts index 5a8a55d470540..953caecefb974 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts @@ -42,7 +42,7 @@ import { } from './services'; import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable'; import { ExpressionsSetup, ExpressionsStart } from '../../../../../../plugins/expressions/public'; -import { IEmbeddableSetup } from '../../../../../../plugins/embeddable/public'; +import { EmbeddableSetup } from '../../../../../../plugins/embeddable/public'; import { visualization as visualizationFunction } from './expressions/visualization_function'; import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; import { @@ -73,7 +73,7 @@ export interface VisualizationsStart extends TypesStart { export interface VisualizationsSetupDeps { expressions: ExpressionsSetup; - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; usageCollection: UsageCollectionSetup; data: DataPublicPluginSetup; } diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index ce4e1b0551881..07e17ad562291 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -20,7 +20,7 @@ import { IScope } from 'angular'; import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public'; -import { IEmbeddableStart, IEmbeddableSetup } from 'src/plugins/embeddable/public'; +import { EmbeddableStart, EmbeddableSetup } from 'src/plugins/embeddable/public'; import { createBrowserHistory } from 'history'; import { LegacyCoreSetup, @@ -68,7 +68,7 @@ export interface PluginsSetup { bfetch: BfetchPublicSetup; charts: ChartsPluginSetup; data: ReturnType; - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; expressions: ReturnType; home: HomePublicPluginSetup; inspector: InspectorSetup; @@ -88,7 +88,7 @@ export interface PluginsStart { bfetch: BfetchPublicStart; charts: ChartsPluginStart; data: ReturnType; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; expressions: ReturnType; inspector: InspectorStart; uiActions: UiActionsStart; diff --git a/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx b/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx index f8c05170e8f67..22cf854a46623 100644 --- a/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx +++ b/src/plugins/dashboard/public/actions/expand_panel_action.test.tsx @@ -28,7 +28,6 @@ import { ContactCardEmbeddableInput, ContactCardEmbeddableOutput, } from '../embeddable_plugin_test_samples'; -import { DashboardOptions } from '../embeddable/dashboard_container_factory'; const embeddableFactories = new Map(); embeddableFactories.set( @@ -40,7 +39,7 @@ let container: DashboardContainer; let embeddable: ContactCardEmbeddable; beforeEach(async () => { - const options: DashboardOptions = { + const options = { ExitFullScreenButton: () => null, SavedObjectFinder: () => null, application: {} as any, diff --git a/src/plugins/dashboard/public/actions/open_replace_panel_flyout.tsx b/src/plugins/dashboard/public/actions/open_replace_panel_flyout.tsx index f15d538703e21..3472d208f814c 100644 --- a/src/plugins/dashboard/public/actions/open_replace_panel_flyout.tsx +++ b/src/plugins/dashboard/public/actions/open_replace_panel_flyout.tsx @@ -24,7 +24,7 @@ import { IEmbeddable, EmbeddableInput, EmbeddableOutput, - IEmbeddableStart, + EmbeddableStart, IContainer, } from '../embeddable_plugin'; @@ -34,7 +34,7 @@ export async function openReplacePanelFlyout(options: { savedObjectFinder: React.ComponentType; notifications: CoreStart['notifications']; panelToRemove: IEmbeddable; - getEmbeddableFactories: IEmbeddableStart['getEmbeddableFactories']; + getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; }) { const { embeddable, diff --git a/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx b/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx index 4438a6c997126..69346dc8c118a 100644 --- a/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx +++ b/src/plugins/dashboard/public/actions/replace_panel_action.test.tsx @@ -27,7 +27,6 @@ import { ContactCardEmbeddableInput, ContactCardEmbeddableOutput, } from '../embeddable_plugin_test_samples'; -import { DashboardOptions } from '../embeddable/dashboard_container_factory'; import { coreMock } from '../../../../core/public/mocks'; import { CoreStart } from 'kibana/public'; @@ -43,7 +42,7 @@ let embeddable: ContactCardEmbeddable; let coreStart: CoreStart; beforeEach(async () => { coreStart = coreMock.createStart(); - const options: DashboardOptions = { + const options = { ExitFullScreenButton: () => null, SavedObjectFinder: () => null, application: {} as any, diff --git a/src/plugins/dashboard/public/actions/replace_panel_action.tsx b/src/plugins/dashboard/public/actions/replace_panel_action.tsx index 26d9c5c8ad4dd..21ec961917d17 100644 --- a/src/plugins/dashboard/public/actions/replace_panel_action.tsx +++ b/src/plugins/dashboard/public/actions/replace_panel_action.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { CoreStart } from '../../../../core/public'; -import { IEmbeddable, ViewMode, IEmbeddableStart } from '../embeddable_plugin'; +import { IEmbeddable, ViewMode, EmbeddableStart } from '../embeddable_plugin'; import { DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '../embeddable'; import { ActionByType, IncompatibleActionError } from '../ui_actions_plugin'; import { openReplacePanelFlyout } from './open_replace_panel_flyout'; @@ -43,7 +43,7 @@ export class ReplacePanelAction implements ActionByType, private notifications: CoreStart['notifications'], - private getEmbeddableFactories: IEmbeddableStart['getEmbeddableFactories'] + private getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories'] ) {} public getDisplayName({ embeddable }: ReplacePanelActionContext) { diff --git a/src/plugins/dashboard/public/actions/replace_panel_flyout.tsx b/src/plugins/dashboard/public/actions/replace_panel_flyout.tsx index 670105650f95a..a1cd865f771d4 100644 --- a/src/plugins/dashboard/public/actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard/public/actions/replace_panel_flyout.tsx @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; -import { GetEmbeddableFactories } from 'src/plugins/embeddable/public'; +import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; import { DashboardPanelState } from '../embeddable'; import { NotificationsStart, Toast } from '../../../../core/public'; import { IContainer, IEmbeddable, EmbeddableInput, EmbeddableOutput } from '../embeddable_plugin'; @@ -31,7 +31,7 @@ interface Props { onClose: () => void; notifications: NotificationsStart; panelToRemove: IEmbeddable; - getEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; } export class ReplacePanelFlyout extends React.Component { diff --git a/src/plugins/dashboard/public/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/embeddable/dashboard_container.tsx index f9443ab97416d..86a6e374d3e25 100644 --- a/src/plugins/dashboard/public/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/embeddable/dashboard_container.tsx @@ -30,7 +30,7 @@ import { ViewMode, EmbeddableFactory, IEmbeddable, - IEmbeddableStart, + EmbeddableStart, } from '../embeddable_plugin'; import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; import { createPanelState } from './panel'; @@ -77,7 +77,7 @@ export interface DashboardContainerOptions { application: CoreStart['application']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; inspector: InspectorStartContract; SavedObjectFinder: React.ComponentType; ExitFullScreenButton: React.ComponentType; diff --git a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx index a358e41f7b507..0fa62fc875603 100644 --- a/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx +++ b/src/plugins/dashboard/public/embeddable/dashboard_container_factory.tsx @@ -18,24 +18,29 @@ */ import { i18n } from '@kbn/i18n'; -import { SavedObjectMetaData } from '../../../saved_objects/public'; -import { SavedObjectAttributes } from '../../../../core/public'; +import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { CoreStart } from '../../../../core/public'; import { ContainerOutput, EmbeddableFactory, ErrorEmbeddable, Container, } from '../embeddable_plugin'; -import { - DashboardContainer, - DashboardContainerInput, - DashboardContainerOptions, -} from './dashboard_container'; -import { DashboardCapabilities } from '../types'; +import { DashboardContainer, DashboardContainerInput } from './dashboard_container'; import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants'; +import { Start as InspectorStartContract } from '../../../inspector/public'; -export interface DashboardOptions extends DashboardContainerOptions { - savedObjectMetaData?: SavedObjectMetaData; +interface StartServices { + capabilities: CoreStart['application']['capabilities']; + application: CoreStart['application']; + overlays: CoreStart['overlays']; + notifications: CoreStart['notifications']; + embeddable: EmbeddableStart; + inspector: InspectorStartContract; + SavedObjectFinder: React.ComponentType; + ExitFullScreenButton: React.ComponentType; + uiActions: UiActionsStart; } export class DashboardContainerFactory extends EmbeddableFactory< @@ -45,23 +50,13 @@ export class DashboardContainerFactory extends EmbeddableFactory< public readonly isContainerType = true; public readonly type = DASHBOARD_CONTAINER_TYPE; - private readonly allowEditing: boolean; - - constructor(private readonly options: DashboardOptions) { - super({ savedObjectMetaData: options.savedObjectMetaData }); - - const capabilities = (options.application.capabilities - .dashboard as unknown) as DashboardCapabilities; - - if (!capabilities || typeof capabilities !== 'object') { - throw new TypeError('Dashboard capabilities not found.'); - } - - this.allowEditing = !!capabilities.createNew && !!capabilities.showWriteControls; + constructor(private readonly getStartServices: () => Promise) { + super(); } - public isEditable() { - return this.allowEditing; + public async isEditable() { + const { capabilities } = await this.getStartServices(); + return !!capabilities.createNew && !!capabilities.showWriteControls; } public getDisplayName() { @@ -82,6 +77,7 @@ export class DashboardContainerFactory extends EmbeddableFactory< initialInput: DashboardContainerInput, parent?: Container ): Promise { - return new DashboardContainer(initialInput, this.options, parent); + const services = await this.getStartServices(); + return new DashboardContainer(initialInput, services, parent); } } diff --git a/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx b/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx index c1a3d88979f49..0f1b9c6dc9307 100644 --- a/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx +++ b/src/plugins/dashboard/public/embeddable/grid/dashboard_grid.test.tsx @@ -23,7 +23,7 @@ import sizeMe from 'react-sizeme'; import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { skip } from 'rxjs/operators'; -import { EmbeddableFactory, GetEmbeddableFactory } from '../../embeddable_plugin'; +import { EmbeddableFactory } from '../../embeddable_plugin'; import { DashboardGrid, DashboardGridProps } from './dashboard_grid'; import { DashboardContainer, DashboardContainerOptions } from '../dashboard_container'; import { getSampleDashboardInput } from '../../test_helpers'; @@ -41,7 +41,7 @@ function prepare(props?: Partial) { CONTACT_CARD_EMBEDDABLE, new ContactCardEmbeddableFactory({} as any, (() => {}) as any, {} as any) ); - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); const initialInput = getSampleDashboardInput({ panels: { '1': { diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 6f78829af19f1..8a6e747aac170 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -23,7 +23,7 @@ import * as React from 'react'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { SharePluginSetup } from 'src/plugins/share/public'; import { UiActionsSetup, UiActionsStart } from '../../../plugins/ui_actions/public'; -import { CONTEXT_MENU_TRIGGER, IEmbeddableSetup, IEmbeddableStart } from './embeddable_plugin'; +import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from './embeddable_plugin'; import { ExpandPanelAction, ReplacePanelAction } from '.'; import { DashboardContainerFactory } from './embeddable/dashboard_container_factory'; import { Start as InspectorStartContract } from '../../../plugins/inspector/public'; @@ -47,13 +47,13 @@ declare module '../../share/public' { } interface SetupDependencies { - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; uiActions: UiActionsSetup; share?: SharePluginSetup; } interface StartDependencies { - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; inspector: InspectorStartContract; uiActions: UiActionsStart; } @@ -72,7 +72,10 @@ export class DashboardEmbeddableContainerPublicPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { share, uiActions }: SetupDependencies): Setup { + public setup( + core: CoreSetup, + { share, uiActions, embeddable }: SetupDependencies + ): Setup { const expandPanelAction = new ExpandPanelAction(); uiActions.registerAction(expandPanelAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction); @@ -86,26 +89,44 @@ export class DashboardEmbeddableContainerPublicPlugin })) ); } + + const getStartServices = async () => { + const [coreStart, deps] = await core.getStartServices(); + + const useHideChrome = () => { + React.useEffect(() => { + coreStart.chrome.setIsVisible(false); + return () => coreStart.chrome.setIsVisible(true); + }, []); + }; + + const ExitFullScreenButton: React.FC = props => { + useHideChrome(); + return ; + }; + return { + capabilities: coreStart.application.capabilities, + application: coreStart.application, + notifications: coreStart.notifications, + overlays: coreStart.overlays, + embeddable: deps.embeddable, + inspector: deps.inspector, + SavedObjectFinder: getSavedObjectFinder(coreStart.savedObjects, coreStart.uiSettings), + ExitFullScreenButton, + uiActions: deps.uiActions, + }; + }; + + const factory = new DashboardContainerFactory(getStartServices); + embeddable.registerEmbeddableFactory(factory.type, factory); } public start(core: CoreStart, plugins: StartDependencies): Start { - const { application, notifications, overlays } = core; - const { embeddable, inspector, uiActions } = plugins; + const { notifications } = core; + const { uiActions } = plugins; const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings); - const useHideChrome = () => { - React.useEffect(() => { - core.chrome.setIsVisible(false); - return () => core.chrome.setIsVisible(true); - }, []); - }; - - const ExitFullScreenButton: React.FC = props => { - useHideChrome(); - return ; - }; - const changeViewAction = new ReplacePanelAction( core, SavedObjectFinder, @@ -114,19 +135,6 @@ export class DashboardEmbeddableContainerPublicPlugin ); uiActions.registerAction(changeViewAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction); - - const factory = new DashboardContainerFactory({ - application, - notifications, - overlays, - embeddable, - inspector, - SavedObjectFinder, - ExitFullScreenButton, - uiActions, - }); - - embeddable.registerEmbeddableFactory(factory.type, factory); } public stop() {} diff --git a/src/plugins/embeddable/public/api/get_embeddable_factories.ts b/src/plugins/embeddable/public/api/get_embeddable_factories.ts deleted file mode 100644 index c12d1283905f5..0000000000000 --- a/src/plugins/embeddable/public/api/get_embeddable_factories.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 { EmbeddableApiPure } from './types'; - -export const getEmbeddableFactories: EmbeddableApiPure['getEmbeddableFactories'] = ({ - embeddableFactories, -}) => () => { - return embeddableFactories.values(); -}; diff --git a/src/plugins/embeddable/public/api/get_embeddable_factory.ts b/src/plugins/embeddable/public/api/get_embeddable_factory.ts deleted file mode 100644 index 8e98da287c5ea..0000000000000 --- a/src/plugins/embeddable/public/api/get_embeddable_factory.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 { EmbeddableApiPure } from './types'; - -export const getEmbeddableFactory: EmbeddableApiPure['getEmbeddableFactory'] = ({ - embeddableFactories, -}) => embeddableFactoryId => { - const factory = embeddableFactories.get(embeddableFactoryId); - - if (!factory) { - throw new Error( - `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.` - ); - } - - return factory; -}; diff --git a/src/plugins/embeddable/public/api/index.ts b/src/plugins/embeddable/public/api/index.ts deleted file mode 100644 index aec539330de9a..0000000000000 --- a/src/plugins/embeddable/public/api/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 { - EmbeddableApiPure, - EmbeddableDependencies, - EmbeddableApi, - EmbeddableDependenciesInternal, -} from './types'; -import { getEmbeddableFactories } from './get_embeddable_factories'; -import { getEmbeddableFactory } from './get_embeddable_factory'; -import { registerEmbeddableFactory } from './register_embeddable_factory'; - -export * from './types'; - -export const pureApi: EmbeddableApiPure = { - getEmbeddableFactories, - getEmbeddableFactory, - registerEmbeddableFactory, -}; - -export const createApi = (deps: EmbeddableDependencies) => { - const partialApi: Partial = {}; - const depsInternal: EmbeddableDependenciesInternal = { ...deps, api: partialApi }; - for (const [key, fn] of Object.entries(pureApi)) { - (partialApi as any)[key] = fn(depsInternal); - } - Object.freeze(partialApi); - const api = partialApi as EmbeddableApi; - return { api, depsInternal }; -}; diff --git a/src/plugins/embeddable/public/api/register_embeddable_factory.ts b/src/plugins/embeddable/public/api/register_embeddable_factory.ts deleted file mode 100644 index 8b7bcdee5911f..0000000000000 --- a/src/plugins/embeddable/public/api/register_embeddable_factory.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 { EmbeddableApiPure } from './types'; - -export const registerEmbeddableFactory: EmbeddableApiPure['registerEmbeddableFactory'] = ({ - embeddableFactories, -}) => (embeddableFactoryId, factory) => { - if (embeddableFactories.has(embeddableFactoryId)) { - throw new Error( - `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] already registered in Embeddables API.` - ); - } - - embeddableFactories.set(embeddableFactoryId, factory); -}; diff --git a/src/plugins/embeddable/public/api/tests/helpers.ts b/src/plugins/embeddable/public/api/tests/helpers.ts deleted file mode 100644 index be8e9a0dec3c2..0000000000000 --- a/src/plugins/embeddable/public/api/tests/helpers.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 { EmbeddableDependencies } from '../types'; - -export const createDeps = (): EmbeddableDependencies => { - const deps: EmbeddableDependencies = { - embeddableFactories: new Map(), - }; - return deps; -}; diff --git a/src/plugins/embeddable/public/api/types.ts b/src/plugins/embeddable/public/api/types.ts deleted file mode 100644 index 179d96a4aff8c..0000000000000 --- a/src/plugins/embeddable/public/api/types.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 { EmbeddableFactoryRegistry } from '../types'; -import { EmbeddableFactory, GetEmbeddableFactories } from '../lib'; - -export interface EmbeddableApi { - getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory; - getEmbeddableFactories: GetEmbeddableFactories; - // TODO: Make `registerEmbeddableFactory` receive only `factory` argument. - registerEmbeddableFactory: ( - id: string, - factory: TEmbeddableFactory - ) => void; -} - -export interface EmbeddableDependencies { - embeddableFactories: EmbeddableFactoryRegistry; -} - -export interface EmbeddableDependenciesInternal extends EmbeddableDependencies { - api: Readonly>; -} - -export type EmbeddableApiPure = { - [K in keyof EmbeddableApi]: (deps: EmbeddableDependenciesInternal) => EmbeddableApi[K]; -}; diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 1474f9ed63052..eca74af4ec253 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -48,8 +48,6 @@ export { EmbeddableRoot, EmbeddableVisTriggerContext, ErrorEmbeddable, - GetEmbeddableFactories, - GetEmbeddableFactory, IContainer, IEmbeddable, isErrorEmbeddable, @@ -68,4 +66,4 @@ export function plugin(initializerContext: PluginInitializerContext) { return new EmbeddablePublicPlugin(initializerContext); } -export { IEmbeddableSetup, IEmbeddableStart } from './plugin'; +export { EmbeddableSetup, EmbeddableStart } from './plugin'; diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 142a237a6a311..9aeaf34f3311b 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -19,11 +19,13 @@ import { EditPanelAction } from './edit_panel_action'; import { EmbeddableFactory, Embeddable, EmbeddableInput } from '../embeddables'; -import { GetEmbeddableFactory, ViewMode } from '../types'; +import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; +import { EmbeddableStart } from '../../plugin'; const embeddableFactories = new Map(); -const getFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); +const getFactory = ((id: string) => + embeddableFactories.get(id)) as EmbeddableStart['getEmbeddableFactory']; class EditableEmbeddable extends Embeddable { public readonly type = 'EDITABLE_EMBEDDABLE'; @@ -82,7 +84,8 @@ test('is not compatible when edit url is not available', async () => { test('is not visible when edit url is available but in view mode', async () => { embeddableFactories.clear(); - const action = new EditPanelAction(type => embeddableFactories.get(type)); + const action = new EditPanelAction((type => + embeddableFactories.get(type)) as EmbeddableStart['getEmbeddableFactory']); expect( await action.isCompatible({ embeddable: new EditableEmbeddable( @@ -98,7 +101,8 @@ test('is not visible when edit url is available but in view mode', async () => { test('is not compatible when edit url is available, in edit mode, but not editable', async () => { embeddableFactories.clear(); - const action = new EditPanelAction(type => embeddableFactories.get(type)); + const action = new EditPanelAction((type => + embeddableFactories.get(type)) as EmbeddableStart['getEmbeddableFactory']); expect( await action.isCompatible({ embeddable: new EditableEmbeddable( diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts index 82f8e33b7ae2f..9125dc0813f98 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts @@ -19,9 +19,10 @@ import { i18n } from '@kbn/i18n'; import { Action } from 'src/plugins/ui_actions/public'; -import { GetEmbeddableFactory, ViewMode } from '../types'; +import { ViewMode } from '../types'; import { EmbeddableFactoryNotFoundError } from '../errors'; import { IEmbeddable } from '../embeddables'; +import { EmbeddableStart } from '../../plugin'; export const ACTION_EDIT_PANEL = 'editPanel'; @@ -34,7 +35,7 @@ export class EditPanelAction implements Action { public readonly id = ACTION_EDIT_PANEL; public order = 15; - constructor(private readonly getEmbeddableFactory: GetEmbeddableFactory) {} + constructor(private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']) {} public getDisplayName({ embeddable }: ActionContext) { const factory = this.getEmbeddableFactory(embeddable.type); diff --git a/src/plugins/embeddable/public/lib/containers/container.ts b/src/plugins/embeddable/public/lib/containers/container.ts index 71e7cca3552bb..5ce79537ccaf3 100644 --- a/src/plugins/embeddable/public/lib/containers/container.ts +++ b/src/plugins/embeddable/public/lib/containers/container.ts @@ -29,7 +29,7 @@ import { } from '../embeddables'; import { IContainer, ContainerInput, ContainerOutput, PanelState } from './i_container'; import { PanelNotFoundError, EmbeddableFactoryNotFoundError } from '../errors'; -import { GetEmbeddableFactory } from '../types'; +import { EmbeddableStart } from '../../plugin'; const getKeys = (o: T): Array => Object.keys(o) as Array; @@ -49,7 +49,7 @@ export abstract class Container< constructor( input: TContainerInput, output: TContainerOutput, - protected readonly getFactory: GetEmbeddableFactory, + protected readonly getFactory: EmbeddableStart['getEmbeddableFactory'], parent?: Container ) { super(input, output, parent); diff --git a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx index 3c9e6e31220b2..07915ce59e6ca 100644 --- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx @@ -20,7 +20,6 @@ import React from 'react'; import { nextTick } from 'test_utils/enzyme_helpers'; import { EmbeddableChildPanel } from './embeddable_child_panel'; -import { GetEmbeddableFactory } from '../types'; import { EmbeddableFactory } from '../embeddables'; import { CONTACT_CARD_EMBEDDABLE } from '../test_samples/embeddables/contact_card/contact_card_embeddable_factory'; import { SlowContactCardEmbeddableFactory } from '../test_samples/embeddables/contact_card/slow_contact_card_embeddable_factory'; @@ -42,7 +41,7 @@ test('EmbeddableChildPanel renders an embeddable when it is done loading', async CONTACT_CARD_EMBEDDABLE, new SlowContactCardEmbeddableFactory({ execAction: (() => null) as any }) ); - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); const container = new HelloWorldContainer({ id: 'hello', panels: {} }, { getEmbeddableFactory, @@ -88,7 +87,7 @@ test('EmbeddableChildPanel renders an embeddable when it is done loading', async test(`EmbeddableChildPanel renders an error message if the factory doesn't exist`, async () => { const inspector = inspectorPluginMock.createStartContract(); - const getEmbeddableFactory: GetEmbeddableFactory = () => undefined; + const getEmbeddableFactory = () => undefined; const container = new HelloWorldContainer( { id: 'hello', diff --git a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx index e15f1faaa397c..4c08a80a356bf 100644 --- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx +++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx @@ -29,15 +29,15 @@ import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { ErrorEmbeddable, IEmbeddable } from '../embeddables'; import { EmbeddablePanel } from '../panel'; import { IContainer } from './i_container'; -import { GetEmbeddableFactory, GetEmbeddableFactories } from '../types'; +import { EmbeddableStart } from '../../plugin'; export interface EmbeddableChildPanelProps { embeddableId: string; className?: string; container: IContainer; getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: GetEmbeddableFactory; - getAllEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; inspector: InspectorStartContract; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts index 162da75c228aa..81f7f35c900c9 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts @@ -74,13 +74,11 @@ export abstract class EmbeddableFactory< this.savedObjectMetaData = savedObjectMetaData; } - // TODO: Can this be a property? If this "...should be based of capabilities service...", - // TODO: maybe then it should be *async*? /** * Returns whether the current user should be allowed to edit this type of - * embeddable. Most of the time this should be based off the capabilities service. + * embeddable. Most of the time this should be based off the capabilities service, hence it's async. */ - public abstract isEditable(): boolean; + public abstract async isEditable(): Promise; /** * Returns a display name for this type of embeddable. Used in "Create new... " options diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx index 7c3a1c6ca45c4..51b83ea0ecaa3 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_renderer.test.tsx @@ -22,21 +22,21 @@ import { HelloWorldEmbeddableFactory, } from '../../../../../../examples/embeddable_examples/public'; import { EmbeddableFactory } from './embeddable_factory'; -import { GetEmbeddableFactory } from '../types'; import { EmbeddableFactoryRenderer } from './embeddable_factory_renderer'; import { mount } from 'enzyme'; import { nextTick } from 'test_utils/enzyme_helpers'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; +import { EmbeddableStart } from '../../plugin'; test('EmbeddableFactoryRenderer renders an embeddable', async () => { const embeddableFactories = new Map(); embeddableFactories.set(HELLO_WORLD_EMBEDDABLE, new HelloWorldEmbeddableFactory()); - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); const component = mount( @@ -54,7 +54,7 @@ test('EmbeddableFactoryRenderer renders an embeddable', async () => { }); test('EmbeddableRoot renders an error if the type does not exist', async () => { - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => undefined; + const getEmbeddableFactory = (id: string) => undefined; const component = mount( >(); const triggerRegistry = new Map(); const embeddableFactories = new Map(); -const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); +const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); const editModeAction = createEditModeAction(); const trigger: Trigger = { diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 28474544f40b5..b95060a73252f 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -27,7 +27,7 @@ import { toMountPoint } from '../../../../kibana_react/public'; import { Start as InspectorStartContract } from '../inspector'; import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER, EmbeddableContext } from '../triggers'; import { IEmbeddable } from '../embeddables/i_embeddable'; -import { ViewMode, GetEmbeddableFactory, GetEmbeddableFactories } from '../types'; +import { ViewMode } from '../types'; import { RemovePanelAction } from './panel_header/panel_actions'; import { AddPanelAction } from './panel_header/panel_actions/add_panel/add_panel_action'; @@ -36,12 +36,13 @@ import { PanelHeader } from './panel_header/panel_header'; import { InspectPanelAction } from './panel_header/panel_actions/inspect_panel_action'; import { EditPanelAction } from '../actions'; import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal'; +import { EmbeddableStart } from '../../plugin'; interface Props { embeddable: IEmbeddable; getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: GetEmbeddableFactory; - getAllEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; inspector: InspectorStartContract; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx index 028d6a530236a..8ee8c8dad9df3 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx @@ -27,15 +27,15 @@ import { } from '../../../../test_samples/embeddables/filterable_embeddable'; import { FilterableEmbeddableFactory } from '../../../../test_samples/embeddables/filterable_embeddable_factory'; import { FilterableContainer } from '../../../../test_samples/embeddables/filterable_container'; -import { GetEmbeddableFactory } from '../../../../types'; // eslint-disable-next-line import { coreMock } from '../../../../../../../../core/public/mocks'; import { ContactCardEmbeddable } from '../../../../test_samples'; import { esFilters, Filter } from '../../../../../../../../plugins/data/public'; +import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; const embeddableFactories = new Map(); embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); -const getFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); +const getFactory = (id: string) => embeddableFactories.get(id); let container: FilterableContainer; let embeddable: FilterableEmbeddable; @@ -58,7 +58,7 @@ beforeEach(async () => { }; container = new FilterableContainer( { id: 'hello', panels: {}, filters: [derivedFilter] }, - getFactory + getFactory as EmbeddableStart['getEmbeddableFactory'] ); const filterableEmbeddable = await container.addNewEmbeddable< diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts index 36bb742040ccc..f3a483bb4bda4 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts @@ -19,7 +19,8 @@ import { i18n } from '@kbn/i18n'; import { Action } from 'src/plugins/ui_actions/public'; import { NotificationsStart, OverlayStart } from 'src/core/public'; -import { ViewMode, GetEmbeddableFactory, GetEmbeddableFactories } from '../../../../types'; +import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; +import { ViewMode } from '../../../../types'; import { openAddPanelFlyout } from './open_add_panel_flyout'; import { IContainer } from '../../../../containers'; @@ -34,8 +35,8 @@ export class AddPanelAction implements Action { public readonly id = ACTION_ADD_PANEL; constructor( - private readonly getFactory: GetEmbeddableFactory, - private readonly getAllFactories: GetEmbeddableFactories, + private readonly getFactory: EmbeddableStart['getEmbeddableFactory'], + private readonly getAllFactories: EmbeddableStart['getEmbeddableFactories'], private readonly overlays: OverlayStart, private readonly notifications: NotificationsStart, private readonly SavedObjectFinder: React.ComponentType diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx index 5f06e4ec44787..2fa21e40ca0f0 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx @@ -19,7 +19,6 @@ import * as React from 'react'; import { AddPanelFlyout } from './add_panel_flyout'; -import { GetEmbeddableFactory } from '../../../../types'; import { ContactCardEmbeddableFactory, CONTACT_CARD_EMBEDDABLE, @@ -32,6 +31,7 @@ import { ReactWrapper } from 'enzyme'; import { coreMock } from '../../../../../../../../core/public/mocks'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; +import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; function DummySavedObjectFinder(props: { children: React.ReactNode }) { return ( @@ -55,7 +55,7 @@ test('createNewEmbeddable() add embeddable to container', async () => { firstName: 'foo', lastName: 'bar', } as any); - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => contactCardEmbeddableFactory; + const getEmbeddableFactory = (id: string) => contactCardEmbeddableFactory; const input: ContainerInput<{ firstName: string; lastName: string }> = { id: '1', panels: {}, @@ -66,7 +66,7 @@ test('createNewEmbeddable() add embeddable to container', async () => { new Set([contactCardEmbeddableFactory]).values()} notifications={core.notifications} SavedObjectFinder={() => null} @@ -100,7 +100,8 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()' firstName: 'foo', lastName: 'bar', } as any); - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => contactCardEmbeddableFactory; + const getEmbeddableFactory = ((id: string) => + contactCardEmbeddableFactory) as EmbeddableStart['getEmbeddableFactory']; const input: ContainerInput<{ firstName: string; lastName: string }> = { id: '1', panels: {}, diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx index 815394ebd97e0..95eeb63710c32 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx @@ -29,16 +29,16 @@ import { EuiTitle, } from '@elastic/eui'; +import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; import { IContainer } from '../../../../containers'; import { EmbeddableFactoryNotFoundError } from '../../../../errors'; -import { GetEmbeddableFactories, GetEmbeddableFactory } from '../../../../types'; import { SavedObjectFinderCreateNew } from './saved_object_finder_create_new'; interface Props { onClose: () => void; container: IContainer; - getFactory: GetEmbeddableFactory; - getAllFactories: GetEmbeddableFactories; + getFactory: EmbeddableStart['getEmbeddableFactory']; + getAllFactories: EmbeddableStart['getEmbeddableFactories']; notifications: CoreSetup['notifications']; SavedObjectFinder: React.ComponentType; } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx index 481693501066c..a452e07b51577 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx @@ -18,15 +18,15 @@ */ import React from 'react'; import { NotificationsStart, OverlayStart } from 'src/core/public'; +import { EmbeddableStart } from '../../../../../plugin'; import { toMountPoint } from '../../../../../../../kibana_react/public'; import { IContainer } from '../../../../containers'; import { AddPanelFlyout } from './add_panel_flyout'; -import { GetEmbeddableFactory, GetEmbeddableFactories } from '../../../../types'; export async function openAddPanelFlyout(options: { embeddable: IContainer; - getFactory: GetEmbeddableFactory; - getAllFactories: GetEmbeddableFactories; + getFactory: EmbeddableStart['getEmbeddableFactory']; + getAllFactories: EmbeddableStart['getEmbeddableFactories']; overlays: OverlayStart; notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts index 4ba63bb025a87..3f7c917cd1617 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts @@ -32,7 +32,6 @@ import { ContactCardEmbeddableFactory, } from '../../../../test_samples/embeddables/contact_card/contact_card_embeddable_factory'; import { HelloWorldContainer } from '../../../../test_samples/embeddables/hello_world_container'; -import { GetEmbeddableFactory } from '../../../../types'; import { EmbeddableFactory } from '../../../../embeddables'; let container: Container; @@ -40,7 +39,7 @@ let embeddable: ContactCardEmbeddable; function createHelloWorldContainer(input = { id: '123', panels: {} }) { const embeddableFactories = new Map(); - const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const getEmbeddableFactory = (id: string) => embeddableFactories.get(id); embeddableFactories.set( CONTACT_CARD_EMBEDDABLE, new ContactCardEmbeddableFactory({}, (() => {}) as any, {} as any) diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx index 8d9beec940acc..e19acda8419da 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx @@ -34,14 +34,14 @@ import { isErrorEmbeddable, ErrorEmbeddable, } from '../../../embeddables'; -import { GetEmbeddableFactory } from '../../../types'; import { of } from '../../../../tests/helpers'; import { esFilters } from '../../../../../../../plugins/data/public'; +import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; const setup = async () => { const embeddableFactories = new Map(); embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); - const getFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); + const getFactory = (id: string) => embeddableFactories.get(id); const container = new FilterableContainer( { id: 'hello', @@ -54,7 +54,7 @@ const setup = async () => { }, ], }, - getFactory + getFactory as EmbeddableStart['getEmbeddableFactory'] ); const embeddable: FilterableEmbeddable | ErrorEmbeddable = await container.addNewEmbeddable< diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx index be096a4cc60ce..f4d5aa148373b 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.test.tsx @@ -20,6 +20,7 @@ import { EmbeddableOutput, isErrorEmbeddable } from '../../../'; import { RemovePanelAction } from './remove_panel_action'; import { EmbeddableFactory } from '../../../embeddables'; +import { EmbeddableStart } from '../../../../plugin'; import { FILTERABLE_EMBEDDABLE, FilterableEmbeddable, @@ -27,13 +28,13 @@ import { } from '../../../test_samples/embeddables/filterable_embeddable'; import { FilterableEmbeddableFactory } from '../../../test_samples/embeddables/filterable_embeddable_factory'; import { FilterableContainer } from '../../../test_samples/embeddables/filterable_container'; -import { GetEmbeddableFactory, ViewMode } from '../../../types'; +import { ViewMode } from '../../../types'; import { ContactCardEmbeddable } from '../../../test_samples/embeddables/contact_card/contact_card_embeddable'; import { esFilters, Filter } from '../../../../../../../plugins/data/public'; const embeddableFactories = new Map(); embeddableFactories.set(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); -const getFactory: GetEmbeddableFactory = (id: string) => embeddableFactories.get(id); +const getFactory = (id: string) => embeddableFactories.get(id); let container: FilterableContainer; let embeddable: FilterableEmbeddable; @@ -46,7 +47,7 @@ beforeEach(async () => { }; container = new FilterableContainer( { id: 'hello', panels: {}, filters: [derivedFilter], viewMode: ViewMode.EDIT }, - getFactory + getFactory as EmbeddableStart['getEmbeddableFactory'] ); const filterableEmbeddable = await container.addNewEmbeddable< diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx index 7a9ba4fbbf6d6..20a5a8112f4d3 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory.tsx @@ -42,7 +42,7 @@ export class ContactCardEmbeddableFactory extends EmbeddableFactory { public readonly type = FILTERABLE_CONTAINER; constructor( - private readonly getFactory: GetEmbeddableFactory, + private readonly getFactory: EmbeddableStart['getEmbeddableFactory'], options: EmbeddableFactoryOptions = {} ) { super(options); @@ -43,7 +43,7 @@ export class FilterableContainerFactory extends EmbeddableFactory { public readonly type = FILTERABLE_EMBEDDABLE; - public isEditable() { + public async isEditable() { return true; } diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx index c5ba054bebb7a..a88c3ba086325 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx @@ -24,7 +24,7 @@ import { UiActionsService } from 'src/plugins/ui_actions/public'; import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { Container, ViewMode, ContainerInput } from '../..'; import { HelloWorldContainerComponent } from './hello_world_container_component'; -import { GetEmbeddableFactory, GetEmbeddableFactories } from '../../types'; +import { EmbeddableStart } from '../../../plugin'; export const HELLO_WORLD_CONTAINER = 'HELLO_WORLD_CONTAINER'; @@ -46,8 +46,8 @@ interface HelloWorldContainerInput extends ContainerInput { interface HelloWorldContainerOptions { getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: GetEmbeddableFactory; - getAllEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; inspector: InspectorStartContract; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container_component.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container_component.tsx index e9acfd4539768..e8c1464edab38 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container_component.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container_component.tsx @@ -24,13 +24,13 @@ import { CoreStart } from 'src/core/public'; import { UiActionsService } from 'src/plugins/ui_actions/public'; import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { IContainer, PanelState, EmbeddableChildPanel } from '../..'; -import { GetEmbeddableFactory, GetEmbeddableFactories } from '../../types'; +import { EmbeddableStart } from '../../../plugin'; interface Props { container: IContainer; getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: GetEmbeddableFactory; - getAllEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; inspector: InspectorStartContract; diff --git a/src/plugins/embeddable/public/lib/types.ts b/src/plugins/embeddable/public/lib/types.ts index 68ea5bc17f7c9..1cfff7baca186 100644 --- a/src/plugins/embeddable/public/lib/types.ts +++ b/src/plugins/embeddable/public/lib/types.ts @@ -18,7 +18,6 @@ */ import { Adapters } from './inspector'; -import { EmbeddableFactory } from './embeddables/embeddable_factory'; export interface Trigger { id: string; @@ -40,6 +39,3 @@ export enum ViewMode { } export { Adapters }; - -export type GetEmbeddableFactory = (id: string) => EmbeddableFactory | undefined; -export type GetEmbeddableFactories = () => IterableIterator; diff --git a/src/plugins/embeddable/public/mocks.ts b/src/plugins/embeddable/public/mocks.ts index fd299bc626fb9..ba2f78e42e10e 100644 --- a/src/plugins/embeddable/public/mocks.ts +++ b/src/plugins/embeddable/public/mocks.ts @@ -17,15 +17,15 @@ * under the License. */ -import { IEmbeddableStart, IEmbeddableSetup } from '.'; +import { EmbeddableStart, EmbeddableSetup } from '.'; import { EmbeddablePublicPlugin } from './plugin'; import { coreMock } from '../../../core/public/mocks'; // eslint-disable-next-line import { uiActionsPluginMock } from '../../ui_actions/public/mocks'; -export type Setup = jest.Mocked; -export type Start = jest.Mocked; +export type Setup = jest.Mocked; +export type Start = jest.Mocked; const createSetupContract = (): Setup => { const setupContract: Setup = { @@ -36,7 +36,6 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { const startContract: Start = { - registerEmbeddableFactory: jest.fn(), getEmbeddableFactories: jest.fn(), getEmbeddableFactory: jest.fn(), }; diff --git a/src/plugins/embeddable/public/api/tests/registry.test.ts b/src/plugins/embeddable/public/plugin.test.ts similarity index 70% rename from src/plugins/embeddable/public/api/tests/registry.test.ts rename to src/plugins/embeddable/public/plugin.test.ts index 30a8a71d243f9..c334411004e2c 100644 --- a/src/plugins/embeddable/public/api/tests/registry.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -16,18 +16,20 @@ * specific language governing permissions and limitations * under the License. */ - -import { createApi } from '..'; -import { createDeps } from './helpers'; +import { coreMock } from '../../../core/public/mocks'; +import { testPlugin } from './tests/test_plugin'; test('cannot register embeddable factory with the same ID', async () => { - const deps = createDeps(); - const { api } = createApi(deps); + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const { setup } = testPlugin(coreSetup, coreStart); const embeddableFactoryId = 'ID'; const embeddableFactory = {} as any; - api.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory); - expect(() => api.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory)).toThrowError( + setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory); + expect(() => + setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory) + ).toThrowError( 'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.' ); }); diff --git a/src/plugins/embeddable/public/plugin.ts b/src/plugins/embeddable/public/plugin.ts index c84fb888412e1..381665c359ffd 100644 --- a/src/plugins/embeddable/public/plugin.ts +++ b/src/plugins/embeddable/public/plugin.ts @@ -16,45 +16,78 @@ * specific language governing permissions and limitations * under the License. */ - import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { EmbeddableFactoryRegistry } from './types'; -import { createApi, EmbeddableApi } from './api'; import { bootstrap } from './bootstrap'; +import { EmbeddableFactory, EmbeddableInput, EmbeddableOutput } from './lib'; -export interface IEmbeddableSetupDependencies { +export interface EmbeddableSetupDependencies { uiActions: UiActionsSetup; } -export interface IEmbeddableSetup { - registerEmbeddableFactory: EmbeddableApi['registerEmbeddableFactory']; +export interface EmbeddableSetup { + registerEmbeddableFactory: ( + id: string, + factory: EmbeddableFactory + ) => void; +} +export interface EmbeddableStart { + getEmbeddableFactory: < + I extends EmbeddableInput = EmbeddableInput, + O extends EmbeddableOutput = EmbeddableOutput + >( + embeddableFactoryId: string + ) => EmbeddableFactory | undefined; + getEmbeddableFactories: () => IterableIterator; } -export type IEmbeddableStart = EmbeddableApi; - -export class EmbeddablePublicPlugin implements Plugin { +export class EmbeddablePublicPlugin implements Plugin { private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); - private api!: EmbeddableApi; constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { uiActions }: IEmbeddableSetupDependencies) { - ({ api: this.api } = createApi({ - embeddableFactories: this.embeddableFactories, - })); + public setup(core: CoreSetup, { uiActions }: EmbeddableSetupDependencies) { bootstrap(uiActions); - const { registerEmbeddableFactory } = this.api; - return { - registerEmbeddableFactory, + registerEmbeddableFactory: this.registerEmbeddableFactory, }; } public start(core: CoreStart) { - return this.api; + return { + getEmbeddableFactory: this.getEmbeddableFactory, + getEmbeddableFactories: () => this.embeddableFactories.values(), + }; } public stop() {} + + private registerEmbeddableFactory = (embeddableFactoryId: string, factory: EmbeddableFactory) => { + if (this.embeddableFactories.has(embeddableFactoryId)) { + throw new Error( + `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] already registered in Embeddables API.` + ); + } + + this.embeddableFactories.set(embeddableFactoryId, factory); + }; + + private getEmbeddableFactory = < + I extends EmbeddableInput = EmbeddableInput, + O extends EmbeddableOutput = EmbeddableOutput + >( + embeddableFactoryId: string + ) => { + const factory = this.embeddableFactories.get(embeddableFactoryId); + + if (!factory) { + throw new Error( + `Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.` + ); + } + + return factory as EmbeddableFactory; + }; } diff --git a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts index 0721acb1a1fba..6beef35bbe136 100644 --- a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts +++ b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts @@ -35,14 +35,14 @@ import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; import { esFilters } from '../../../../plugins/data/public'; test('ApplyFilterAction applies the filter to the root of the container tree', async () => { - const { doStart } = testPlugin(); + const { doStart, setup } = testPlugin(); const api = doStart(); const factory1 = new FilterableContainerFactory(api.getEmbeddableFactory); const factory2 = new FilterableEmbeddableFactory(); - api.registerEmbeddableFactory(factory1.type, factory1); - api.registerEmbeddableFactory(factory2.type, factory2); + setup.registerEmbeddableFactory(factory1.type, factory1); + setup.registerEmbeddableFactory(factory2.type, factory2); const applyFilterAction = createFilterAction(); @@ -93,7 +93,7 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a }); test('ApplyFilterAction is incompatible if the root container does not accept a filter as input', async () => { - const { doStart, coreStart } = testPlugin(); + const { doStart, coreStart, setup } = testPlugin(); const api = doStart(); const inspector = inspectorPluginMock.createStartContract(); @@ -112,7 +112,7 @@ test('ApplyFilterAction is incompatible if the root container does not accept a ); const factory = new FilterableEmbeddableFactory(); - api.registerEmbeddableFactory(factory.type, factory); + setup.registerEmbeddableFactory(factory.type, factory); const embeddable = await parent.addNewEmbeddable< FilterableContainerInput, @@ -129,12 +129,12 @@ test('ApplyFilterAction is incompatible if the root container does not accept a }); test('trying to execute on incompatible context throws an error ', async () => { - const { doStart, coreStart } = testPlugin(); + const { doStart, coreStart, setup } = testPlugin(); const api = doStart(); const inspector = inspectorPluginMock.createStartContract(); const factory = new FilterableEmbeddableFactory(); - api.registerEmbeddableFactory(factory.type, factory); + setup.registerEmbeddableFactory(factory.type, factory); const applyFilterAction = createFilterAction(); const parent = new HelloWorldContainer( diff --git a/src/plugins/embeddable/public/tests/container.test.ts b/src/plugins/embeddable/public/tests/container.test.ts index be19ac206999d..1ee52f4749135 100644 --- a/src/plugins/embeddable/public/tests/container.test.ts +++ b/src/plugins/embeddable/public/tests/container.test.ts @@ -562,7 +562,7 @@ test('Panel added to input state', async () => { test('Container changes made directly after adding a new embeddable are propagated', async done => { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); - const { doStart, uiActions } = testPlugin(coreSetup, coreStart); + const { setup, doStart, uiActions } = testPlugin(coreSetup, coreStart); const start = doStart(); const container = new HelloWorldContainer( @@ -586,7 +586,7 @@ test('Container changes made directly after adding a new embeddable are propagat loadTickCount: 3, execAction: uiActions.executeTriggerActions, }); - start.registerEmbeddableFactory(factory.type, factory); + setup.registerEmbeddableFactory(factory.type, factory); const subscription = Rx.merge(container.getOutput$(), container.getInput$()) .pipe(skip(2)) @@ -755,7 +755,7 @@ test('untilEmbeddableLoaded() resolves if child is loaded in the container', asy }); test('untilEmbeddableLoaded resolves with undefined if child is subsequently removed', async done => { - const { doStart, coreStart, uiActions } = testPlugin( + const { doStart, setup, coreStart, uiActions } = testPlugin( coreMock.createSetup(), coreMock.createStart() ); @@ -764,7 +764,7 @@ test('untilEmbeddableLoaded resolves with undefined if child is subsequently rem loadTickCount: 3, execAction: uiActions.executeTriggerActions, }); - start.registerEmbeddableFactory(factory.type, factory); + setup.registerEmbeddableFactory(factory.type, factory); const container = new HelloWorldContainer( { id: 'hello', @@ -795,7 +795,7 @@ test('untilEmbeddableLoaded resolves with undefined if child is subsequently rem }); test('adding a panel then subsequently removing it before its loaded removes the panel', async done => { - const { doStart, coreStart, uiActions } = testPlugin( + const { doStart, coreStart, uiActions, setup } = testPlugin( coreMock.createSetup(), coreMock.createStart() ); @@ -804,7 +804,7 @@ test('adding a panel then subsequently removing it before its loaded removes the loadTickCount: 1, execAction: uiActions.executeTriggerActions, }); - start.registerEmbeddableFactory(factory.type, factory); + setup.registerEmbeddableFactory(factory.type, factory); const container = new HelloWorldContainer( { id: 'hello', diff --git a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx index 70d7c99d3fb9d..99d5a7c747d15 100644 --- a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx +++ b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx @@ -34,16 +34,16 @@ import { HelloWorldContainer } from '../lib/test_samples/embeddables/hello_world // eslint-disable-next-line import { coreMock } from '../../../../core/public/mocks'; import { testPlugin } from './test_plugin'; -import { EmbeddableApi } from '../api'; import { CustomizePanelModal } from '../lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal'; import { mount } from 'enzyme'; +import { EmbeddableStart } from '../plugin'; -let api: EmbeddableApi; +let api: EmbeddableStart; let container: Container; let embeddable: ContactCardEmbeddable; beforeEach(async () => { - const { doStart, coreStart, uiActions } = testPlugin( + const { doStart, coreStart, uiActions, setup } = testPlugin( coreMock.createSetup(), coreMock.createStart() ); @@ -54,7 +54,7 @@ beforeEach(async () => { uiActions.executeTriggerActions, {} as any ); - api.registerEmbeddableFactory(contactCardFactory.type, contactCardFactory); + setup.registerEmbeddableFactory(contactCardFactory.type, contactCardFactory); container = new HelloWorldContainer( { id: '123', panels: {} }, diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts index 1edc332780336..e199ef193aa1c 100644 --- a/src/plugins/embeddable/public/tests/test_plugin.ts +++ b/src/plugins/embeddable/public/tests/test_plugin.ts @@ -22,14 +22,14 @@ import { CoreSetup, CoreStart } from 'src/core/public'; import { uiActionsPluginMock } from 'src/plugins/ui_actions/public/mocks'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { coreMock } from '../../../../core/public/mocks'; -import { EmbeddablePublicPlugin, IEmbeddableSetup, IEmbeddableStart } from '../plugin'; +import { EmbeddablePublicPlugin, EmbeddableSetup, EmbeddableStart } from '../plugin'; export interface TestPluginReturn { plugin: EmbeddablePublicPlugin; coreSetup: CoreSetup; coreStart: CoreStart; - setup: IEmbeddableSetup; - doStart: (anotherCoreStart?: CoreStart) => IEmbeddableStart; + setup: EmbeddableSetup; + doStart: (anotherCoreStart?: CoreStart) => EmbeddableStart; uiActions: UiActionsStart; } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx index 144954800c91f..54d13efe4d790 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx @@ -19,18 +19,15 @@ import { EuiTab } from '@elastic/eui'; import React, { Component } from 'react'; import { CoreStart } from 'src/core/public'; -import { - GetEmbeddableFactory, - GetEmbeddableFactories, -} from 'src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { EmbeddableStart } from 'src/plugins/embeddable/public'; import { UiActionsService } from '../../../../../../../../src/plugins/ui_actions/public'; import { DashboardContainerExample } from './dashboard_container_example'; import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; export interface AppProps { getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: GetEmbeddableFactory; - getAllEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; inspector: InspectorStartContract; diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx index 7cc9c1df1c948..f8625e4490e51 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx @@ -18,18 +18,19 @@ */ import React from 'react'; import { EuiButton, EuiLoadingChart } from '@elastic/eui'; +import { ContainerOutput } from 'src/plugins/embeddable/public'; import { ErrorEmbeddable, ViewMode, isErrorEmbeddable, EmbeddablePanel, - GetEmbeddableFactory, - GetEmbeddableFactories, + EmbeddableStart, } from '../embeddable_api'; import { DASHBOARD_CONTAINER_TYPE, DashboardContainer, DashboardContainerFactory, + DashboardContainerInput, } from '../../../../../../../../src/plugins/dashboard/public'; import { CoreStart } from '../../../../../../../../src/core/public'; @@ -39,8 +40,8 @@ import { UiActionsService } from '../../../../../../../../src/plugins/ui_actions interface Props { getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: GetEmbeddableFactory; - getAllEmbeddableFactories: GetEmbeddableFactories; + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; overlays: CoreStart['overlays']; notifications: CoreStart['notifications']; inspector: InspectorStartContract; @@ -67,9 +68,10 @@ export class DashboardContainerExample extends React.Component { public async componentDidMount() { this.mounted = true; - const dashboardFactory = this.props.getEmbeddableFactory( - DASHBOARD_CONTAINER_TYPE - ) as DashboardContainerFactory; + const dashboardFactory = this.props.getEmbeddableFactory< + DashboardContainerInput, + ContainerOutput + >(DASHBOARD_CONTAINER_TYPE) as DashboardContainerFactory; if (dashboardFactory) { this.container = await dashboardFactory.create(dashboardInput); if (this.mounted) { diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddables/hello_world_embeddable_factory.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddables/hello_world_embeddable_factory.ts deleted file mode 100644 index 0c90cb3b85867..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddables/hello_world_embeddable_factory.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - */ - -// eslint-disable-next-line -import { npSetup } from '../../../../../../../../src/legacy/ui/public/new_platform'; -// eslint-disable-next-line -import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from '../../../../../../../../examples/embeddable_examples/public'; - -npSetup.plugins.embeddable.registerEmbeddableFactory( - HELLO_WORLD_EMBEDDABLE, - new HelloWorldEmbeddableFactory() -); diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx index 25666dc0359d9..18ceec652392d 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx @@ -31,21 +31,16 @@ import { CONTEXT_MENU_TRIGGER } from './embeddable_api'; const REACT_ROOT_ID = 'embeddableExplorerRoot'; -import { - SayHelloAction, - createSendMessageAction, - ContactCardEmbeddableFactory, -} from './embeddable_api'; +import { SayHelloAction, createSendMessageAction } from './embeddable_api'; import { App } from './app'; import { getSavedObjectFinder } from '../../../../../../../src/plugins/saved_objects/public'; -import { HelloWorldEmbeddableFactory } from '../../../../../../../examples/embeddable_examples/public'; import { - IEmbeddableStart, - IEmbeddableSetup, + EmbeddableStart, + EmbeddableSetup, } from '.../../../../../../../src/plugins/embeddable/public'; export interface SetupDependencies { - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; inspector: InspectorSetupContract; __LEGACY: { ExitFullScreenButton: React.ComponentType; @@ -53,7 +48,7 @@ export interface SetupDependencies { } interface StartDependencies { - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; uiActions: UiActionsStart; inspector: InspectorStartContract; __LEGACY: { @@ -74,12 +69,6 @@ export class EmbeddableExplorerPublicPlugin const helloWorldAction = createHelloWorldAction(core.overlays); const sayHelloAction = new SayHelloAction(alert); const sendMessageAction = createSendMessageAction(core.overlays); - const helloWorldEmbeddableFactory = new HelloWorldEmbeddableFactory(); - const contactCardEmbeddableFactory = new ContactCardEmbeddableFactory( - {}, - plugins.uiActions.executeTriggerActions, - core.overlays - ); plugins.uiActions.registerAction(helloWorldAction); plugins.uiActions.registerAction(sayHelloAction); @@ -87,15 +76,6 @@ export class EmbeddableExplorerPublicPlugin plugins.uiActions.attachAction(CONTEXT_MENU_TRIGGER, helloWorldAction); - plugins.embeddable.registerEmbeddableFactory( - helloWorldEmbeddableFactory.type, - helloWorldEmbeddableFactory - ); - plugins.embeddable.registerEmbeddableFactory( - contactCardEmbeddableFactory.type, - contactCardEmbeddableFactory - ); - plugins.__LEGACY.onRenderComplete(() => { const root = document.getElementById(REACT_ROOT_ID); ReactDOM.render( diff --git a/test/plugin_functional/test_suites/embeddable_explorer/dashboard_container.js b/test/plugin_functional/test_suites/embeddable_explorer/dashboard_container.js index 203378e547c8a..4a1bcecc0d5a1 100644 --- a/test/plugin_functional/test_suites/embeddable_explorer/dashboard_container.js +++ b/test/plugin_functional/test_suites/embeddable_explorer/dashboard_container.js @@ -17,11 +17,8 @@ * under the License. */ -import expect from '@kbn/expect'; - export default function({ getService }) { const testSubjects = getService('testSubjects'); - const retry = getService('retry'); const pieChart = getService('pieChart'); const dashboardExpect = getService('dashboardExpect'); @@ -30,17 +27,6 @@ export default function({ getService }) { await testSubjects.click('embedExplorerTab-dashboardContainer'); }); - it('hello world embeddable renders', async () => { - await retry.try(async () => { - const text = await testSubjects.getVisibleText('helloWorldEmbeddable'); - expect(text).to.be('HELLO WORLD!'); - }); - }); - - it('contact card embeddable renders', async () => { - await testSubjects.existOrFail('embeddablePanelHeading-HelloSue'); - }); - it('pie charts', async () => { await pieChart.expectPieSliceCount(5); }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index d30ad62b385c2..2bde698e23562 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -27,17 +27,19 @@ import { Embeddable } from './embeddable'; import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence'; import { getEditPath } from '../../../../../../plugins/lens/common'; +interface StartServices { + timefilter: TimefilterContract; + coreHttp: HttpSetup; + capabilities: RecursiveReadonly; + savedObjectsClient: SavedObjectsClientContract; + expressionRenderer: ReactExpressionRendererType; + indexPatternService: IndexPatternsContract; +} + export class EmbeddableFactory extends AbstractEmbeddableFactory { type = DOC_TYPE; - constructor( - private timefilter: TimefilterContract, - private coreHttp: HttpSetup, - private capabilities: RecursiveReadonly, - private savedObjectsClient: SavedObjectsClientContract, - private expressionRenderer: ReactExpressionRendererType, - private indexPatternService: IndexPatternsContract - ) { + constructor(private getStartServices: () => Promise) { super({ savedObjectMetaData: { name: i18n.translate('xpack.lens.lensSavedObjectLabel', { @@ -49,8 +51,9 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory { }); } - public isEditable() { - return this.capabilities.visualize.save as boolean; + public async isEditable() { + const { capabilities } = await this.getStartServices(); + return capabilities.visualize.save as boolean; } canCreateNew() { @@ -68,13 +71,20 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory { input: Partial & { id: string }, parent?: IContainer ) { - const store = new SavedObjectIndexStore(this.savedObjectsClient); + const { + savedObjectsClient, + coreHttp, + indexPatternService, + timefilter, + expressionRenderer, + } = await this.getStartServices(); + const store = new SavedObjectIndexStore(savedObjectsClient); const savedVis = await store.load(savedObjectId); const promises = savedVis.state.datasourceMetaData.filterableIndexPatterns.map( async ({ id }) => { try { - return await this.indexPatternService.get(id); + return await indexPatternService.get(id); } catch (error) { // Unable to load index pattern, ignore error as the index patterns are only used to // configure the filter and query bar - there is still a good chance to get the visualization @@ -90,12 +100,12 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory { ); return new Embeddable( - this.timefilter, - this.expressionRenderer, + timefilter, + expressionRenderer, { savedVis, - editUrl: this.coreHttp.basePath.prepend(getEditPath(savedObjectId)), - editable: this.isEditable(), + editUrl: coreHttp.basePath.prepend(getEditPath(savedObjectId)), + editable: await this.isEditable(), indexPatterns, }, input, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx index 2e1645c816140..6b9dc88e7ed12 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx @@ -12,6 +12,7 @@ import { createMockSetupDependencies, createMockStartDependencies, } from './mocks'; +import { CoreSetup } from 'kibana/public'; jest.mock('ui/new_platform'); @@ -41,7 +42,10 @@ describe('editor_frame service', () => { it('should create an editor frame instance which mounts and unmounts', async () => { await expect( (async () => { - pluginInstance.setup(coreMock.createSetup(), pluginSetupDependencies); + pluginInstance.setup( + coreMock.createSetup() as CoreSetup, + pluginSetupDependencies + ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); const instance = await publicAPI.createInstance({}); instance.mount(mountpoint, { @@ -57,7 +61,10 @@ describe('editor_frame service', () => { }); it('should not have child nodes after unmount', async () => { - pluginInstance.setup(coreMock.createSetup(), pluginSetupDependencies); + pluginInstance.setup( + coreMock.createSetup() as CoreSetup, + pluginSetupDependencies + ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); const instance = await publicAPI.createInstance({}); instance.mount(mountpoint, { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx index 5347be47e145e..1375c60060ca8 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx @@ -12,10 +12,7 @@ import { ExpressionsSetup, ExpressionsStart, } from '../../../../../../src/plugins/expressions/public'; -import { - IEmbeddableSetup, - IEmbeddableStart, -} from '../../../../../../src/plugins/embeddable/public'; +import { EmbeddableSetup, EmbeddableStart } from '../../../../../../src/plugins/embeddable/public'; import { DataPublicPluginSetup, DataPublicPluginStart, @@ -35,13 +32,13 @@ import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; export interface EditorFrameSetupPlugins { data: DataPublicPluginSetup; - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; expressions: ExpressionsSetup; } export interface EditorFrameStartPlugins { data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; expressions: ExpressionsStart; } @@ -63,10 +60,27 @@ export class EditorFrameService { private readonly datasources: Array> = []; private readonly visualizations: Array> = []; - public setup(core: CoreSetup, plugins: EditorFrameSetupPlugins): EditorFrameSetup { + public setup( + core: CoreSetup, + plugins: EditorFrameSetupPlugins + ): EditorFrameSetup { plugins.expressions.registerFunction(() => mergeTables); plugins.expressions.registerFunction(() => formatColumn); + const getStartServices = async () => { + const [coreStart, deps] = await core.getStartServices(); + return { + capabilities: coreStart.application.capabilities, + savedObjectsClient: coreStart.savedObjects.client, + coreHttp: coreStart.http, + timefilter: deps.data.query.timefilter.timefilter, + expressionRenderer: deps.expressions.ReactExpressionRenderer, + indexPatternService: deps.data.indexPatterns, + }; + }; + + plugins.embeddable.registerEmbeddableFactory('lens', new EmbeddableFactory(getStartServices)); + return { registerDatasource: datasource => { this.datasources.push(datasource as Datasource); @@ -78,18 +92,6 @@ export class EditorFrameService { } public start(core: CoreStart, plugins: EditorFrameStartPlugins): EditorFrameStart { - plugins.embeddable.registerEmbeddableFactory( - 'lens', - new EmbeddableFactory( - plugins.data.query.timefilter.timefilter, - core.http, - core.application.capabilities, - core.savedObjects.client, - plugins.expressions.ReactExpressionRenderer, - plugins.data.indexPatterns - ) - ); - const createInstance = async (): Promise => { let domElement: Element; const [resolvedDatasources, resolvedVisualizations] = await Promise.all([ diff --git a/x-pack/legacy/plugins/lens/public/plugin.tsx b/x-pack/legacy/plugins/lens/public/plugin.tsx index 7afe6d7abedc0..cc029fee49d1d 100644 --- a/x-pack/legacy/plugins/lens/public/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/plugin.tsx @@ -36,7 +36,7 @@ import { getLensUrlFromDashboardAbsoluteUrl, } from '../../../../../src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper'; import { FormatFactory } from './legacy_imports'; -import { IEmbeddableSetup, IEmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { EmbeddableSetup, EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; import { EditorFrameStart } from './types'; import { getLensAliasConfig } from './vis_type_alias'; import { VisualizationsSetup } from './legacy_imports'; @@ -45,7 +45,7 @@ export interface LensPluginSetupDependencies { kibanaLegacy: KibanaLegacySetup; expressions: ExpressionsSetup; data: DataPublicPluginSetup; - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; __LEGACY: { formatFactory: FormatFactory; visualizations: VisualizationsSetup; @@ -54,7 +54,7 @@ export interface LensPluginSetupDependencies { export interface LensPluginStartDependencies { data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; expressions: ExpressionsStart; } diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 0c93cd51abd79..888df8447a728 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -9,18 +9,13 @@ import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; import minimatch from 'minimatch'; import { ViewMode } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { - IndexPatternMapping, - MapEmbeddable, - RenderTooltipContentParams, - SetQuery, - EmbeddableApi, -} from './types'; +import { IndexPatternMapping, MapEmbeddable, RenderTooltipContentParams, SetQuery } from './types'; import { getLayerList } from './map_config'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../maps/common/constants'; import * as i18n from './translations'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; +import { EmbeddableStart } from '../../../../../../../src/plugins/embeddable/public'; import { IndexPatternSavedObject } from '../../hooks/types'; /** @@ -45,7 +40,7 @@ export const createEmbeddable = async ( endDate: number, setQuery: SetQuery, portalNode: PortalNode, - embeddableApi: EmbeddableApi + embeddableApi: EmbeddableStart ): Promise => { const factory = embeddableApi.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index 812d327ce4488..cc253beb08eae 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -9,7 +9,6 @@ import { EmbeddableInput, EmbeddableOutput, IEmbeddable, - EmbeddableFactory, } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { inputsModel } from '../../store/inputs'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; @@ -85,8 +84,3 @@ export interface RenderTooltipContentParams { } export type MapToolTipProps = Partial; - -export interface EmbeddableApi { - getEmbeddableFactory: (embeddableFactoryId: string) => EmbeddableFactory; - registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void; -} diff --git a/x-pack/legacy/plugins/siem/public/plugin.tsx b/x-pack/legacy/plugins/siem/public/plugin.tsx index f22add59a95d4..71fa3a54df768 100644 --- a/x-pack/legacy/plugins/siem/public/plugin.tsx +++ b/x-pack/legacy/plugins/siem/public/plugin.tsx @@ -13,7 +13,7 @@ import { } from '../../../../../src/core/public'; import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; -import { IEmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; import { Start as NewsfeedStart } from '../../../../../src/plugins/newsfeed/public'; import { Start as InspectorStart } from '../../../../../src/plugins/inspector/public'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; @@ -37,7 +37,7 @@ export interface SetupPlugins { } export interface StartPlugins { data: DataPublicPluginStart; - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; inspector: InspectorStart; newsfeed?: NewsfeedStart; uiActions: UiActionsStart; diff --git a/x-pack/plugins/advanced_ui_actions/public/plugin.ts b/x-pack/plugins/advanced_ui_actions/public/plugin.ts index 2f6935cdf1961..b9f0ce43d3cdc 100644 --- a/x-pack/plugins/advanced_ui_actions/public/plugin.ts +++ b/x-pack/plugins/advanced_ui_actions/public/plugin.ts @@ -15,8 +15,8 @@ import { UiActionsStart, UiActionsSetup } from '../../../../src/plugins/ui_actio import { CONTEXT_MENU_TRIGGER, PANEL_BADGE_TRIGGER, - IEmbeddableSetup, - IEmbeddableStart, + EmbeddableSetup, + EmbeddableStart, } from '../../../../src/plugins/embeddable/public'; import { CustomTimeRangeAction, @@ -32,12 +32,12 @@ import { import { CommonlyUsedRange } from './types'; interface SetupDependencies { - embeddable: IEmbeddableSetup; // Embeddable are needed because they register basic triggers/actions. + embeddable: EmbeddableSetup; // Embeddable are needed because they register basic triggers/actions. uiActions: UiActionsSetup; } interface StartDependencies { - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; uiActions: UiActionsStart; } diff --git a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_container.ts b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_container.ts index 789a4181c2aff..3d143b0cacd06 100644 --- a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_container.ts +++ b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_container.ts @@ -8,7 +8,7 @@ import { ContainerInput, Container, ContainerOutput, - GetEmbeddableFactory, + EmbeddableStart, } from '../../../../../src/plugins/embeddable/public'; import { TimeRange } from '../../../../../src/plugins/data/public'; @@ -37,7 +37,7 @@ export class TimeRangeContainer extends Container< public readonly type = TIME_RANGE_CONTAINER; constructor( initialInput: ContainerTimeRangeInput, - getFactory: GetEmbeddableFactory, + getFactory: EmbeddableStart['getEmbeddableFactory'], parent?: Container ) { super(initialInput, { embeddableLoaded: {} }, getFactory, parent); diff --git a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts index efbf7a3bd2dc6..311d3357476b9 100644 --- a/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts +++ b/x-pack/plugins/advanced_ui_actions/public/test_helpers/time_range_embeddable_factory.ts @@ -19,7 +19,7 @@ interface EmbeddableTimeRangeInput extends EmbeddableInput { export class TimeRangeEmbeddableFactory extends EmbeddableFactory { public readonly type = TIME_RANGE_EMBEDDABLE; - public isEditable() { + public async isEditable() { return true; } diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts index f5d1aad93ed57..c8e038869efcd 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts @@ -15,7 +15,7 @@ import { ResolverEmbeddable } from './embeddable'; export class ResolverEmbeddableFactory extends EmbeddableFactory { public readonly type = 'resolver'; - public isEditable() { + public async isEditable() { return true; } diff --git a/x-pack/plugins/endpoint/public/plugin.ts b/x-pack/plugins/endpoint/public/plugin.ts index 155d709042fe7..2759db26bb6c8 100644 --- a/x-pack/plugins/endpoint/public/plugin.ts +++ b/x-pack/plugins/endpoint/public/plugin.ts @@ -5,7 +5,7 @@ */ import { Plugin, CoreSetup, AppMountParameters, CoreStart } from 'kibana/public'; -import { IEmbeddableSetup } from 'src/plugins/embeddable/public'; +import { EmbeddableSetup } from 'src/plugins/embeddable/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { i18n } from '@kbn/i18n'; import { ResolverEmbeddableFactory } from './embeddables/resolver'; @@ -13,7 +13,7 @@ import { ResolverEmbeddableFactory } from './embeddables/resolver'; export type EndpointPluginStart = void; export type EndpointPluginSetup = void; export interface EndpointPluginSetupDependencies { - embeddable: IEmbeddableSetup; + embeddable: EmbeddableSetup; data: DataPublicPluginStart; } export interface EndpointPluginStartDependencies { diff --git a/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts b/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts index 045cc56238ac9..502164554c711 100644 --- a/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts +++ b/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts @@ -6,13 +6,13 @@ import { Plugin, CoreSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { IEmbeddable, IEmbeddableStart } from '../../../../../../src/plugins/embeddable/public'; +import { IEmbeddable, EmbeddableStart } from '../../../../../../src/plugins/embeddable/public'; export type ResolverTestPluginSetup = void; export type ResolverTestPluginStart = void; export interface ResolverTestPluginSetupDependencies {} // eslint-disable-line @typescript-eslint/no-empty-interface export interface ResolverTestPluginStartDependencies { - embeddable: IEmbeddableStart; + embeddable: EmbeddableStart; } export class ResolverTestPlugin @@ -41,7 +41,9 @@ export class ResolverTestPlugin (async () => { const [, { embeddable }] = await core.getStartServices(); const factory = embeddable.getEmbeddableFactory('resolver'); - resolveEmbeddable!(factory.create({ id: 'test basic render' })); + if (factory) { + resolveEmbeddable!(factory.create({ id: 'test basic render' })); + } })(); const { renderApp } = await import('./applications/resolver_test'); From 77a859d43db820b468e45cd1628de63fbc9d3585 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 16 Mar 2020 15:46:17 -0400 Subject: [PATCH 19/76] [Remote clusters] Add support for proxy mode (#59221) --- .../remote_clusters_add.test.js | 50 + .../remote_clusters_list.test.js | 53 +- .../remote_clusters/common/constants.ts | 3 + .../common/lib/cluster_serialization.test.ts | 45 + .../common/lib/cluster_serialization.ts | 110 +- .../remote_clusters/common/lib/index.ts | 2 +- .../fixtures/remote_cluster.js | 10 + .../remote_cluster_form.test.js.snap | 1534 ++++++++++++++++- .../remote_cluster_form.js | 275 ++- .../remote_cluster_form.test.js | 10 + .../__snapshots__/validate_proxy.test.js.snap | 25 + .../remote_cluster_form/validators/index.js | 3 +- .../validators/validate_proxy.js | 44 + .../validators/validate_proxy.test.js | 25 + .../validators/validate_seed.js | 8 +- .../remote_cluster_edit.js | 40 +- .../connection_status/connection_status.js | 13 +- .../detail_panel/detail_panel.js | 425 +++-- .../remote_cluster_table.js | 84 +- .../application/services/documentation.ts | 2 + .../public/application/services/index.js | 2 +- ...idate_seed_node.js => validate_address.js} | 4 +- ..._node.test.js => validate_address.test.js} | 34 +- .../server/routes/api/add_route.test.ts | 91 +- .../server/routes/api/add_route.ts | 15 +- .../server/routes/api/delete_route.test.ts | 24 +- .../server/routes/api/get_route.test.ts | 3 + .../server/routes/api/get_route.ts | 17 +- .../server/routes/api/update_route.test.ts | 28 +- .../server/routes/api/update_route.ts | 17 +- .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - .../remote_clusters.helpers.js | 3 +- .../remote_clusters/remote_clusters.js | 7 + 34 files changed, 2765 insertions(+), 251 deletions(-) create mode 100644 x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap create mode 100644 x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js create mode 100644 x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js rename x-pack/plugins/remote_clusters/public/application/services/{validate_seed_node.js => validate_address.js} (91%) rename x-pack/plugins/remote_clusters/public/application/services/{validate_seed_node.test.js => validate_address.test.js} (55%) diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js index 8f34e7d84a08b..78482198b1a5d 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js @@ -53,6 +53,17 @@ describe('Create Remote cluster', () => { expect(find('remoteClusterFormSkipUnavailableFormToggle').props()['aria-checked']).toBe(true); }); + test('should have a toggle to enable "proxy" mode for a remote cluster', () => { + expect(exists('remoteClusterFormConnectionModeToggle')).toBe(true); + + // By default it should be set to "false" + expect(find('remoteClusterFormConnectionModeToggle').props()['aria-checked']).toBe(false); + + form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle'); + + expect(find('remoteClusterFormConnectionModeToggle').props()['aria-checked']).toBe(true); + }); + test('should display errors and disable the save button when clicking "save" without filling the form', () => { expect(exists('remoteClusterFormGlobalError')).toBe(false); expect(find('remoteClusterFormSaveButton').props().disabled).toBe(false); @@ -144,5 +155,44 @@ describe('Create Remote cluster', () => { expect(form.getErrorsMessages()).toContain('A port is required.'); }); }); + + describe('proxy address', () => { + let actions; + let form; + + beforeEach(async () => { + ({ form, actions } = setup()); + + // Enable "proxy" mode + form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle'); + }); + + test('should only allow alpha-numeric characters and "-" (dash) in the proxy address "host" part', () => { + actions.clickSaveForm(); // display form errors + + const notInArray = array => value => array.indexOf(value) < 0; + + const expectInvalidChar = char => { + form.setInputValue('remoteClusterFormProxyAddressInput', `192.16${char}:3000`); + expect(form.getErrorsMessages()).toContain( + 'Address must use host:port format. Example: 127.0.0.1:9400, localhost:9400. Hosts can only consist of letters, numbers, and dashes.' + ); + }; + + [...NON_ALPHA_NUMERIC_CHARS, ...ACCENTED_CHARS] + .filter(notInArray(['-', '_', ':'])) + .forEach(expectInvalidChar); + }); + + test('should require a numeric "port" to be set', () => { + actions.clickSaveForm(); + + form.setInputValue('remoteClusterFormProxyAddressInput', '192.168.1.1'); + expect(form.getErrorsMessages()).toContain('A port is required.'); + + form.setInputValue('remoteClusterFormProxyAddressInput', '192.168.1.1:abc'); + expect(form.getErrorsMessages()).toContain('A port is required.'); + }); + }); }); }); diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js index 1b7c600218cee..954deb8b98d3e 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js @@ -15,6 +15,8 @@ import { import { getRouter } from '../../public/application/services'; import { getRemoteClusterMock } from '../../fixtures/remote_cluster'; +import { PROXY_MODE } from '../../common/constants'; + jest.mock('ui/new_platform'); const { setup } = pageHelpers.remoteClustersList; @@ -84,12 +86,26 @@ describe('', () => { const remoteCluster2 = getRemoteClusterMock({ name: `b${getRandomString()}`, isConnected: false, - connectedNodesCount: 0, - seeds: ['localhost:9500'], + connectedSocketsCount: 0, + proxyAddress: 'localhost:9500', isConfiguredByNode: true, + mode: PROXY_MODE, + seeds: null, + connectedNodesCount: null, + }); + const remoteCluster3 = getRemoteClusterMock({ + name: `c${getRandomString()}`, + isConnected: false, + connectedSocketsCount: 0, + proxyAddress: 'localhost:9500', + isConfiguredByNode: false, + mode: PROXY_MODE, + hasDeprecatedProxySetting: true, + seeds: null, + connectedNodesCount: null, }); - const remoteClusters = [remoteCluster1, remoteCluster2]; + const remoteClusters = [remoteCluster1, remoteCluster2, remoteCluster3]; beforeEach(async () => { httpRequestsMockHelpers.setLoadRemoteClustersResponse(remoteClusters); @@ -118,17 +134,28 @@ describe('', () => { [ '', // Empty because the first column is the checkbox to select the row remoteCluster1.name, - remoteCluster1.seeds.join(', '), 'Connected', + 'default', + remoteCluster1.seeds.join(', '), remoteCluster1.connectedNodesCount.toString(), '', // Empty because the last column is for the "actions" on the resource ], [ '', remoteCluster2.name, - remoteCluster2.seeds.join(', '), 'Not connected', - remoteCluster2.connectedNodesCount.toString(), + PROXY_MODE, + remoteCluster2.proxyAddress, + remoteCluster2.connectedSocketsCount.toString(), + '', + ], + [ + '', + remoteCluster3.name, + 'Not connected', + PROXY_MODE, + remoteCluster2.proxyAddress, + remoteCluster2.connectedSocketsCount.toString(), '', ], ]); @@ -141,6 +168,14 @@ describe('', () => { ).toBe(1); }); + test('should have a tooltip to indicate that the cluster has a deprecated setting', () => { + const secondRow = rows[2].reactWrapper; // The third cluster has been defined with deprecated setting + expect( + findTestSubject(secondRow, 'remoteClustersTableListClusterWithDeprecatedSettingTooltip') + .length + ).toBe(1); + }); + describe('bulk delete button', () => { test('should be visible when a remote cluster is selected', () => { expect(exists('remoteClusterBulkDeleteButton')).toBe(false); @@ -199,8 +234,8 @@ describe('', () => { errors: [], }); - // Make sure that we have our 2 remote clusters in the table - expect(rows.length).toBe(2); + // Make sure that we have our 3 remote clusters in the table + expect(rows.length).toBe(3); actions.selectRemoteClusterAt(0); actions.clickBulkDeleteButton(); @@ -211,7 +246,7 @@ describe('', () => { ({ rows } = table.getMetaData('remoteClusterListTable')); - expect(rows.length).toBe(1); + expect(rows.length).toBe(2); expect(rows[0].columns[1].value).toEqual(remoteCluster2.name); }); }); diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 353160de8bf4a..20ad6da227c55 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -20,3 +20,6 @@ export const PLUGIN = { }; export const API_BASE_PATH = '/api/remote_clusters'; + +export const SNIFF_MODE = 'sniff'; +export const PROXY_MODE = 'proxy'; diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts index 476fbee7fb6a0..5be6ed8828e6f 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.test.ts @@ -9,6 +9,7 @@ import { deserializeCluster, serializeCluster } from './cluster_serialization'; describe('cluster_serialization', () => { describe('deserializeCluster()', () => { it('should throw an error for invalid arguments', () => { + // @ts-ignore expect(() => deserializeCluster('foo', 'bar')).toThrowError(); }); @@ -60,6 +61,39 @@ describe('cluster_serialization', () => { }); }); + it('should deserialize a cluster that contains a deprecated proxy address', () => { + expect( + deserializeCluster( + 'test_cluster', + { + seeds: ['localhost:9300'], + connected: true, + num_nodes_connected: 1, + max_connections_per_cluster: 3, + initial_connect_timeout: '30s', + skip_unavailable: false, + transport: { + ping_schedule: '-1', + compress: false, + }, + }, + 'localhost:9300' + ) + ).toEqual({ + name: 'test_cluster', + proxyAddress: 'localhost:9300', + mode: 'proxy', + hasDeprecatedProxySetting: true, + isConnected: true, + connectedNodesCount: 1, + maxConnectionsPerCluster: 3, + initialConnectTimeout: '30s', + skipUnavailable: false, + transportPingSchedule: '-1', + transportCompress: false, + }); + }); + it('should deserialize a cluster object with arbitrary missing properties', () => { expect( deserializeCluster('test_cluster', { @@ -84,6 +118,7 @@ describe('cluster_serialization', () => { describe('serializeCluster()', () => { it('should throw an error for invalid arguments', () => { + // @ts-ignore expect(() => serializeCluster('foo')).toThrowError(); }); @@ -105,8 +140,13 @@ describe('cluster_serialization', () => { cluster: { remote: { test_cluster: { + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, seeds: ['localhost:9300'], skip_unavailable: false, + server_name: null, }, }, }, @@ -125,8 +165,13 @@ describe('cluster_serialization', () => { cluster: { remote: { test_cluster: { + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, seeds: ['localhost:9300'], skip_unavailable: null, + server_name: null, }, }, }, diff --git a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts index 07ea79d42b800..53dc72eb1695a 100644 --- a/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts +++ b/x-pack/plugins/remote_clusters/common/lib/cluster_serialization.ts @@ -4,29 +4,96 @@ * you may not use this file except in compliance with the Elastic License. */ -export function deserializeCluster(name: string, esClusterObject: any): any { +import { PROXY_MODE } from '../constants'; + +export interface ClusterEs { + seeds?: string[]; + mode?: 'proxy' | 'sniff'; + connected?: boolean; + num_nodes_connected?: number; + max_connections_per_cluster?: number; + initial_connect_timeout?: string; + skip_unavailable?: boolean; + transport?: { + ping_schedule?: string; + compress?: boolean; + }; + address?: string; + max_socket_connections?: number; + num_sockets_connected?: number; +} + +export interface Cluster { + name: string; + seeds?: string[]; + skipUnavailable?: boolean; + nodeConnections?: number; + proxyAddress?: string; + proxySocketConnections?: number; + serverName?: string; + mode?: 'proxy' | 'sniff'; + isConnected?: boolean; + transportPingSchedule?: string; + transportCompress?: boolean; + connectedNodesCount?: number; + maxConnectionsPerCluster?: number; + initialConnectTimeout?: string; + connectedSocketsCount?: number; + hasDeprecatedProxySetting?: boolean; +} +export interface ClusterPayload { + persistent: { + cluster: { + remote: { + [key: string]: { + skip_unavailable?: boolean | null; + mode?: 'sniff' | 'proxy' | null; + proxy_address?: string | null; + proxy_socket_connections?: number | null; + server_name?: string | null; + seeds?: string[] | null; + node_connections?: number | null; + }; + }; + }; + }; +} + +export function deserializeCluster( + name: string, + esClusterObject: ClusterEs, + deprecatedProxyAddress?: string | undefined +): Cluster { if (!name || !esClusterObject || typeof esClusterObject !== 'object') { throw new Error('Unable to deserialize cluster'); } const { seeds, + mode, connected: isConnected, num_nodes_connected: connectedNodesCount, max_connections_per_cluster: maxConnectionsPerCluster, initial_connect_timeout: initialConnectTimeout, skip_unavailable: skipUnavailable, transport, + address: proxyAddress, + max_socket_connections: proxySocketConnections, + num_sockets_connected: connectedSocketsCount, } = esClusterObject; - let deserializedClusterObject: any = { + let deserializedClusterObject: Cluster = { name, - seeds, + mode, isConnected, connectedNodesCount, maxConnectionsPerCluster, initialConnectTimeout, skipUnavailable, + seeds, + proxyAddress, + proxySocketConnections, + connectedSocketsCount, }; if (transport) { @@ -39,30 +106,57 @@ export function deserializeCluster(name: string, esClusterObject: any): any { }; } + // If a user has a remote cluster with the deprecated proxy setting, + // we transform the data to support the new implementation and also flag the deprecation + if (deprecatedProxyAddress) { + deserializedClusterObject = { + ...deserializedClusterObject, + proxyAddress: deprecatedProxyAddress, + seeds: undefined, + hasDeprecatedProxySetting: true, + mode: PROXY_MODE, + }; + } + // It's unnecessary to send undefined values back to the client, so we can remove them. Object.keys(deserializedClusterObject).forEach(key => { - if (deserializedClusterObject[key] === undefined) { - delete deserializedClusterObject[key]; + if (deserializedClusterObject[key as keyof Cluster] === undefined) { + delete deserializedClusterObject[key as keyof Cluster]; } }); return deserializedClusterObject; } -export function serializeCluster(deserializedClusterObject: any): any { +export function serializeCluster(deserializedClusterObject: Cluster): ClusterPayload { if (!deserializedClusterObject || typeof deserializedClusterObject !== 'object') { throw new Error('Unable to serialize cluster'); } - const { name, seeds, skipUnavailable } = deserializedClusterObject; + const { + name, + seeds, + skipUnavailable, + mode, + nodeConnections, + proxyAddress, + proxySocketConnections, + serverName, + } = deserializedClusterObject; return { + // Background on why we only save as persistent settings detailed here: https://github.com/elastic/kibana/pull/26067#issuecomment-441848124 persistent: { cluster: { remote: { [name]: { - seeds: seeds ? seeds : null, skip_unavailable: skipUnavailable !== undefined ? skipUnavailable : null, + mode: mode ?? null, + proxy_address: proxyAddress ?? null, + proxy_socket_connections: proxySocketConnections ?? null, + server_name: serverName ?? null, + seeds: seeds ?? null, + node_connections: nodeConnections ?? null, }, }, }, diff --git a/x-pack/plugins/remote_clusters/common/lib/index.ts b/x-pack/plugins/remote_clusters/common/lib/index.ts index bc67bf21af038..52a0536bfd55b 100644 --- a/x-pack/plugins/remote_clusters/common/lib/index.ts +++ b/x-pack/plugins/remote_clusters/common/lib/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { deserializeCluster, serializeCluster } from './cluster_serialization'; +export { deserializeCluster, serializeCluster, Cluster, ClusterEs } from './cluster_serialization'; diff --git a/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js index e3e087548cf00..6a3bcba21d772 100644 --- a/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js +++ b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js @@ -5,12 +5,18 @@ */ import { getRandomString } from '../../../test_utils'; +import { SNIFF_MODE } from '../common/constants'; + export const getRemoteClusterMock = ({ name = getRandomString(), isConnected = true, connectedNodesCount = 1, + connectedSocketsCount, seeds = ['localhost:9400'], isConfiguredByNode = false, + mode = SNIFF_MODE, + proxyAddress, + hasDeprecatedProxySetting = false, } = {}) => ({ name, seeds, @@ -20,4 +26,8 @@ export const getRemoteClusterMock = ({ maxConnectionsPerCluster: 3, initialConnectTimeout: '30s', skipUnavailable: false, + mode, + connectedSocketsCount, + proxyAddress, + hasDeprecatedProxySetting, }); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 8d6c5b040ce84..88b869b1d1d8f 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -1,5 +1,1429 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`RemoteClusterForm proxy mode renders correct connection settings when user enables proxy mode 1`] = ` + + +
+ + } + fullWidth={true} + title={ + +

+ +

+
+ } + > + +
+ + + Name + + + +
+ +
+ + + + +
+ +
+ + A unique name for the remote cluster. + +
+
+
+
+
+
+ +
+ + } + fullWidth={true} + hasChildLabel={true} + hasEmptyLabelSpace={false} + helpText={ + + } + isInvalid={false} + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + Name can only contain letters, numbers, underscores, and dashes. + +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + , + } + } + /> + } + labelType="label" + > + + } + onChange={[Function]} + /> + + + } + fullWidth={true} + title={ + +

+ +

+
+ } + > + +
+ + + Connection mode + + + +
+ +
+ + + + +
+ +
+ + Remote cluster connections work by configuring a remote cluster and connecting only to a limited number of nodes in that remote cluster. + + + + , + } + } + /> + } + labelType="label" + > +
+
+ + } + onBlur={[Function]} + onChange={[Function]} + onFocus={[Function]} + > +
+ + + + Use proxy mode + + +
+
+ +
+ + + , + } + } + > + Configure a remote cluster with a single proxy address. + + + + +
+
+
+
+
+
+
+
+
+
+
+ +
+ + } + fullWidth={true} + hasChildLabel={true} + hasEmptyLabelSpace={false} + helpText={ + + } + isInvalid={false} + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + The address used for all remote connections. + +
+
+
+
+
+ + } + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + The number of socket connections to open per remote cluster. + +
+
+
+
+
+ + } + label={ + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+
+ + + + +
+
+
+
+ +
+ + An optional hostname string which will be sent in the server_name field of the TLS Server Name Indication extension if TLS is enabled. + +
+
+
+
+
+
+
+
+
+
+
+
+ +

+ + + , + "optionName": + + , + } + } + /> +

+ + } + fullWidth={true} + title={ + +

+ +

+
+ } + > + +
+ + + Make remote cluster optional + + + +
+ +
+ + + + +
+ +
+

+ + + , + "optionName": + + , + } + } + > + By default, a request fails if any of the queried remote clusters are unavailable. To continue sending a request to other remote clusters if this cluster is unavailable, enable + + + Skip if unavailable + + + . + + + + +

+
+
+
+
+
+
+ +
+ +
+
+ +
+ + + Skip if unavailable + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+ + + +
+
+
+
+
+
+ +
+ + + +
+
+
+
+ +`; + exports[`RemoteClusterForm renders untouched state 1`] = ` Array [
@@ -191,7 +1676,48 @@ Array [ > transport port - of the remote cluster. + of the remote cluster. Specify multiple seed nodes so discovery doesn't fail if a node is unavailable. +
+ + +
+
+ +
+
+
+
+ +
+
+
+ The number of gateway nodes to connect to.
@@ -490,7 +2016,7 @@ Array [ > transport port - of the remote cluster. + of the remote cluster. Specify multiple seed nodes so discovery doesn't fail if a node is unavailable. , diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index 08cd01496a8b9..358ffc03da783 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -15,6 +15,7 @@ import { EuiCallOut, EuiComboBox, EuiDescribedFormGroup, + EuiFieldNumber, EuiFieldText, EuiFlexGroup, EuiFlexItem, @@ -33,16 +34,27 @@ import { htmlIdGenerator, } from '@elastic/eui'; -import { skippingDisconnectedClustersUrl, transportPortUrl } from '../../../services/documentation'; +import { + skippingDisconnectedClustersUrl, + transportPortUrl, + proxyModeUrl, +} from '../../../services/documentation'; import { RequestFlyout } from './request_flyout'; -import { validateName, validateSeeds, validateSeed } from './validators'; +import { validateName, validateSeeds, validateProxy, validateSeed } from './validators'; + +import { SNIFF_MODE, PROXY_MODE } from '../../../../../common/constants'; const defaultFields = { name: '', seeds: [], skipUnavailable: false, + mode: SNIFF_MODE, + nodeConnections: 3, + proxyAddress: '', + proxySocketConnections: 18, + serverName: '', }; const ERROR_TITLE_ID = 'removeClustersErrorTitle'; @@ -88,10 +100,12 @@ export class RemoteClusterForm extends Component { }; getFieldsErrors(fields, seedInput = '') { - const { name, seeds } = fields; + const { name, seeds, mode, proxyAddress } = fields; + return { name: validateName(name), - seeds: validateSeeds(seeds, seedInput), + seeds: mode === SNIFF_MODE ? validateSeeds(seeds, seedInput) : null, + proxyAddress: mode === PROXY_MODE ? validateProxy(proxyAddress) : null, }; } @@ -110,13 +124,38 @@ export class RemoteClusterForm extends Component { getAllFields() { const { - fields: { name, seeds, skipUnavailable }, + fields: { + name, + mode, + seeds, + nodeConnections, + proxyAddress, + proxySocketConnections, + serverName, + skipUnavailable, + }, } = this.state; + let modeSettings; + + if (mode === PROXY_MODE) { + modeSettings = { + proxyAddress, + proxySocketConnections, + serverName, + }; + } else { + modeSettings = { + seeds, + nodeConnections, + }; + } + return { name, - seeds, skipUnavailable, + mode, + ...modeSettings, }; } @@ -215,10 +254,10 @@ export class RemoteClusterForm extends Component { return hasErrors; }; - renderSeeds() { + renderSniffModeSettings() { const { areErrorsVisible, - fields: { seeds }, + fields: { seeds, nodeConnections }, fieldsErrors: { seeds: errorsSeeds }, localSeedErrors, } = this.state; @@ -231,26 +270,7 @@ export class RemoteClusterForm extends Component { const formattedSeeds = seeds.map(seed => ({ label: seed })); return ( - -

- -

- - } - description={ - - } - fullWidth - > + <> @@ -296,6 +316,187 @@ export class RemoteClusterForm extends Component { data-test-subj="remoteClusterFormSeedsInput" /> + + + } + helpText={ + + } + fullWidth + > + this.onFieldsChange({ nodeConnections: Number(e.target.value) || null })} + fullWidth + /> + + + ); + } + + renderProxyModeSettings() { + const { + areErrorsVisible, + fields: { proxyAddress, proxySocketConnections, serverName }, + fieldsErrors: { proxyAddress: errorProxyAddress }, + } = this.state; + + return ( + <> + + } + helpText={ + + } + isInvalid={Boolean(areErrorsVisible && errorProxyAddress)} + error={errorProxyAddress} + fullWidth + > + this.onFieldsChange({ proxyAddress: e.target.value })} + isInvalid={Boolean(areErrorsVisible && errorProxyAddress)} + data-test-subj="remoteClusterFormProxyAddressInput" + fullWidth + /> + + + + } + helpText={ + + } + fullWidth + > + + this.onFieldsChange({ proxySocketConnections: Number(e.target.value) || null }) + } + fullWidth + /> + + + } + helpText={ + + } + fullWidth + > + this.onFieldsChange({ serverName: e.target.value })} + fullWidth + /> + + + ); + } + + renderMode() { + const { + fields: { mode }, + } = this.state; + + return ( + +

+ +

+ + } + description={ + <> + + + + + ), + }} + /> + } + > + + } + checked={mode === PROXY_MODE} + data-test-subj="remoteClusterFormConnectionModeToggle" + onChange={e => + this.onFieldsChange({ mode: e.target.checked ? PROXY_MODE : SNIFF_MODE }) + } + /> + + + } + fullWidth + > + {mode === PROXY_MODE ? this.renderProxyModeSettings() : this.renderSniffModeSettings()}
); } @@ -522,7 +723,7 @@ export class RemoteClusterForm extends Component { renderErrors = () => { const { areErrorsVisible, - fieldsErrors: { name: errorClusterName, seeds: errorsSeeds }, + fieldsErrors: { name: errorClusterName, seeds: errorsSeeds, proxyAddress: errorProxyAddress }, localSeedErrors, } = this.state; @@ -564,6 +765,16 @@ export class RemoteClusterForm extends Component { }); } + if (errorProxyAddress) { + errorExplanations.push({ + key: 'seedsExplanation', + field: i18n.translate('xpack.remoteClusters.remoteClusterForm.inputProxyErrorMessage', { + defaultMessage: 'The "Proxy address" field is invalid.', + }), + error: errorProxyAddress, + }); + } + const messagesToBeRendered = errorExplanations.length && (
@@ -662,7 +873,7 @@ export class RemoteClusterForm extends Component { - {this.renderSeeds()} + {this.renderMode()} {this.renderSkipUnavailable()} diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js index 799bf1f4fd051..907fd2183265f 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js @@ -21,6 +21,16 @@ describe('RemoteClusterForm', () => { expect(component).toMatchSnapshot(); }); + describe('proxy mode', () => { + test('renders correct connection settings when user enables proxy mode', () => { + const component = mountWithIntl( {}} />); + + findTestSubject(component, 'remoteClusterFormConnectionModeToggle').simulate('click'); + + expect(component).toMatchSnapshot(); + }); + }); + describe('validation', () => { test('renders invalid state and a global form error when the user tries to submit an invalid form', () => { const component = mountWithIntl( {}} />); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap new file mode 100644 index 0000000000000..646b0b509f4e4 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_proxy.test.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validateProxy rejects proxy address when the address is invalid 1`] = ` + +`; + +exports[`validateProxy rejects proxy address when the port is invalid 1`] = ` + +`; + +exports[`validateProxy rejects proxy address when there's no input 1`] = ` + +`; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js index 66a1016c7fcc8..ec5f0b1166ce5 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js @@ -5,5 +5,6 @@ */ export { validateName } from './validate_name'; -export { validateSeed } from './validate_seed'; +export { validateProxy } from './validate_proxy'; export { validateSeeds } from './validate_seeds'; +export { validateSeed } from './validate_seed'; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js new file mode 100644 index 0000000000000..9648bd36c1a4e --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.js @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { isAddressValid, isPortValid } from '../../../../services'; + +export function validateProxy(proxy) { + if (!proxy) { + return ( + + ); + } + + const isValid = isAddressValid(proxy); + + if (!isValid) { + return ( + + ); + } + + if (!isPortValid(proxy)) { + return ( + + ); + } + + return null; +} diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js new file mode 100644 index 0000000000000..e6e69849e13aa --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_proxy.test.js @@ -0,0 +1,25 @@ +/* + * 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 { validateProxy } from './validate_proxy'; + +describe('validateProxy', () => { + test(`rejects proxy address when there's no input`, () => { + expect(validateProxy(undefined)).toMatchSnapshot(); + }); + + test(`rejects proxy address when the address is invalid`, () => { + expect(validateProxy('___')).toMatchSnapshot(); + }); + + test(`rejects proxy address when the port is invalid`, () => { + expect(validateProxy('noport')).toMatchSnapshot(); + }); + + test(`accepts valid proxy address`, () => { + expect(validateProxy('localhost:3000')).toBe(null); + }); +}); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js index e2260504cc033..e312e77972fbb 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; -import { isSeedNodeValid, isSeedNodePortValid } from '../../../../services'; +import { isAddressValid, isPortValid } from '../../../../services'; export function validateSeed(seed) { const errors = []; @@ -15,7 +15,7 @@ export function validateSeed(seed) { return errors; } - const isValid = isSeedNodeValid(seed); + const isValid = isAddressValid(seed); if (!isValid) { errors.push( @@ -30,9 +30,7 @@ export function validateSeed(seed) { ); } - const isPortValid = isSeedNodePortValid(seed); - - if (!isPortValid) { + if (!isPortValid(seed)) { errors.push( i18n.translate('xpack.remoteClusters.remoteClusterForm.localSeedError.invalidPortMessage', { defaultMessage: 'A port is required.', diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js index f48d854da7255..2c0936b319d09 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js @@ -158,7 +158,7 @@ export class RemoteClusterEdit extends Component { ); } - const { isConfiguredByNode } = cluster; + const { isConfiguredByNode, hasDeprecatedProxySetting } = cluster; if (isConfiguredByNode) { return ( @@ -178,14 +178,36 @@ export class RemoteClusterEdit extends Component { } return ( - + <> + {hasDeprecatedProxySetting ? ( + <> + + } + color="warning" + iconType="help" + > + + + + + ) : null} + + ); } diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js index d6d3272c2abe4..f032636af0bc3 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js @@ -10,7 +10,9 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiIconTip, EuiText } from '@elastic/eui'; -export function ConnectionStatus({ isConnected }) { +import { SNIFF_MODE, PROXY_MODE } from '../../../../../../common/constants'; + +export function ConnectionStatus({ isConnected, mode }) { let icon; let message; @@ -47,13 +49,16 @@ export function ConnectionStatus({ isConnected }) { - - - + {!isConnected && mode === SNIFF_MODE && ( + + + + )} ); } ConnectionStatus.propTypes = { isConnected: PropTypes.bool, + mode: PropTypes.oneOf([SNIFF_MODE, PROXY_MODE]), }; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js index 1c8ba372aa745..89a48927f6833 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js @@ -7,10 +7,13 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { + EuiBadge, EuiButton, EuiButtonEmpty, + EuiCallOut, EuiDescriptionList, EuiDescriptionListDescription, EuiDescriptionListTitle, @@ -21,6 +24,7 @@ import { EuiFlyoutFooter, EuiFlyoutHeader, EuiIcon, + EuiLink, EuiSpacer, EuiText, EuiTextColor, @@ -28,9 +32,11 @@ import { } from '@elastic/eui'; import { CRUD_APP_BASE_PATH } from '../../../constants'; +import { PROXY_MODE } from '../../../../../common/constants'; import { getRouterLinkProps } from '../../../services'; import { ConfiguredByNodeWarning } from '../../components'; import { ConnectionStatus, RemoveClusterButtonProvider } from '../components'; +import { proxyModeUrl } from '../../../services/documentation'; export class DetailPanel extends Component { static propTypes = { @@ -106,139 +112,312 @@ export class DetailPanel extends Component { ); } - renderCluster({ + renderClusterWithDeprecatedSettingWarning( + { hasDeprecatedProxySetting, isConfiguredByNode }, + clusterName + ) { + if (!hasDeprecatedProxySetting) { + return null; + } + return ( + <> + + } + color="warning" + iconType="help" + > + + + + ) : ( + + + + ), + }} + /> + + + + ); + } + + renderSniffModeDescriptionList({ isConnected, connectedNodesCount, skipUnavailable, seeds, maxConnectionsPerCluster, initialConnectTimeout, + mode, }) { return ( -
- -

- -

-
+ + + + + + + + + + + + + + + + + + + + + + + {connectedNodesCount} + + + - - - - - - - - + + + + + + + + + + {seeds.map(seed => ( + {seed} + ))} + + - - - - + + + + + + + + + {this.renderSkipUnavailableValue(skipUnavailable)} + + + - - - - - - + - - {connectedNodesCount} - - - + + + + + + + + + + {maxConnectionsPerCluster} + + - + + + + + + + + + {initialConnectTimeout} + + + + + ); + } - - - - - - - - - - {seeds.map(seed => ( - {seed} - ))} - - + renderProxyModeDescriptionList({ + isConnected, + skipUnavailable, + initialConnectTimeout, + proxyAddress, + proxySocketConnections, + connectedSocketsCount, + mode, + }) { + return ( + + + + + + + + + + + + + - - - - - - + + + + + + + + + {connectedSocketsCount ? connectedSocketsCount : '-'} + + + - - {this.renderSkipUnavailableValue(skipUnavailable)} - - - + - + + + + + + + + + + {proxyAddress} + + - - - - - - - + + + + + + + + + {this.renderSkipUnavailableValue(skipUnavailable)} + + + - - {maxConnectionsPerCluster} - - + - - - - - - + + + + + + + + + + {proxySocketConnections ? proxySocketConnections : '-'} + + - - {initialConnectTimeout} - - - - + + + + + + + + + {initialConnectTimeout} + + + + + ); + } + + renderCluster(cluster) { + return ( +
+ +

+ +

+
+ + + + {cluster.mode === PROXY_MODE + ? this.renderProxyModeDescriptionList(cluster) + : this.renderSniffModeDescriptionList(cluster)}
); } renderFlyoutBody() { - const { cluster } = this.props; + const { cluster, clusterName } = this.props; return ( @@ -246,6 +425,7 @@ export class DetailPanel extends Component { {cluster && ( {this.renderClusterConfiguredByNodeWarning(cluster)} + {this.renderClusterWithDeprecatedSettingWarning(cluster, clusterName)} {this.renderCluster(cluster)} )} @@ -315,7 +495,7 @@ export class DetailPanel extends Component { } render() { - const { isOpen, closeDetailPanel, clusterName } = this.props; + const { isOpen, closeDetailPanel, clusterName, cluster } = this.props; if (!isOpen) { return null; @@ -327,16 +507,33 @@ export class DetailPanel extends Component { onClose={closeDetailPanel} aria-labelledby="remoteClusterDetailsFlyoutTitle" size="m" - maxWidth={400} + maxWidth={550} > - -

{clusterName}

-
+ + + +

{clusterName}

+
+
+ {cluster && cluster.mode === PROXY_MODE ? ( + + {' '} + + {cluster.mode} + + + ) : null} +
{this.renderFlyoutBody()} diff --git a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js index 62c417b19904a..ec20805ccd919 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { CRUD_APP_BASE_PATH, UIM_SHOW_DETAILS_CLICK } from '../../../constants'; +import { PROXY_MODE } from '../../../../../common/constants'; import { getRouterLinkProps, trackUiMetric, METRIC_TYPE } from '../../../services'; import { ConnectionStatus, RemoveClusterButtonProvider } from '../components'; @@ -83,7 +84,7 @@ export class RemoteClusterTable extends Component { }), sortable: true, truncateText: false, - render: (name, { isConfiguredByNode }) => { + render: (name, { isConfiguredByNode, hasDeprecatedProxySetting }) => { const link = ( + + {link} + + + + + } + /> + + + ); + } + return link; }, }, - { - field: 'seeds', - name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.seedsColumnTitle', { - defaultMessage: 'Seeds', - }), - truncateText: true, - render: seeds => seeds.join(', '), - }, { field: 'isConnected', name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.connectedColumnTitle', { - defaultMessage: 'Connection', + defaultMessage: 'Status', }), sortable: true, - render: isConnected => , + render: (isConnected, { mode }) => ( + + ), width: '240px', }, { - field: 'connectedNodesCount', + field: 'mode', + name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.modeColumnTitle', { + defaultMessage: 'Mode', + }), + sortable: true, + render: mode => + mode === PROXY_MODE + ? mode + : i18n.translate('xpack.remoteClusters.remoteClusterList.table.sniffModeDescription', { + defaultMessage: 'default', + }), + }, + { + field: 'mode', + name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.addressesColumnTitle', { + defaultMessage: 'Addresses', + }), + truncateText: true, + render: (mode, { seeds, proxyAddress }) => { + if (mode === PROXY_MODE) { + return proxyAddress; + } + return seeds.join(', '); + }, + }, + { + field: 'mode', name: i18n.translate( - 'xpack.remoteClusters.remoteClusterList.table.connectedNodesColumnTitle', + 'xpack.remoteClusters.remoteClusterList.table.connectionsColumnTitle', { - defaultMessage: 'Connected nodes', + defaultMessage: 'Connections', } ), sortable: true, width: '160px', + render: (mode, { connectedNodesCount, connectedSocketsCount }) => { + if (mode === PROXY_MODE) { + return connectedSocketsCount; + } + return connectedNodesCount; + }, }, { name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.actionsColumnTitle', { diff --git a/x-pack/plugins/remote_clusters/public/application/services/documentation.ts b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts index 38cf2223a313b..f6f5dc987c2eb 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/documentation.ts +++ b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts @@ -9,6 +9,7 @@ import { DocLinksStart } from 'kibana/public'; export let skippingDisconnectedClustersUrl: string; export let remoteClustersUrl: string; export let transportPortUrl: string; +export let proxyModeUrl: string; export function init(docLinks: DocLinksStart): void { const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; @@ -17,4 +18,5 @@ export function init(docLinks: DocLinksStart): void { skippingDisconnectedClustersUrl = `${esDocBasePath}/modules-cross-cluster-search.html#_skipping_disconnected_clusters`; remoteClustersUrl = `${esDocBasePath}/modules-remote-clusters.html`; transportPortUrl = `${esDocBasePath}/modules-transport.html`; + proxyModeUrl = `${esDocBasePath}/modules-remote-clusters.html#proxy-mode`; } diff --git a/x-pack/plugins/remote_clusters/public/application/services/index.js b/x-pack/plugins/remote_clusters/public/application/services/index.js index 031770d9500ed..387a04b6e5d8c 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/index.js +++ b/x-pack/plugins/remote_clusters/public/application/services/index.js @@ -10,7 +10,7 @@ export { showApiError, showApiWarning } from './api_errors'; export { initRedirect, redirect } from './redirect'; -export { isSeedNodeValid, isSeedNodePortValid } from './validate_seed_node'; +export { isAddressValid, isPortValid } from './validate_address'; export { extractQueryParams } from './query_params'; diff --git a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js b/x-pack/plugins/remote_clusters/public/application/services/validate_address.js similarity index 91% rename from x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js rename to x-pack/plugins/remote_clusters/public/application/services/validate_address.js index 714b5cf44de23..7e12b9c06595d 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js +++ b/x-pack/plugins/remote_clusters/public/application/services/validate_address.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export function isSeedNodeValid(seedNode) { +export function isAddressValid(seedNode) { if (!seedNode) { return false; } @@ -23,7 +23,7 @@ export function isSeedNodeValid(seedNode) { return !containsInvalidCharacters; } -export function isSeedNodePortValid(seedNode) { +export function isPortValid(seedNode) { if (!seedNode) { return false; } diff --git a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js b/x-pack/plugins/remote_clusters/public/application/services/validate_address.test.js similarity index 55% rename from x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js rename to x-pack/plugins/remote_clusters/public/application/services/validate_address.test.js index 36e989a41b066..2551f4fac7908 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js +++ b/x-pack/plugins/remote_clusters/public/application/services/validate_address.test.js @@ -4,75 +4,75 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isSeedNodeValid, isSeedNodePortValid } from './validate_seed_node'; +import { isAddressValid, isPortValid } from './validate_address'; -describe('Validate seed node', () => { +describe('Validate address', () => { describe('isSeedNodeValid', () => { describe('rejects', () => { it('adjacent periods', () => { - expect(isSeedNodeValid('a..b')).toBe(false); + expect(isAddressValid('a..b')).toBe(false); }); it('underscores', () => { - expect(isSeedNodeValid('____')).toBe(false); + expect(isAddressValid('____')).toBe(false); }); ['/', '\\', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '=', '+', '?'].forEach(char => { it(char, () => { - expect(isSeedNodeValid(char)).toBe(false); + expect(isAddressValid(char)).toBe(false); }); }); }); describe('accepts', () => { it('uppercase letters', () => { - expect(isSeedNodeValid('A.B.C.D')).toBe(true); + expect(isAddressValid('A.B.C.D')).toBe(true); }); it('lowercase letters', () => { - expect(isSeedNodeValid('a')).toBe(true); + expect(isAddressValid('a')).toBe(true); }); it('numbers', () => { - expect(isSeedNodeValid('56546354')).toBe(true); + expect(isAddressValid('56546354')).toBe(true); }); it('dashes', () => { - expect(isSeedNodeValid('----')).toBe(true); + expect(isAddressValid('----')).toBe(true); }); it('many parts', () => { - expect(isSeedNodeValid('abcd.efgh.ijkl.mnop.qrst.uvwx.yz')).toBe(true); + expect(isAddressValid('abcd.efgh.ijkl.mnop.qrst.uvwx.yz')).toBe(true); }); }); }); - describe('isSeedNodePortValid', () => { + describe('isPortValid', () => { describe('rejects', () => { it('missing port', () => { - expect(isSeedNodePortValid('abcd')).toBe(false); + expect(isPortValid('abcd')).toBe(false); }); it('empty port', () => { - expect(isSeedNodePortValid('abcd:')).toBe(false); + expect(isPortValid('abcd:')).toBe(false); }); it('letters', () => { - expect(isSeedNodePortValid('ab:cd')).toBe(false); + expect(isPortValid('ab:cd')).toBe(false); }); it('non-numbers', () => { - expect(isSeedNodePortValid('ab:5 0')).toBe(false); + expect(isPortValid('ab:5 0')).toBe(false); }); it('multiple ports', () => { - expect(isSeedNodePortValid('ab:cd:9000')).toBe(false); + expect(isPortValid('ab:cd:9000')).toBe(false); }); }); describe('accepts', () => { it('a single numeric port, even beyond the standard port range', () => { - expect(isSeedNodePortValid('abcd:100000000')).toBe(true); + expect(isPortValid('abcd:100000000')).toBe(true); }); }); }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts index a6edd15995d72..34d741aa4b7da 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts @@ -80,7 +80,7 @@ describe('ADD remote clusters', () => { }; describe('success', () => { - addRemoteClustersTest('adds remote cluster', { + addRemoteClustersTest(`adds remote cluster with "sniff" mode`, { apiResponses: [ async () => ({}), async () => ({ @@ -106,6 +106,7 @@ describe('ADD remote clusters', () => { payload: { name: 'test', seeds: ['127.0.0.1:9300'], + mode: 'sniff', skipUnavailable: false, }, asserts: { @@ -117,7 +118,79 @@ describe('ADD remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, + }, + }, + }, + }, + ], + ], + statusCode: 200, + result: { + acknowledged: true, + }, + }, + }); + addRemoteClustersTest(`adds remote cluster with "proxy" mode`, { + apiResponses: [ + async () => ({}), + async () => ({ + acknowledged: true, + persistent: { + cluster: { + remote: { + test: { + connected: true, + mode: 'proxy', + seeds: ['127.0.0.1:9300'], + num_sockets_connected: 1, + max_socket_connections: 18, + initial_connect_timeout: '30s', + skip_unavailable: false, + }, + }, + }, + }, + transient: {}, + }), + ], + payload: { + name: 'test', + proxyAddress: '127.0.0.1:9300', + mode: 'proxy', + skipUnavailable: false, + serverName: 'foobar', + }, + asserts: { + apiArguments: [ + ['cluster.remoteInfo'], + [ + 'cluster.putSettings', + { + body: { + persistent: { + cluster: { + remote: { + test: { + seeds: null, + skip_unavailable: false, + mode: 'proxy', + node_connections: null, + proxy_address: '127.0.0.1:9300', + proxy_socket_connections: null, + server_name: 'foobar', + }, + }, }, }, }, @@ -151,6 +224,7 @@ describe('ADD remote clusters', () => { name: 'test', seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, asserts: { apiArguments: [['cluster.remoteInfo']], @@ -167,6 +241,7 @@ describe('ADD remote clusters', () => { name: 'test', seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, asserts: { apiArguments: [ @@ -177,7 +252,17 @@ describe('ADD remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index e4ede01ca23ea..5e0fce82376e0 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -9,17 +9,22 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { RequestHandler } from 'src/core/server'; -import { serializeCluster } from '../../../common/lib'; +import { serializeCluster, Cluster } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; -import { API_BASE_PATH } from '../../../common/constants'; +import { API_BASE_PATH, PROXY_MODE, SNIFF_MODE } from '../../../common/constants'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; import { isEsError } from '../../lib/is_es_error'; import { RouteDependencies } from '../../types'; const bodyValidation = schema.object({ name: schema.string(), - seeds: schema.arrayOf(schema.string()), skipUnavailable: schema.boolean(), + mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), + seeds: schema.nullable(schema.arrayOf(schema.string())), + nodeConnections: schema.nullable(schema.number()), + proxyAddress: schema.nullable(schema.string()), + proxySocketConnections: schema.nullable(schema.number()), + serverName: schema.nullable(schema.string()), }); type RouteBody = TypeOf; @@ -33,7 +38,7 @@ export const register = (deps: RouteDependencies): void => { try { const callAsCurrentUser = ctx.core.elasticsearch.dataClient.callAsCurrentUser; - const { name, seeds, skipUnavailable } = request.body; + const { name } = request.body; // Check if cluster already exists. const existingCluster = await doesClusterExist(callAsCurrentUser, name); @@ -50,7 +55,7 @@ export const register = (deps: RouteDependencies): void => { }); } - const addClusterPayload = serializeCluster({ name, seeds, skipUnavailable }); + const addClusterPayload = serializeCluster(request.body as Cluster); const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body: addClusterPayload, }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts index 04deb62d2c2d2..cf14f8a67054e 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts @@ -113,7 +113,17 @@ describe('DELETE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: null, skip_unavailable: null } }, + remote: { + test: { + seeds: null, + skip_unavailable: null, + mode: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + node_connections: null, + }, + }, }, }, }, @@ -211,7 +221,17 @@ describe('DELETE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: null, skip_unavailable: null } }, + remote: { + test: { + seeds: null, + skip_unavailable: null, + mode: null, + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts index 90955be85859d..d81b50f1148de 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts @@ -89,6 +89,7 @@ describe('GET remote clusters', () => { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false, + mode: 'sniff', }, }, }, @@ -120,6 +121,7 @@ describe('GET remote clusters', () => { initialConnectTimeout: '30s', skipUnavailable: false, isConfiguredByNode: false, + mode: 'sniff', }, ], }, @@ -170,6 +172,7 @@ describe('GET remote clusters', () => { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false, + mode: 'sniff', }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts index 44b6284109ac5..abd44977d8e46 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts @@ -33,13 +33,28 @@ export const register = (deps: RouteDependencies): void => { const cluster = clustersByName[clusterName]; const isTransient = transientClusterNames.includes(clusterName); const isPersistent = persistentClusterNames.includes(clusterName); + // If the cluster hasn't been stored in the cluster state, then it's defined by the // node's config file. const isConfiguredByNode = !isTransient && !isPersistent; + // Pre-7.6, ES supported an undocumented "proxy" field + // ES does not handle migrating this to the new implementation, so we need to surface it in the UI + // This value is not available via the GET /_remote/info API, so we get it from the cluster settings + const deprecatedProxyAddress = isPersistent + ? get(clusterSettings, `persistent.cluster.remote[${clusterName}].proxy`, undefined) + : undefined; + + // server_name is not available via the GET /_remote/info API, so we get it from the cluster settings + // Per https://github.com/elastic/kibana/pull/26067#issuecomment-441848124, we only look at persistent settings + const serverName = isPersistent + ? get(clusterSettings, `persistent.cluster.remote[${clusterName}].server_name`, undefined) + : undefined; + return { - ...deserializeCluster(clusterName, cluster), + ...deserializeCluster(clusterName, cluster, deprecatedProxyAddress), isConfiguredByNode, + serverName, }; }); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts index 9ba239c3ff661..84ba9587ddfa6 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts @@ -129,6 +129,7 @@ describe('UPDATE remote clusters', () => { payload: { seeds: ['127.0.0.1:9300'], skipUnavailable: true, + mode: 'sniff', }, asserts: { apiArguments: [ @@ -139,7 +140,17 @@ describe('UPDATE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: true } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: true, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, @@ -156,6 +167,7 @@ describe('UPDATE remote clusters', () => { name: 'test', seeds: ['127.0.0.1:9300'], skipUnavailable: true, + mode: 'sniff', }, }, }); @@ -167,6 +179,7 @@ describe('UPDATE remote clusters', () => { payload: { seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, params: { name: 'test', @@ -198,6 +211,7 @@ describe('UPDATE remote clusters', () => { payload: { seeds: ['127.0.0.1:9300'], skipUnavailable: false, + mode: 'sniff', }, params: { name: 'test', @@ -211,7 +225,17 @@ describe('UPDATE remote clusters', () => { body: { persistent: { cluster: { - remote: { test: { seeds: ['127.0.0.1:9300'], skip_unavailable: false } }, + remote: { + test: { + seeds: ['127.0.0.1:9300'], + skip_unavailable: false, + mode: 'sniff', + node_connections: null, + proxy_address: null, + proxy_socket_connections: null, + server_name: null, + }, + }, }, }, }, diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index ed584307d84c1..14b161b6f26b5 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -9,16 +9,21 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { RequestHandler } from 'src/core/server'; -import { API_BASE_PATH } from '../../../common/constants'; -import { serializeCluster, deserializeCluster } from '../../../common/lib'; +import { API_BASE_PATH, SNIFF_MODE, PROXY_MODE } from '../../../common/constants'; +import { serializeCluster, deserializeCluster, Cluster, ClusterEs } from '../../../common/lib'; import { doesClusterExist } from '../../lib/does_cluster_exist'; import { RouteDependencies } from '../../types'; import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; import { isEsError } from '../../lib/is_es_error'; const bodyValidation = schema.object({ - seeds: schema.arrayOf(schema.string()), skipUnavailable: schema.boolean(), + mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), + seeds: schema.nullable(schema.arrayOf(schema.string())), + nodeConnections: schema.nullable(schema.number()), + proxyAddress: schema.nullable(schema.string()), + proxySocketConnections: schema.nullable(schema.number()), + serverName: schema.nullable(schema.string()), }); const paramsValidation = schema.object({ @@ -39,7 +44,6 @@ export const register = (deps: RouteDependencies): void => { const callAsCurrentUser = ctx.core.elasticsearch.dataClient.callAsCurrentUser; const { name } = request.params; - const { seeds, skipUnavailable } = request.body; // Check if cluster does exist. const existingCluster = await doesClusterExist(callAsCurrentUser, name); @@ -57,13 +61,14 @@ export const register = (deps: RouteDependencies): void => { } // Update cluster as new settings - const updateClusterPayload = serializeCluster({ name, seeds, skipUnavailable }); + const updateClusterPayload = serializeCluster({ ...request.body, name } as Cluster); + const updateClusterResponse = await callAsCurrentUser('cluster.putSettings', { body: updateClusterPayload, }); const acknowledged = get(updateClusterResponse, 'acknowledged'); - const cluster = get(updateClusterResponse, `persistent.cluster.remote.${name}`); + const cluster = get(updateClusterResponse, `persistent.cluster.remote.${name}`) as ClusterEs; if (acknowledged && cluster) { const body = { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ad49f0242e8e6..9b842f736b8bb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10081,10 +10081,7 @@ "xpack.remoteClusters.remoteClusterForm.saveButtonLabel": "保存", "xpack.remoteClusters.remoteClusterForm.sectionNameDescription": "リモートクラスターの固有の名前です。", "xpack.remoteClusters.remoteClusterForm.sectionNameTitle": "名前", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsDescription1": "クラスターステータスのクエリを実行するリモートクラスターノードのリストです。1 つのノードが利用できない場合にディスカバリが失敗しないよう、複数シードノードを指定してください。", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText": "リモートクラスターの {transportPort} の前にくる IP アドレスまたはホスト名です。", "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText.transportPortLinkText": "トランスポートポート", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsTitle": "クラスターディスカバリのシードノード", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription": "デフォルトで、リクエストのリモートクラスターのどれかが利用できないと、リクエストは失敗となります。このクラスターが利用できない場合にリクエストを他のリモートクラスターに送信し続けるには、{optionName} を有効にします。{learnMoreLink}", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.learnMoreLinkLabel": "詳細", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.optionNameLabel": "利用不可の場合スキップ", @@ -10106,11 +10103,9 @@ "xpack.remoteClusters.remoteClusterList.table.actionEditDescription": "リモートクラスターを編集します", "xpack.remoteClusters.remoteClusterList.table.actionsColumnTitle": "アクション", "xpack.remoteClusters.remoteClusterList.table.connectedColumnTitle": "接続", - "xpack.remoteClusters.remoteClusterList.table.connectedNodesColumnTitle": "接続済みのノード", "xpack.remoteClusters.remoteClusterList.table.isConfiguredByNodeMessage": "elasticsearch.yml で定義されています", "xpack.remoteClusters.remoteClusterList.table.nameColumnTitle": "名前", "xpack.remoteClusters.remoteClusterList.table.removeButtonLabel": "{count, plural, one {リモートクラスター} other {{count}リモートクラスター}}を削除", - "xpack.remoteClusters.remoteClusterList.table.seedsColumnTitle": "シード", "xpack.remoteClusters.remoteClusterListTitle": "リモートクラスター", "xpack.remoteClusters.removeAction.errorMultipleNotificationTitle": "「{count}」リモートクラスターの削除中にエラーが発生", "xpack.remoteClusters.removeAction.errorSingleNotificationTitle": "リモートクラスター「{name}」の削除中にエラーが発生", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 76ecf333eb10a..97c30f180dfe1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10081,10 +10081,7 @@ "xpack.remoteClusters.remoteClusterForm.saveButtonLabel": "保存", "xpack.remoteClusters.remoteClusterForm.sectionNameDescription": "远程集群的唯一名称。", "xpack.remoteClusters.remoteClusterForm.sectionNameTitle": "名称", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsDescription1": "要查询集群状态的远程集群节点的列表。指定多个种子节点,以便在节点不可用时发现不会失败。", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText": "IP 地址或主机名,后跟远程集群的 {transportPort}。", "xpack.remoteClusters.remoteClusterForm.sectionSeedsHelpText.transportPortLinkText": "传输端口", - "xpack.remoteClusters.remoteClusterForm.sectionSeedsTitle": "用于集群发现的种子节点", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription": "默认情况下,如果任何查询的远程集群不可用,请求将失败。要在此集群不可用时继续向其他远程集群发送请求,请启用 {optionName}。{learnMoreLink}", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.learnMoreLinkLabel": "了解详情。", "xpack.remoteClusters.remoteClusterForm.sectionSkipUnavailableDescription.optionNameLabel": "如果不可用,则跳过", @@ -10106,11 +10103,9 @@ "xpack.remoteClusters.remoteClusterList.table.actionEditDescription": "编辑远程集群", "xpack.remoteClusters.remoteClusterList.table.actionsColumnTitle": "操作", "xpack.remoteClusters.remoteClusterList.table.connectedColumnTitle": "连接", - "xpack.remoteClusters.remoteClusterList.table.connectedNodesColumnTitle": "已连接节点", "xpack.remoteClusters.remoteClusterList.table.isConfiguredByNodeMessage": "在 elasticsearch.yml 中定义", "xpack.remoteClusters.remoteClusterList.table.nameColumnTitle": "名称", "xpack.remoteClusters.remoteClusterList.table.removeButtonLabel": "删除 {count, plural, one { 个远程集群} other {{count} 个远程集群}}", - "xpack.remoteClusters.remoteClusterList.table.seedsColumnTitle": "种子", "xpack.remoteClusters.remoteClusterListTitle": "远程集群", "xpack.remoteClusters.removeAction.errorMultipleNotificationTitle": "删除 “{count}” 个远程集群时出错", "xpack.remoteClusters.removeAction.errorSingleNotificationTitle": "删除远程集群 “{name}” 时出错", diff --git a/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js b/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js index d8cee1db9a2bc..4462fcf75d5d8 100644 --- a/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js +++ b/x-pack/test/api_integration/apis/management/cross_cluster_replication/remote_clusters.helpers.js @@ -25,7 +25,8 @@ export const registerHelpers = supertest => { .post(`${REMOTE_CLUSTERS_API_BASE_PATH}`) .set('kbn-xsrf', 'xxx') .send({ - name: name, + name, + mode: 'sniff', seeds: [`localhost:${esTransportPort}`], skipUnavailable: true, }); diff --git a/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js b/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js index 677d22ff74984..7921186000e19 100644 --- a/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js +++ b/x-pack/test/api_integration/apis/management/remote_clusters/remote_clusters.js @@ -40,6 +40,7 @@ export default function({ getService }) { name: 'test_cluster', seeds: [NODE_SEED], skipUnavailable: true, + mode: 'sniff', }) .expect(200); @@ -58,6 +59,7 @@ export default function({ getService }) { name: 'test_cluster', seeds: [NODE_SEED], skipUnavailable: false, + mode: 'sniff', }) .expect(409); @@ -79,6 +81,7 @@ export default function({ getService }) { .send({ skipUnavailable: false, seeds: [NODE_SEED], + mode: 'sniff', }) .expect(200); @@ -87,6 +90,7 @@ export default function({ getService }) { skipUnavailable: 'false', // ES issue #35671 seeds: [NODE_SEED], isConfiguredByNode: false, + mode: 'sniff', }); }); }); @@ -109,6 +113,7 @@ export default function({ getService }) { initialConnectTimeout: '30s', skipUnavailable: false, isConfiguredByNode: false, + mode: 'sniff', }, ]); }); @@ -139,6 +144,7 @@ export default function({ getService }) { name: 'test_cluster1', seeds: [NODE_SEED], skipUnavailable: true, + mode: 'sniff', }) .expect(200); @@ -149,6 +155,7 @@ export default function({ getService }) { name: 'test_cluster2', seeds: [NODE_SEED], skipUnavailable: true, + mode: 'sniff', }) .expect(200); From 93914b6cb5afbc382699818c1beab6568a5dabac Mon Sep 17 00:00:00 2001 From: marshallmain <55718608+marshallmain@users.noreply.github.com> Date: Mon, 16 Mar 2020 15:53:49 -0400 Subject: [PATCH 20/76] [Endpoint] Sample data generator CLI script (#59952) * start on cli * make it work * cleanup * remove failed attempt code * update package and tsconfig * remove empty file * generate resolver events from multiple endpoints * re-add child randomization * align index names with real plugin * remove duplication * better naming * add temporary mapping to sample data generator * error handling, move tsconfig * add readme * Update README.md * move mapping from common to scripts * make delete index option * remove unnecessary map call * fix import style Co-authored-by: Elastic Machine --- .../endpoint/common/generate_data.test.ts | 2 +- .../plugins/endpoint/common/generate_data.ts | 111 +- x-pack/plugins/endpoint/package.json | 7 +- .../endpoint/store/managing/index.test.ts | 2 +- .../store/managing/middleware.test.ts | 2 +- x-pack/plugins/endpoint/scripts/README.md | 46 + .../endpoint/scripts/cli_tsconfig.json | 8 + x-pack/plugins/endpoint/scripts/mapping.json | 2367 +++++++++++++++++ .../endpoint/scripts/resolver_generator.ts | 161 ++ 9 files changed, 2634 insertions(+), 72 deletions(-) create mode 100644 x-pack/plugins/endpoint/scripts/README.md create mode 100644 x-pack/plugins/endpoint/scripts/cli_tsconfig.json create mode 100644 x-pack/plugins/endpoint/scripts/mapping.json create mode 100644 x-pack/plugins/endpoint/scripts/resolver_generator.ts diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts index ebe3c25eef829..e14f506c825f2 100644 --- a/x-pack/plugins/endpoint/common/generate_data.test.ts +++ b/x-pack/plugins/endpoint/common/generate_data.test.ts @@ -151,7 +151,7 @@ describe('data generator', () => { const timestamp = new Date().getTime(); const root = generator.generateEvent({ timestamp }); const generations = 2; - const events = generator.generateDescendantsTree(root, generations); + const events = [root, ...generator.generateDescendantsTree(root, generations)]; const rootNode = buildResolverTree(events); const visitedEvents = countResolverEvents(rootNode, generations); expect(visitedEvents).toEqual(events.length); diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index a91cf0ffca783..b539e309d76f7 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -6,7 +6,7 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; -import { AlertEvent, EndpointEvent, EndpointMetadata, OSFields } from './types'; +import { AlertEvent, EndpointEvent, EndpointMetadata, OSFields, HostFields } from './types'; export type Event = AlertEvent | EndpointEvent; @@ -67,31 +67,51 @@ const FILE_OPERATIONS: string[] = ['creation', 'open', 'rename', 'execution', 'd // These are from the v1 schemas and aren't all valid ECS event categories, still in flux const OTHER_EVENT_CATEGORIES: string[] = ['driver', 'file', 'library', 'network', 'registry']; +interface HostInfo { + agent: { + version: string; + id: string; + }; + host: HostFields; + endpoint: { + policy: { + id: string; + }; + }; +} + export class EndpointDocGenerator { - agentId: string; - hostId: string; - hostname: string; - macAddress: string[]; - ip: string[]; - agentVersion: string; - os: OSFields; - policy: { name: string; id: string }; + commonInfo: HostInfo; random: seedrandom.prng; constructor(seed = Math.random().toString()) { this.random = seedrandom(seed); - this.hostId = this.seededUUIDv4(); - this.agentId = this.seededUUIDv4(); - this.hostname = this.randomHostname(); - this.ip = this.randomArray(3, () => this.randomIP()); - this.macAddress = this.randomArray(3, () => this.randomMac()); - this.agentVersion = this.randomVersion(); - this.os = this.randomChoice(OS); - this.policy = this.randomChoice(POLICIES); + this.commonInfo = this.createHostData(); } - public randomizeIPs() { - this.ip = this.randomArray(3, () => this.randomIP()); + // This function will create new values for all the host fields, so documents from a different endpoint can be created + // This provides a convenient way to make documents from multiple endpoints that are all tied to a single seed value + public randomizeHostData() { + this.commonInfo = this.createHostData(); + } + + private createHostData(): HostInfo { + return { + agent: { + version: this.randomVersion(), + id: this.seededUUIDv4(), + }, + host: { + id: this.seededUUIDv4(), + hostname: this.randomHostname(), + ip: this.randomArray(3, () => this.randomIP()), + mac: this.randomArray(3, () => this.randomMac()), + os: this.randomChoice(OS), + }, + endpoint: { + policy: this.randomChoice(POLICIES), + }, + }; } public generateEndpointMetadata(ts = new Date().getTime()): EndpointMetadata { @@ -100,22 +120,7 @@ export class EndpointDocGenerator { event: { created: ts, }, - endpoint: { - policy: { - id: this.policy.id, - }, - }, - agent: { - version: this.agentVersion, - id: this.agentId, - }, - host: { - id: this.hostId, - hostname: this.hostname, - ip: this.ip, - mac: this.macAddress, - os: this.os, - }, + ...this.commonInfo, }; } @@ -125,11 +130,8 @@ export class EndpointDocGenerator { parentEntityID?: string ): AlertEvent { return { + ...this.commonInfo, '@timestamp': ts, - agent: { - id: this.agentId, - version: this.agentVersion, - }, event: { action: this.randomChoice(FILE_OPERATIONS), kind: 'alert', @@ -139,11 +141,6 @@ export class EndpointDocGenerator { module: 'endpoint', type: 'creation', }, - endpoint: { - policy: { - id: this.policy.id, - }, - }, file: { owner: 'SYSTEM', name: 'fake_malware.exe', @@ -169,13 +166,6 @@ export class EndpointDocGenerator { }, temp_file_path: 'C:/temp/fake_malware.exe', }, - host: { - id: this.hostId, - hostname: this.hostname, - ip: this.ip, - mac: this.macAddress, - os: this.os, - }, process: { pid: 2, name: 'malware writer', @@ -243,11 +233,7 @@ export class EndpointDocGenerator { public generateEvent(options: EventOptions = {}): EndpointEvent { return { '@timestamp': options.timestamp ? options.timestamp : new Date().getTime(), - agent: { - id: this.agentId, - version: this.agentVersion, - type: 'endpoint', - }, + agent: { ...this.commonInfo.agent, type: 'endgame' }, ecs: { version: '1.4.0', }, @@ -257,13 +243,7 @@ export class EndpointDocGenerator { type: options.eventType ? options.eventType : 'creation', id: this.seededUUIDv4(), }, - host: { - id: this.hostId, - hostname: this.hostname, - ip: this.ip, - mac: this.macAddress, - os: this.os, - }, + host: this.commonInfo.host, process: { entity_id: options.entityID ? options.entityID : this.randomString(10), parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined, @@ -323,14 +303,13 @@ export class EndpointDocGenerator { percentNodesWithRelated = 100, percentChildrenTerminated = 100 ): Event[] { - let events: Event[] = [root]; + let events: Event[] = []; let parents = [root]; let timestamp = root['@timestamp']; for (let i = 0; i < generations; i++) { const newParents: EndpointEvent[] = []; parents.forEach(element => { - // const numChildren = randomN(maxChildrenPerNode); - const numChildren = maxChildrenPerNode; + const numChildren = this.randomN(maxChildrenPerNode); for (let j = 0; j < numChildren; j++) { timestamp = timestamp + 1000; const child = this.generateEvent({ diff --git a/x-pack/plugins/endpoint/package.json b/x-pack/plugins/endpoint/package.json index c7ba8b3fb4196..fc4f4bd586bef 100644 --- a/x-pack/plugins/endpoint/package.json +++ b/x-pack/plugins/endpoint/package.json @@ -4,10 +4,11 @@ "version": "0.0.0", "private": true, "license": "Elastic-License", - "scripts": {}, + "scripts": { + "test:generate": "ts-node --project scripts/cli_tsconfig.json scripts/resolver_generator.ts" + }, "dependencies": { - "react-redux": "^7.1.0", - "seedrandom": "^3.0.5" + "react-redux": "^7.1.0" }, "devDependencies": { "@types/seedrandom": ">=2.0.0 <4.0.0", diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts index fba1dacb0d3bd..e435fded13f4c 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts @@ -20,7 +20,7 @@ describe('endpoint_list store concerns', () => { dispatch = store.dispatch; }; const generateEndpoint = (): EndpointMetadata => { - return generator.generateEndpointMetadata(new Date().getTime()); + return generator.generateEndpointMetadata(); }; const loadDataToStore = () => { dispatch({ diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts index d98dc82624149..459a1789a58da 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts @@ -27,7 +27,7 @@ describe('endpoint list saga', () => { const generator = new EndpointDocGenerator(); // https://github.com/elastic/endpoint-app-team/issues/131 const generateEndpoint = (): EndpointMetadata => { - return generator.generateEndpointMetadata(new Date().getTime()); + return generator.generateEndpointMetadata(); }; let history: History; diff --git a/x-pack/plugins/endpoint/scripts/README.md b/x-pack/plugins/endpoint/scripts/README.md new file mode 100644 index 0000000000000..f0c8c5a9b0b66 --- /dev/null +++ b/x-pack/plugins/endpoint/scripts/README.md @@ -0,0 +1,46 @@ +This script makes it easy to create the endpoint metadata, alert, and event documents needed to test Resolver in Kibana. +The default behavior is to create 1 endpoint with 1 alert and a moderate number of events (random, typically on the order of 20). +A seed value can be provided as a string for the random number generator for repeatable behavior, useful for demos etc. +Use the `-d` option if you want to delete and remake the indices, otherwise it will add documents to existing indices. + +The sample data generator script depends on ts-node, install with npm: + +```npm install -g ts-node``` + +Example command sequence to get ES and kibana running with sample data after installing ts-node: + +```yarn es snapshot``` -> starts ES + +```npx yarn start --xpack.endpoint.enabled=true --no-base-path``` -> starts kibana + +```cd ~/path/to/kibana/x-pack/plugins/endpoint``` + +```yarn test:generate --auth elastic:changeme``` -> run the resolver_generator.ts script + +Resolver generator CLI options: +```--help Show help [boolean] + --seed, -s random seed to use for document generator [string] + --node, -n elasticsearch node url + [string] [default: "http://localhost:9200"] + --eventIndex, --ei index to store events in + [string] [default: "events-endpoint-1"] + --metadataIndex, --mi index to store endpoint metadata in + [string] [default: "endpoint-agent-1"] + --auth elasticsearch username and password, separated by + a colon [string] + --ancestors, --anc number of ancestors of origin to create + [number] [default: 3] + --generations, --gen number of child generations to create + [number] [default: 3] + --children, --ch maximum number of children per node + [number] [default: 3] + --relatedEvents, --related number of related events to create for each + process event [number] [default: 5] + --percentWithRelated, --pr percent of process events to add related events to + [number] [default: 30] + --percentTerminated, --pt percent of process events to add termination event + for [number] [default: 30] + --numEndpoints, --ne number of different endpoints to generate alerts + for [number] [default: 1] + --alertsPerEndpoint, --ape number of resolver trees to make for each endpoint + [number] [default: 1]``` diff --git a/x-pack/plugins/endpoint/scripts/cli_tsconfig.json b/x-pack/plugins/endpoint/scripts/cli_tsconfig.json new file mode 100644 index 0000000000000..25afe109a42ea --- /dev/null +++ b/x-pack/plugins/endpoint/scripts/cli_tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "target": "es2019", + "resolveJsonModule": true + } + } + \ No newline at end of file diff --git a/x-pack/plugins/endpoint/scripts/mapping.json b/x-pack/plugins/endpoint/scripts/mapping.json new file mode 100644 index 0000000000000..34c039d643517 --- /dev/null +++ b/x-pack/plugins/endpoint/scripts/mapping.json @@ -0,0 +1,2367 @@ +{ + "mappings": { + "_meta": { + "version": "1.5.0-dev" + }, + "date_detection": false, + "dynamic": false, + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "compile_time": { + "type": "date" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classifier": { + "properties": { + "features": { + "properties": { + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mapped_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "mapped_size": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "endpoint": { + "properties": { + "artifact": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "policy": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "entry_modified": { + "type": "double" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "macro": { + "properties": { + "code_page": { + "type": "long" + }, + "collection": { + "properties": { + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "object" + }, + "errors": { + "properties": { + "count": { + "type": "long" + }, + "error_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "file_extension": { + "type": "long" + }, + "project_file": { + "properties": { + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "object" + }, + "stream": { + "properties": { + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_code_size": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "malware_classifier": { + "properties": { + "features": { + "properties": { + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "temp_file_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_percent": { + "type": "double" + }, + "cwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "env_variables": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "handles": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classifier": { + "properties": { + "features": { + "properties": { + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory_percent": { + "type": "double" + }, + "memory_region": { + "properties": { + "allocation_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocation_protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram": { + "properties": { + "histogram_array": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_flavor": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_resolution": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "length": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "permission": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_tag": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_on_disk": { + "type": "boolean" + } + }, + "type": "nested" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "num_threads": { + "type": "long" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "phys_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "services": { + "ignore_above": 1024, + "type": "keyword" + }, + "session_id": { + "type": "long" + }, + "short_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty_device": { + "properties": { + "major_number": { + "type": "integer" + }, + "minor_number": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "target": { + "properties": { + "dll": { + "properties": { + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "compile_time": { + "type": "date" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classifier": { + "properties": { + "features": { + "properties": { + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mapped_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "mapped_size": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_percent": { + "type": "double" + }, + "cwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "env_variables": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "handles": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classifier": { + "properties": { + "features": { + "properties": { + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory_percent": { + "type": "double" + }, + "memory_region": { + "properties": { + "allocation_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocation_protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram": { + "properties": { + "histogram_array": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_flavor": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_resolution": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "length": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "permission": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_tag": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_on_disk": { + "type": "boolean" + } + }, + "type": "nested" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "num_threads": { + "type": "long" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "phys_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "services": { + "ignore_above": 1024, + "type": "keyword" + }, + "session_id": { + "type": "long" + }, + "short_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty_device": { + "properties": { + "major_number": { + "type": "integer" + }, + "minor_number": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": 10000 + } + }, + "refresh_interval": "5s" + } + } +} \ No newline at end of file diff --git a/x-pack/plugins/endpoint/scripts/resolver_generator.ts b/x-pack/plugins/endpoint/scripts/resolver_generator.ts new file mode 100644 index 0000000000000..a3e56497f0790 --- /dev/null +++ b/x-pack/plugins/endpoint/scripts/resolver_generator.ts @@ -0,0 +1,161 @@ +/* + * 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 * as yargs from 'yargs'; +import { Client, ClientOptions } from '@elastic/elasticsearch'; +import { ResponseError } from '@elastic/elasticsearch/lib/errors'; +import { EndpointDocGenerator } from '../common/generate_data'; +import { default as mapping } from './mapping.json'; + +main(); + +async function main() { + const argv = yargs.help().options({ + seed: { + alias: 's', + describe: 'random seed to use for document generator', + type: 'string', + }, + node: { + alias: 'n', + describe: 'elasticsearch node url', + default: 'http://localhost:9200', + type: 'string', + }, + eventIndex: { + alias: 'ei', + describe: 'index to store events in', + default: 'events-endpoint-1', + type: 'string', + }, + metadataIndex: { + alias: 'mi', + describe: 'index to store endpoint metadata in', + default: 'endpoint-agent-1', + type: 'string', + }, + auth: { + describe: 'elasticsearch username and password, separated by a colon', + type: 'string', + }, + ancestors: { + alias: 'anc', + describe: 'number of ancestors of origin to create', + type: 'number', + default: 3, + }, + generations: { + alias: 'gen', + describe: 'number of child generations to create', + type: 'number', + default: 3, + }, + children: { + alias: 'ch', + describe: 'maximum number of children per node', + type: 'number', + default: 3, + }, + relatedEvents: { + alias: 'related', + describe: 'number of related events to create for each process event', + type: 'number', + default: 5, + }, + percentWithRelated: { + alias: 'pr', + describe: 'percent of process events to add related events to', + type: 'number', + default: 30, + }, + percentTerminated: { + alias: 'pt', + describe: 'percent of process events to add termination event for', + type: 'number', + default: 30, + }, + numEndpoints: { + alias: 'ne', + describe: 'number of different endpoints to generate alerts for', + type: 'number', + default: 1, + }, + alertsPerEndpoint: { + alias: 'ape', + describe: 'number of resolver trees to make for each endpoint', + type: 'number', + default: 1, + }, + delete: { + alias: 'd', + describe: 'delete indices and remake them', + type: 'boolean', + default: false, + }, + }).argv; + const clientOptions: ClientOptions = { + node: argv.node, + }; + if (argv.auth) { + const [username, password]: string[] = argv.auth.split(':', 2); + clientOptions.auth = { username, password }; + } + const client = new Client(clientOptions); + if (argv.delete) { + try { + await client.indices.delete({ + index: [argv.eventIndex, argv.metadataIndex], + }); + } catch (err) { + if (err instanceof ResponseError && err.statusCode !== 404) { + // eslint-disable-next-line no-console + console.log(err); + process.exit(1); + } + } + } + try { + await client.indices.create({ + index: argv.eventIndex, + body: mapping, + }); + } catch (err) { + if ( + err instanceof ResponseError && + err.body.error.type !== 'resource_already_exists_exception' + ) { + // eslint-disable-next-line no-console + console.log(err.body); + process.exit(1); + } + } + + const generator = new EndpointDocGenerator(argv.seed); + for (let i = 0; i < argv.numEndpoints; i++) { + await client.index({ + index: argv.metadataIndex, + body: generator.generateEndpointMetadata(), + }); + for (let j = 0; j < argv.alertsPerEndpoint; j++) { + const resolverDocs = generator.generateFullResolverTree( + argv.ancestors, + argv.generations, + argv.children, + argv.relatedEvents, + argv.percentWithRelated, + argv.percentTerminated + ); + const body = resolverDocs.reduce( + (array: Array>, doc) => ( + array.push({ index: { _index: argv.eventIndex } }, doc), array + ), + [] + ); + + await client.bulk({ body }); + } + generator.randomizeHostData(); + } +} From 92aec698f5d20630ed194f55b5eea4fd00c5f231 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 16 Mar 2020 21:28:34 +0100 Subject: [PATCH 21/76] Embeddable triggers (#58440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add Embeddable.supportedTriggers() method * feat: 🎸 report supported triggers on visualization embeddable * feat: 🎸 hard-code supportedTriggers() in visualize_embed * feat: 🎸 use VIS_EVENT_TO_TRIGGER when executing trigger * perf: ⚡️ improve type * fix: 🐛 revert back trigger check to how it is done on master * chore: 🤖 remove unused import * feat: 🎸 hard-code triggers for each visualization * fix: 🐛 fix import * feat: 🎸 reshuffle vis_types in supportedTriggers() method Co-authored-by: Elastic Machine --- .../np_ready/public/embeddable/events.ts | 33 +++++++++++++++++ .../public/embeddable/visualize_embeddable.ts | 36 +++++++++++++++---- .../public/lib/embeddables/embeddable.tsx | 5 +++ .../public/lib/embeddables/i_embeddable.ts | 6 ++++ 4 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts new file mode 100644 index 0000000000000..53d04bf6eb04a --- /dev/null +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/events.ts @@ -0,0 +1,33 @@ +/* + * 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 { + SELECT_RANGE_TRIGGER, + VALUE_CLICK_TRIGGER, +} from '../../../../../../../plugins/ui_actions/public'; + +export interface VisEventToTrigger { + ['brush']: typeof SELECT_RANGE_TRIGGER; + ['filter']: typeof VALUE_CLICK_TRIGGER; +} + +export const VIS_EVENT_TO_TRIGGER: VisEventToTrigger = { + brush: SELECT_RANGE_TRIGGER, + filter: VALUE_CLICK_TRIGGER, +}; diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts index 474912ed508f8..c45e6832dc836 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts @@ -36,10 +36,6 @@ import { Container, EmbeddableVisTriggerContext, } from '../../../../../../../plugins/embeddable/public'; -import { - selectRangeTrigger, - valueClickTrigger, -} from '../../../../../../../plugins/ui_actions/public'; import { dispatchRenderComplete } from '../../../../../../../plugins/kibana_utils/public'; import { IExpressionLoaderParams, @@ -50,6 +46,7 @@ import { buildPipeline } from '../legacy/build_pipeline'; import { Vis } from '../vis'; import { getExpressions, getUiActions } from '../services'; import { VisSavedObject } from '../types'; +import { VIS_EVENT_TO_TRIGGER } from './events'; const getKeys = (o: T): Array => Object.keys(o) as Array; @@ -295,8 +292,8 @@ export class VisualizeEmbeddable extends Embeddable { + return []; + } } diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts index 62121cb0f23dd..7fef80edde85f 100644 --- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts +++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts @@ -21,6 +21,7 @@ import { Observable } from 'rxjs'; import { Adapters } from '../types'; import { IContainer } from '../containers/i_container'; import { ViewMode } from '../types'; +import { TriggerContextMapping } from '../../../../ui_actions/public'; export interface EmbeddableInput { viewMode?: ViewMode; @@ -161,4 +162,9 @@ export interface IEmbeddable< * Cleans up subscriptions, destroy nodes mounted from calls to render. */ destroy(): void; + + /** + * List of triggers that this embeddable will execute. + */ + supportedTriggers(): Array; } From c898e799a5fd605177e823e112a4ad2f430fcabc Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Mon, 16 Mar 2020 14:33:56 -0600 Subject: [PATCH 22/76] Migrate dual validated range (#59689) * Move validated range files to new NP location * Update refs in code * Clean up old validated range files * Change relative paths to 'kibana-react'. Some clean up * Change to relative paths * Fix i18n errors * i18n clean up. Export module explicitly * Change files over to TS to prevent build issue where validated range was missing * Clean up TS conversion * More clean up. Extend EuiRangeProps * Remove unneeded ts-ignore * Review feedback and test fixes * Change double to single quotes * min and max aren't always passed, make optional * Type updates * Review feedback. Set state to empty on init and add ignore comment * Review feedback * Add back in last 2 ts-ignores. Build fails without focusable attribute on EuiDualRange & No good alternatives for spread syntax in TS components * Rollback change to state init. Initializing state to null actually triggers a react browser warning and complicates using 'prevState' in getDerivedStateFromProps Co-authored-by: Elastic Machine --- .../public/components/vis/range_control.tsx | 3 +- .../public/legacy_imports.ts | 2 - .../public/components/tag_cloud_options.tsx | 3 +- .../public/legacy_imports.ts | 1 - .../ui/public/validated_range/index.d.ts | 25 --------- src/plugins/kibana_react/public/index.ts | 1 + .../public/validated_range/index.ts} | 0 .../validated_range/is_range_valid.test.ts} | 0 .../public/validated_range/is_range_valid.ts} | 24 ++++++--- .../validated_range/validated_dual_range.tsx} | 54 ++++++++++++------- .../layer_settings/layer_settings.js | 2 +- .../components/size/size_range_selector.js | 2 +- .../translations/translations/ja-JP.json | 3 -- .../translations/translations/zh-CN.json | 3 -- 14 files changed, 58 insertions(+), 65 deletions(-) delete mode 100644 src/legacy/ui/public/validated_range/index.d.ts rename src/{legacy/ui/public/validated_range/index.js => plugins/kibana_react/public/validated_range/index.ts} (100%) rename src/{legacy/ui/public/validated_range/is_range_valid.test.js => plugins/kibana_react/public/validated_range/is_range_valid.test.ts} (100%) rename src/{legacy/ui/public/validated_range/is_range_valid.js => plugins/kibana_react/public/validated_range/is_range_valid.ts} (74%) rename src/{legacy/ui/public/validated_range/validated_dual_range.js => plugins/kibana_react/public/validated_range/validated_dual_range.tsx} (67%) diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx b/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx index cd3982afd9afd..0cd2a2b331980 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/range_control.tsx @@ -19,8 +19,7 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; - -import { ValidatedDualRange } from '../../legacy_imports'; +import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; import { FormRow } from './form_row'; import { RangeControl as RangeControlClass } from '../../control/range_control_factory'; diff --git a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts b/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts index b6c4eb28e974f..8c58ac2386da4 100644 --- a/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts +++ b/src/legacy/core_plugins/input_control_vis/public/legacy_imports.ts @@ -22,7 +22,5 @@ import { SearchSource as SearchSourceClass, ISearchSource } from '../../../../pl export { SearchSourceFields } from '../../../../plugins/data/public'; -export { ValidatedDualRange } from 'ui/validated_range'; - export type SearchSource = Class; export const SearchSource = SearchSourceClass; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index ab7c2cd980c42..a9e816f70cf53 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -20,11 +20,10 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - +import { ValidatedDualRange } from '../../../../../../src/plugins/kibana_react/public'; import { VisOptionsProps } from '../../../vis_default_editor/public'; import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public'; import { TagCloudVisParams } from '../types'; -import { ValidatedDualRange } from '../legacy_imports'; function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { const handleFontSizeChange = ([minFontSize, maxFontSize]: [string | number, string | number]) => { diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts index d5b442bc5b346..0d76bc5d8b68b 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/legacy_imports.ts @@ -18,5 +18,4 @@ */ export { Schemas } from 'ui/agg_types'; -export { ValidatedDualRange } from 'ui/validated_range'; export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; diff --git a/src/legacy/ui/public/validated_range/index.d.ts b/src/legacy/ui/public/validated_range/index.d.ts deleted file mode 100644 index 50cacbc517be8..0000000000000 --- a/src/legacy/ui/public/validated_range/index.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 React from 'react'; -import { EuiRangeProps } from '@elastic/eui'; - -export class ValidatedDualRange extends React.Component { - allowEmptyRange?: boolean; -} diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index f04c6f1f19c33..e88ca7178cde3 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -25,6 +25,7 @@ export * from './ui_settings'; export * from './field_icon'; export * from './table_list_view'; export * from './split_panel'; +export { ValidatedDualRange } from './validated_range'; export { Markdown, MarkdownSimple } from './markdown'; export { reactToUiComponent, uiToReactComponent } from './adapters'; export { useUrlTracker } from './use_url_tracker'; diff --git a/src/legacy/ui/public/validated_range/index.js b/src/plugins/kibana_react/public/validated_range/index.ts similarity index 100% rename from src/legacy/ui/public/validated_range/index.js rename to src/plugins/kibana_react/public/validated_range/index.ts diff --git a/src/legacy/ui/public/validated_range/is_range_valid.test.js b/src/plugins/kibana_react/public/validated_range/is_range_valid.test.ts similarity index 100% rename from src/legacy/ui/public/validated_range/is_range_valid.test.js rename to src/plugins/kibana_react/public/validated_range/is_range_valid.test.ts diff --git a/src/legacy/ui/public/validated_range/is_range_valid.js b/src/plugins/kibana_react/public/validated_range/is_range_valid.ts similarity index 74% rename from src/legacy/ui/public/validated_range/is_range_valid.js rename to src/plugins/kibana_react/public/validated_range/is_range_valid.ts index 9b733815a66ba..1f822c0cb94b9 100644 --- a/src/legacy/ui/public/validated_range/is_range_valid.js +++ b/src/plugins/kibana_react/public/validated_range/is_range_valid.ts @@ -18,14 +18,24 @@ */ import { i18n } from '@kbn/i18n'; +import { ValueMember, Value } from './validated_dual_range'; const LOWER_VALUE_INDEX = 0; const UPPER_VALUE_INDEX = 1; -export function isRangeValid(value, min, max, allowEmptyRange) { - allowEmptyRange = typeof allowEmptyRange === 'boolean' ? allowEmptyRange : true; //cannot use default props since that uses falsy check - let lowerValue = isNaN(value[LOWER_VALUE_INDEX]) ? '' : value[LOWER_VALUE_INDEX]; - let upperValue = isNaN(value[UPPER_VALUE_INDEX]) ? '' : value[UPPER_VALUE_INDEX]; +export function isRangeValid( + value: Value = [0, 0], + min: ValueMember = 0, + max: ValueMember = 0, + allowEmptyRange?: boolean +) { + allowEmptyRange = typeof allowEmptyRange === 'boolean' ? allowEmptyRange : true; // cannot use default props since that uses falsy check + let lowerValue: ValueMember = isNaN(value[LOWER_VALUE_INDEX] as number) + ? '' + : `${value[LOWER_VALUE_INDEX]}`; + let upperValue: ValueMember = isNaN(value[UPPER_VALUE_INDEX] as number) + ? '' + : `${value[UPPER_VALUE_INDEX]}`; const isLowerValueValid = lowerValue.toString() !== ''; const isUpperValueValid = upperValue.toString() !== ''; @@ -39,7 +49,7 @@ export function isRangeValid(value, min, max, allowEmptyRange) { let errorMessage = ''; const bothMustBeSetErrorMessage = i18n.translate( - 'common.ui.dualRangeControl.mustSetBothErrorMessage', + 'kibana-react.dualRangeControl.mustSetBothErrorMessage', { defaultMessage: 'Both lower and upper values must be set', } @@ -55,13 +65,13 @@ export function isRangeValid(value, min, max, allowEmptyRange) { errorMessage = bothMustBeSetErrorMessage; } else if ((isLowerValueValid && lowerValue < min) || (isUpperValueValid && upperValue > max)) { isValid = false; - errorMessage = i18n.translate('common.ui.dualRangeControl.outsideOfRangeErrorMessage', { + errorMessage = i18n.translate('kibana-react.dualRangeControl.outsideOfRangeErrorMessage', { defaultMessage: 'Values must be on or between {min} and {max}', values: { min, max }, }); } else if (isLowerValueValid && isUpperValueValid && upperValue < lowerValue) { isValid = false; - errorMessage = i18n.translate('common.ui.dualRangeControl.upperValidErrorMessage', { + errorMessage = i18n.translate('kibana-react.dualRangeControl.upperValidErrorMessage', { defaultMessage: 'Upper value must be greater or equal to lower value', }); } diff --git a/src/legacy/ui/public/validated_range/validated_dual_range.js b/src/plugins/kibana_react/public/validated_range/validated_dual_range.tsx similarity index 67% rename from src/legacy/ui/public/validated_range/validated_dual_range.js rename to src/plugins/kibana_react/public/validated_range/validated_dual_range.tsx index 3b0efba11afcc..e7392eeba3830 100644 --- a/src/legacy/ui/public/validated_range/validated_dual_range.js +++ b/src/plugins/kibana_react/public/validated_range/validated_dual_range.tsx @@ -18,17 +18,38 @@ */ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { isRangeValid } from './is_range_valid'; - import { EuiFormRow, EuiDualRange } from '@elastic/eui'; +import { EuiFormRowDisplayKeys } from '@elastic/eui/src/components/form/form_row/form_row'; +import { EuiDualRangeProps } from '@elastic/eui/src/components/form/range/dual_range'; +import { isRangeValid } from './is_range_valid'; // Wrapper around EuiDualRange that ensures onChange callback is only called when range value // is valid and within min/max -export class ValidatedDualRange extends Component { - state = {}; - static getDerivedStateFromProps(nextProps, prevState) { +export type Value = EuiDualRangeProps['value']; +export type ValueMember = EuiDualRangeProps['value'][0]; + +interface Props extends Omit { + value?: Value; + allowEmptyRange?: boolean; + label?: string; + formRowDisplay?: EuiFormRowDisplayKeys; + onChange?: (val: [string, string]) => void; + min?: ValueMember; + max?: ValueMember; +} + +interface State { + isValid?: boolean; + errorMessage?: string; + value: [ValueMember, ValueMember]; + prevValue?: Value; +} + +export class ValidatedDualRange extends Component { + static defaultProps: { fullWidth: boolean; allowEmptyRange: boolean; compressed: boolean }; + + static getDerivedStateFromProps(nextProps: Props, prevState: State) { if (nextProps.value !== prevState.prevValue) { const { isValid, errorMessage } = isRangeValid( nextProps.value, @@ -47,7 +68,10 @@ export class ValidatedDualRange extends Component { return null; } - _onChange = value => { + // @ts-ignore state populated by getDerivedStateFromProps + state: State = {}; + + _onChange = (value: Value) => { const { isValid, errorMessage } = isRangeValid( value, this.props.min, @@ -61,8 +85,8 @@ export class ValidatedDualRange extends Component { errorMessage, }); - if (isValid) { - this.props.onChange(value); + if (this.props.onChange && isValid) { + this.props.onChange([value[0] as string, value[1] as string]); } }; @@ -75,7 +99,8 @@ export class ValidatedDualRange extends Component { value, // eslint-disable-line no-unused-vars onChange, // eslint-disable-line no-unused-vars allowEmptyRange, // eslint-disable-line no-unused-vars - ...rest + // @ts-ignore + ...rest // TODO: Consider alternatives for spread operator in component } = this.props; return ( @@ -92,6 +117,7 @@ export class ValidatedDualRange extends Component { fullWidth={fullWidth} value={this.state.value} onChange={this._onChange} + // @ts-ignore focusable={false} // remove when #59039 is fixed {...rest} /> @@ -100,14 +126,6 @@ export class ValidatedDualRange extends Component { } } -ValidatedDualRange.propTypes = { - allowEmptyRange: PropTypes.bool, - fullWidth: PropTypes.bool, - compressed: PropTypes.bool, - label: PropTypes.node, - formRowDisplay: PropTypes.string, -}; - ValidatedDualRange.defaultProps = { allowEmptyRange: true, fullWidth: false, diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js index ac17915b5f277..eb23607aa2150 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js @@ -11,7 +11,7 @@ import { EuiTitle, EuiPanel, EuiFormRow, EuiFieldText, EuiSpacer } from '@elasti import { ValidatedRange } from '../../../components/validated_range'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ValidatedDualRange } from 'ui/validated_range'; +import { ValidatedDualRange } from '../../../../../../../../src/plugins/kibana_react/public'; import { MAX_ZOOM, MIN_ZOOM } from '../../../../common/constants'; export function LayerSettings(props) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js index 1d5815a84920c..5de7b462136e1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js @@ -6,7 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { ValidatedDualRange } from 'ui/validated_range'; +import { ValidatedDualRange } from '../../../../../../../../../../src/plugins/kibana_react/public'; import { MIN_SIZE, MAX_SIZE } from '../../vector_style_defaults'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9b842f736b8bb..3fdcf9b815931 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -78,9 +78,6 @@ "messages": { "common.ui.aggResponse.allDocsTitle": "すべてのドキュメント", "common.ui.directives.paginate.size.allDropDownOptionLabel": "すべて", - "common.ui.dualRangeControl.mustSetBothErrorMessage": "下と上の値の両方を設定する必要があります", - "common.ui.dualRangeControl.outsideOfRangeErrorMessage": "値は {min} と {max} の間でなければなりません", - "common.ui.dualRangeControl.upperValidErrorMessage": "上の値は下の値以上でなければなりません", "common.ui.errorAutoCreateIndex.breadcrumbs.errorText": "エラー", "common.ui.errorAutoCreateIndex.errorDescription": "Elasticsearch クラスターの {autoCreateIndexActionConfig} 設定が原因で、Kibana が保存されたオブジェクトを格納するインデックスを自動的に作成できないようです。Kibana は、保存されたオブジェクトインデックスが適切なマッピング/スキーマを使用し Kibana から Elasticsearch へのポーリングの回数を減らすための最適な手段であるため、この Elasticsearch の機能を使用します。", "common.ui.errorAutoCreateIndex.errorDisclaimer": "申し訳ございませんが、この問題が解決されるまで Kibana で何も保存することができません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 97c30f180dfe1..1bcbcca055c32 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -78,9 +78,6 @@ "messages": { "common.ui.aggResponse.allDocsTitle": "所有文档", "common.ui.directives.paginate.size.allDropDownOptionLabel": "全部", - "common.ui.dualRangeControl.mustSetBothErrorMessage": "下限值和上限值都须设置", - "common.ui.dualRangeControl.outsideOfRangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", - "common.ui.dualRangeControl.upperValidErrorMessage": "上限值必须大于或等于下限值", "common.ui.errorAutoCreateIndex.breadcrumbs.errorText": "错误", "common.ui.errorAutoCreateIndex.errorDescription": "似乎 Elasticsearch 集群的 {autoCreateIndexActionConfig} 设置使 Kibana 无法自动创建用于存储已保存对象的索引。Kibana 将使用此 Elasticsearch 功能,因为这是确保已保存对象索引使用正确映射/架构的最好方式,而且其允许 Kibana 较少地轮询 Elasticsearch。", "common.ui.errorAutoCreateIndex.errorDisclaimer": "但是,只有解决了此问题后,您才能在 Kibana 保存内容。", From 69ec60d744893b86449a913fa65836f2f2dd6295 Mon Sep 17 00:00:00 2001 From: nnamdifrankie <56440728+nnamdifrankie@users.noreply.github.com> Date: Mon, 16 Mar 2020 17:18:49 -0400 Subject: [PATCH 23/76] EMT-248: implement ack resource to accept event payload to acknowledge agent actions (#60218) [Ingest]EMT-248: implement ack resource to accept event payload to acknowledge agent actions --- .../common/types/rest_spec/agent.ts | 7 +- .../server/routes/agent/acks_handlers.test.ts | 94 ++++++++++++ .../server/routes/agent/acks_handlers.ts | 69 +++++++++ .../server/routes/agent/handlers.ts | 36 +---- .../server/routes/agent/index.ts | 11 +- .../server/services/agents/acks.test.ts | 118 +++++++++++++++ .../server/services/agents/acks.ts | 99 +++++++++++-- .../server/services/agents/crud.ts | 2 +- .../server/types/models/agent.ts | 5 + .../server/types/rest_spec/agent.ts | 4 +- .../api_integration/apis/fleet/agents/acks.ts | 138 +++++++++++++++++- .../es_archives/fleet/agents/data.json | 12 ++ 12 files changed, 539 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index af919d973b7d9..7bbaf42422bb2 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -69,13 +69,18 @@ export interface PostAgentEnrollResponse { export interface PostAgentAcksRequest { body: { - action_ids: string[]; + events: AgentEvent[]; }; params: { agentId: string; }; } +export interface PostAgentAcksResponse { + action: string; + success: boolean; +} + export interface PostAgentUnenrollRequest { body: { kuery: string } | { ids: string[] }; } diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts new file mode 100644 index 0000000000000..84923d5c33664 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts @@ -0,0 +1,94 @@ +/* + * 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 { postAgentAcksHandlerBuilder } from './acks_handlers'; +import { + KibanaResponseFactory, + RequestHandlerContext, + SavedObjectsClientContract, +} from 'kibana/server'; +import { httpServerMock, savedObjectsClientMock } from '../../../../../../src/core/server/mocks'; +import { PostAgentAcksResponse } from '../../../common/types/rest_spec'; +import { AckEventSchema } from '../../types/models'; +import { AcksService } from '../../services/agents'; + +describe('test acks schema', () => { + it('validate that ack event schema expect action id', async () => { + expect(() => + AckEventSchema.validate({ + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + agent_id: 'agent', + message: 'hello', + payload: 'payload', + }) + ).toThrow(Error); + + expect( + AckEventSchema.validate({ + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + agent_id: 'agent', + action_id: 'actionId', + message: 'hello', + payload: 'payload', + }) + ).toBeTruthy(); + }); +}); + +describe('test acks handlers', () => { + let mockResponse: jest.Mocked; + let mockSavedObjectsClient: jest.Mocked; + + beforeEach(() => { + mockSavedObjectsClient = savedObjectsClientMock.create(); + mockResponse = httpServerMock.createResponseFactory(); + }); + + it('should succeed on valid agent event', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + headers: { + authorization: 'ApiKey TmVqTDBIQUJsRkw1em52R1ZIUF86NS1NaTItdHFUTHFHbThmQW1Fb0ljUQ==', + }, + body: { + events: [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'action1', + agent_id: 'agent', + message: 'message', + }, + ], + }, + }); + + const ackService: AcksService = { + acknowledgeAgentActions: jest.fn().mockReturnValueOnce([ + { + type: 'CONFIG_CHANGE', + id: 'action1', + }, + ]), + getAgentByAccessAPIKeyId: jest.fn().mockReturnValueOnce({ + id: 'agent', + }), + getSavedObjectsClientContract: jest.fn().mockReturnValueOnce(mockSavedObjectsClient), + saveAgentEvents: jest.fn(), + } as jest.Mocked; + + const postAgentAcksHandler = postAgentAcksHandlerBuilder(ackService); + await postAgentAcksHandler(({} as unknown) as RequestHandlerContext, mockRequest, mockResponse); + expect(mockResponse.ok.mock.calls[0][0]?.body as PostAgentAcksResponse).toEqual({ + action: 'acks', + success: true, + }); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts new file mode 100644 index 0000000000000..53b677bb1389e --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts @@ -0,0 +1,69 @@ +/* + * 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. + */ + +// handlers that handle events from agents in response to actions received + +import { RequestHandler } from 'kibana/server'; +import { TypeOf } from '@kbn/config-schema'; +import { PostAgentAcksRequestSchema } from '../../types/rest_spec'; +import * as APIKeyService from '../../services/api_keys'; +import { AcksService } from '../../services/agents'; +import { AgentEvent } from '../../../common/types/models'; +import { PostAgentAcksResponse } from '../../../common/types/rest_spec'; + +export const postAgentAcksHandlerBuilder = function( + ackService: AcksService +): RequestHandler< + TypeOf, + undefined, + TypeOf +> { + return async (context, request, response) => { + try { + const soClient = ackService.getSavedObjectsClientContract(request); + const res = APIKeyService.parseApiKey(request.headers); + const agent = await ackService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId as string); + const agentEvents = request.body.events as AgentEvent[]; + + // validate that all events are for the authorized agent obtained from the api key + const notAuthorizedAgentEvent = agentEvents.filter( + agentEvent => agentEvent.agent_id !== agent.id + ); + + if (notAuthorizedAgentEvent && notAuthorizedAgentEvent.length > 0) { + return response.badRequest({ + body: + 'agent events contains events with different agent id from currently authorized agent', + }); + } + + const agentActions = await ackService.acknowledgeAgentActions(soClient, agent, agentEvents); + + if (agentActions.length > 0) { + await ackService.saveAgentEvents(soClient, agentEvents); + } + + const body: PostAgentAcksResponse = { + action: 'acks', + success: true, + }; + + return response.ok({ body }); + } catch (e) { + if (e.isBoom) { + return response.customError({ + statusCode: e.output.statusCode, + body: { message: e.message }, + }); + } + + return response.customError({ + statusCode: 500, + body: { message: e.message }, + }); + } + }; +}; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index cb4e4d557d74f..cf1fd2476f310 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -23,7 +23,6 @@ import { GetOneAgentEventsRequestSchema, PostAgentCheckinRequestSchema, PostAgentEnrollRequestSchema, - PostAgentAcksRequestSchema, PostAgentUnenrollRequestSchema, GetAgentStatusRequestSchema, } from '../../types'; @@ -31,7 +30,7 @@ import * as AgentService from '../../services/agents'; import * as APIKeyService from '../../services/api_keys'; import { appContextService } from '../../services/app_context'; -function getInternalUserSOClient(request: KibanaRequest) { +export function getInternalUserSOClient(request: KibanaRequest) { // soClient as kibana internal users, be carefull on how you use it, security is not enabled return appContextService.getSavedObjects().getScopedClient(request, { excludedWrappers: ['security'], @@ -210,39 +209,6 @@ export const postAgentCheckinHandler: RequestHandler< } }; -export const postAgentAcksHandler: RequestHandler< - TypeOf, - undefined, - TypeOf -> = async (context, request, response) => { - try { - const soClient = getInternalUserSOClient(request); - const res = APIKeyService.parseApiKey(request.headers); - const agent = await AgentService.getAgentByAccessAPIKeyId(soClient, res.apiKeyId as string); - - await AgentService.acknowledgeAgentActions(soClient, agent, request.body.action_ids); - - const body = { - action: 'acks', - success: true, - }; - - return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); - } -}; - export const postAgentEnrollHandler: RequestHandler< undefined, undefined, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index 8a65fa9c50e8b..c85629ea22ad9 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -31,10 +31,12 @@ import { getAgentEventsHandler, postAgentCheckinHandler, postAgentEnrollHandler, - postAgentAcksHandler, postAgentsUnenrollHandler, getAgentStatusForConfigHandler, + getInternalUserSOClient, } from './handlers'; +import { postAgentAcksHandlerBuilder } from './acks_handlers'; +import * as AgentService from '../../services/agents'; export const registerRoutes = (router: IRouter) => { // Get one @@ -101,7 +103,12 @@ export const registerRoutes = (router: IRouter) => { validate: PostAgentAcksRequestSchema, options: { tags: [] }, }, - postAgentAcksHandler + postAgentAcksHandlerBuilder({ + acknowledgeAgentActions: AgentService.acknowledgeAgentActions, + getAgentByAccessAPIKeyId: AgentService.getAgentByAccessAPIKeyId, + getSavedObjectsClientContract: getInternalUserSOClient, + saveAgentEvents: AgentService.saveAgentEvents, + }) ); router.post( diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts new file mode 100644 index 0000000000000..3c07463e3af5d --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts @@ -0,0 +1,118 @@ +/* + * 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 { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; +import { Agent, AgentAction, AgentEvent } from '../../../common/types/models'; +import { AGENT_TYPE_PERMANENT } from '../../../common/constants'; +import { acknowledgeAgentActions } from './acks'; +import { isBoom } from 'boom'; + +describe('test agent acks services', () => { + it('should succeed on valid and matched actions', async () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + const agentActions = await acknowledgeAgentActions( + mockSavedObjectsClient, + ({ + id: 'id', + type: AGENT_TYPE_PERMANENT, + actions: [ + { + type: 'CONFIG_CHANGE', + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + ], + } as unknown) as Agent, + [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'action1', + agent_id: 'id', + } as AgentEvent, + ] + ); + expect(agentActions).toEqual([ + ({ + type: 'CONFIG_CHANGE', + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + } as unknown) as AgentAction, + ]); + }); + + it('should fail for actions that cannot be found on agent actions list', async () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + try { + await acknowledgeAgentActions( + mockSavedObjectsClient, + ({ + id: 'id', + type: AGENT_TYPE_PERMANENT, + actions: [ + { + type: 'CONFIG_CHANGE', + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + ], + } as unknown) as Agent, + [ + ({ + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'action2', + agent_id: 'id', + } as unknown) as AgentEvent, + ] + ); + expect(true).toBeFalsy(); + } catch (e) { + expect(isBoom(e)).toBeTruthy(); + } + }); + + it('should fail for events that have types not in the allowed acknowledgement type list', async () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + try { + await acknowledgeAgentActions( + mockSavedObjectsClient, + ({ + id: 'id', + type: AGENT_TYPE_PERMANENT, + actions: [ + { + type: 'CONFIG_CHANGE', + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + ], + } as unknown) as Agent, + [ + ({ + type: 'ACTION', + subtype: 'FAILED', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'action1', + agent_id: 'id', + } as unknown) as AgentEvent, + ] + ); + expect(true).toBeFalsy(); + } catch (e) { + expect(isBoom(e)).toBeTruthy(); + } + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index 1732ff9cf5b5c..892d8cdbe657f 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -4,25 +4,100 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; -import { Agent, AgentSOAttributes } from '../../types'; -import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { + KibanaRequest, + SavedObjectsBulkCreateObject, + SavedObjectsBulkResponse, + SavedObjectsClientContract, +} from 'kibana/server'; +import Boom from 'boom'; +import { + Agent, + AgentAction, + AgentEvent, + AgentEventSOAttributes, + AgentSOAttributes, +} from '../../types'; +import { AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; + +const ALLOWED_ACKNOWLEDGEMENT_TYPE: string[] = ['ACTION_RESULT']; export async function acknowledgeAgentActions( soClient: SavedObjectsClientContract, agent: Agent, - actionIds: string[] -) { + agentEvents: AgentEvent[] +): Promise { const now = new Date().toISOString(); - const updatedActions = agent.actions.map(action => { - if (action.sent_at) { - return action; + const agentActionMap: Map = new Map( + agent.actions.map(agentAction => [agentAction.id, agentAction]) + ); + + const matchedUpdatedActions: AgentAction[] = []; + + agentEvents.forEach(agentEvent => { + if (!isAllowedType(agentEvent.type)) { + throw Boom.badRequest(`${agentEvent.type} not allowed for acknowledgment only ACTION_RESULT`); + } + if (agentActionMap.has(agentEvent.action_id!)) { + const action = agentActionMap.get(agentEvent.action_id!) as AgentAction; + if (!action.sent_at) { + action.sent_at = now; + } + matchedUpdatedActions.push(action); + } else { + throw Boom.badRequest('all actions should belong to current agent'); } - return { ...action, sent_at: actionIds.indexOf(action.id) >= 0 ? now : undefined }; }); - await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { - actions: updatedActions, - }); + if (matchedUpdatedActions.length > 0) { + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { + actions: matchedUpdatedActions, + }); + } + + return matchedUpdatedActions; +} + +function isAllowedType(eventType: string): boolean { + return ALLOWED_ACKNOWLEDGEMENT_TYPE.indexOf(eventType) >= 0; +} + +export async function saveAgentEvents( + soClient: SavedObjectsClientContract, + events: AgentEvent[] +): Promise> { + const objects: Array> = events.map( + eventData => { + return { + attributes: { + ...eventData, + payload: eventData.payload ? JSON.stringify(eventData.payload) : undefined, + }, + type: AGENT_EVENT_SAVED_OBJECT_TYPE, + }; + } + ); + + return await soClient.bulkCreate(objects); +} + +export interface AcksService { + acknowledgeAgentActions: ( + soClient: SavedObjectsClientContract, + agent: Agent, + actionIds: AgentEvent[] + ) => Promise; + + getAgentByAccessAPIKeyId: ( + soClient: SavedObjectsClientContract, + accessAPIKeyId: string + ) => Promise; + + getSavedObjectsClientContract: (kibanaRequest: KibanaRequest) => SavedObjectsClientContract; + + saveAgentEvents: ( + soClient: SavedObjectsClientContract, + events: AgentEvent[] + ) => Promise>; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts index bcd825fee8725..cdbdf164e834d 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts @@ -68,7 +68,7 @@ export async function getAgent(soClient: SavedObjectsClientContract, agentId: st export async function getAgentByAccessAPIKeyId( soClient: SavedObjectsClientContract, accessAPIKeyId: string -) { +): Promise { const response = await soClient.find({ type: AGENT_SAVED_OBJECT_TYPE, searchFields: ['access_api_key_id'], diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index 276dddf9e3d1c..e0d252faaaf87 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -44,6 +44,11 @@ const AgentEventBase = { stream_id: schema.maybe(schema.string()), }; +export const AckEventSchema = schema.object({ + ...AgentEventBase, + ...{ action_id: schema.string() }, +}); + export const AgentEventSchema = schema.object({ ...AgentEventBase, }); diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index 92422274d5cf4..9fe84c12521ad 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -5,7 +5,7 @@ */ import { schema } from '@kbn/config-schema'; -import { AgentEventSchema, AgentTypeSchema } from '../models'; +import { AckEventSchema, AgentEventSchema, AgentTypeSchema } from '../models'; export const GetAgentsRequestSchema = { query: schema.object({ @@ -45,7 +45,7 @@ export const PostAgentEnrollRequestSchema = { export const PostAgentAcksRequestSchema = { body: schema.object({ - action_ids: schema.arrayOf(schema.string()), + events: schema.arrayOf(AckEventSchema), }), params: schema.object({ agentId: schema.string(), diff --git a/x-pack/test/api_integration/apis/fleet/agents/acks.ts b/x-pack/test/api_integration/apis/fleet/agents/acks.ts index 1ab54554d62f0..a2eba2c23c39d 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/acks.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/acks.ts @@ -59,7 +59,7 @@ export default function(providerContext: FtrProviderContext) { .expect(401); }); - it('should return a 200 if this a valid acks access', async () => { + it('should return a 200 if this a valid acks request', async () => { const { body: apiResponse } = await supertest .post(`/api/ingest_manager/fleet/agents/agent1/acks`) .set('kbn-xsrf', 'xx') @@ -68,12 +68,144 @@ export default function(providerContext: FtrProviderContext) { `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` ) .send({ - action_ids: ['action1'], + events: [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-05T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a2', + agent_id: 'agent1', + message: 'hello2', + payload: 'payload2', + }, + ], }) .expect(200); - expect(apiResponse.action).to.be('acks'); expect(apiResponse.success).to.be(true); + const { body: eventResponse } = await supertest + .get(`/api/ingest_manager/fleet/agents/agent1/events`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .expect(200); + const expectedEvents = eventResponse.list.filter( + (item: Record) => + item.action_id === '48cebde1-c906-4893-b89f-595d943b72a1' || + item.action_id === '48cebde1-c906-4893-b89f-595d943b72a2' + ); + expect(expectedEvents.length).to.eql(2); + const expectedEvent = expectedEvents.find( + (item: Record) => item.action_id === '48cebde1-c906-4893-b89f-595d943b72a1' + ); + expect(expectedEvent).to.eql({ + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }); + }); + + it('should return a 400 when request event list contains event for another agent id', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/acks`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + events: [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent2', + message: 'hello', + payload: 'payload', + }, + ], + }) + .expect(400); + expect(apiResponse.message).to.eql( + 'agent events contains events with different agent id from currently authorized agent' + ); + }); + + it('should return a 400 when request event list contains action that does not belong to agent current actions', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/acks`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + events: [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'does-not-exist', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + ], + }) + .expect(400); + expect(apiResponse.message).to.eql('all actions should belong to current agent'); + }); + + it('should return a 400 when request event list contains action types that are not allowed for acknowledgement', async () => { + const { body: apiResponse } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/acks`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + events: [ + { + type: 'ACTION', + subtype: 'FAILED', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: '48cebde1-c906-4893-b89f-595d943b72a1', + agent_id: 'agent1', + message: 'hello', + payload: 'payload', + }, + ], + }) + .expect(400); + expect(apiResponse.message).to.eql( + 'ACTION not allowed for acknowledgment only ACTION_RESULT' + ); }); }); } diff --git a/x-pack/test/functional/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index 36928018d15a0..9b29767d5162d 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -23,6 +23,18 @@ "type": "PAUSE", "created_at": "2019-09-04T15:01:07+0000", "sent_at": "2019-09-04T15:03:07+0000" + }, + { + "created_at" : "2020-03-15T03:47:15.129Z", + "id" : "48cebde1-c906-4893-b89f-595d943b72a1", + "type" : "CONFIG_CHANGE", + "sent_at": "2020-03-04T15:03:07+0000" + }, + { + "created_at" : "2020-03-16T03:47:15.129Z", + "id" : "48cebde1-c906-4893-b89f-595d943b72a2", + "type" : "CONFIG_CHANGE", + "sent_at": "2020-03-04T15:03:07+0000" }] } } From 132383c28ca53e9e348863e198bf01300d39d122 Mon Sep 17 00:00:00 2001 From: Eric Davis Date: Mon, 16 Mar 2020 17:25:14 -0400 Subject: [PATCH 24/76] [Endpoint] TEST: verify alerts page header says 'Alerts' (#60206) * test to verify alerts page header says alerts * updating test with pr feedback * updating test with pr feedback and better verbiage * updating test with pr feedback for better test titling Co-authored-by: Elastic Machine --- .../public/applications/endpoint/view/alerts/index.tsx | 2 +- x-pack/test/functional/apps/endpoint/alert_list.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx index 9718b4e4ef8cd..b900a0a35dbf5 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -233,7 +233,7 @@ export const AlertIndex = memo(() => { -

+

{ await esArchiver.load('endpoint/alerts/api_feature'); await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/alerts'); }); - it('loads the Alert List Page', async () => { + it('loads in the browser', async () => { await testSubjects.existOrFail('alertListPage'); }); + it('contains the Alert List Page title', async () => { + const alertsTitle = await testSubjects.getVisibleText('alertsViewTitle'); + expect(alertsTitle).to.equal('Alerts'); + }); it('includes alerts search bar', async () => { await testSubjects.existOrFail('alertsSearchBar'); }); From 537fa8c1eb6b959625ddc0ec9599827ad07e0635 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Mon, 16 Mar 2020 14:26:47 -0700 Subject: [PATCH 25/76] [Reporting] Fix error handling for job handler in route (#60161) * fix bogus rison error * add generate route test * update test name --- .../server/routes/generate_from_jobparams.ts | 9 +- .../server/routes/generation.test.ts | 140 ++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 x-pack/legacy/plugins/reporting/server/routes/generation.test.ts diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts index 49868bb7ad5d5..56622617586f7 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts @@ -82,16 +82,21 @@ export function registerGenerateFromJobParams( } const { exportType } = request.params; + let jobParams; let response; try { - const jobParams = rison.decode(jobParamsRison) as object | null; + jobParams = rison.decode(jobParamsRison) as object | null; if (!jobParams) { throw new Error('missing jobParams!'); } - response = await handler(exportType, jobParams, legacyRequest, h); } catch (err) { throw boom.badRequest(`invalid rison: ${jobParamsRison}`); } + try { + response = await handler(exportType, jobParams, legacyRequest, h); + } catch (err) { + throw handleError(exportType, err); + } return response; }, }); diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.test.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.test.ts new file mode 100644 index 0000000000000..54d9671692c5d --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.test.ts @@ -0,0 +1,140 @@ +/* + * 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 Hapi from 'hapi'; +import { createMockReportingCore } from '../../test_helpers'; +import { Logger, ServerFacade } from '../../types'; +import { ReportingCore, ReportingSetupDeps } from '../../server/types'; + +jest.mock('./lib/authorized_user_pre_routing', () => ({ + authorizedUserPreRoutingFactory: () => () => ({}), +})); +jest.mock('./lib/reporting_feature_pre_routing', () => ({ + reportingFeaturePreRoutingFactory: () => () => () => ({ + jobTypes: ['unencodedJobType', 'base64EncodedJobType'], + }), +})); + +import { registerJobGenerationRoutes } from './generation'; + +let mockServer: Hapi.Server; +let mockReportingPlugin: ReportingCore; +const mockLogger = ({ + error: jest.fn(), + debug: jest.fn(), +} as unknown) as Logger; + +beforeEach(async () => { + mockServer = new Hapi.Server({ + debug: false, + port: 8080, + routes: { log: { collect: true } }, + }); + mockServer.config = () => ({ get: jest.fn(), has: jest.fn() }); + mockReportingPlugin = await createMockReportingCore(); + mockReportingPlugin.getEnqueueJob = async () => + jest.fn().mockImplementation(() => ({ toJSON: () => '{ "job": "data" }' })); +}); + +const mockPlugins = { + elasticsearch: { + adminClient: { callAsInternalUser: jest.fn() }, + }, + security: null, +}; + +const getErrorsFromRequest = (request: Hapi.Request) => { + // @ts-ignore error property doesn't exist on RequestLog + return request.logs.filter(log => log.tags.includes('error')).map(log => log.error); // NOTE: error stack is available +}; + +test(`returns 400 if there are no job params`, async () => { + registerJobGenerationRoutes( + mockReportingPlugin, + (mockServer as unknown) as ServerFacade, + (mockPlugins as unknown) as ReportingSetupDeps, + mockLogger + ); + + const options = { + method: 'POST', + url: '/api/reporting/generate/printablePdf', + }; + + const { payload, request } = await mockServer.inject(options); + expect(payload).toMatchInlineSnapshot( + `"{\\"statusCode\\":400,\\"error\\":\\"Bad Request\\",\\"message\\":\\"A jobParams RISON string is required\\"}"` + ); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: A jobParams RISON string is required], + ] + `); +}); + +test(`returns 400 if job params is invalid`, async () => { + registerJobGenerationRoutes( + mockReportingPlugin, + (mockServer as unknown) as ServerFacade, + (mockPlugins as unknown) as ReportingSetupDeps, + mockLogger + ); + + const options = { + method: 'POST', + url: '/api/reporting/generate/printablePdf', + payload: { jobParams: `foo:` }, + }; + + const { payload, request } = await mockServer.inject(options); + expect(payload).toMatchInlineSnapshot( + `"{\\"statusCode\\":400,\\"error\\":\\"Bad Request\\",\\"message\\":\\"invalid rison: foo:\\"}"` + ); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: invalid rison: foo:], + ] + `); +}); + +test(`returns 500 if job handler throws an error`, async () => { + mockReportingPlugin.getEnqueueJob = async () => + jest.fn().mockImplementation(() => ({ + toJSON: () => { + throw new Error('you found me'); + }, + })); + + registerJobGenerationRoutes( + mockReportingPlugin, + (mockServer as unknown) as ServerFacade, + (mockPlugins as unknown) as ReportingSetupDeps, + mockLogger + ); + + const options = { + method: 'POST', + url: '/api/reporting/generate/printablePdf', + payload: { jobParams: `abc` }, + }; + + const { payload, request } = await mockServer.inject(options); + expect(payload).toMatchInlineSnapshot( + `"{\\"statusCode\\":500,\\"error\\":\\"Internal Server Error\\",\\"message\\":\\"An internal server error occurred\\"}"` + ); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: you found me], + [Error: you found me], + ] + `); +}); From ef3261132a43342c4709efdba235a2cdb9ed1e98 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 16 Mar 2020 15:40:52 -0600 Subject: [PATCH 26/76] [Maps] move MapSavedObject type out of telemetry (#60127) * [Maps] move MapSavedObject type out of telemetry * move SavedObject from server to core/types * review feedback * results from check_published_api_changes --- .../kibana-plugin-core-public.savedobject.md | 1 - .../kibana-plugin-core-server.savedobject.md | 1 - src/core/public/public.api.md | 2 + src/core/public/saved_objects/index.ts | 13 ++-- src/core/server/saved_objects/types.ts | 59 ++----------------- src/core/server/server.api.md | 2 + src/core/types/saved_objects.ts | 51 ++++++++++++++++ .../server/maps_telemetry/maps_telemetry.ts | 32 +--------- .../maps/common/map_saved_object_type.ts | 22 +++++++ 9 files changed, 93 insertions(+), 90 deletions(-) create mode 100644 x-pack/plugins/maps/common/map_saved_object_type.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobject.md b/docs/development/core/public/kibana-plugin-core-public.savedobject.md index 9ced619ad4bfe..c6bc13b98bc06 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobject.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobject.md @@ -4,7 +4,6 @@ ## SavedObject interface - Signature: ```typescript diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobject.md index ebb105c846aff..0df97b0d4221a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobject.md @@ -4,7 +4,6 @@ ## SavedObject interface - Signature: ```typescript diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 46667230edc3b..fa5dc745e6931 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -944,6 +944,8 @@ export type RecursiveReadonly = T extends (...args: any[]) => any ? T : T ext [K in keyof T]: RecursiveReadonly; }> : T; +// Warning: (ae-missing-release-tag) "SavedObject" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SavedObject { attributes: T; diff --git a/src/core/public/saved_objects/index.ts b/src/core/public/saved_objects/index.ts index 5015a9c3db78e..13b4a12893666 100644 --- a/src/core/public/saved_objects/index.ts +++ b/src/core/public/saved_objects/index.ts @@ -32,11 +32,6 @@ export { export { SimpleSavedObject } from './simple_saved_object'; export { SavedObjectsStart, SavedObjectsService } from './saved_objects_service'; export { - SavedObject, - SavedObjectAttribute, - SavedObjectAttributes, - SavedObjectAttributeSingle, - SavedObjectReference, SavedObjectsBaseOptions, SavedObjectsFindOptions, SavedObjectsMigrationVersion, @@ -48,3 +43,11 @@ export { SavedObjectsImportError, SavedObjectsImportRetry, } from '../../server/types'; + +export { + SavedObject, + SavedObjectAttribute, + SavedObjectAttributes, + SavedObjectAttributeSingle, + SavedObjectReference, +} from '../../types'; diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 1d927211b43e5..962965a08f8b2 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -35,66 +35,17 @@ export { import { LegacyConfig } from '../legacy'; import { SavedObjectUnsanitizedDoc } from './serialization'; import { SavedObjectsMigrationLogger } from './migrations/core/migration_logger'; +import { SavedObject } from '../../types'; + export { SavedObjectAttributes, SavedObjectAttribute, SavedObjectAttributeSingle, + SavedObject, + SavedObjectReference, + SavedObjectsMigrationVersion, } from '../../types'; -/** - * Information about the migrations that have been applied to this SavedObject. - * When Kibana starts up, KibanaMigrator detects outdated documents and - * migrates them based on this value. For each migration that has been applied, - * the plugin's name is used as a key and the latest migration version as the - * value. - * - * @example - * migrationVersion: { - * dashboard: '7.1.1', - * space: '6.6.6', - * } - * - * @public - */ -export interface SavedObjectsMigrationVersion { - [pluginName: string]: string; -} - -/** - * @public - */ -export interface SavedObject { - /** The ID of this Saved Object, guaranteed to be unique for all objects of the same `type` */ - id: string; - /** The type of Saved Object. Each plugin can define it's own custom Saved Object types. */ - type: string; - /** An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control. */ - version?: string; - /** Timestamp of the last time this document had been updated. */ - updated_at?: string; - error?: { - message: string; - statusCode: number; - }; - /** {@inheritdoc SavedObjectAttributes} */ - attributes: T; - /** {@inheritdoc SavedObjectReference} */ - references: SavedObjectReference[]; - /** {@inheritdoc SavedObjectsMigrationVersion} */ - migrationVersion?: SavedObjectsMigrationVersion; -} - -/** - * A reference to another saved object. - * - * @public - */ -export interface SavedObjectReference { - name: string; - type: string; - id: string; -} - /** * * @public diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 84fe081adaae6..229ffc4d21575 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1595,6 +1595,8 @@ export interface RouteValidatorOptions { // @public export type SafeRouteMethod = 'get' | 'options'; +// Warning: (ae-missing-release-tag) "SavedObject" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SavedObject { attributes: T; diff --git a/src/core/types/saved_objects.ts b/src/core/types/saved_objects.ts index 73eb2db11d62f..d3faab6c557cd 100644 --- a/src/core/types/saved_objects.ts +++ b/src/core/types/saved_objects.ts @@ -46,3 +46,54 @@ export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttri export interface SavedObjectAttributes { [key: string]: SavedObjectAttribute; } + +/** + * A reference to another saved object. + * + * @public + */ +export interface SavedObjectReference { + name: string; + type: string; + id: string; +} + +/** + * Information about the migrations that have been applied to this SavedObject. + * When Kibana starts up, KibanaMigrator detects outdated documents and + * migrates them based on this value. For each migration that has been applied, + * the plugin's name is used as a key and the latest migration version as the + * value. + * + * @example + * migrationVersion: { + * dashboard: '7.1.1', + * space: '6.6.6', + * } + * + * @public + */ +export interface SavedObjectsMigrationVersion { + [pluginName: string]: string; +} + +export interface SavedObject { + /** The ID of this Saved Object, guaranteed to be unique for all objects of the same `type` */ + id: string; + /** The type of Saved Object. Each plugin can define it's own custom Saved Object types. */ + type: string; + /** An opaque version number which changes on each successful write operation. Can be used for implementing optimistic concurrency control. */ + version?: string; + /** Timestamp of the last time this document had been updated. */ + updated_at?: string; + error?: { + message: string; + statusCode: number; + }; + /** {@inheritdoc SavedObjectAttributes} */ + attributes: T; + /** {@inheritdoc SavedObjectReference} */ + references: SavedObjectReference[]; + /** {@inheritdoc SavedObjectsMigrationVersion} */ + migrationVersion?: SavedObjectsMigrationVersion; +} diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 3ce46e2955f50..6a363af9e57d4 100644 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -19,6 +19,7 @@ import { // @ts-ignore } from '../../common/constants'; import { LayerDescriptor } from '../../common/descriptor_types'; +import { MapSavedObject } from '../../../../../plugins/maps/common/map_saved_object_type'; interface IStats { [key: string]: { @@ -32,33 +33,6 @@ interface ILayerTypeCount { [key: string]: number; } -interface IMapSavedObject { - [key: string]: any; - fields: IFieldType[]; - title: string; - id?: string; - type?: string; - timeFieldName?: string; - fieldFormatMap?: Record< - string, - { - id: string; - params: unknown; - } - >; - attributes?: { - title?: string; - description?: string; - mapStateJSON?: string; - layerListJSON?: string; - uiStateJSON?: string; - bounds?: { - type?: string; - coordinates?: []; - }; - }; -} - function getUniqueLayerCounts(layerCountsList: ILayerTypeCount[], mapsCount: number) { const uniqueLayerTypes = _.uniq(_.flatten(layerCountsList.map(lTypes => Object.keys(lTypes)))); @@ -102,7 +76,7 @@ export function buildMapsTelemetry({ indexPatternSavedObjects, settings, }: { - mapSavedObjects: IMapSavedObject[]; + mapSavedObjects: MapSavedObject[]; indexPatternSavedObjects: IIndexPattern[]; settings: SavedObjectAttribute; }): SavedObjectAttributes { @@ -183,7 +157,7 @@ export async function getMapsTelemetry( savedObjectsClient: SavedObjectsClientContract, config: Function ) { - const mapSavedObjects: IMapSavedObject[] = await getMapSavedObjects(savedObjectsClient); + const mapSavedObjects: MapSavedObject[] = await getMapSavedObjects(savedObjectsClient); const indexPatternSavedObjects: IIndexPattern[] = await getIndexPatternSavedObjects( savedObjectsClient ); diff --git a/x-pack/plugins/maps/common/map_saved_object_type.ts b/x-pack/plugins/maps/common/map_saved_object_type.ts new file mode 100644 index 0000000000000..e5b4876186fd8 --- /dev/null +++ b/x-pack/plugins/maps/common/map_saved_object_type.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +import { SavedObject } from '../../../../src/core/types/saved_objects'; + +export type MapSavedObjectAttributes = { + title?: string; + description?: string; + mapStateJSON?: string; + layerListJSON?: string; + uiStateJSON?: string; + bounds?: { + type?: string; + coordinates?: []; + }; +}; + +export type MapSavedObject = SavedObject; From 4a8fd0afee9262916348c51e98f2ac955e25b2ac Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 16 Mar 2020 15:58:53 -0700 Subject: [PATCH 27/76] Revert "adds new test (#60064)" This reverts commit a946adbf10b0148bf510b0588713dd7a2a04579f. --- .../cypress/integration/detections.spec.ts | 43 +------------------ .../siem/cypress/screens/detections.ts | 4 +- .../plugins/siem/cypress/tasks/detections.ts | 7 --- 3 files changed, 2 insertions(+), 52 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts index c048510c50c36..1624586d4ca14 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -5,14 +5,12 @@ */ import { NUMBER_OF_SIGNALS, - OPEN_CLOSE_SIGNALS_BTN, SELECTED_SIGNALS, SHOWING_SIGNALS, SIGNALS, } from '../screens/detections'; import { - closeFirstSignal, closeSignals, goToClosedSignals, goToOpenedSignals, @@ -28,7 +26,7 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Detections', () => { - beforeEach(() => { + before(() => { esArchiverLoad('signals'); loginAndWaitForPage(DETECTIONS); }); @@ -113,43 +111,4 @@ describe('Detections', () => { .should('eql', expectedNumberOfOpenedSignals.toString()); }); }); - - it('Closes one signal when more than one opened signals are selected', () => { - waitForSignalsToBeLoaded(); - - cy.get(NUMBER_OF_SIGNALS) - .invoke('text') - .then(numberOfSignals => { - const numberOfSignalsToBeClosed = 1; - const numberOfSignalsToBeSelected = 3; - - cy.get(OPEN_CLOSE_SIGNALS_BTN).should('have.attr', 'disabled'); - selectNumberOfSignals(numberOfSignalsToBeSelected); - cy.get(OPEN_CLOSE_SIGNALS_BTN).should('not.have.attr', 'disabled'); - - closeFirstSignal(); - cy.reload(); - waitForSignalsToBeLoaded(); - waitForSignals(); - - const expectedNumberOfSignals = +numberOfSignals - numberOfSignalsToBeClosed; - cy.get(NUMBER_OF_SIGNALS) - .invoke('text') - .should('eq', expectedNumberOfSignals.toString()); - cy.get(SHOWING_SIGNALS) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfSignals.toString()} signals`); - - goToClosedSignals(); - waitForSignals(); - - cy.get(NUMBER_OF_SIGNALS) - .invoke('text') - .should('eql', numberOfSignalsToBeClosed.toString()); - cy.get(SHOWING_SIGNALS) - .invoke('text') - .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signal`); - cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); - }); - }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index f388ac1215d01..8b5ba23578807 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -12,9 +12,7 @@ export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; -export const OPEN_CLOSE_SIGNAL_BTN = '[data-test-subj="update-signal-status-button"]'; - -export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] button'; +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 3416e3eb81de3..21a0c136b90df 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -8,7 +8,6 @@ import { CLOSED_SIGNALS_BTN, LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN, - OPEN_CLOSE_SIGNAL_BTN, OPEN_CLOSE_SIGNALS_BTN, OPENED_SIGNALS_BTN, SIGNALS, @@ -16,12 +15,6 @@ import { } from '../screens/detections'; import { REFRESH_BUTTON } from '../screens/siem_header'; -export const closeFirstSignal = () => { - cy.get(OPEN_CLOSE_SIGNAL_BTN) - .first() - .click({ force: true }); -}; - export const closeSignals = () => { cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); }; From 4ebdc4edadc5def675c6ddca1444695257976b66 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Mon, 16 Mar 2020 17:02:41 -0700 Subject: [PATCH 28/76] Added message variables button for Webhook body form field (#60174) * Added message variables button for Webhook body form field * Fixed test issue --- .../builtin_action_types/webhook.test.tsx | 1 + .../builtin_action_types/webhook.tsx | 43 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx index 5e6c0404db532..fdb60bd2d9146 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx @@ -160,6 +160,7 @@ describe('WebhookParamsFields renders', () => { .first() .prop('value') ).toStrictEqual('test message'); + expect(wrapper.find('[data-test-subj="webhookAddVariableButton"]').length > 0).toBeTruthy(); }); test('params validation fails when body is not valid', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx index 8625487282880..5d07483c8a989 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.tsx @@ -22,6 +22,9 @@ import { EuiCodeEditor, EuiSwitch, EuiButtonEmpty, + EuiContextMenuItem, + EuiPopover, + EuiContextMenuPanel, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { @@ -454,10 +457,24 @@ const WebhookParamsFields: React.FunctionComponent { const { body } = actionParams; - + const [isVariablesPopoverOpen, setIsVariablesPopoverOpen] = useState(false); + const messageVariablesItems = messageVariables?.map((variable: string, i: number) => ( + { + editAction('body', (body ?? '').concat(` {{${variable}}}`), index); + setIsVariablesPopoverOpen(false); + }} + > + {`{{${variable}}}`} + + )); return ( 0 && body !== undefined} fullWidth error={errors.body} + labelAppend={ + // TODO: replace this button with a proper Eui component, when it will be ready + setIsVariablesPopoverOpen(true)} + iconType="indexOpen" + aria-label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addVariablePopoverButton', + { + defaultMessage: 'Add variable', + } + )} + /> + } + isOpen={isVariablesPopoverOpen} + closePopover={() => setIsVariablesPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + } > Date: Mon, 16 Mar 2020 17:05:11 -0700 Subject: [PATCH 29/76] [EPM] Packages list tabs (#60167) * Memo'ize some layout and EPM header components * Split EPM home page into two tabs * Clean up dead files and import paths * Add empty state * Use react routing for rendering tab content * Fix import paths (again) --- .../common/types/models/agent.ts | 2 +- .../common/types/models/agent_config.ts | 2 +- .../common/types/models/enrollment_api_key.ts | 2 +- .../ingest_manager/common/types/models/epm.ts | 6 +- .../ingest_manager/components/header.tsx | 14 +- .../ingest_manager/constants/index.ts | 2 + .../epm => }/hooks/use_breadcrumbs.tsx | 4 +- .../ingest_manager/hooks/use_core.ts | 2 +- .../hooks/use_request/agent_config.ts | 2 +- .../ingest_manager/hooks/use_request/epm.ts | 2 +- .../hooks/use_request/use_request.ts | 2 +- .../applications/ingest_manager/index.tsx | 2 +- .../epm/components/package_list_grid.tsx | 88 ++++++- .../sections/epm/hooks/index.tsx | 3 +- .../sections/epm/hooks/use_local_search.tsx | 27 +++ .../ingest_manager/sections/epm/index.tsx | 6 +- .../sections/epm/screens/detail/header.tsx | 2 - .../epm/screens/home/category_facets.tsx | 29 ++- .../sections/epm/screens/home/header.tsx | 15 +- .../sections/epm/screens/home/hooks.tsx | 47 ---- .../sections/epm/screens/home/index.tsx | 218 ++++++++++-------- x-pack/plugins/ingest_manager/public/index.ts | 2 +- .../plugins/ingest_manager/public/plugin.ts | 2 +- x-pack/plugins/ingest_manager/server/index.ts | 2 +- .../plugins/ingest_manager/server/plugin.ts | 2 +- .../server/routes/agent/handlers.ts | 2 +- .../server/routes/agent/index.ts | 2 +- .../server/routes/agent_config/handlers.ts | 2 +- .../server/routes/agent_config/index.ts | 2 +- .../server/routes/datasource/handlers.ts | 2 +- .../server/routes/datasource/index.ts | 2 +- .../routes/enrollment_api_key/handler.ts | 2 +- .../server/routes/enrollment_api_key/index.ts | 2 +- .../server/routes/epm/handlers.ts | 2 +- .../ingest_manager/server/routes/epm/index.ts | 2 +- .../server/routes/install_script/index.ts | 2 +- .../server/routes/setup/handlers.ts | 2 +- .../server/routes/setup/index.ts | 2 +- .../server/services/agent_config.ts | 2 +- .../server/services/agent_config_update.ts | 2 +- .../server/services/agents/acks.ts | 2 +- .../server/services/agents/checkin.ts | 2 +- .../server/services/agents/crud.ts | 2 +- .../server/services/agents/enroll.ts | 2 +- .../server/services/agents/events.ts | 2 +- .../server/services/agents/saved_objects.ts | 2 +- .../server/services/agents/status.ts | 2 +- .../server/services/agents/unenroll.ts | 2 +- .../server/services/agents/update.ts | 2 +- .../services/api_keys/enrollment_api_key.ts | 2 +- .../server/services/api_keys/index.ts | 2 +- .../server/services/api_keys/security.ts | 2 +- .../server/services/app_context.ts | 2 +- .../server/services/datasource.ts | 2 +- .../epm/kibana/index_pattern/install.ts | 2 +- .../server/services/epm/packages/get.ts | 2 +- .../services/epm/packages/get_objects.ts | 2 +- .../server/services/epm/packages/index.ts | 2 +- .../server/services/epm/packages/install.ts | 2 +- .../server/services/epm/packages/remove.ts | 2 +- .../ingest_manager/server/services/output.ts | 2 +- .../ingest_manager/server/services/setup.ts | 2 +- .../ingest_manager/server/types/index.tsx | 2 +- 63 files changed, 313 insertions(+), 248 deletions(-) rename x-pack/plugins/ingest_manager/public/applications/ingest_manager/{sections/epm => }/hooks/use_breadcrumbs.tsx (76%) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_local_search.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/hooks.tsx diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index a0575c71d3aba..ad06e8d3c9c11 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectAttributes } from '../../../../../../src/core/public'; +import { SavedObjectAttributes } from 'src/core/public'; import { AGENT_TYPE_EPHEMERAL, AGENT_TYPE_PERMANENT, AGENT_TYPE_TEMPORARY } from '../../constants'; export type AgentType = diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts index c63e496273ada..002c3784446a8 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectAttributes } from '../../../../../../src/core/public'; +import { SavedObjectAttributes } from 'src/core/public'; import { Datasource, DatasourcePackage, diff --git a/x-pack/plugins/ingest_manager/common/types/models/enrollment_api_key.ts b/x-pack/plugins/ingest_manager/common/types/models/enrollment_api_key.ts index 35cb851a72933..204ce4b15ea5b 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/enrollment_api_key.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/enrollment_api_key.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectAttributes } from '../../../../../../src/core/public'; +import { SavedObjectAttributes } from 'src/core/public'; export interface EnrollmentAPIKey { id: string; diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index a1a39444c3b50..6b8403b74a759 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -6,11 +6,7 @@ // Follow pattern from https://github.com/elastic/kibana/pull/52447 // TODO: Update when https://github.com/elastic/kibana/issues/53021 is closed -import { - SavedObject, - SavedObjectAttributes, - SavedObjectReference, -} from '../../../../../../src/core/public'; +import { SavedObject, SavedObjectAttributes, SavedObjectReference } from 'src/core/public'; export enum InstallationStatus { installed = 'installed', diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx index c87a77320d3f7..e1f29fdbeb323 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { memo } from 'react'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; @@ -35,13 +35,17 @@ export interface HeaderProps { tabs?: EuiTabProps[]; } +const HeaderColumns: React.FC> = memo(({ leftColumn, rightColumn }) => ( + + {leftColumn ? {leftColumn} : null} + {rightColumn ? {rightColumn} : null} + +)); + export const Header: React.FC = ({ leftColumn, rightColumn, tabs }) => ( - - {leftColumn ? {leftColumn} : null} - {rightColumn ? {rightColumn} : null} - + {tabs ? ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts index 1ac5bef629fde..b313dbf629f32 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts @@ -7,6 +7,8 @@ export { PLUGIN_ID, EPM_API_ROUTES, AGENT_CONFIG_SAVED_OBJECT_TYPE } from '../.. export const BASE_PATH = '/app/ingestManager'; export const EPM_PATH = '/epm'; +export const EPM_LIST_ALL_PACKAGES_PATH = EPM_PATH; +export const EPM_LIST_INSTALLED_PACKAGES_PATH = `${EPM_PATH}/installed`; export const EPM_DETAIL_VIEW_PATH = `${EPM_PATH}/detail/:pkgkey/:panel?`; export const AGENT_CONFIG_PATH = '/configs'; export const AGENT_CONFIG_DETAILS_PATH = `${AGENT_CONFIG_PATH}/`; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_breadcrumbs.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx similarity index 76% rename from x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_breadcrumbs.tsx rename to x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx index 6222d346432c3..ff6656e969c93 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ChromeBreadcrumb } from '../../../../../../../../../src/core/public'; -import { useCore } from '../../../hooks'; +import { ChromeBreadcrumb } from 'src/core/public'; +import { useCore } from './use_core'; export function useBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]) { const { chrome } = useCore(); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts index c6e91444d21f5..f4e9a032b925a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_core.ts @@ -5,7 +5,7 @@ */ import React, { useContext } from 'react'; -import { CoreStart } from 'kibana/public'; +import { CoreStart } from 'src/core/public'; export const CoreContext = React.createContext(null); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts index fe3fb4aa32965..d44cc67e2dc4c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { HttpFetchQuery } from 'kibana/public'; +import { HttpFetchQuery } from 'src/core/public'; import { useRequest, sendRequest } from './use_request'; import { agentConfigRouteService } from '../../services'; import { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts index 02865ffe6fb1a..128ef8de68aae 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/epm.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpFetchQuery } from 'kibana/public'; +import { HttpFetchQuery } from 'src/core/public'; import { useRequest, sendRequest } from './use_request'; import { epmRouteService } from '../../services'; import { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts index 4b434bd1a149e..c63383637e792 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'kibana/public'; +import { HttpSetup } from 'src/core/public'; import { SendRequestConfig, SendRequestResponse, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx index 9a85358a2a69c..f7c2805c6ea7c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx @@ -9,7 +9,7 @@ import { useObservable } from 'react-use'; import { HashRouter as Router, Redirect, Switch, Route, RouteProps } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiErrorBoundary } from '@elastic/eui'; -import { CoreStart, AppMountParameters } from 'kibana/public'; +import { CoreStart, AppMountParameters } from 'src/core/public'; import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components'; import { IngestManagerSetupDeps, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx index 34e1763c44255..2ca49298decf9 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_list_grid.tsx @@ -3,25 +3,76 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; -import React, { Fragment, ReactNode } from 'react'; +import React, { Fragment, ReactNode, useState } from 'react'; +import { + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, + // @ts-ignore + EuiSearchBar, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Loading } from '../../../components'; import { PackageList } from '../../../types'; +import { useLocalSearch, searchIdField } from '../hooks'; import { BadgeProps, PackageCard } from './package_card'; type ListProps = { + isLoading?: boolean; controls?: ReactNode; title: string; list: PackageList; } & BadgeProps; -export function PackageListGrid({ controls, title, list, showInstalledBadge }: ListProps) { +export function PackageListGrid({ + isLoading, + controls, + title, + list, + showInstalledBadge, +}: ListProps) { + const [searchTerm, setSearchTerm] = useState(''); + const localSearchRef = useLocalSearch(list); + const controlsContent = ; - const gridContent = ; + let gridContent: JSX.Element; + + if (isLoading || !localSearchRef.current) { + gridContent = ; + } else { + const filteredList = searchTerm + ? list.filter(item => + (localSearchRef.current!.search(searchTerm) as PackageList) + .map(match => match[searchIdField]) + .includes(item[searchIdField]) + ) + : list; + gridContent = ; + } return ( - + {controlsContent} - {gridContent} + + { + setSearchTerm(userInput); + }} + /> + + {gridContent} + ); } @@ -34,9 +85,9 @@ interface ControlsColumnProps { function ControlsColumn({ controls, title }: ControlsColumnProps) { return ( - +

{title}

-
+ {controls} @@ -53,11 +104,24 @@ type GridColumnProps = { function GridColumn({ list }: GridColumnProps) { return ( - {list.map(item => ( - - + {list.length ? ( + list.map(item => ( + + + + )) + ) : ( + + +

+ +

+
- ))} + )}
); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/index.tsx index 589ce5f5dbd25..48986481b6061 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/index.tsx @@ -3,9 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -// export { useBreadcrumbs } from './use_breadcrumbs'; export { useLinks } from './use_links'; +export { useLocalSearch, searchIdField } from './use_local_search'; export { PackageInstallProvider, useDeletePackage, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_local_search.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_local_search.tsx new file mode 100644 index 0000000000000..26f1ef6a80271 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_local_search.tsx @@ -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. + */ +import { Search as LocalSearch } from 'js-search'; +import { useEffect, useRef } from 'react'; +import { PackageList, PackageListItem } from '../../../types'; + +export type SearchField = keyof PackageListItem; +export const searchIdField: SearchField = 'name'; +export const fieldsToSearch: SearchField[] = ['description', 'name', 'title']; + +export function useLocalSearch(packageList: PackageList) { + const localSearchRef = useRef(null); + + useEffect(() => { + if (!packageList.length) return; + + const localSearch = new LocalSearch(searchIdField); + fieldsToSearch.forEach(field => localSearch.addIndex(field)); + localSearch.addDocuments(packageList); + localSearchRef.current = localSearch; + }, [packageList]); + + return localSearchRef; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx index b8dd08eb46a54..2c8ee7ca2fcf3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { HashRouter as Router, Switch, Route } from 'react-router-dom'; import { useConfig } from '../../hooks'; import { CreateDatasourcePage } from '../agent_config/create_datasource_page'; -import { Home } from './screens/home'; +import { EPMHomePage } from './screens/home'; import { Detail } from './screens/detail'; export const EPMApp: React.FunctionComponent = () => { @@ -23,8 +23,8 @@ export const EPMApp: React.FunctionComponent = () => { - - + + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx index 5a51515d49486..a7204dd722603 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx @@ -36,8 +36,6 @@ export function Header(props: HeaderProps) { const { iconType, name, title, version } = props; const hasWriteCapabilites = useCapabilities().write; const { toListView } = useLinks(); - // useBreadcrumbs([{ text: PLUGIN.TITLE, href: toListView() }, { text: title }]); - const ADD_DATASOURCE_URI = useLink(`${EPM_PATH}/${name}-${version}/add-datasource`); return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/category_facets.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/category_facets.tsx index e138f9f531a39..52730664aac05 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/category_facets.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/category_facets.tsx @@ -5,30 +5,37 @@ */ import { EuiFacetButton, EuiFacetGroup } from '@elastic/eui'; import React from 'react'; +import { Loading } from '../../../../components'; import { CategorySummaryItem, CategorySummaryList } from '../../../../types'; export function CategoryFacets({ + isLoading, categories, selectedCategory, onCategoryChange, }: { + isLoading?: boolean; categories: CategorySummaryList; selectedCategory: string; onCategoryChange: (category: CategorySummaryItem) => unknown; }) { const controls = ( - {categories.map(category => ( - onCategoryChange(category)} - > - {category.title} - - ))} + {isLoading ? ( + + ) : ( + categories.map(category => ( + onCategoryChange(category)} + > + {category.title} + + )) + )} ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx index 2cb5aca39c807..4230775c04e00 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx @@ -3,14 +3,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import React, { memo } from 'react'; +import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; -import styled from 'styled-components'; import { useLinks } from '../../hooks'; -export function HeroCopy() { +export const HeroCopy = memo(() => { return ( @@ -35,12 +34,12 @@ export function HeroCopy() { ); -} +}); -export function HeroImage() { +export const HeroImage = memo(() => { const { toAssets } = useLinks(); const ImageWrapper = styled.div` - margin-bottom: -38px; // revert to -62px when tabs are restored + margin-bottom: -62px; `; return ( @@ -51,4 +50,4 @@ export function HeroImage() { /> ); -} +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/hooks.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/hooks.tsx deleted file mode 100644 index c3e29f723dcba..0000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/hooks.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { useEffect, useRef, useState } from 'react'; -import { PackageList } from '../../../../types'; -import { fieldsToSearch, LocalSearch, searchIdField } from './search_packages'; - -export function useAllPackages(selectedCategory: string, categoryPackages: PackageList = []) { - const [allPackages, setAllPackages] = useState([]); - - useEffect(() => { - if (!selectedCategory) setAllPackages(categoryPackages); - }, [selectedCategory, categoryPackages]); - - return [allPackages, setAllPackages] as [typeof allPackages, typeof setAllPackages]; -} - -export function useLocalSearch(allPackages: PackageList) { - const localSearchRef = useRef(null); - - useEffect(() => { - if (!allPackages.length) return; - - const localSearch = new LocalSearch(searchIdField); - fieldsToSearch.forEach(field => localSearch.addIndex(field)); - localSearch.addDocuments(allPackages); - localSearchRef.current = localSearch; - }, [allPackages]); - - return localSearchRef; -} - -export function useInstalledPackages(allPackages: PackageList) { - const [installedPackages, setInstalledPackages] = useState([]); - - useEffect(() => { - setInstalledPackages(allPackages.filter(({ status }) => status === 'installed')); - }, [allPackages]); - - return [installedPackages, setInstalledPackages] as [ - typeof installedPackages, - typeof setInstalledPackages - ]; -} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx index 640e4a30a40ca..5f215b7788259 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx @@ -4,136 +4,152 @@ * you may not use this file except in compliance with the Elastic License. */ +import React, { useState } from 'react'; +import { useRouteMatch, Switch, Route } from 'react-router-dom'; +import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; +import { i18n } from '@kbn/i18n'; import { - EuiHorizontalRule, - // @ts-ignore - EuiSearchBar, - EuiSpacer, -} from '@elastic/eui'; -import React, { Fragment, useState } from 'react'; -import { useGetCategories, useGetPackages } from '../../../../hooks'; + EPM_LIST_ALL_PACKAGES_PATH, + EPM_LIST_INSTALLED_PACKAGES_PATH, +} from '../../../../constants'; +import { useLink, useGetCategories, useGetPackages } from '../../../../hooks'; import { WithHeaderLayout } from '../../../../layouts'; -import { CategorySummaryItem, PackageList } from '../../../../types'; +import { CategorySummaryItem } from '../../../../types'; import { PackageListGrid } from '../../components/package_list_grid'; -// import { useBreadcrumbs, useLinks } from '../../hooks'; import { CategoryFacets } from './category_facets'; import { HeroCopy, HeroImage } from './header'; -import { useAllPackages, useInstalledPackages, useLocalSearch } from './hooks'; -import { SearchPackages } from './search_packages'; -export function Home() { - // useBreadcrumbs([{ text: PLUGIN.TITLE, href: toListView() }]); +export function EPMHomePage() { + const { + params: { tabId }, + } = useRouteMatch<{ tabId?: string }>(); - const state = useHomeState(); - const searchBar = ( - { - state.setSearchTerm(userInput); - }} - /> - ); - const body = state.searchTerm ? ( - - ) : ( - - {state.installedPackages.length ? ( - - - - - ) : null} - - - ); + const ALL_PACKAGES_URI = useLink(EPM_LIST_ALL_PACKAGES_PATH); + const INSTALLED_PACKAGES_URI = useLink(EPM_LIST_INSTALLED_PACKAGES_PATH); return ( } rightColumn={} - // tabs={[ - // { - // id: 'all_packages', - // name: 'All packages', - // isSelected: true, - // }, - // { - // id: 'installed_packages', - // name: 'Installed packages', - // }, - // ]} + tabs={ + ([ + { + id: 'all_packages', + name: i18n.translate('xpack.ingestManager.epmList.allPackagesTabText', { + defaultMessage: 'All packages', + }), + href: ALL_PACKAGES_URI, + isSelected: tabId !== 'installed', + }, + { + id: 'installed_packages', + name: i18n.translate('xpack.ingestManager.epmList.installedPackagesTabText', { + defaultMessage: 'Installed packages', + }), + href: INSTALLED_PACKAGES_URI, + isSelected: tabId === 'installed', + }, + ] as unknown) as EuiTabProps[] + } > - {searchBar} - - {body} + + + + + + + + ); } -type HomeState = ReturnType; - -export function useHomeState() { - const [searchTerm, setSearchTerm] = useState(''); +function InstalledPackages() { + const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages(); const [selectedCategory, setSelectedCategory] = useState(''); - const { data: categoriesRes } = useGetCategories(); - const categories = categoriesRes?.response; - const { data: categoryPackagesRes } = useGetPackages({ category: selectedCategory }); - const categoryPackages = categoryPackagesRes?.response; - const [allPackages, setAllPackages] = useAllPackages(selectedCategory, categoryPackages); - const localSearchRef = useLocalSearch(allPackages); - const [installedPackages, setInstalledPackages] = useInstalledPackages(allPackages); + const packages = + allPackages && allPackages.response && selectedCategory === '' + ? allPackages.response.filter(pkg => pkg.status === 'installed') + : []; - return { - searchTerm, - setSearchTerm, - selectedCategory, - setSelectedCategory, - categories, - allPackages, - setAllPackages, - installedPackages, - localSearchRef, - setInstalledPackages, - categoryPackages, - }; -} + const title = i18n.translate('xpack.ingestManager.epmList.installedPackagesTitle', { + defaultMessage: 'Installed packages', + }); -function InstalledPackages({ list }: { list: PackageList }) { - const title = 'Your Packages'; + const categories = [ + { + id: '', + title: i18n.translate('xpack.ingestManager.epmList.allPackagesFilterLinkText', { + defaultMessage: 'All', + }), + count: packages.length, + }, + { + id: 'updates_available', + title: i18n.translate('xpack.ingestManager.epmList.updatesAvailableFilterLinkText', { + defaultMessage: 'Updates available', + }), + count: 0, // TODO: Update with real count when available + }, + ]; - return ; + const controls = ( + setSelectedCategory(id)} + /> + ); + + return ( + + ); } -function AvailablePackages({ - allPackages, - categories, - categoryPackages, - selectedCategory, - setSelectedCategory, -}: HomeState) { - const title = 'Available Packages'; - const noFilter = { - id: '', - title: 'All', - count: allPackages.length, - }; +function AvailablePackages() { + const [selectedCategory, setSelectedCategory] = useState(''); + const { data: categoryPackagesRes, isLoading: isLoadingPackages } = useGetPackages({ + category: selectedCategory, + }); + const { data: categoriesRes, isLoading: isLoadingCategories } = useGetCategories(); + const packages = + categoryPackagesRes && categoryPackagesRes.response ? categoryPackagesRes.response : []; + + const title = i18n.translate('xpack.ingestManager.epmList.allPackagesTitle', { + defaultMessage: 'All packages', + }); + + const categories = [ + { + id: '', + title: i18n.translate('xpack.ingestManager.epmList.allPackagesFilterLinkText', { + defaultMessage: 'All', + }), + count: packages.length, + }, + ...(categoriesRes ? categoriesRes.response : []), + ]; const controls = categories ? ( setSelectedCategory(id)} /> ) : null; - return ; + return ( + + ); } diff --git a/x-pack/plugins/ingest_manager/public/index.ts b/x-pack/plugins/ingest_manager/public/index.ts index a9e40a2a42302..aa1e0e79e548b 100644 --- a/x-pack/plugins/ingest_manager/public/index.ts +++ b/x-pack/plugins/ingest_manager/public/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'kibana/public'; +import { PluginInitializerContext } from 'src/core/public'; import { IngestManagerPlugin } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => { diff --git a/x-pack/plugins/ingest_manager/public/plugin.ts b/x-pack/plugins/ingest_manager/public/plugin.ts index a1dc2c057e9e5..99dcebd9bfba1 100644 --- a/x-pack/plugins/ingest_manager/public/plugin.ts +++ b/x-pack/plugins/ingest_manager/public/plugin.ts @@ -9,7 +9,7 @@ import { Plugin, PluginInitializerContext, CoreStart, -} from 'kibana/public'; +} from 'src/core/public'; import { i18n } from '@kbn/i18n'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; diff --git a/x-pack/plugins/ingest_manager/server/index.ts b/x-pack/plugins/ingest_manager/server/index.ts index b732cb8005efb..df7c3d7cf0fbf 100644 --- a/x-pack/plugins/ingest_manager/server/index.ts +++ b/x-pack/plugins/ingest_manager/server/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema, TypeOf } from '@kbn/config-schema'; -import { PluginInitializerContext } from 'kibana/server'; +import { PluginInitializerContext } from 'src/core/server'; import { IngestManagerPlugin } from './plugin'; export const config = { diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index c162ea5fadabe..67737c6fe502e 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -11,7 +11,7 @@ import { Plugin, PluginInitializerContext, SavedObjectsServiceStart, -} from 'kibana/server'; +} from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { EncryptedSavedObjectsPluginStart } from '../../encrypted_saved_objects/server'; import { SecurityPluginSetup } from '../../security/server'; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index cf1fd2476f310..7d991f5ad2cc2 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandler, KibanaRequest } from 'kibana/server'; +import { RequestHandler, KibanaRequest } from 'src/core/server'; import { TypeOf } from '@kbn/config-schema'; import { GetAgentsResponse, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index c85629ea22ad9..414d2d79e9067 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -9,7 +9,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter } from 'src/core/server'; import { PLUGIN_ID, AGENT_API_ROUTES } from '../../constants'; import { GetAgentsRequestSchema, 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 f670a797c3fb1..67f758c2c1263 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 @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; -import { RequestHandler } from 'kibana/server'; +import { RequestHandler } from 'src/core/server'; import bluebird from 'bluebird'; import { appContextService, agentConfigService, datasourceService } from '../../services'; import { listAgents } from '../../services/agents'; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts index c3b3c00a9574c..b8e827974ff81 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter } from 'src/core/server'; import { PLUGIN_ID, AGENT_CONFIG_API_ROUTES } from '../../constants'; import { GetAgentConfigsRequestSchema, diff --git a/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts index 349e88d8fb59d..7ae562cf130ab 100644 --- a/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; -import { RequestHandler } from 'kibana/server'; +import { RequestHandler } from 'src/core/server'; import { appContextService, datasourceService } from '../../services'; import { ensureInstalledPackage } from '../../services/epm/packages'; import { diff --git a/x-pack/plugins/ingest_manager/server/routes/datasource/index.ts b/x-pack/plugins/ingest_manager/server/routes/datasource/index.ts index e5891cc7377e9..7217f28053cf3 100644 --- a/x-pack/plugins/ingest_manager/server/routes/datasource/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/datasource/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter } from 'src/core/server'; import { PLUGIN_ID, DATASOURCE_API_ROUTES } from '../../constants'; import { GetDatasourcesRequestSchema, diff --git a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts index 478078a934186..9d3eb5360dbe3 100644 --- a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/handler.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandler } from 'kibana/server'; +import { RequestHandler } from 'src/core/server'; import { TypeOf } from '@kbn/config-schema'; import { GetEnrollmentAPIKeysRequestSchema, diff --git a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/index.ts b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/index.ts index 6df5299d30bd4..9d0ff65ab0b3e 100644 --- a/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/enrollment_api_key/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter } from 'src/core/server'; import { PLUGIN_ID, ENROLLMENT_API_KEY_ROUTES } from '../../constants'; import { GetEnrollmentAPIKeysRequestSchema, diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts index 6b1dde92ec0e1..8623d02e72862 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; -import { RequestHandler, CustomHttpResponseOptions } from 'kibana/server'; +import { RequestHandler, CustomHttpResponseOptions } from 'src/core/server'; import { GetPackagesRequestSchema, GetFileRequestSchema, diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts index cb9ec5cc532c4..fcf81f9894d5e 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter } from 'src/core/server'; import { PLUGIN_ID, EPM_API_ROUTES } from '../../constants'; import { getCategoriesHandler, diff --git a/x-pack/plugins/ingest_manager/server/routes/install_script/index.ts b/x-pack/plugins/ingest_manager/server/routes/install_script/index.ts index 5470df31adbdd..b007e61594e9d 100644 --- a/x-pack/plugins/ingest_manager/server/routes/install_script/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/install_script/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import url from 'url'; -import { IRouter, BasePath, HttpServerInfo, KibanaRequest } from 'kibana/server'; +import { IRouter, BasePath, HttpServerInfo, KibanaRequest } from 'src/core/server'; import { INSTALL_SCRIPT_API_ROUTES } from '../../constants'; import { getScript } from '../../services/install_script'; import { InstallScriptRequestSchema } from '../../types'; diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts index 30e725bb5ad4a..38188bc76f5f4 100644 --- a/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { TypeOf } from '@kbn/config-schema'; -import { RequestHandler } from 'kibana/server'; +import { RequestHandler } from 'src/core/server'; import { outputService, agentConfigService } from '../../services'; import { CreateFleetSetupRequestSchema, CreateFleetSetupResponse } from '../../types'; import { setup } from '../../services/setup'; diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts index 7e09d8dbef1f6..a2c641503e825 100644 --- a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import { IRouter } from 'src/core/server'; import { PLUGIN_ID, FLEET_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../constants'; import { GetFleetSetupRequestSchema, CreateFleetSetupRequestSchema } from '../../types'; import { 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 e5b20de3bf911..a941494072ae3 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { uniq } from 'lodash'; -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { AuthenticatedUser } from '../../../security/server'; import { DEFAULT_AGENT_CONFIG, AGENT_CONFIG_SAVED_OBJECT_TYPE } from '../constants'; import { diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts b/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts index 38894ff321a0b..8c0e73201e1ff 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config_update.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { generateEnrollmentAPIKey, deleteEnrollmentApiKeyForConfigId } from './api_keys'; import { updateAgentsForConfigId, unenrollForConfigId } from './agents'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index 892d8cdbe657f..98a5f69f9d2b0 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -9,7 +9,7 @@ import { SavedObjectsBulkCreateObject, SavedObjectsBulkResponse, SavedObjectsClientContract, -} from 'kibana/server'; +} from 'src/core/server'; import Boom from 'boom'; import { Agent, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index 76dfc0867fb4e..0ff4af4ffe351 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, SavedObjectsBulkCreateObject } from 'kibana/server'; +import { SavedObjectsClientContract, SavedObjectsBulkCreateObject } from 'src/core/server'; import uuid from 'uuid'; import { Agent, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts index cdbdf164e834d..41bd2476c99a1 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts @@ -5,7 +5,7 @@ */ import Boom from 'boom'; -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts index b48d311da4440..0f73f71817eb0 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts @@ -5,7 +5,7 @@ */ import Boom from 'boom'; -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { AgentType, Agent, AgentSOAttributes } from '../../types'; import { savedObjectToAgent } from './saved_objects'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/events.ts b/x-pack/plugins/ingest_manager/server/services/agents/events.ts index 908d289fbc4bb..707229845531c 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/events.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/events.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; import { AgentEventSOAttributes, AgentEvent } from '../../types'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts index adb096a444903..dbe268818713d 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject } from 'kibana/server'; +import { SavedObject } from 'src/core/server'; import { Agent, AgentSOAttributes } from '../../types'; export function savedObjectToAgent(so: SavedObject): Agent { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/status.ts b/x-pack/plugins/ingest_manager/server/services/agents/status.ts index f6477bf1c7334..21e200d701e69 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/status.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/status.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { listAgents } from './crud'; import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; import { AgentStatus, Agent } from '../../types'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts index e45620c3cf588..bf6f6526be069 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { AgentSOAttributes } from '../../types'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/update.ts b/x-pack/plugins/ingest_manager/server/services/agents/update.ts index 8452c05d53a1f..9eabf0944bdc4 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/update.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { listAgents } from './crud'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { unenrollAgents } from './unenroll'; diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts index 9a1a91f9ed8a9..d81b998d5a752 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts @@ -5,7 +5,7 @@ */ import uuid from 'uuid'; -import { SavedObjectsClientContract, SavedObject } from 'kibana/server'; +import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; import { EnrollmentAPIKey, EnrollmentAPIKeySOAttributes } from '../../types'; import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; import { createAPIKey, invalidateAPIKey } from './security'; diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/index.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/index.ts index a7c74f279d169..9b0182b86fc88 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, SavedObject, KibanaRequest } from 'kibana/server'; +import { SavedObjectsClientContract, SavedObject, KibanaRequest } from 'src/core/server'; import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; import { EnrollmentAPIKeySOAttributes, EnrollmentAPIKey } from '../../types'; import { createAPIKey } from './security'; diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/security.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/security.ts index ffc269bca94eb..dfd53d55fbbf5 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/security.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/security.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, FakeRequest, SavedObjectsClientContract } from 'kibana/server'; +import { KibanaRequest, FakeRequest, SavedObjectsClientContract } from 'src/core/server'; import { CallESAsCurrentUser } from '../../types'; import { appContextService } from '../app_context'; import { outputService } from '../output'; diff --git a/x-pack/plugins/ingest_manager/server/services/app_context.ts b/x-pack/plugins/ingest_manager/server/services/app_context.ts index c06b282389fc7..a0a7c8dd7c05a 100644 --- a/x-pack/plugins/ingest_manager/server/services/app_context.ts +++ b/x-pack/plugins/ingest_manager/server/services/app_context.ts @@ -5,7 +5,7 @@ */ import { BehaviorSubject, Observable } from 'rxjs'; import { first } from 'rxjs/operators'; -import { SavedObjectsServiceStart } from 'kibana/server'; +import { SavedObjectsServiceStart } from 'src/core/server'; import { EncryptedSavedObjectsPluginStart } from '../../../encrypted_saved_objects/server'; import { SecurityPluginSetup } from '../../../security/server'; import { IngestManagerConfigType } from '../../common'; diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts index 444937343e31f..8fa1428f3a055 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { AuthenticatedUser } from '../../../security/server'; import { DeleteDatasourcesResponse, packageToConfigDatasource } from '../../common'; import { DATASOURCE_SAVED_OBJECT_TYPE } from '../constants'; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts index 264000f9892ba..1f11136360465 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { INDEX_PATTERN_SAVED_OBJECT_TYPE } from '../../../../constants'; import * as Registry from '../../registry'; import { loadFieldsFromYaml, Fields, Field } from '../../fields/field'; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts index 58416b7f66d2d..d655b81f8cdef 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server/'; +import { SavedObjectsClientContract } from 'src/core/server'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import { Installation, InstallationStatus, PackageInfo } from '../../../types'; import * as Registry from '../registry'; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get_objects.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get_objects.ts index e0424aa8a36f5..b924c045870f3 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/get_objects.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get_objects.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject, SavedObjectsBulkCreateObject } from 'src/core/server/'; +import { SavedObject, SavedObjectsBulkCreateObject } from 'src/core/server'; import { AssetType } from '../../../types'; import * as Registry from '../registry'; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts index 2f84ea5b6f8db..79259ce79ff41 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject } from '../../../../../../../src/core/server'; +import { SavedObject } from 'src/core/server'; import { AssetType, Installable, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts index acf77998fdb3c..3cce238f582f4 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObject, SavedObjectsClientContract } from 'src/core/server/'; +import { SavedObject, SavedObjectsClientContract } from 'src/core/server'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import { AssetReference, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts index e57729a7ab2ba..2e73160453c2b 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server/'; +import { SavedObjectsClientContract } from 'src/core/server'; import { PACKAGES_SAVED_OBJECT_TYPE } from '../../../constants'; import { AssetReference, AssetType, ElasticsearchAssetType } from '../../../types'; import { CallESAsCurrentUser } from '../../../types'; diff --git a/x-pack/plugins/ingest_manager/server/services/output.ts b/x-pack/plugins/ingest_manager/server/services/output.ts index 066f8e8a316a5..8503bbb56ee84 100644 --- a/x-pack/plugins/ingest_manager/server/services/output.ts +++ b/x-pack/plugins/ingest_manager/server/services/output.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { NewOutput, Output } from '../types'; import { DEFAULT_OUTPUT, OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; import { appContextService } from './app_context'; diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts index 4b79cd639b613..7f72cdb88463f 100644 --- a/x-pack/plugins/ingest_manager/server/services/setup.ts +++ b/x-pack/plugins/ingest_manager/server/services/setup.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { CallESAsCurrentUser } from '../types'; import { agentConfigService } from './agent_config'; import { outputService } from './output'; diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index c9a4bf79f3516..59c7f152e5cbc 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ScopedClusterClient } from 'src/core/server/'; +import { ScopedClusterClient } from 'src/core/server'; export { // Object types From 59551e7e81d245b52628dd350710ced406f28a1f Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Mon, 16 Mar 2020 18:46:38 -0700 Subject: [PATCH 30/76] Closes 59786 by removing the update toast (#60172) Co-authored-by: Elastic Machine --- .../components/app/ServiceMap/index.tsx | 37 +------------------ 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index 2942ce64729e7..7bbb77a49c84b 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton } from '@elastic/eui'; import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import { ElementDefinition } from 'cytoscape'; @@ -16,7 +15,6 @@ import React, { useRef, useState } from 'react'; -import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; import { isValidPlatinumLicense } from '../../../../../../../plugins/apm/common/service_map'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceMapAPIResponse } from '../../../../../../../plugins/apm/server/lib/service_map/get_service_map'; @@ -78,7 +76,6 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { }); const renderedElements = useRef([]); - const openToast = useRef(null); const [responses, setResponses] = useState([]); @@ -160,41 +157,11 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { return !find(renderedElements.current, el => isEqual(el, element)); }); - const updateMap = () => { + if (newElements.length > 0 && renderedElements.current.length > 0) { renderedElements.current = elements; - if (openToast.current) { - notifications.toasts.remove(openToast.current); - } forceUpdate(); - }; - - if (newElements.length > 0 && renderedElements.current.length > 0) { - openToast.current = notifications.toasts.add({ - title: i18n.translate('xpack.apm.newServiceMapData', { - defaultMessage: `Newly discovered connections are available.` - }), - onClose: () => { - openToast.current = null; - }, - toastLifeTimeMs: 24 * 60 * 60 * 1000, - text: toMountPoint( - - {i18n.translate('xpack.apm.updateServiceMap', { - defaultMessage: 'Update map' - })} - - ) - }).id; } - - return () => { - if (openToast.current) { - notifications.toasts.remove(openToast.current); - } - }; - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [elements]); + }, [elements, forceUpdate]); const { ref: wrapperRef, width, height } = useRefDimensions(); From 35d6a0a635edca1781e7f3f96b5459be9dfebaad Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Mon, 16 Mar 2020 22:20:51 -0400 Subject: [PATCH 31/76] adds test that action vars are rendered for alert action parms (#60310) resolves https://github.com/elastic/kibana/issues/60083 --- .../apps/triggers_actions_ui/alerts.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 75ae6b9ea7c21..791712fa24489 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -80,10 +80,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await loggingMessageInput.click(); await loggingMessageInput.clearValue(); await loggingMessageInput.type('test message'); - // TODO: uncomment variables test when server API will be ready - // await testSubjects.click('slackAddVariableButton'); - // const variableMenuButton = await testSubjects.find('variableMenuButton-0'); - // await variableMenuButton.click(); + await testSubjects.click('slackAddVariableButton'); + const variableMenuButton = await testSubjects.find('variableMenuButton-0'); + await variableMenuButton.click(); await find.clickByCssSelector('[data-test-subj="saveAlertButton"]'); const toastTitle = await pageObjects.common.closeToast(); expect(toastTitle).to.eql(`Saved '${alertName}'`); From 90f3778bc69fa5f7af18294c5ecb98e2e842015e Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Mon, 16 Mar 2020 19:39:21 -0700 Subject: [PATCH 32/76] Added variables button for text fields in Pagerduty component. (#60189) * Added variables button for text fields in Pagerduty component. Fixed bugs mentioned in https://github.com/elastic/kibana/issues/60067 * Fixed due to comments * fixed language check issue * Fixed tests * Fixed due to comments --- .../builtin_action_types/pagerduty.test.tsx | 1 + .../builtin_action_types/pagerduty.tsx | 208 ++++++++++++++++-- 2 files changed, 193 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx index 31a69f9fd94ac..bb06f7961b20b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx @@ -183,5 +183,6 @@ describe('PagerDutyParamsFields renders', () => { expect(wrapper.find('[data-test-subj="groupInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="sourceInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="pagerdutySummaryInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="dedupKeyAddVariableButton"]').length > 0).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx index 3c1b1d258cfe2..7666129e4abdc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; +import React, { Fragment, useState } from 'react'; import { EuiFieldText, EuiFlexGroup, @@ -11,6 +11,10 @@ import { EuiFormRow, EuiSelect, EuiLink, + EuiContextMenuItem, + EuiPopover, + EuiButtonIcon, + EuiContextMenuPanel, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -158,6 +162,7 @@ const PagerDutyParamsFields: React.FunctionComponent { const { @@ -171,16 +176,124 @@ const PagerDutyParamsFields: React.FunctionComponent>({ + dedupKey: false, + summary: false, + source: false, + timestamp: false, + component: false, + group: false, + class: false, + }); + // TODO: replace this button with a proper Eui component, when it will be ready + const getMessageVariables = (paramsProperty: string) => + messageVariables?.map((variable: string) => ( + { + editAction( + paramsProperty, + ((actionParams as any)[paramsProperty] ?? '').concat(` {{${variable}}}`), + index + ); + setIsVariablesPopoverOpen({ ...isVariablesPopoverOpen, [paramsProperty]: false }); + }} + > + {`{{${variable}}}`} + + )); + + const getAddVariableComponent = (paramsProperty: string, buttonName: string) => { + return ( + + setIsVariablesPopoverOpen({ ...isVariablesPopoverOpen, [paramsProperty]: true }) + } + iconType="indexOpen" + aria-label={buttonName} + /> + } + isOpen={isVariablesPopoverOpen[paramsProperty]} + closePopover={() => + setIsVariablesPopoverOpen({ ...isVariablesPopoverOpen, [paramsProperty]: false }) + } + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + ); + }; return ( @@ -190,7 +303,7 @@ const PagerDutyParamsFields: React.FunctionComponent @@ -211,7 +324,7 @@ const PagerDutyParamsFields: React.FunctionComponent @@ -237,6 +350,15 @@ const PagerDutyParamsFields: React.FunctionComponent { - if (!index) { + if (!dedupKey) { editAction('dedupKey', '', index); } }} @@ -263,6 +385,15 @@ const PagerDutyParamsFields: React.FunctionComponent { - if (!index) { + if (!timestamp) { editAction('timestamp', '', index); } }} @@ -289,6 +420,15 @@ const PagerDutyParamsFields: React.FunctionComponent { - if (!index) { + if (!component) { editAction('component', '', index); } }} @@ -313,6 +453,15 @@ const PagerDutyParamsFields: React.FunctionComponent { - if (!index) { + if (!group) { editAction('group', '', index); } }} @@ -337,6 +486,15 @@ const PagerDutyParamsFields: React.FunctionComponent { - if (!index) { + if (!source) { editAction('source', '', index); } }} @@ -364,6 +522,15 @@ const PagerDutyParamsFields: React.FunctionComponent Date: Tue, 17 Mar 2020 00:29:33 -0400 Subject: [PATCH 33/76] resolves https://github.com/elastic/kibana/issues/58905 (#60120) The current index threshold alert uses a `size` limit on term aggregation, when used, but does not sort the buckets, so it's just using descending count on the grouped buckets as the sort to determine what to return. The watcher API for the index threshold notes this as "top N of", implying a sort. This PR applies sorting when the using `groupBy: top`, and the `aggType != count`. For count, ES is already sorting the way we want. The sort is calculated as a separate agg beside the date_range aggregation, which is the same metrics agg specified in the query - `aggType(aggField)`. This field is then referenced in a new `order` property in the terms agg, using 'asc' sorting for `min`, and `desc` sorting for `avg`, `max`, and `sum`. This doesn't change the shape of the output at all, just changes which term buckets will be returned, if there are more term buckets than requested with the `termSize` parameter. --- .../index_threshold/lib/time_series_query.ts | 26 +++++++++++ .../time_series_query_endpoint.ts | 46 +++++++++++++++---- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_query.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_query.ts index a4f64c0f37f41..0382792dafb35 100644 --- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_query.ts +++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/lib/time_series_query.ts @@ -77,6 +77,15 @@ export async function timeSeriesQuery( }, }, }; + + // if not count add an order + if (!isCountAgg) { + const sortOrder = aggType === 'min' ? 'asc' : 'desc'; + aggParent.aggs.groupAgg.terms.order = { + sortValueAgg: sortOrder, + }; + } + aggParent = aggParent.aggs.groupAgg; } @@ -89,6 +98,16 @@ export async function timeSeriesQuery( }, }, }; + + // if not count, add a sorted value agg + if (!isCountAgg) { + aggParent.aggs.sortValueAgg = { + [aggType]: { + field: aggField, + }, + }; + } + aggParent = aggParent.aggs.dateAgg; // finally, the metric aggregation, if requested @@ -106,13 +125,20 @@ export async function timeSeriesQuery( const logPrefix = 'indexThreshold timeSeriesQuery: callCluster'; logger.debug(`${logPrefix} call: ${JSON.stringify(esQuery)}`); + // note there are some commented out console.log()'s below, which are left + // in, as they are VERY useful when debugging these queries; debug logging + // isn't as nice since it's a single long JSON line. + + // console.log('time_series_query.ts request\n', JSON.stringify(esQuery, null, 4)); try { esResult = await callCluster('search', esQuery); } catch (err) { + // console.log('time_series_query.ts error\n', JSON.stringify(err, null, 4)); logger.warn(`${logPrefix} error: ${JSON.stringify(err.message)}`); throw new Error('error running search'); } + // console.log('time_series_query.ts response\n', JSON.stringify(esResult, null, 4)); logger.debug(`${logPrefix} result: ${JSON.stringify(esResult)}`); return getResultFromEs(isCountAgg, isGroupAgg, esResult); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts index ee44c7f25cf61..1aa1d3d21f00d 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts @@ -196,14 +196,6 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider const expected = { results: [ - { - group: 'groupA', - metrics: [ - [START_DATE_MINUS_2INTERVALS, 4 / 1], - [START_DATE_MINUS_1INTERVALS, (4 + 2) / 2], - [START_DATE_MINUS_0INTERVALS, (4 + 2 + 1) / 3], - ], - }, { group: 'groupB', metrics: [ @@ -212,12 +204,50 @@ export default function timeSeriesQueryEndpointTests({ getService }: FtrProvider [START_DATE_MINUS_0INTERVALS, (5 + 3 + 2) / 3], ], }, + { + group: 'groupA', + metrics: [ + [START_DATE_MINUS_2INTERVALS, 4 / 1], + [START_DATE_MINUS_1INTERVALS, (4 + 2) / 2], + [START_DATE_MINUS_0INTERVALS, (4 + 2 + 1) / 3], + ], + }, ], }; expect(await runQueryExpect(query, 200)).eql(expected); }); + it('should return correct sorted group for average', async () => { + const query = getQueryBody({ + aggType: 'avg', + aggField: 'testedValue', + groupBy: 'top', + termField: 'group', + termSize: 1, + dateStart: START_DATE_MINUS_2INTERVALS, + dateEnd: START_DATE_MINUS_0INTERVALS, + }); + const result = await runQueryExpect(query, 200); + expect(result.results.length).to.be(1); + expect(result.results[0].group).to.be('groupB'); + }); + + it('should return correct sorted group for min', async () => { + const query = getQueryBody({ + aggType: 'min', + aggField: 'testedValue', + groupBy: 'top', + termField: 'group', + termSize: 1, + dateStart: START_DATE_MINUS_2INTERVALS, + dateEnd: START_DATE_MINUS_0INTERVALS, + }); + const result = await runQueryExpect(query, 200); + expect(result.results.length).to.be(1); + expect(result.results[0].group).to.be('groupA'); + }); + it('should return an error when passed invalid input', async () => { const query = { ...getQueryBody(), aggType: 'invalid-agg-type' }; const expected = { From 56010b1c6543cf6b3b2a8e39e415a7085296eabf Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Tue, 17 Mar 2020 08:33:08 +0100 Subject: [PATCH 34/76] Give better stack traces for Unhandled Promise Rejection warnings (#60235) --- src/setup_node_env/exit_on_warning.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/setup_node_env/exit_on_warning.js b/src/setup_node_env/exit_on_warning.js index 5be5ccd72bd02..6321cd7ba8db0 100644 --- a/src/setup_node_env/exit_on_warning.js +++ b/src/setup_node_env/exit_on_warning.js @@ -35,4 +35,16 @@ if (process.noProcessWarnings !== true) { process.exit(1); }); + + // While the above warning listener would also be called on + // unhandledRejection warnings, we can give a better error message if we + // handle them separately: + process.on('unhandledRejection', function(reason) { + console.error('Unhandled Promise rejection detected:'); + console.error(); + console.error(reason); + console.error(); + console.error('Terminating process...'); + process.exit(1); + }); } From 7f901f9e0355f06c9f0e2651e5f0958524a9c27b Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Tue, 17 Mar 2020 09:20:00 +0000 Subject: [PATCH 35/76] [ML] Fixes to error handling for analytics jobs and file data viz (#60249) * [ML] Fixes to error handling for analytics jobs and file data viz * [ML] Fix failing tests and address comments from review * [ML] Add key prop to error messages map * [ML] Add errors.ts --- x-pack/plugins/ml/common/types/errors.ts | 18 ++++++++ .../components/analytics_list/columns.tsx | 8 ++-- .../components/analytics_list/common.ts | 2 +- .../analytics_list/expanded_row.tsx | 12 +++++- .../create_analytics_form/messages.tsx | 41 +++++++++++-------- .../use_create_analytics_form.test.tsx | 23 +++++++---- .../use_create_analytics_form.ts | 5 +++ .../file_datavisualizer_view.js | 11 ++++- .../components/analytics_panel/table.tsx | 2 +- 9 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 x-pack/plugins/ml/common/types/errors.ts diff --git a/x-pack/plugins/ml/common/types/errors.ts b/x-pack/plugins/ml/common/types/errors.ts new file mode 100644 index 0000000000000..63e222490082b --- /dev/null +++ b/x-pack/plugins/ml/common/types/errors.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface ErrorResponse { + body: { + statusCode: number; + error: string; + message: string; + }; + name: string; +} + +export function isErrorResponse(arg: any): arg is ErrorResponse { + return arg?.body?.error !== undefined && arg?.body?.message !== undefined; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx index 00cd9e3f1e0dd..2ab8cb4a78d86 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx @@ -43,13 +43,13 @@ enum TASK_STATE_COLOR { export const getTaskStateBadge = ( state: DataFrameAnalyticsStats['state'], - reason?: DataFrameAnalyticsStats['reason'] + failureReason?: DataFrameAnalyticsStats['failure_reason'] ) => { const color = TASK_STATE_COLOR[state]; - if (isDataFrameAnalyticsFailed(state) && reason !== undefined) { + if (isDataFrameAnalyticsFailed(state) && failureReason !== undefined) { return ( - + {state} @@ -229,7 +229,7 @@ export const getColumns = ( sortable: (item: DataFrameAnalyticsListRow) => item.stats.state, truncateText: true, render(item: DataFrameAnalyticsListRow) { - return getTaskStateBadge(item.stats.state, item.stats.reason); + return getTaskStateBadge(item.stats.state, item.stats.failure_reason); }, width: '100px', 'data-test-subj': 'mlAnalyticsTableColumnStatus', diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts index ff7da8d67852f..2c3ded52eba9b 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/common.ts @@ -50,7 +50,7 @@ export interface DataFrameAnalyticsStats { transport_address: string; }; progress: ProgressSection[]; - reason?: string; + failure_reason?: string; state: DATA_FRAME_TASK_STATE; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx index 8772be698bf58..43ef6b36c3972 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx @@ -24,6 +24,7 @@ import { loadEvalData, Eval, } from '../../../../common'; +import { getTaskStateBadge } from './columns'; import { isCompletedAnalyticsJob } from './common'; import { isRegressionAnalysis, @@ -157,8 +158,15 @@ export const ExpandedRow: FC = ({ item }) => { title: i18n.translate('xpack.ml.dataframe.analyticsList.expandedRow.tabs.jobSettings.state', { defaultMessage: 'State', }), - items: Object.entries(stateValues).map(s => { - return { title: s[0].toString(), description: getItemDescription(s[1]) }; + items: Object.entries(stateValues).map(([stateKey, stateValue]) => { + const title = stateKey.toString(); + if (title === 'state') { + return { + title, + description: getTaskStateBadge(getItemDescription(stateValue)), + }; + } + return { title, description: getItemDescription(stateValue) }; }), position: 'left', }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/messages.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/messages.tsx index f228d8fe90097..68a9a264ddf78 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/messages.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/messages.tsx @@ -6,25 +6,34 @@ import React, { Fragment, FC } from 'react'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; import { FormMessage } from '../../hooks/use_create_analytics_form/state'; // State interface Props { - messages: any; // TODO: fix --> something like State['requestMessages']; + messages: FormMessage[]; } -export const Messages: FC = ({ messages }) => - messages.map((requestMessage: FormMessage, i: number) => ( - - - {requestMessage.error !== undefined ?

{requestMessage.error}

: null} -
- -
- )); +export const Messages: FC = ({ messages }) => { + return ( + <> + {messages.map((requestMessage, i) => ( + + + {requestMessage.error !== undefined && ( + + {requestMessage.error} + + )} + + + + ))} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx index 2bdcc28e31fff..1a248f8559ffa 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx @@ -22,16 +22,23 @@ const getMountedHook = () => describe('getErrorMessage()', () => { test('verify error message response formats', () => { - const errorMessage = getErrorMessage(new Error('the-error-message')); - expect(errorMessage).toBe('the-error-message'); + const customError1 = { + body: { statusCode: 403, error: 'Forbidden', message: 'the-error-message' }, + }; + const errorMessage1 = getErrorMessage(customError1); + expect(errorMessage1).toBe('Forbidden: the-error-message'); - const customError1 = { customErrorMessage: 'the-error-message' }; - const errorMessageMessage1 = getErrorMessage(customError1); - expect(errorMessageMessage1).toBe('{"customErrorMessage":"the-error-message"}'); + const customError2 = new Error('the-error-message'); + const errorMessage2 = getErrorMessage(customError2); + expect(errorMessage2).toBe('the-error-message'); - const customError2 = { message: 'the-error-message' }; - const errorMessageMessage2 = getErrorMessage(customError2); - expect(errorMessageMessage2).toBe('the-error-message'); + const customError3 = { customErrorMessage: 'the-error-message' }; + const errorMessage3 = getErrorMessage(customError3); + expect(errorMessage3).toBe('{"customErrorMessage":"the-error-message"}'); + + const customError4 = { message: 'the-error-message' }; + const errorMessage4 = getErrorMessage(customError4); + expect(errorMessage4).toBe('the-error-message'); }); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index 74161d7c48c24..3b6b8538c2ffd 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -9,6 +9,7 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; import { SimpleSavedObject } from 'kibana/public'; +import { isErrorResponse } from '../../../../../../../common/types/errors'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; @@ -40,6 +41,10 @@ export interface CreateAnalyticsFormProps { } export function getErrorMessage(error: any) { + if (isErrorResponse(error)) { + return `${error.body.error}: ${error.body.message}`; + } + if (typeof error === 'object' && typeof error.message === 'string') { return error.message; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js index a4300de5abbbb..12e5a14b51871 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js @@ -19,6 +19,7 @@ import { FileCouldNotBeRead, FileTooLarge } from './file_error_callouts'; import { EditFlyout } from '../edit_flyout'; import { ImportView } from '../import_view'; import { MAX_BYTES } from '../../../../../../common/constants/file_datavisualizer'; +import { isErrorResponse } from '../../../../../../common/types/errors'; import { readFile, createUrlOverrides, @@ -177,12 +178,20 @@ export class FileDataVisualizerView extends Component { }); } catch (error) { console.error(error); + + let serverErrorMsg; + if (isErrorResponse(error) === true) { + serverErrorMsg = `${error.body.error}: ${error.body.message}`; + } else { + serverErrorMsg = JSON.stringify(error, null, 2); + } + this.setState({ results: undefined, loaded: false, loading: false, fileCouldNotBeRead: true, - serverErrorMessage: error.message, + serverErrorMessage: serverErrorMsg, }); // as long as the previous overrides are different to the current overrides, diff --git a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx index ff81f0e87aca8..c3b8e8dd4e27f 100644 --- a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx @@ -61,7 +61,7 @@ export const AnalyticsTable: FC = ({ items }) => { sortable: (item: DataFrameAnalyticsListRow) => item.stats.state, truncateText: true, render(item: DataFrameAnalyticsListRow) { - return getTaskStateBadge(item.stats.state, item.stats.reason); + return getTaskStateBadge(item.stats.state, item.stats.failure_reason); }, width: '100px', }, From 55003b61ddf11f7769c5de71704012ec482f86c1 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Tue, 17 Mar 2020 11:50:34 +0100 Subject: [PATCH 36/76] [SIEM] Fix Timeline footer styling (#59587) --- .../timeline_data_providers.spec.ts | 2 +- .../timeline_flyout_button.spec.ts | 4 +- .../plugins/siem/cypress/screens/timeline.ts | 2 + x-pack/legacy/plugins/siem/package.json | 2 +- .../public/components/charts/areachart.tsx | 4 +- .../public/components/charts/barchart.tsx | 4 +- .../drag_drop_context_wrapper.tsx | 7 +- .../drag_and_drop/draggable_wrapper.test.tsx | 12 - .../drag_and_drop/draggable_wrapper.tsx | 83 +- .../drag_and_drop/droppable_wrapper.tsx | 5 +- .../draggables/field_badge/index.tsx | 21 +- .../public/components/draggables/index.tsx | 13 +- .../embeddables/map_tool_tip/map_tool_tip.tsx | 79 +- .../components/event_details/columns.tsx | 40 +- .../event_details_width_context.tsx | 27 + .../events_viewer/events_viewer.tsx | 53 +- .../components/fields_browser/field_items.tsx | 102 +- .../flyout/__snapshots__/index.test.tsx.snap | 1 - .../public/components/flyout/header/index.tsx | 27 - .../__snapshots__/index.test.tsx.snap | 13 + .../header_with_close_button/index.test.tsx | 45 + .../flyout/header_with_close_button/index.tsx | 49 + .../header_with_close_button/translations.ts | 14 + .../public/components/flyout/index.test.tsx | 133 +- .../siem/public/components/flyout/index.tsx | 44 +- .../pane/__snapshots__/index.test.tsx.snap | 6 - .../components/flyout/pane/index.test.tsx | 139 +- .../public/components/flyout/pane/index.tsx | 162 +-- .../components/flyout/pane/translations.ts | 7 - .../siem/public/components/inspect/index.tsx | 33 +- .../delete_timeline_modal/index.tsx | 10 +- .../siem/public/components/page/index.tsx | 37 +- .../components/recent_timelines/index.tsx | 4 +- .../components/skeleton_row/index.test.tsx | 11 +- .../public/components/skeleton_row/index.tsx | 5 +- .../__snapshots__/timeline.test.tsx.snap | 1230 +++++++++-------- .../__snapshots__/index.test.tsx.snap | 2 +- .../body/column_headers/column_header.tsx | 116 +- .../timeline/body/column_headers/index.tsx | 254 ++-- .../__snapshots__/index.test.tsx.snap | 42 +- .../body/data_driven_columns/index.tsx | 49 +- .../components/timeline/body/events/index.tsx | 5 +- .../timeline/body/events/stateful_event.tsx | 149 +- .../body/events/stateful_event_child.tsx | 138 -- .../public/components/timeline/body/index.tsx | 15 +- .../get_row_renderer.test.tsx.snap | 8 +- .../plain_row_renderer.test.tsx.snap | 8 +- .../generic_row_renderer.test.tsx.snap | 6 - .../auditd/generic_row_renderer.test.tsx | 38 +- .../renderers/auditd/generic_row_renderer.tsx | 48 +- .../body/renderers/get_row_renderer.test.tsx | 19 +- .../netflow_row_renderer.test.tsx.snap | 3 - .../netflow/netflow_row_renderer.test.tsx | 17 - .../netflow/netflow_row_renderer.tsx | 126 +- .../renderers/plain_row_renderer.test.tsx | 4 +- .../body/renderers/plain_row_renderer.tsx | 2 +- .../timeline/body/renderers/row_renderer.tsx | 25 +- .../suricata_row_renderer.test.tsx.snap | 3 - .../suricata/suricata_row_renderer.test.tsx | 22 +- .../suricata/suricata_row_renderer.tsx | 16 +- .../renderers/suricata/suricata_signature.tsx | 10 +- .../generic_row_renderer.test.tsx.snap | 6 - .../system/generic_row_renderer.test.tsx | 74 +- .../renderers/system/generic_row_renderer.tsx | 161 +-- .../zeek_row_renderer.test.tsx.snap | 3 - .../renderers/zeek/zeek_row_renderer.test.tsx | 19 +- .../body/renderers/zeek/zeek_row_renderer.tsx | 11 +- .../body/renderers/zeek/zeek_signature.tsx | 10 +- .../timeline/body/stateful_body.tsx | 42 +- .../timeline/data_providers/empty.tsx | 26 +- .../provider_item_and_drag_drop.tsx | 23 +- .../timeline/expandable_event/index.tsx | 48 +- .../timeline/fetch_kql_timeline.tsx | 10 +- .../footer/__snapshots__/index.test.tsx.snap | 162 ++- .../components/timeline/footer/index.test.tsx | 10 - .../components/timeline/footer/index.tsx | 203 +-- .../header/__snapshots__/index.test.tsx.snap | 6 +- .../components/timeline/header/index.test.tsx | 21 +- .../components/timeline/header/index.tsx | 33 +- .../public/components/timeline/helpers.tsx | 19 - .../siem/public/components/timeline/index.tsx | 17 +- .../timeline/properties/helpers.tsx | 47 +- .../timeline/properties/index.test.tsx | 30 +- .../components/timeline/properties/index.tsx | 57 +- .../timeline/properties/properties_left.tsx | 12 +- .../components/timeline/properties/styles.tsx | 13 +- .../components/timeline/refetch_timeline.tsx | 23 +- .../public/components/timeline/styles.tsx | 46 +- .../components/timeline/timeline.test.tsx | 522 ++----- .../public/components/timeline/timeline.tsx | 190 +-- .../components/timeline/timeline_context.tsx | 15 +- .../components/toasters/modal_all_errors.tsx | 19 +- .../plugins/siem/public/components/utils.ts | 11 + .../siem/public/containers/timeline/index.tsx | 6 +- .../components/import_rule_modal/index.tsx | 4 +- .../plugins/siem/public/pages/home/index.tsx | 30 +- .../translations/translations/ja-JP.json | 2 +- .../translations/translations/zh-CN.json | 2 +- yarn.lock | 8 +- 99 files changed, 2306 insertions(+), 3190 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/events_viewer/event_details_width_context.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/translations.ts delete mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx diff --git a/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts index 4889d40ae7d39..aca988e195161 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_data_providers.spec.ts @@ -49,7 +49,7 @@ describe('timeline data providers', () => { .first() .invoke('text') .should(hostname => { - expect(dataProviderText).to.eq(`host.name: "${hostname}"`); + expect(dataProviderText).to.eq(hostname); }); }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts index 1a94a4abbe5bf..736eee421a305 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TIMELINE_FLYOUT_BODY, TIMELINE_NOT_READY_TO_DROP_BUTTON } from '../screens/timeline'; +import { TIMELINE_FLYOUT_HEADER, TIMELINE_NOT_READY_TO_DROP_BUTTON } from '../screens/timeline'; import { dragFirstHostToTimeline, waitForAllHostsToBeLoaded } from '../tasks/hosts/all_hosts'; import { loginAndWaitForPage } from '../tasks/login'; @@ -26,7 +26,7 @@ describe('timeline flyout button', () => { it('toggles open the timeline', () => { openTimeline(); - cy.get(TIMELINE_FLYOUT_BODY).should('have.css', 'visibility', 'visible'); + cy.get(TIMELINE_FLYOUT_HEADER).should('have.css', 'visibility', 'visible'); }); it('sets the flyout button background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the flyout button', () => { diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts index 5638b8d23e83a..fbce585a70f86 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts @@ -31,6 +31,8 @@ export const TIMELINE_DROPPED_DATA_PROVIDERS = '[data-test-subj="providerContain export const TIMELINE_FIELDS_BUTTON = '[data-test-subj="timeline"] [data-test-subj="show-field-browser"]'; +export const TIMELINE_FLYOUT_HEADER = '[data-test-subj="eui-flyout-header"]'; + export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]'; export const TIMELINE_INSPECT_BUTTON = '[data-test-subj="inspect-empty-button"]'; diff --git a/x-pack/legacy/plugins/siem/package.json b/x-pack/legacy/plugins/siem/package.json index ad4a6e86ffc88..472a473842f02 100644 --- a/x-pack/legacy/plugins/siem/package.json +++ b/x-pack/legacy/plugins/siem/package.json @@ -14,7 +14,7 @@ "devDependencies": { "@types/lodash": "^4.14.110", "@types/js-yaml": "^3.12.1", - "@types/react-beautiful-dnd": "^11.0.4" + "@types/react-beautiful-dnd": "^12.1.1" }, "dependencies": { "lodash": "^4.17.15", diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx index f3b2b736ed87d..5c15f2d3c8d4f 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx @@ -16,8 +16,8 @@ import { RecursivePartial, } from '@elastic/charts'; import { getOr, get, isNull, isNumber } from 'lodash/fp'; -import useResizeObserver from 'use-resize-observer/polyfilled'; +import { useThrottledResizeObserver } from '../utils'; import { ChartPlaceHolder } from './chart_place_holder'; import { useTimeZone } from '../../lib/kibana'; import { @@ -131,7 +131,7 @@ interface AreaChartComponentProps { } export const AreaChartComponent: React.FC = ({ areaChart, configs }) => { - const { ref: measureRef, width, height } = useResizeObserver({}); + const { ref: measureRef, width, height } = useThrottledResizeObserver(); const customHeight = get('customHeight', configs); const customWidth = get('customWidth', configs); const chartHeight = getChartHeight(customHeight, height); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx index da0f3d1d0047f..f53a1555fa1f4 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts'; import { getOr, get, isNumber } from 'lodash/fp'; import deepmerge from 'deepmerge'; -import useResizeObserver from 'use-resize-observer/polyfilled'; +import { useThrottledResizeObserver } from '../utils'; import { useTimeZone } from '../../lib/kibana'; import { ChartPlaceHolder } from './chart_place_holder'; import { @@ -105,7 +105,7 @@ interface BarChartComponentProps { } export const BarChartComponent: React.FC = ({ barChart, configs }) => { - const { ref: measureRef, width, height } = useResizeObserver({}); + const { ref: measureRef, width, height } = useThrottledResizeObserver(); const customHeight = get('customHeight', configs); const customWidth = get('customWidth', configs); const chartHeight = getChartHeight(customHeight, height); diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx index 72f5a62d0af97..11db33fff6d72 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { defaultTo, noop } from 'lodash/fp'; +import { noop } from 'lodash/fp'; import React, { useCallback } from 'react'; import { DropResult, DragDropContext } from 'react-beautiful-dnd'; import { connect, ConnectedProps } from 'react-redux'; @@ -103,10 +103,7 @@ DragDropContextWrapperComponent.displayName = 'DragDropContextWrapperComponent'; const emptyDataProviders: dragAndDropModel.IdToDataProvider = {}; // stable reference const mapStateToProps = (state: State) => { - const dataProviders = defaultTo( - emptyDataProviders, - dragAndDropSelectors.dataProvidersSelector(state) - ); + const dataProviders = dragAndDropSelectors.dataProvidersSelector(state) ?? emptyDataProviders; return { dataProviders }; }; diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx index 9dcc335d4ff16..11891afabbf3d 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx @@ -88,21 +88,9 @@ describe('DraggableWrapper', () => { describe('ConditionalPortal', () => { const mount = useMountAppended(); const props = { - usePortal: false, registerProvider: jest.fn(), - isDragging: true, }; - it(`doesn't call registerProvider is NOT isDragging`, () => { - mount( - -
- - ); - - expect(props.registerProvider.mock.calls.length).toEqual(0); - }); - it('calls registerProvider when isDragging', () => { mount( diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx index b7d368639ed92..3a6a4de7984db 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Draggable, DraggableProvided, @@ -15,7 +15,6 @@ import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import { EuiPortal } from '@elastic/eui'; import { dragAndDropActions } from '../../store/drag_and_drop'; import { DataProvider } from '../timeline/data_providers/data_provider'; import { TruncatableText } from '../truncatable_text'; @@ -27,9 +26,6 @@ export const DragEffects = styled.div``; DragEffects.displayName = 'DragEffects'; -export const DraggablePortalContext = createContext(false); -export const useDraggablePortalContext = () => useContext(DraggablePortalContext); - /** * Wraps the `react-beautiful-dnd` error boundary. See also: * https://github.com/atlassian/react-beautiful-dnd/blob/v12.0.0/docs/guides/setup-problem-detection-and-error-recovery.md @@ -89,7 +85,6 @@ export const DraggableWrapper = React.memo( ({ dataProvider, render, truncate }) => { const [providerRegistered, setProviderRegistered] = useState(false); const dispatch = useDispatch(); - const usePortal = useDraggablePortalContext(); const registerProvider = useCallback(() => { if (!providerRegistered) { @@ -113,7 +108,26 @@ export const DraggableWrapper = React.memo( return ( - + ( + +
+ + {render(dataProvider, provided, snapshot)} + +
+
+ )} + > {droppableProvided => (
( key={getDraggableId(dataProvider.id)} > {(provided, snapshot) => ( - - - {truncate && !snapshot.isDragging ? ( - - {render(dataProvider, provided, snapshot)} - - ) : ( - - {render(dataProvider, provided, snapshot)} - - )} - - + {truncate && !snapshot.isDragging ? ( + + {render(dataProvider, provided, snapshot)} + + ) : ( + + {render(dataProvider, provided, snapshot)} + + )} + )} {droppableProvided.placeholder} @@ -178,20 +183,16 @@ DraggableWrapper.displayName = 'DraggableWrapper'; interface ConditionalPortalProps { children: React.ReactNode; - usePortal: boolean; - isDragging: boolean; registerProvider: () => void; } export const ConditionalPortal = React.memo( - ({ children, usePortal, registerProvider, isDragging }) => { + ({ children, registerProvider }) => { useEffect(() => { - if (isDragging) { - registerProvider(); - } - }, [isDragging, registerProvider]); + registerProvider(); + }, [registerProvider]); - return usePortal ? {children} : <>{children}; + return <>{children}; } ); diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/droppable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/droppable_wrapper.tsx index 821ef9be10e8d..a81954f57564e 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/droppable_wrapper.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/droppable_wrapper.tsx @@ -6,7 +6,7 @@ import { rgba } from 'polished'; import React from 'react'; -import { Droppable } from 'react-beautiful-dnd'; +import { Droppable, DraggableChildrenFn } from 'react-beautiful-dnd'; import styled from 'styled-components'; interface Props { @@ -16,6 +16,7 @@ interface Props { isDropDisabled?: boolean; type?: string; render?: ({ isDraggingOver }: { isDraggingOver: boolean }) => React.ReactNode; + renderClone?: DraggableChildrenFn; } const ReactDndDropTarget = styled.div<{ isDraggingOver: boolean; height: string }>` @@ -94,12 +95,14 @@ export const DroppableWrapper = React.memo( isDropDisabled = false, type, render = null, + renderClone, }) => ( {(provided, snapshot) => ( (({ width }) => { + if (width) { + return { + style: { + width: `${width}px`, + }, + }; + } +})` background-color: ${({ theme }) => theme.eui.euiColorEmptyShade}; border: ${({ theme }) => theme.eui.euiBorderThin}; box-shadow: 0 2px 2px -1px ${({ theme }) => rgba(theme.eui.euiColorMediumShade, 0.3)}, @@ -24,12 +36,9 @@ Field.displayName = 'Field'; * Renders a field (e.g. `event.action`) as a draggable badge */ -// Passing the styles directly to the component because the width is -// being calculated and is recommended by Styled Components for performance -// https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 -export const DraggableFieldBadge = React.memo<{ fieldId: string; fieldWidth?: string }>( +export const DraggableFieldBadge = React.memo<{ fieldId: string; fieldWidth?: number }>( ({ fieldId, fieldWidth }) => ( - + {fieldId} ) diff --git a/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx b/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx index 57f047416ec1c..1fe6c936d2823 100644 --- a/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBadge, EuiBadgeProps, EuiToolTip, IconType } from '@elastic/eui'; +import { EuiBadge, EuiToolTip, IconType } from '@elastic/eui'; import React from 'react'; +import styled from 'styled-components'; import { Omit } from '../../../common/utility_types'; import { DragEffects, DraggableWrapper } from '../drag_and_drop/draggable_wrapper'; @@ -116,13 +117,9 @@ export const DefaultDraggable = React.memo( DefaultDraggable.displayName = 'DefaultDraggable'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const Badge = styled(EuiBadge)` -// vertical-align: top; -// `; -export const Badge = (props: EuiBadgeProps) => ( - -); +export const Badge = styled(EuiBadge)` + vertical-align: top; +`; Badge.displayName = 'Badge'; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx index 249aae1eda0eb..15c423a3b3dc1 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.tsx @@ -12,7 +12,6 @@ import { EuiOutsideClickDetector, } from '@elastic/eui'; import { FeatureGeometry, FeatureProperty, MapToolTipProps } from '../types'; -import { DraggablePortalContext } from '../../drag_and_drop/draggable_wrapper'; import { ToolTipFooter } from './tooltip_footer'; import { LineToolTipContent } from './line_tool_tip_content'; import { PointToolTipContent } from './point_tool_tip_content'; @@ -101,46 +100,44 @@ export const MapToolTipComponent = ({ ) : ( - - { - if (closeTooltip != null) { - closeTooltip(); - setFeatureIndex(0); - } - }} - > -
- {featureGeometry != null && featureGeometry.type === 'LineString' ? ( - - ) : ( - - )} - {features.length > 1 && ( - { - setFeatureIndex(featureIndex - 1); - setIsLoadingNextFeature(true); - }} - nextFeature={() => { - setFeatureIndex(featureIndex + 1); - setIsLoadingNextFeature(true); - }} - /> - )} - {isLoadingNextFeature && } -
-
-
+ { + if (closeTooltip != null) { + closeTooltip(); + setFeatureIndex(0); + } + }} + > +
+ {featureGeometry != null && featureGeometry.type === 'LineString' ? ( + + ) : ( + + )} + {features.length > 1 && ( + { + setFeatureIndex(featureIndex - 1); + setIsLoadingNextFeature(true); + }} + nextFeature={() => { + setFeatureIndex(featureIndex + 1); + setIsLoadingNextFeature(true); + }} + /> + )} + {isLoadingNextFeature && } +
+
); }; diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx index e9903ce66d799..cd94a9fdcb5ac 100644 --- a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx @@ -115,6 +115,17 @@ export const getColumns = ({ )} isDropDisabled={true} type={DRAG_TYPE_FIELD} + renderClone={provided => ( +
+ + + +
+ )} > - {(provided, snapshot) => ( + {provided => (
- {!snapshot.isDragging ? ( - - ) : ( - - - - )} +
)}
diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/event_details_width_context.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/event_details_width_context.tsx new file mode 100644 index 0000000000000..86a776a0313cc --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/event_details_width_context.tsx @@ -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. + */ + +import React, { createContext, useContext } from 'react'; +import { useThrottledResizeObserver } from '../utils'; + +const EventDetailsWidthContext = createContext(0); + +export const useEventDetailsWidthContext = () => useContext(EventDetailsWidthContext); + +export const EventDetailsWidthProvider = React.memo(({ children }) => { + const { ref, width } = useThrottledResizeObserver(); + + return ( + <> + + {children} + +
+ + ); +}); + +EventDetailsWidthProvider.displayName = 'EventDetailsWidthProvider'; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index a913186d9ad3b..ea2cb661763fa 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -9,7 +9,6 @@ import { getOr, isEmpty, union } from 'lodash/fp'; import React, { useMemo } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import useResizeObserver from 'use-resize-observer/polyfilled'; import { BrowserFields } from '../../containers/source'; import { TimelineQuery } from '../../containers/timeline'; @@ -25,8 +24,8 @@ import { OnChangeItemsPerPage } from '../timeline/events'; import { Footer, footerHeight } from '../timeline/footer'; import { combineQueries } from '../timeline/helpers'; import { TimelineRefetch } from '../timeline/refetch_timeline'; -import { isCompactFooter } from '../timeline/timeline'; import { ManageTimelineContext, TimelineTypeContextProps } from '../timeline/timeline_context'; +import { EventDetailsWidthProvider } from './event_details_width_context'; import * as i18n from './translations'; import { Filter, @@ -38,15 +37,15 @@ import { inputsModel } from '../../store'; const DEFAULT_EVENTS_VIEWER_HEIGHT = 500; -const WrappedByAutoSizer = styled.div` - width: 100%; -`; // required by AutoSizer -WrappedByAutoSizer.displayName = 'WrappedByAutoSizer'; - const StyledEuiPanel = styled(EuiPanel)` max-width: 100%; `; +const EventsContainerLoading = styled.div` + width: 100%; + overflow: auto; +`; + interface Props { browserFields: BrowserFields; columns: ColumnHeaderOptions[]; @@ -94,7 +93,6 @@ const EventsViewerComponent: React.FC = ({ toggleColumn, utilityBar, }) => { - const { ref: measureRef, width = 0 } = useResizeObserver({}); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const kibana = useKibana(); const combinedQueries = combineQueries({ @@ -117,25 +115,25 @@ const EventsViewerComponent: React.FC = ({ ), [columnsHeader, timelineTypeContext.queryFields] ); + const sortField = useMemo( + () => ({ + sortFieldId: sort.columnId, + direction: sort.sortDirection as Direction, + }), + [sort.columnId, sort.sortDirection] + ); return ( - <> - -
- - - {combinedQueries != null ? ( + {combinedQueries != null ? ( + {({ @@ -169,15 +167,8 @@ const EventsViewerComponent: React.FC = ({ {utilityBar?.(refetch, totalCountMinusDeleted)} -
- + + = ({ />
= ({ tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)} /> -
+ ); }}
- ) : null} - +
+ ) : null} ); }; diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx index 990c2678b1006..62f9297c38ef5 100644 --- a/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx +++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/field_items.tsx @@ -90,6 +90,13 @@ export const getFieldItems = ({ key={`field-browser-field-items-field-droppable-wrapper-${timelineId}-${categoryId}-${field.name}`} isDropDisabled={true} type={DRAG_TYPE_FIELD} + renderClone={provided => ( +
+ + + +
+ )} > - {(provided, snapshot) => ( -
- {!snapshot.isDragging ? ( - - - - c.id === field.name) !== -1} - data-test-subj={`field-${field.name}-checkbox`} - id={field.name || ''} - onChange={() => - toggleColumn({ - columnHeaderType: defaultColumnHeaderType, - id: field.name || '', - width: DEFAULT_COLUMN_MIN_WIDTH, - }) - } - /> - - - - - - - - + {provided => ( +
+ + + + c.id === field.name) !== -1} + data-test-subj={`field-${field.name}-checkbox`} + id={field.name || ''} + onChange={() => + toggleColumn({ + columnHeaderType: defaultColumnHeaderType, + id: field.name || '', + width: DEFAULT_COLUMN_MIN_WIDTH, + }) + } + /> + + - - + + - - - ) : ( - - - - )} + + + + + + +
)} diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/flyout/__snapshots__/index.test.tsx.snap index abdc4f4681294..4bf0033bcb430 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/flyout/__snapshots__/index.test.tsx.snap @@ -3,7 +3,6 @@ exports[`Flyout rendering it renders correctly against snapshot 1`] = ` ( isDataInTimeline, isDatepickerLocked, title, - width = DEFAULT_TIMELINE_WIDTH, noteIds, notesById, timelineId, @@ -77,7 +75,6 @@ const StatefulFlyoutHeader = React.memo( updateTitle={updateTitle} updateNote={updateNote} usersViewing={usersViewing} - width={width} /> ); } @@ -103,7 +100,6 @@ const makeMapStateToProps = () => { kqlQuery, title = '', noteIds = emptyNotesId, - width = DEFAULT_TIMELINE_WIDTH, } = timeline; const history = emptyHistory; // TODO: get history from store via selector @@ -118,7 +114,6 @@ const makeMapStateToProps = () => { isDatepickerLocked: globalInput.linkTo.includes('timeline'), noteIds, title, - width, }; }; return mapStateToProps; @@ -126,28 +121,6 @@ const makeMapStateToProps = () => { const mapDispatchToProps = (dispatch: Dispatch, { timelineId }: OwnProps) => ({ associateNote: (noteId: string) => dispatch(timelineActions.addNote({ id: timelineId, noteId })), - applyDeltaToWidth: ({ - id, - delta, - bodyClientWidthPixels, - maxWidthPercent, - minWidthPixels, - }: { - id: string; - delta: number; - bodyClientWidthPixels: number; - maxWidthPercent: number; - minWidthPixels: number; - }) => - dispatch( - timelineActions.applyDeltaToWidth({ - id, - delta, - bodyClientWidthPixels, - maxWidthPercent, - minWidthPixels, - }) - ), createTimeline: ({ id, show }: { id: string; show?: boolean }) => dispatch( timelineActions.createTimeline({ diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..df96f2a1f7eba --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/__snapshots__/index.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FlyoutHeaderWithCloseButton renders correctly against snapshot 1`] = ` + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.test.tsx new file mode 100644 index 0000000000000..e0eace2ad5b10 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.test.tsx @@ -0,0 +1,45 @@ +/* + * 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 { mount, shallow } from 'enzyme'; +import React from 'react'; + +import { TestProviders } from '../../../mock'; +import { FlyoutHeaderWithCloseButton } from '.'; + +describe('FlyoutHeaderWithCloseButton', () => { + test('renders correctly against snapshot', () => { + const EmptyComponent = shallow( + + + + ); + expect(EmptyComponent.find('FlyoutHeaderWithCloseButton')).toMatchSnapshot(); + }); + + test('it should invoke onClose when the close button is clicked', () => { + const closeMock = jest.fn(); + const wrapper = mount( + + + + ); + wrapper + .find('[data-test-subj="close-timeline"] button') + .first() + .simulate('click'); + + expect(closeMock).toBeCalled(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.tsx new file mode 100644 index 0000000000000..a4d9f0e8293df --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/index.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; +import styled from 'styled-components'; + +import { FlyoutHeader } from '../header'; +import * as i18n from './translations'; + +const FlyoutHeaderContainer = styled.div` + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; +`; + +// manually wrap the close button because EuiButtonIcon can't be a wrapped `styled` +const WrappedCloseButton = styled.div` + margin-right: 5px; +`; + +const FlyoutHeaderWithCloseButtonComponent: React.FC<{ + onClose: () => void; + timelineId: string; + usersViewing: string[]; +}> = ({ onClose, timelineId, usersViewing }) => ( + + + + + + + + +); + +export const FlyoutHeaderWithCloseButton = React.memo(FlyoutHeaderWithCloseButtonComponent); + +FlyoutHeaderWithCloseButton.displayName = 'FlyoutHeaderWithCloseButton'; diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/translations.ts b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/translations.ts new file mode 100644 index 0000000000000..7fcffc9c1f0b4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/flyout/header_with_close_button/translations.ts @@ -0,0 +1,14 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const CLOSE_TIMELINE = i18n.translate( + 'xpack.siem.timeline.flyout.header.closeTimelineButtonLabel', + { + defaultMessage: 'Close timeline', + } +); diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.test.tsx index 83b842956e10e..ab41b4617894e 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.test.tsx @@ -13,9 +13,14 @@ import { apolloClientObservable, mockGlobalState, TestProviders } from '../../mo import { createStore, State } from '../../store'; import { mockDataProviders } from '../timeline/data_providers/mock/mock_data_providers'; -import { Flyout, FlyoutComponent, flyoutHeaderHeight } from '.'; +import { Flyout, FlyoutComponent } from '.'; import { FlyoutButton } from './button'; +jest.mock('../timeline', () => ({ + // eslint-disable-next-line react/display-name + StatefulTimeline: () =>
, +})); + const testFlyoutHeight = 980; const usersViewing = ['elastic']; @@ -26,12 +31,7 @@ describe('Flyout', () => { test('it renders correctly against snapshot', () => { const wrapper = shallow( - + ); expect(wrapper.find('Flyout')).toMatchSnapshot(); @@ -40,12 +40,7 @@ describe('Flyout', () => { test('it renders the default flyout state as a button', () => { const wrapper = mount( - + ); @@ -57,41 +52,13 @@ describe('Flyout', () => { ).toContain('Timeline'); }); - test('it renders the title field when its state is set to flyout is true', () => { - const stateShowIsTrue = set('timeline.timelineById.test.show', true, state); - const storeShowIsTrue = createStore(stateShowIsTrue, apolloClientObservable); - - const wrapper = mount( - - - - ); - - expect( - wrapper - .find('[data-test-subj="timeline-title"]') - .first() - .props().placeholder - ).toContain('Untitled Timeline'); - }); - test('it does NOT render the fly out button when its state is set to flyout is true', () => { const stateShowIsTrue = set('timeline.timelineById.test.show', true, state); const storeShowIsTrue = createStore(stateShowIsTrue, apolloClientObservable); const wrapper = mount( - + ); @@ -100,31 +67,6 @@ describe('Flyout', () => { ); }); - test('it renders the flyout body', () => { - const stateShowIsTrue = set('timeline.timelineById.test.show', true, state); - const storeShowIsTrue = createStore(stateShowIsTrue, apolloClientObservable); - - const wrapper = mount( - - -

{'Fake flyout body'}

-
-
- ); - - expect( - wrapper - .find('[data-test-subj="eui-flyout-body"]') - .first() - .text() - ).toContain('Fake flyout body'); - }); - test('it does render the data providers badge when the number is greater than 0', () => { const stateWithDataProviders = set( 'timeline.timelineById.test.dataProviders', @@ -135,12 +77,7 @@ describe('Flyout', () => { const wrapper = mount( - + ); @@ -157,12 +94,7 @@ describe('Flyout', () => { const wrapper = mount( - + ); @@ -177,12 +109,7 @@ describe('Flyout', () => { test('it hides the data providers badge when the timeline does NOT have data providers', () => { const wrapper = mount( - + ); @@ -204,12 +131,7 @@ describe('Flyout', () => { const wrapper = mount( - + ); @@ -228,7 +150,6 @@ describe('Flyout', () => { { expect(showTimeline).toBeCalled(); }); - - test('should call the onClose when the close button is clicked', () => { - const stateShowIsTrue = set('timeline.timelineById.test.show', true, state); - const storeShowIsTrue = createStore(stateShowIsTrue, apolloClientObservable); - - const showTimeline = (jest.fn() as unknown) as ActionCreator<{ id: string; show: boolean }>; - const wrapper = mount( - - - - ); - - wrapper - .find('[data-test-subj="close-timeline"] button') - .first() - .simulate('click'); - - expect(showTimeline).toBeCalled(); - }); }); describe('showFlyoutButton', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx index 22fc9f27ce26c..44abe5b679c8e 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx @@ -5,7 +5,6 @@ */ import { EuiBadge } from '@elastic/eui'; -import { defaultTo, getOr } from 'lodash/fp'; import React, { useCallback } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import styled from 'styled-components'; @@ -16,9 +15,8 @@ import { FlyoutButton } from './button'; import { Pane } from './pane'; import { timelineActions } from '../../store/actions'; import { DEFAULT_TIMELINE_WIDTH } from '../timeline/body/constants'; - -/** The height in pixels of the flyout header, exported for use in height calculations */ -export const flyoutHeaderHeight: number = 60; +import { StatefulTimeline } from '../timeline'; +import { TimelineById } from '../../store/timeline/types'; export const Badge = styled(EuiBadge)` position: absolute; @@ -38,9 +36,7 @@ const Visible = styled.div<{ show?: boolean }>` Visible.displayName = 'Visible'; interface OwnProps { - children?: React.ReactNode; flyoutHeight: number; - headerHeight: number; timelineId: string; usersViewing: string[]; } @@ -48,17 +44,7 @@ interface OwnProps { type Props = OwnProps & ProsFromRedux; export const FlyoutComponent = React.memo( - ({ - children, - dataProviders, - flyoutHeight, - headerHeight, - show, - showTimeline, - timelineId, - usersViewing, - width, - }) => { + ({ dataProviders, flyoutHeight, show, showTimeline, timelineId, usersViewing, width }) => { const handleClose = useCallback(() => showTimeline({ id: timelineId, show: false }), [ showTimeline, timelineId, @@ -73,17 +59,15 @@ export const FlyoutComponent = React.memo( - {children} + ( FlyoutComponent.displayName = 'FlyoutComponent'; +const DEFAULT_DATA_PROVIDERS: DataProvider[] = []; +const DEFAULT_TIMELINE_BY_ID = {}; + const mapStateToProps = (state: State, { timelineId }: OwnProps) => { - const timelineById = defaultTo({}, timelineSelectors.timelineByIdSelector(state)); - const dataProviders = getOr([], `${timelineId}.dataProviders`, timelineById) as DataProvider[]; - const show = getOr(false, `${timelineId}.show`, timelineById) as boolean; - const width = getOr(DEFAULT_TIMELINE_WIDTH, `${timelineId}.width`, timelineById) as number; + const timelineById: TimelineById = + timelineSelectors.timelineByIdSelector(state) ?? DEFAULT_TIMELINE_BY_ID; + /* + In case timelineById[timelineId]?.dataProviders is an empty array it will cause unnecessary rerender + of StatefulTimeline which can be expensive, so to avoid that return DEFAULT_DATA_PROVIDERS + */ + const dataProviders = timelineById[timelineId]?.dataProviders.length + ? timelineById[timelineId]?.dataProviders + : DEFAULT_DATA_PROVIDERS; + const show = timelineById[timelineId]?.show ?? false; + const width = timelineById[timelineId]?.width ?? DEFAULT_TIMELINE_WIDTH; return { dataProviders, show, width }; }; diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/pane/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/flyout/pane/__snapshots__/index.test.tsx.snap index efa682cd4d18e..d30fd6f31012c 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/pane/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/flyout/pane/__snapshots__/index.test.tsx.snap @@ -3,14 +3,8 @@ exports[`Pane renders correctly against snapshot 1`] = ` diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx index 365f99c6667b8..53cf8f95de0ce 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.test.tsx @@ -8,12 +8,10 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; import { TestProviders } from '../../../mock'; -import { flyoutHeaderHeight } from '..'; import { Pane } from '.'; const testFlyoutHeight = 980; const testWidth = 640; -const usersViewing = ['elastic']; describe('Pane', () => { test('renders correctly against snapshot', () => { @@ -21,10 +19,8 @@ describe('Pane', () => { {'I am a child of flyout'} @@ -39,10 +35,8 @@ describe('Pane', () => { {'I am a child of flyout'} @@ -53,87 +47,13 @@ describe('Pane', () => { expect(wrapper.find('Resizable').get(0).props.maxWidth).toEqual('95vw'); }); - test('it applies timeline styles to the EuiFlyout', () => { - const wrapper = mount( - - - {'I am a child of flyout'} - - - ); - - expect( - wrapper - .find('[data-test-subj="eui-flyout"]') - .first() - .hasClass('timeline-flyout') - ).toEqual(true); - }); - - test('it applies timeline styles to the EuiFlyoutHeader', () => { - const wrapper = mount( - - - {'I am a child of flyout'} - - - ); - - expect( - wrapper - .find('[data-test-subj="eui-flyout-header"]') - .first() - .hasClass('timeline-flyout-header') - ).toEqual(true); - }); - - test('it applies timeline styles to the EuiFlyoutBody', () => { - const wrapper = mount( - - - {'I am a child of flyout'} - - - ); - - expect( - wrapper - .find('[data-test-subj="eui-flyout-body"]') - .first() - .hasClass('timeline-flyout-body') - ).toEqual(true); - }); - test('it should render a resize handle', () => { const wrapper = mount( {'I am a child of flyout'} @@ -149,74 +69,19 @@ describe('Pane', () => { ).toEqual(true); }); - test('it should render an empty title', () => { + test('it should render children', () => { const wrapper = mount( - {'I am a child of flyout'} - - - ); - - expect( - wrapper - .find('[data-test-subj="timeline-title"]') - .first() - .text() - ).toContain(''); - }); - - test('it should render the flyout body', () => { - const wrapper = mount( - - {'I am a mock body'} ); - expect( - wrapper - .find('[data-test-subj="eui-flyout-body"]') - .first() - .text() - ).toContain('I am a mock body'); - }); - - test('it should invoke onClose when the close button is clicked', () => { - const closeMock = jest.fn(); - const wrapper = mount( - - - {'I am a mock child'} - - - ); - wrapper - .find('[data-test-subj="close-timeline"] button') - .first() - .simulate('click'); - - expect(closeMock).toBeCalled(); + expect(wrapper.first().text()).toContain('I am a mock body'); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx index 38ec4a4b6f1f3..3b5041c1ee346 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx @@ -4,130 +4,85 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonIcon, EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiToolTip } from '@elastic/eui'; -import React, { useCallback, useState } from 'react'; -import { connect, ConnectedProps } from 'react-redux'; +import { EuiFlyout } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { Resizable, ResizeCallback } from 're-resizable'; -import { throttle } from 'lodash/fp'; import { TimelineResizeHandle } from './timeline_resize_handle'; -import { FlyoutHeader } from '../header'; +import { EventDetailsWidthProvider } from '../../events_viewer/event_details_width_context'; import * as i18n from './translations'; import { timelineActions } from '../../../store/actions'; const minWidthPixels = 550; // do not allow the flyout to shrink below this width (pixels) const maxWidthPercent = 95; // do not allow the flyout to grow past this percentage of the view -interface OwnProps { +interface FlyoutPaneComponentProps { children: React.ReactNode; flyoutHeight: number; - headerHeight: number; onClose: () => void; timelineId: string; - usersViewing: string[]; width: number; } -type Props = OwnProps & PropsFromRedux; - -const EuiFlyoutContainer = styled.div<{ headerHeight: number }>` +const EuiFlyoutContainer = styled.div` .timeline-flyout { min-width: 150px; width: auto; } - .timeline-flyout-header { - align-items: center; - box-shadow: none; - display: flex; - flex-direction: row; - height: ${({ headerHeight }) => `${headerHeight}px`}; - max-height: ${({ headerHeight }) => `${headerHeight}px`}; - overflow: hidden; - padding: 5px 0 0 10px; - } - .timeline-flyout-body { - overflow-y: hidden; - padding: 0; - .euiFlyoutBody__overflowContent { - padding: 0; - } - } `; -const FlyoutHeaderContainer = styled.div` - align-items: center; +const StyledResizable = styled(Resizable)` display: flex; - flex-direction: row; - justify-content: space-between; - width: 100%; -`; - -// manually wrap the close button because EuiButtonIcon can't be a wrapped `styled` -const WrappedCloseButton = styled.div` - margin-right: 5px; + flex-direction: column; `; -const FlyoutHeaderWithCloseButtonComponent: React.FC<{ - onClose: () => void; - timelineId: string; - usersViewing: string[]; -}> = ({ onClose, timelineId, usersViewing }) => ( - - - - - - - - -); - -const FlyoutHeaderWithCloseButton = React.memo( - FlyoutHeaderWithCloseButtonComponent, - (prevProps, nextProps) => - prevProps.timelineId === nextProps.timelineId && - prevProps.usersViewing === nextProps.usersViewing -); +const RESIZABLE_ENABLE = { left: true }; -const FlyoutPaneComponent: React.FC = ({ - applyDeltaToWidth, +const FlyoutPaneComponent: React.FC = ({ children, flyoutHeight, - headerHeight, onClose, timelineId, - usersViewing, width, }) => { - const [lastDelta, setLastDelta] = useState(0); + const dispatch = useDispatch(); + const onResizeStop: ResizeCallback = useCallback( (e, direction, ref, delta) => { const bodyClientWidthPixels = document.body.clientWidth; if (delta.width) { - applyDeltaToWidth({ - bodyClientWidthPixels, - delta: -(delta.width - lastDelta), - id: timelineId, - maxWidthPercent, - minWidthPixels, - }); - setLastDelta(delta.width); + dispatch( + timelineActions.applyDeltaToWidth({ + bodyClientWidthPixels, + delta: -delta.width, + id: timelineId, + maxWidthPercent, + minWidthPixels, + }) + ); } }, - [applyDeltaToWidth, maxWidthPercent, minWidthPixels, lastDelta] + [dispatch] + ); + const resizableDefaultSize = useMemo( + () => ({ + width, + height: '100%', + }), + [] + ); + const resizableHandleComponent = useMemo( + () => ({ + left: , + }), + [flyoutHeight] ); - const resetLastDelta = useCallback(() => setLastDelta(0), [setLastDelta]); - const throttledResize = throttle(100, onResizeStop); return ( - + = ({ onClose={onClose} size="l" > - - ), - }} - onResizeStart={resetLastDelta} - onResize={throttledResize} + handleComponent={resizableHandleComponent} + onResizeStop={onResizeStop} > - - - - - {children} - - + {children} + ); }; -const mapDispatchToProps = { - applyDeltaToWidth: timelineActions.applyDeltaToWidth, -}; - -const connector = connect(null, mapDispatchToProps); - -type PropsFromRedux = ConnectedProps; - -export const Pane = connector(React.memo(FlyoutPaneComponent)); +export const Pane = React.memo(FlyoutPaneComponent); Pane.displayName = 'Pane'; diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/pane/translations.ts b/x-pack/legacy/plugins/siem/public/components/flyout/pane/translations.ts index 4ba0307eb527b..0c31cdb81e8e1 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/pane/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/flyout/pane/translations.ts @@ -12,10 +12,3 @@ export const TIMELINE_DESCRIPTION = i18n.translate( defaultMessage: 'Timeline Properties', } ); - -export const CLOSE_TIMELINE = i18n.translate( - 'xpack.siem.timeline.flyout.pane.closeTimelineButtonLabel', - { - defaultMessage: 'Close timeline', - } -); diff --git a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx index d6f8143745356..f10a740db2b93 100644 --- a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx @@ -7,11 +7,10 @@ import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { getOr, omit } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import styled, { css } from 'styled-components'; -import { inputsModel, inputsSelectors, State } from '../../store'; +import { inputsSelectors, State } from '../../store'; import { InputsModelId } from '../../store/inputs/constants'; import { inputsActions } from '../../store/inputs'; @@ -60,24 +59,7 @@ interface OwnProps { title: string | React.ReactElement | React.ReactNode; } -interface InspectButtonReducer { - id: string; - isInspected: boolean; - loading: boolean; - inspect: inputsModel.InspectQuery | null; - selectedInspectIndex: number; -} - -interface InspectButtonDispatch { - setIsInspected: ActionCreator<{ - id: string; - inputId: InputsModelId; - isInspected: boolean; - selectedInspectIndex: number; - }>; -} - -type InspectButtonProps = OwnProps & InspectButtonReducer & InspectButtonDispatch; +type InspectButtonProps = OwnProps & PropsFromRedux; const InspectButtonComponent: React.FC = ({ compact = false, @@ -175,7 +157,8 @@ const mapDispatchToProps = { setIsInspected: inputsActions.setInspectionParameter, }; -export const InspectButton = connect( - makeMapStateToProps, - mapDispatchToProps -)(React.memo(InspectButtonComponent)); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const InspectButton = connector(React.memo(InspectButtonComponent)); diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/delete_timeline_modal/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/delete_timeline_modal/index.tsx index caa9cd0689c76..982937659c0aa 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/delete_timeline_modal/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/delete_timeline_modal/index.tsx @@ -5,7 +5,7 @@ */ import { EuiButtonIcon, EuiModal, EuiToolTip, EuiOverlayMask } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { DeleteTimelineModal, DELETE_TIMELINE_MODAL_WIDTH } from './delete_timeline_modal'; import * as i18n from '../translations'; @@ -23,15 +23,15 @@ export const DeleteTimelineModalButton = React.memo( ({ deleteTimelines, savedObjectId, title }) => { const [showModal, setShowModal] = useState(false); - const openModal = () => setShowModal(true); - const closeModal = () => setShowModal(false); + const openModal = useCallback(() => setShowModal(true), [setShowModal]); + const closeModal = useCallback(() => setShowModal(false), [setShowModal]); - const onDelete = () => { + const onDelete = useCallback(() => { if (deleteTimelines != null && savedObjectId != null) { deleteTimelines([savedObjectId]); } closeModal(); - }; + }, [deleteTimelines, savedObjectId, closeModal]); return ( <> diff --git a/x-pack/legacy/plugins/siem/public/components/page/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/index.tsx index 781155c3ddc38..ef6a19f4b7448 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/index.tsx @@ -4,15 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { - EuiBadge, - EuiBadgeProps, - EuiDescriptionList, - EuiFlexGroup, - EuiIcon, - EuiPage, -} from '@elastic/eui'; +import { EuiBadge, EuiDescriptionList, EuiFlexGroup, EuiIcon, EuiPage } from '@elastic/eui'; import styled, { createGlobalStyle } from 'styled-components'; /* @@ -20,6 +12,12 @@ import styled, { createGlobalStyle } from 'styled-components'; and `EuiPopover`, `EuiToolTip` global styles */ export const AppGlobalStyle = createGlobalStyle` + /* dirty hack to fix draggables with tooltip on FF */ + body#siem-app { + position: static; + } + /* end of dirty hack to fix draggables with tooltip on FF */ + div.app-wrapper { background-color: rgba(0,0,0,0); } @@ -107,6 +105,7 @@ export const PageHeader = styled.div` PageHeader.displayName = 'PageHeader'; export const FooterContainer = styled.div` + flex: 0; bottom: 0; color: #666; left: 0; @@ -154,13 +153,9 @@ export const Pane1FlexContent = styled.div` Pane1FlexContent.displayName = 'Pane1FlexContent'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const Badge = styled(EuiBadge)` -// margin-left: 5px; -// `; -export const CountBadge = (props: EuiBadgeProps) => ( - -); +export const CountBadge = styled(EuiBadge)` + margin-left: 5px; +`; CountBadge.displayName = 'CountBadge'; @@ -170,13 +165,9 @@ export const Spacer = styled.span` Spacer.displayName = 'Spacer'; -// Ref: https://github.com/elastic/eui/issues/1655 -// export const Badge = styled(EuiBadge)` -// vertical-align: top; -// `; -export const Badge = (props: EuiBadgeProps) => ( - -); +export const Badge = styled(EuiBadge)` + vertical-align: top; +`; Badge.displayName = 'Badge'; diff --git a/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx b/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx index 6f22287774d7e..a557e4cea2df0 100644 --- a/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx @@ -9,7 +9,6 @@ import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; import React, { useCallback } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; import { AllTimelinesQuery } from '../../containers/timeline/all'; import { SortFieldTimeline, Direction } from '../../graphql/types'; @@ -31,14 +30,13 @@ export type Props = OwnProps & PropsFromRedux; const StatefulRecentTimelinesComponent = React.memo( ({ apolloClient, filterBy, updateIsLoading, updateTimeline }) => { - const actionDispatcher = updateIsLoading as ActionCreator<{ id: string; isLoading: boolean }>; const onOpenTimeline: OnOpenTimeline = useCallback( ({ duplicate, timelineId }: { duplicate: boolean; timelineId: string }) => { queryTimelineById({ apolloClient, duplicate, timelineId, - updateIsLoading: actionDispatcher, + updateIsLoading, updateTimeline, }); }, diff --git a/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.test.tsx index 1fdcd8eee941f..0ee54a1a20003 100644 --- a/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.test.tsx @@ -26,16 +26,10 @@ describe('SkeletonRow', () => { expect(wrapper.find('.siemSkeletonRow__cell')).toHaveLength(10); }); - test('it applies row and cell styles when cellColor/cellMargin/rowHeight/rowPadding/style provided', () => { + test('it applies row and cell styles when cellColor/cellMargin/rowHeight/rowPadding provided', () => { const wrapper = mount( - + ); const siemSkeletonRow = wrapper.find('.siemSkeletonRow').first(); @@ -43,7 +37,6 @@ describe('SkeletonRow', () => { expect(siemSkeletonRow).toHaveStyleRule('height', '100px'); expect(siemSkeletonRow).toHaveStyleRule('padding', '10px'); - expect(siemSkeletonRow.props().style!.width).toBe('auto'); expect(siemSkeletonRowCell).toHaveStyleRule('background-color', 'red'); expect(siemSkeletonRowCell).toHaveStyleRule('margin-left', '10px', { modifier: '& + &', diff --git a/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx b/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx index dce360877130e..ae30f11d8bb16 100644 --- a/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/skeleton_row/index.tsx @@ -54,11 +54,10 @@ Cell.displayName = 'Cell'; export interface SkeletonRowProps extends CellProps, RowProps { cellCount?: number; - style?: object; } export const SkeletonRow = React.memo( - ({ cellColor, cellCount = 4, cellMargin, rowHeight, rowPadding, style }) => { + ({ cellColor, cellCount = 4, cellMargin, rowHeight, rowPadding }) => { const cells = useMemo( () => [...Array(cellCount)].map( @@ -69,7 +68,7 @@ export const SkeletonRow = React.memo( ); return ( - + {cells} ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap index 372930ee3167d..02938cb2b86b9 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap @@ -1,668 +1,702 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Timeline rendering renders correctly against snapshot 1`] = ` - - - + + + - + onChangeDataProviderKqlQuery={[MockFunction]} + onChangeDroppableAndProvider={[MockFunction]} + onDataProviderEdited={[MockFunction]} + onDataProviderRemoved={[MockFunction]} + onToggleDataProviderEnabled={[MockFunction]} + onToggleDataProviderExcluded={[MockFunction]} + show={true} + showCallOutUnauthorizedMsg={false} + /> + + - + `; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap index b8b03be4e4720..03e4f4b5f0f2b 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap @@ -490,7 +490,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = ` isCombineEnabled={false} isDropDisabled={false} mode="standard" - renderClone={null} + renderClone={[Function]} type="drag-type-field" > diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx index c3f28fd513d08..e070ed8fa1d2a 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/column_header.tsx @@ -4,27 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Draggable } from 'react-beautiful-dnd'; import { Resizable, ResizeCallback } from 're-resizable'; +import deepEqual from 'fast-deep-equal'; import { ColumnHeaderOptions } from '../../../../store/timeline/model'; -import { DragEffects } from '../../../drag_and_drop/draggable_wrapper'; -import { getDraggableFieldId, DRAG_TYPE_FIELD } from '../../../drag_and_drop/helpers'; -import { DraggableFieldBadge } from '../../../draggables/field_badge'; +import { getDraggableFieldId } from '../../../drag_and_drop/helpers'; import { OnColumnRemoved, OnColumnSorted, OnFilterChange, OnColumnResized } from '../../events'; import { EventsTh, EventsThContent, EventsHeadingHandle } from '../../styles'; import { Sort } from '../sort'; -import { DraggingContainer } from './common/dragging_container'; import { Header } from './header'; +const RESIZABLE_ENABLE = { right: true }; + interface ColumneHeaderProps { draggableIndex: number; header: ColumnHeaderOptions; onColumnRemoved: OnColumnRemoved; onColumnSorted: OnColumnSorted; onColumnResized: OnColumnResized; + isDragging: boolean; onFilterChange?: OnFilterChange; sort: Sort; timelineId: string; @@ -34,69 +35,82 @@ const ColumnHeaderComponent: React.FC = ({ draggableIndex, header, timelineId, + isDragging, onColumnRemoved, onColumnResized, onColumnSorted, onFilterChange, sort, }) => { - const [isDragging, setIsDragging] = React.useState(false); - const handleResizeStop: ResizeCallback = (e, direction, ref, delta) => { - onColumnResized({ columnId: header.id, delta: delta.width }); - }; + const resizableSize = useMemo( + () => ({ + width: header.width, + height: 'auto', + }), + [header.width] + ); + const resizableStyle: { + position: 'absolute' | 'relative'; + } = useMemo( + () => ({ + position: isDragging ? 'absolute' : 'relative', + }), + [isDragging] + ); + const resizableHandleComponent = useMemo( + () => ({ + right: , + }), + [] + ); + const handleResizeStop: ResizeCallback = useCallback( + (e, direction, ref, delta) => { + onColumnResized({ columnId: header.id, delta: delta.width }); + }, + [header.id, onColumnResized] + ); + const draggableId = useMemo( + () => + getDraggableFieldId({ + contextId: `timeline-column-headers-${timelineId}`, + fieldId: header.id, + }), + [timelineId, header.id] + ); return ( , - }} + enable={RESIZABLE_ENABLE} + size={resizableSize} + style={resizableStyle} + handleComponent={resizableHandleComponent} onResizeStop={handleResizeStop} > - {(dragProvided, dragSnapshot) => ( + {dragProvided => ( - {!dragSnapshot.isDragging ? ( - -
- - ) : ( - - - - - - )} + +
+ )} @@ -104,4 +118,16 @@ const ColumnHeaderComponent: React.FC = ({ ); }; -export const ColumnHeader = React.memo(ColumnHeaderComponent); +export const ColumnHeader = React.memo( + ColumnHeaderComponent, + (prevProps, nextProps) => + prevProps.draggableIndex === nextProps.draggableIndex && + prevProps.timelineId === nextProps.timelineId && + prevProps.isDragging === nextProps.isDragging && + prevProps.onColumnRemoved === nextProps.onColumnRemoved && + prevProps.onColumnResized === nextProps.onColumnResized && + prevProps.onColumnSorted === nextProps.onColumnSorted && + prevProps.onFilterChange === nextProps.onFilterChange && + prevProps.sort === nextProps.sort && + deepEqual(prevProps.header, nextProps.header) +); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx index ab8dc629dd577..7a072f1dbf578 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/column_headers/index.tsx @@ -6,9 +6,12 @@ import { EuiCheckbox } from '@elastic/eui'; import { noop } from 'lodash/fp'; -import React from 'react'; -import { Droppable } from 'react-beautiful-dnd'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import { Droppable, DraggableChildrenFn } from 'react-beautiful-dnd'; +import deepEqual from 'fast-deep-equal'; +import { DragEffects } from '../../../drag_and_drop/draggable_wrapper'; +import { DraggableFieldBadge } from '../../../draggables/field_badge'; import { BrowserFields } from '../../../../containers/source'; import { ColumnHeaderOptions } from '../../../../store/timeline/model'; import { DRAG_TYPE_FIELD, droppableTimelineColumnsPrefix } from '../../../drag_and_drop/helpers'; @@ -53,6 +56,26 @@ interface Props { toggleColumn: (column: ColumnHeaderOptions) => void; } +interface DraggableContainerProps { + children: React.ReactNode; + onMount: () => void; + onUnmount: () => void; +} + +export const DraggableContainer = React.memo( + ({ children, onMount, onUnmount }) => { + useEffect(() => { + onMount(); + + return () => onUnmount(); + }, [onMount, onUnmount]); + + return <>{children}; + } +); + +DraggableContainer.displayName = 'DraggableContainer'; + /** Renders the timeline header columns */ export const ColumnHeadersComponent = ({ actionsColumnWidth, @@ -71,86 +94,157 @@ export const ColumnHeadersComponent = ({ sort, timelineId, toggleColumn, -}: Props) => ( - - - - {showEventsSelect && ( - - - - - - )} - {showSelectAllCheckbox && ( +}: Props) => { + const [draggingIndex, setDraggingIndex] = useState(null); + + const handleSelectAllChange = useCallback( + (event: React.ChangeEvent) => { + onSelectAll({ isSelected: event.currentTarget.checked }); + }, + [onSelectAll] + ); + + const renderClone: DraggableChildrenFn = useCallback( + (dragProvided, dragSnapshot, rubric) => { + // TODO: Remove after github.com/DefinitelyTyped/DefinitelyTyped/pull/43057 is merged + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const index = (rubric as any).source.index; + const header = columnHeaders[index]; + + const onMount = () => setDraggingIndex(index); + const onUnmount = () => setDraggingIndex(null); + + return ( + + + + + + + + ); + }, + [columnHeaders, setDraggingIndex] + ); + + const ColumnHeaderList = useMemo( + () => + columnHeaders.map((header, draggableIndex) => ( + + )), + [ + columnHeaders, + timelineId, + draggingIndex, + onColumnRemoved, + onFilterChange, + onColumnResized, + sort, + ] + ); + + return ( + + + + {showEventsSelect && ( + + + + + + )} + {showSelectAllCheckbox && ( + + + + + + )} - - ) => { - onSelectAll({ isSelected: event.currentTarget.checked }); - }} + + - )} - - - - - - - - - {(dropProvided, snapshot) => ( - <> - - {columnHeaders.map((header, draggableIndex) => ( - - ))} - - {dropProvided.placeholder} - - )} - - - -); + -export const ColumnHeaders = React.memo(ColumnHeadersComponent); + + {(dropProvided, snapshot) => ( + <> + + {ColumnHeaderList} + + + )} + + + + ); +}; + +export const ColumnHeaders = React.memo( + ColumnHeadersComponent, + (prevProps, nextProps) => + prevProps.actionsColumnWidth === nextProps.actionsColumnWidth && + prevProps.isEventViewer === nextProps.isEventViewer && + prevProps.isSelectAllChecked === nextProps.isSelectAllChecked && + prevProps.onColumnRemoved === nextProps.onColumnRemoved && + prevProps.onColumnResized === nextProps.onColumnResized && + prevProps.onColumnSorted === nextProps.onColumnSorted && + prevProps.onSelectAll === nextProps.onSelectAll && + prevProps.onUpdateColumns === nextProps.onUpdateColumns && + prevProps.onFilterChange === nextProps.onFilterChange && + prevProps.showEventsSelect === nextProps.showEventsSelect && + prevProps.showSelectAllCheckbox === nextProps.showSelectAllCheckbox && + prevProps.sort === nextProps.sort && + prevProps.timelineId === nextProps.timelineId && + prevProps.toggleColumn === nextProps.toggleColumn && + deepEqual(prevProps.columnHeaders, nextProps.columnHeaders) && + deepEqual(prevProps.browserFields, nextProps.browserFields) +); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap index 93e12a0ed4fcd..75623252181db 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap @@ -6,11 +6,7 @@ exports[`Columns it renders the expected columns 1`] = ` > ( - ({ _id, columnHeaders, columnRenderers, data, ecsData, timelineId }) => { - // Passing the styles directly to the component because the width is - // being calculated and is recommended by Styled Components for performance - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 - return ( - - {columnHeaders.map((header, index) => ( - - - {getColumnRenderer(header.id, columnRenderers, data).renderColumn({ - columnName: header.id, - eventId: _id, - field: header, - linkValues: getOr([], header.linkField ?? '', ecsData), - timelineId, - truncate: true, - values: getMappedNonEcsValue({ - data, - fieldName: header.id, - }), - })} - - - ))} - - ); - } + ({ _id, columnHeaders, columnRenderers, data, ecsData, timelineId }) => ( + + {columnHeaders.map(header => ( + + + {getColumnRenderer(header.id, columnRenderers, data).renderColumn({ + columnName: header.id, + eventId: _id, + field: header, + linkValues: getOr([], header.linkField ?? '', ecsData), + timelineId, + truncate: true, + values: getMappedNonEcsValue({ + data, + fieldName: header.id, + }), + })} + + + ))} + + ) ); DataDrivenColumns.displayName = 'DataDrivenColumns'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx index 84c4253076dc9..4178bc656f32d 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/index.tsx @@ -51,9 +51,6 @@ interface Props { updateNote: UpdateNote; } -// Passing the styles directly to the component because the width is -// being calculated and is recommended by Styled Components for performance -// https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 const EventsComponent: React.FC = ({ actionsColumnWidth, addNoteToEvent, @@ -93,7 +90,7 @@ const EventsComponent: React.FC = ({ getNotesByIds={getNotesByIds} isEventPinned={eventIsPinned({ eventId: event._id, pinnedEventIds })} isEventViewer={isEventViewer} - key={event._id} + key={`${event._id}_${event._index}`} loadingEventIds={loadingEventIds} maxDelay={maxDelay(i)} onColumnResized={onColumnResized} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx index 1f09ae4337c42..6e5c292064dc6 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event.tsx @@ -25,13 +25,14 @@ import { } from '../../events'; import { ExpandableEvent } from '../../expandable_event'; import { STATEFUL_EVENT_CSS_CLASS_NAME } from '../../helpers'; -import { EventsTrGroup, EventsTrSupplement, OFFSET_SCROLLBAR } from '../../styles'; -import { useTimelineWidthContext } from '../../timeline_context'; +import { EventsTrGroup, EventsTrSupplement, EventsTrSupplementContainer } from '../../styles'; import { ColumnRenderer } from '../renderers/column_renderer'; import { getRowRenderer } from '../renderers/get_row_renderer'; import { RowRenderer } from '../renderers/row_renderer'; import { getEventType } from '../helpers'; -import { StatefulEventChild } from './stateful_event_child'; +import { NoteCards } from '../../../notes/note_cards'; +import { useEventDetailsWidthContext } from '../../../events_viewer/event_details_width_context'; +import { EventColumnView } from './event_column_view'; interface Props { actionsColumnWidth: number; @@ -89,28 +90,14 @@ const TOP_OFFSET = 50; */ const BOTTOM_OFFSET = -500; -interface AttributesProps { - children: React.ReactNode; -} - -const AttributesComponent: React.FC = ({ children }) => { - const width = useTimelineWidthContext(); +const emptyNotes: string[] = []; - // Passing the styles directly to the component because the width is - // being calculated and is recommended by Styled Components for performance - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 - return ( - - {children} - - ); -}; +const EventsTrSupplementContainerWrapper = React.memo(({ children }) => { + const width = useEventDetailsWidthContext(); + return {children}; +}); -const Attributes = React.memo(AttributesComponent); +EventsTrSupplementContainerWrapper.displayName = 'EventsTrSupplementContainerWrapper'; const StatefulEventComponent: React.FC = ({ actionsColumnWidth, @@ -221,60 +208,75 @@ const StatefulEventComponent: React.FC = ({ data-test-subj="event" eventType={getEventType(event.ecs)} showLeftBorder={!isEventViewer} - ref={c => { - if (c != null) { - divElement.current = c; - } - }} + ref={divElement} > - {getRowRenderer(event.ecs, rowRenderers).renderRow({ - browserFields, - data: event.ecs, - children: ( - + + + + - ), - timelineId, - })} + - - - + {getRowRenderer(event.ecs, rowRenderers).renderRow({ + browserFields, + data: event.ecs, + timelineId, + })} + + + + + )} @@ -286,10 +288,7 @@ const StatefulEventComponent: React.FC = ({ ? `${divElement.current.clientHeight}px` : DEFAULT_ROW_HEIGHT; - // height is being inlined directly in here because of performance with StyledComponents - // involving quick and constant changes to the DOM. - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 - return ; + return ; } }} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx deleted file mode 100644 index 04f4ddf2a6eab..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/events/stateful_event_child.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import uuid from 'uuid'; - -import { TimelineNonEcsData, Ecs } from '../../../../graphql/types'; -import { Note } from '../../../../lib/note'; -import { ColumnHeaderOptions } from '../../../../store/timeline/model'; -import { AddNoteToEvent, UpdateNote } from '../../../notes/helpers'; -import { NoteCards } from '../../../notes/note_cards'; -import { OnPinEvent, OnColumnResized, OnUnPinEvent, OnRowSelected } from '../../events'; -import { EventsTrSupplement, OFFSET_SCROLLBAR } from '../../styles'; -import { useTimelineWidthContext } from '../../timeline_context'; -import { ColumnRenderer } from '../renderers/column_renderer'; -import { EventColumnView } from './event_column_view'; - -interface Props { - id: string; - actionsColumnWidth: number; - addNoteToEvent: AddNoteToEvent; - onPinEvent: OnPinEvent; - columnHeaders: ColumnHeaderOptions[]; - columnRenderers: ColumnRenderer[]; - data: TimelineNonEcsData[]; - ecsData: Ecs; - expanded: boolean; - eventIdToNoteIds: Readonly>; - isEventViewer?: boolean; - isEventPinned: boolean; - loading: boolean; - loadingEventIds: Readonly; - onColumnResized: OnColumnResized; - onRowSelected: OnRowSelected; - onUnPinEvent: OnUnPinEvent; - selectedEventIds: Readonly>; - showCheckboxes: boolean; - showNotes: boolean; - timelineId: string; - updateNote: UpdateNote; - onToggleExpanded: () => void; - onToggleShowNotes: () => void; - getNotesByIds: (noteIds: string[]) => Note[]; - associateNote: (noteId: string) => void; -} - -export const getNewNoteId = (): string => uuid.v4(); - -const emptyNotes: string[] = []; - -export const StatefulEventChild = React.memo( - ({ - id, - actionsColumnWidth, - associateNote, - addNoteToEvent, - onPinEvent, - columnHeaders, - columnRenderers, - expanded, - data, - ecsData, - eventIdToNoteIds, - getNotesByIds, - isEventViewer = false, - isEventPinned = false, - loading, - loadingEventIds, - onColumnResized, - onRowSelected, - onToggleExpanded, - onUnPinEvent, - selectedEventIds, - showCheckboxes, - showNotes, - timelineId, - onToggleShowNotes, - updateNote, - }) => { - const width = useTimelineWidthContext(); - - // Passing the styles directly to the component because the width is - // being calculated and is recommended by Styled Components for performance - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 - return ( - <> - - - - - - - ); - } -); -StatefulEventChild.displayName = 'StatefulEventChild'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx index ea80d3351408a..fac8cc61cddd2 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx @@ -38,7 +38,7 @@ export interface BodyProps { columnRenderers: ColumnRenderer[]; data: TimelineItem[]; getNotesByIds: (noteIds: string[]) => Note[]; - height: number; + height?: number; id: string; isEventViewer?: boolean; isSelectAllChecked: boolean; @@ -96,9 +96,10 @@ export const Body = React.memo( }) => { const containerElementRef = useRef(null); const timelineTypeContext = useTimelineTypeContext(); - const additionalActionWidth = - timelineTypeContext.timelineActions?.reduce((acc, v) => acc + v.width, 0) ?? 0; - + const additionalActionWidth = useMemo( + () => timelineTypeContext.timelineActions?.reduce((acc, v) => acc + v.width, 0) ?? 0, + [timelineTypeContext.timelineActions] + ); const actionsColumnWidth = useMemo( () => getActionsColumnWidth(isEventViewer, showCheckboxes, additionalActionWidth), [isEventViewer, showCheckboxes, additionalActionWidth] @@ -113,11 +114,7 @@ export const Body = React.memo( return ( <> - + - - some child - - -`; +exports[`get_column_renderer renders correctly against snapshot 1`] = ``; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/plain_row_renderer.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/plain_row_renderer.test.tsx.snap index 5731921907fc8..66a1b293cf8b9 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/plain_row_renderer.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/plain_row_renderer.test.tsx.snap @@ -1,9 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`plain_row_renderer renders correctly against snapshot 1`] = ` - - - some children - - -`; +exports[`plain_row_renderer renders correctly against snapshot 1`] = ``; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap index 0b2a1b2f2a0ae..b24a90589ce65 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/__snapshots__/generic_row_renderer.test.tsx.snap @@ -2,9 +2,6 @@ exports[`GenericRowRenderer #createGenericAuditRowRenderer renders correctly against snapshot 1`] = ` - - some children - - - some children - { const children = connectedToRenderer.renderRow({ browserFields, data: auditd, - children: {'some children'}, timelineId: 'test', }); @@ -66,26 +65,10 @@ describe('GenericRowRenderer', () => { } }); - test('should render children normally if it does not have a auditd object', () => { - const children = connectedToRenderer.renderRow({ - browserFields: mockBrowserFields, - data: nonAuditd, - children: {'some children'}, - timelineId: 'test', - }); - const wrapper = mount( - - {children} - - ); - expect(wrapper.text()).toEqual('some children'); - }); - test('should render a auditd row', () => { const children = connectedToRenderer.renderRow({ browserFields: mockBrowserFields, data: auditd, - children: {'some children '}, timelineId: 'test', }); const wrapper = mount( @@ -94,7 +77,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Session246alice@zeek-londonsome textwget(1490)wget www.example.comwith resultsuccessDestination93.184.216.34:80' + 'Session246alice@zeek-londonsome textwget(1490)wget www.example.comwith resultsuccessDestination93.184.216.34:80' ); }); }); @@ -119,7 +102,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields, data: auditdFile, - children: {'some children'}, timelineId: 'test', }); @@ -145,26 +127,10 @@ describe('GenericRowRenderer', () => { } }); - test('should render children normally if it does not have a auditd object', () => { - const children = fileToRenderer.renderRow({ - browserFields: mockBrowserFields, - data: nonAuditd, - children: {'some children'}, - timelineId: 'test', - }); - const wrapper = mount( - - {children} - - ); - expect(wrapper.text()).toEqual('some children'); - }); - test('should render a auditd row', () => { const children = fileToRenderer.renderRow({ browserFields: mockBrowserFields, data: auditdFile, - children: {'some children '}, timelineId: 'test', }); const wrapper = mount( @@ -173,7 +139,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Sessionunsetroot@zeek-londonin/some text/proc/15990/attr/currentusingsystemd-journal(27244)/lib/systemd/systemd-journaldwith resultsuccess' + 'Sessionunsetroot@zeek-londonin/some text/proc/15990/attr/currentusingsystemd-journal(27244)/lib/systemd/systemd-journaldwith resultsuccess' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.tsx index bcf464ab6da15..4ed4ae10ed810 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.tsx @@ -32,19 +32,16 @@ export const createGenericAuditRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -67,20 +64,17 @@ export const createGenericFileRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx index f367769b78f40..7ad8cfed5256b 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx @@ -38,7 +38,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: nonSuricata, - children: {'some child'}, timelineId: 'test', }); @@ -51,7 +50,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: nonSuricata, - children: {'some child'}, timelineId: 'test', }); const wrapper = mount( @@ -59,7 +57,7 @@ describe('get_column_renderer', () => { {row} ); - expect(wrapper.text()).toContain('some child'); + expect(wrapper.text()).toEqual(''); }); test('should render a suricata row data when it is a suricata row', () => { @@ -67,7 +65,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, - children: {'some child '}, timelineId: 'test', }); const wrapper = mount( @@ -76,7 +73,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child 4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343' + '4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343' ); }); @@ -86,7 +83,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, - children: {'some child '}, timelineId: 'test', }); const wrapper = mount( @@ -95,7 +91,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child 4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343' + '4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343' ); }); @@ -105,7 +101,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: zeek, - children: {'some child '}, timelineId: 'test', }); const wrapper = mount( @@ -114,7 +109,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child C8DRTq362Fios6hw16connectionREJSrConnection attempt rejectedtcpSource185.176.26.101:44059Destination207.154.238.205:11568' + 'C8DRTq362Fios6hw16connectionREJSrConnection attempt rejectedtcpSource185.176.26.101:44059Destination207.154.238.205:11568' ); }); @@ -124,7 +119,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: system, - children: {'some child '}, timelineId: 'test', }); const wrapper = mount( @@ -133,7 +127,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child Braden@zeek-londonattempted a login via(6278)with resultfailureSource128.199.212.120' + 'Braden@zeek-londonattempted a login via(6278)with resultfailureSource128.199.212.120' ); }); @@ -143,7 +137,6 @@ describe('get_column_renderer', () => { const row = rowRenderer.renderRow({ browserFields: mockBrowserFields, data: auditd, - children: {'some child '}, timelineId: 'test', }); const wrapper = mount( @@ -152,7 +145,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child Sessionalice@zeek-sanfranin/executedgpgconf(5402)gpgconf--list-dirsagent-socketgpgconf --list-dirs agent-socket' + 'Sessionalice@zeek-sanfranin/executedgpgconf(5402)gpgconf--list-dirsagent-socketgpgconf --list-dirs agent-socket' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap index 4326b7372604d..d7bdacbcc61ef 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap @@ -2,9 +2,6 @@ exports[`netflowRowRenderer renders correctly against snapshot 1`] = ` - - some children -
{ const children = netflowRowRenderer.renderRow({ browserFields, data: getMockNetflowData(), - children: {'some children'}, timelineId: 'test', }); @@ -98,26 +97,10 @@ describe('netflowRowRenderer', () => { }); }); - test('should render children normally when given non-netflow data', () => { - const children = netflowRowRenderer.renderRow({ - browserFields: mockBrowserFields, - data: justIdAndTimestamp, - children: {'some children'}, - timelineId: 'test', - }); - const wrapper = mount( - - {children} - - ); - expect(wrapper.text()).toEqual('some children'); - }); - test('should render netflow data', () => { const children = netflowRowRenderer.renderRow({ browserFields: mockBrowserFields, data: getMockNetflowData(), - children: {'some children'}, timelineId: 'test', }); const wrapper = mount( diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx index 754d6ad99b7fe..10d80e1952f40 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/netflow/netflow_row_renderer.tsx @@ -78,73 +78,63 @@ export const eventActionMatches = (eventAction: string | object | undefined | nu }; export const netflowRowRenderer: RowRenderer = { - isInstance: ecs => { - return ( - eventCategoryMatches(get(EVENT_CATEGORY_FIELD, ecs)) || - eventActionMatches(get(EVENT_ACTION_FIELD, ecs)) - ); - }, - renderRow: ({ data, children, timelineId }) => ( - <> - {children} - -
- -
-
- + isInstance: ecs => + eventCategoryMatches(get(EVENT_CATEGORY_FIELD, ecs)) || + eventActionMatches(get(EVENT_ACTION_FIELD, ecs)), + renderRow: ({ data, timelineId }) => ( + +
+ +
+
), }; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.test.tsx index 50ea7ca05b921..467f507e8be7d 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.test.tsx @@ -25,7 +25,6 @@ describe('plain_row_renderer', () => { const children = plainRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockDatum, - children: {'some children'}, timelineId: 'test', }); const wrapper = shallow({children}); @@ -40,7 +39,6 @@ describe('plain_row_renderer', () => { const children = plainRowRenderer.renderRow({ browserFields: mockBrowserFields, data: mockDatum, - children: {'some children'}, timelineId: 'test', }); const wrapper = mount( @@ -48,6 +46,6 @@ describe('plain_row_renderer', () => { {children} ); - expect(wrapper.text()).toEqual('some children'); + expect(wrapper.text()).toEqual(''); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.tsx index 6725830c97d0a..da78f41f09ed4 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/plain_row_renderer.tsx @@ -12,5 +12,5 @@ import { RowRenderer } from './row_renderer'; export const plainRowRenderer: RowRenderer = { isInstance: _ => true, - renderRow: ({ children }) => <>{children}, + renderRow: () => <>, }; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/row_renderer.tsx index df92fc1e9f634..2d9f877fe4af0 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/row_renderer.tsx @@ -8,28 +8,17 @@ import React from 'react'; import { BrowserFields } from '../../../../containers/source'; import { Ecs } from '../../../../graphql/types'; -import { EventsTrSupplement, OFFSET_SCROLLBAR } from '../../styles'; -import { useTimelineWidthContext } from '../../timeline_context'; +import { EventsTrSupplement } from '../../styles'; interface RowRendererContainerProps { children: React.ReactNode; } -export const RowRendererContainer = React.memo(({ children }) => { - const width = useTimelineWidthContext(); - - // Passing the styles directly to the component because the width is - // being calculated and is recommended by Styled Components for performance - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 - return ( - - {children} - - ); -}); +export const RowRendererContainer = React.memo(({ children }) => ( + + {children} + +)); RowRendererContainer.displayName = 'RowRendererContainer'; export interface RowRenderer { @@ -37,12 +26,10 @@ export interface RowRenderer { renderRow: ({ browserFields, data, - children, timelineId, }: { browserFields: BrowserFields; data: Ecs; - children: React.ReactNode; timelineId: string; }) => React.ReactNode; } diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap index 3608a81234677..93b3046b57ed6 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/__snapshots__/suricata_row_renderer.test.tsx.snap @@ -2,9 +2,6 @@ exports[`suricata_row_renderer renders correctly against snapshot 1`] = ` - - some children - { const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: nonSuricata, - children: {'some children'}, timelineId: 'test', }); @@ -45,26 +44,10 @@ describe('suricata_row_renderer', () => { expect(suricataRowRenderer.isInstance(suricata)).toBe(true); }); - test('should render children normally if it does not have a signature', () => { - const children = suricataRowRenderer.renderRow({ - browserFields: mockBrowserFields, - data: nonSuricata, - children: {'some children'}, - timelineId: 'test', - }); - const wrapper = mount( - - {children} - - ); - expect(wrapper.text()).toEqual('some children'); - }); - test('should render a suricata row', () => { const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, - children: {'some children '}, timelineId: 'test', }); const wrapper = mount( @@ -73,7 +56,7 @@ describe('suricata_row_renderer', () => { ); expect(wrapper.text()).toContain( - 'some children 4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343' + '4ETEXPLOITNETGEARWNR2000v5 hidden_lang_avi Stack Overflow (CVE-2016-10174)Source192.168.0.3:53Destination192.168.0.3:6343' ); }); @@ -82,7 +65,6 @@ describe('suricata_row_renderer', () => { const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, - children: {'some children'}, timelineId: 'test', }); const wrapper = mount( @@ -90,6 +72,6 @@ describe('suricata_row_renderer', () => { {children} ); - expect(wrapper.text()).toEqual('some children'); + expect(wrapper.text()).toEqual(''); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx index b227326551e01..e49a5f65b47c1 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_row_renderer.tsx @@ -17,15 +17,9 @@ export const suricataRowRenderer: RowRenderer = { const module: string | null | undefined = get('event.module[0]', ecs); return module != null && module.toLowerCase() === 'suricata'; }, - renderRow: ({ browserFields, data, children, timelineId }) => { - return ( - <> - {children} - - - - - - ); - }, + renderRow: ({ browserFields, data, timelineId }) => ( + + + + ), }; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx index 2b9adfe21b120..9ccd1fb7a0519 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBadge, EuiBadgeProps, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; @@ -28,11 +28,9 @@ const SignatureFlexItem = styled(EuiFlexItem)` SignatureFlexItem.displayName = 'SignatureFlexItem'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const Badge = styled(EuiBadge)` -// vertical-align: top; -// `; -const Badge = (props: EuiBadgeProps) => ; +const Badge = styled(EuiBadge)` + vertical-align: top; +`; Badge.displayName = 'Badge'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap index 9ed6587145584..6fff32925abf3 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/__snapshots__/generic_row_renderer.test.tsx.snap @@ -2,9 +2,6 @@ exports[`GenericRowRenderer #createGenericFileRowRenderer renders correctly against snapshot 1`] = ` - - some children - - - some children - { const children = connectedToRenderer.renderRow({ browserFields, data: system, - children: {'some children'}, timelineId: 'test', }); @@ -99,7 +98,6 @@ describe('GenericRowRenderer', () => { const children = connectedToRenderer.renderRow({ browserFields: mockBrowserFields, data: system, - children: {'some children '}, timelineId: 'test', }); const wrapper = mount( @@ -108,7 +106,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Evan@zeek-londonsome text(6278)with resultfailureSource128.199.212.120' + 'Evan@zeek-londonsome text(6278)with resultfailureSource128.199.212.120' ); }); }); @@ -133,7 +131,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields, data: systemFile, - children: {'some children'}, timelineId: 'test', }); @@ -162,7 +159,6 @@ describe('GenericRowRenderer', () => { const children = fileToRenderer.renderRow({ browserFields: mockBrowserFields, data: systemFile, - children: {'some children '}, timelineId: 'test', }); const wrapper = mount( @@ -171,7 +167,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Braden@zeek-londonsome text(6278)with resultfailureSource128.199.212.120' + 'Braden@zeek-londonsome text(6278)with resultfailureSource128.199.212.120' ); }); }); @@ -195,14 +191,13 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54started processMicrosoft.Photos.exe(441684)C:\\Program Files\\WindowsApps\\Microsoft.Windows.Photos_2018.18091.17210.0_x64__8wekyb3d8bbwe\\Microsoft.Photos.exe-ServerName:App.AppXzst44mncqdg84v7sv6p7yznqwssy6f7f.mcavia parent processsvchost.exe(8)d4c97ed46046893141652e2ec0056a698f6445109949d7fcabbce331146889ee12563599116157778a22600d2a163d8112aed84562d06d7235b37895b68de56687895743' + 'Arun\\Anvi-Acer@HD-obe-8bf77f54started processMicrosoft.Photos.exe(441684)C:\\Program Files\\WindowsApps\\Microsoft.Windows.Photos_2018.18091.17210.0_x64__8wekyb3d8bbwe\\Microsoft.Photos.exe-ServerName:App.AppXzst44mncqdg84v7sv6p7yznqwssy6f7f.mcavia parent processsvchost.exe(8)d4c97ed46046893141652e2ec0056a698f6445109949d7fcabbce331146889ee12563599116157778a22600d2a163d8112aed84562d06d7235b37895b68de56687895743' ); }); @@ -224,14 +219,13 @@ describe('GenericRowRenderer', () => { endgameProcessTerminationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameTerminationEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54terminated processRuntimeBroker.exe(442384)with exit code087976f3430cc99bc939e0694247c0759961a49832b87218f4313d6fc0bc3a776797255e72d5ed5c058d4785950eba7abaa057653bd4401441a21bf1abce6404f4231db4d' + 'Arun\\Anvi-Acer@HD-obe-8bf77f54terminated processRuntimeBroker.exe(442384)with exit code087976f3430cc99bc939e0694247c0759961a49832b87218f4313d6fc0bc3a776797255e72d5ed5c058d4785950eba7abaa057653bd4401441a21bf1abce6404f4231db4d' ); }); @@ -253,7 +247,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - children: {'some children '}, timelineId: 'test', })} @@ -284,7 +277,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - children: {'some children '}, timelineId: 'test', })} @@ -315,7 +307,6 @@ describe('GenericRowRenderer', () => { endgameProcessCreationEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameCreationEvent, - children: {'some children '}, timelineId: 'test', })} @@ -344,14 +335,13 @@ describe('GenericRowRenderer', () => { endgameFileCreateEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileCreateEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54created a fileinC:\\Users\\Arun\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\63d78c21-e593-4484-b7a9-db33cd522ddc.tmpviachrome.exe(11620)' + 'Arun\\Anvi-Acer@HD-obe-8bf77f54created a fileinC:\\Users\\Arun\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\63d78c21-e593-4484-b7a9-db33cd522ddc.tmpviachrome.exe(11620)' ); }); @@ -373,14 +363,13 @@ describe('GenericRowRenderer', () => { endgameFileDeleteEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileDeleteEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children SYSTEM\\NT AUTHORITY@HD-v1s-d2118419deleted a filetmp000002f6inC:\\Windows\\TEMP\\tmp00000404\\tmp000002f6viaAmSvc.exe(1084)' + 'SYSTEM\\NT AUTHORITY@HD-v1s-d2118419deleted a filetmp000002f6inC:\\Windows\\TEMP\\tmp00000404\\tmp000002f6viaAmSvc.exe(1084)' ); }); @@ -402,15 +391,12 @@ describe('GenericRowRenderer', () => { fileCreatedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: fimFileCreatedEvent, - children: {'some children '}, timelineId: 'test', })} ); - expect(wrapper.text()).toEqual( - 'some children foohostcreated a filein/etc/subgidviaan unknown process' - ); + expect(wrapper.text()).toEqual('foohostcreated a filein/etc/subgidviaan unknown process'); }); test('it renders a FIM (non-endgame) file deleted event', () => { @@ -431,14 +417,13 @@ describe('GenericRowRenderer', () => { fileDeletedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: fimFileDeletedEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children foohostdeleted a filein/etc/gshadow.lockviaan unknown process' + 'foohostdeleted a filein/etc/gshadow.lockviaan unknown process' ); }); @@ -460,7 +445,6 @@ describe('GenericRowRenderer', () => { endgameFileCreateEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileCreateEvent, - children: {'some children '}, timelineId: 'test', })} @@ -491,7 +475,6 @@ describe('GenericRowRenderer', () => { endgameFileCreateEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: endgameFileCreateEvent, - children: {'some children '}, timelineId: 'test', })} @@ -522,7 +505,6 @@ describe('GenericRowRenderer', () => { fileCreatedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: fimFileCreatedEvent, - children: {'some children '}, timelineId: 'test', })} @@ -551,14 +533,13 @@ describe('GenericRowRenderer', () => { endgameIpv4ConnectionAcceptEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv4ConnectionAcceptEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children SYSTEM\\NT AUTHORITY@HD-gqf-0af7b4feaccepted a connection viaAmSvc.exe(1084)tcp1:network-community_idSource127.0.0.1:49306Destination127.0.0.1:49305' + 'SYSTEM\\NT AUTHORITY@HD-gqf-0af7b4feaccepted a connection viaAmSvc.exe(1084)tcp1:network-community_idSource127.0.0.1:49306Destination127.0.0.1:49305' ); }); @@ -580,14 +561,13 @@ describe('GenericRowRenderer', () => { endgameIpv6ConnectionAcceptEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv6ConnectionAcceptEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children SYSTEM\\NT AUTHORITY@HD-55b-3ec87f66accepted a connection via(4)tcp1:network-community_idSource::1:51324Destination::1:5357' + 'SYSTEM\\NT AUTHORITY@HD-55b-3ec87f66accepted a connection via(4)tcp1:network-community_idSource::1:51324Destination::1:5357' ); }); @@ -609,14 +589,13 @@ describe('GenericRowRenderer', () => { endgameIpv4DisconnectReceivedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv4DisconnectReceivedEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children Arun\\Anvi-Acer@HD-obe-8bf77f54disconnected viachrome.exe(11620)8.1KBtcp1:LxYHJJv98b2O0fNccXu6HheXmwk=Source192.168.0.6:59356(25.78%)2.1KB(74.22%)6KBDestination10.156.162.53:443' + 'Arun\\Anvi-Acer@HD-obe-8bf77f54disconnected viachrome.exe(11620)8.1KBtcp1:LxYHJJv98b2O0fNccXu6HheXmwk=Source192.168.0.6:59356(25.78%)2.1KB(74.22%)6KBDestination10.156.162.53:443' ); }); @@ -638,14 +617,13 @@ describe('GenericRowRenderer', () => { endgameIpv6DisconnectReceivedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv6DisconnectReceivedEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children SYSTEM\\NT AUTHORITY@HD-55b-3ec87f66disconnected via(4)7.9KBtcp1:ZylzQhsB1dcptA2t4DY8S6l9o8E=Source::1:51338(96.92%)7.7KB(3.08%)249BDestination::1:2869' + 'SYSTEM\\NT AUTHORITY@HD-55b-3ec87f66disconnected via(4)7.9KBtcp1:ZylzQhsB1dcptA2t4DY8S6l9o8E=Source::1:51338(96.92%)7.7KB(3.08%)249BDestination::1:2869' ); }); @@ -667,14 +645,13 @@ describe('GenericRowRenderer', () => { socketOpenedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: socketOpenedEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children root@foohostopened a socket withgoogle_accounts(2166)Outbound socket (10.4.20.1:59554 -> 10.1.2.3:80) Ooutboundtcp1:network-community_idSource10.4.20.1:59554Destination10.1.2.3:80' + 'root@foohostopened a socket withgoogle_accounts(2166)Outbound socket (10.4.20.1:59554 -> 10.1.2.3:80) Ooutboundtcp1:network-community_idSource10.4.20.1:59554Destination10.1.2.3:80' ); }); @@ -696,14 +673,13 @@ describe('GenericRowRenderer', () => { socketClosedEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: socketClosedEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children root@foohostclosed a socket withgoogle_accounts(2166)Outbound socket (10.4.20.1:59508 -> 10.1.2.3:80) Coutboundtcp1:network-community_idSource10.4.20.1:59508Destination10.1.2.3:80' + 'root@foohostclosed a socket withgoogle_accounts(2166)Outbound socket (10.4.20.1:59508 -> 10.1.2.3:80) Coutboundtcp1:network-community_idSource10.4.20.1:59508Destination10.1.2.3:80' ); }); @@ -725,7 +701,6 @@ describe('GenericRowRenderer', () => { endgameIpv4ConnectionAcceptEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: ipv4ConnectionAcceptEvent, - children: {'some children '}, timelineId: 'test', })} @@ -750,14 +725,13 @@ describe('GenericRowRenderer', () => { userLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: userLogonEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children SYSTEM\\NT AUTHORITY@HD-v1s-d2118419successfully logged inusing logon type5 - Service(target logon ID0x3e7)viaC:\\Windows\\System32\\services.exe(432)as requested by subjectWIN-Q3DOP1UKA81$(subject logon ID0x3e7)4624' + 'SYSTEM\\NT AUTHORITY@HD-v1s-d2118419successfully logged inusing logon type5 - Service(target logon ID0x3e7)viaC:\\Windows\\System32\\services.exe(432)as requested by subjectWIN-Q3DOP1UKA81$(subject logon ID0x3e7)4624' ); }); @@ -775,14 +749,13 @@ describe('GenericRowRenderer', () => { adminLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: adminLogonEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children With special privileges,SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54successfully logged inviaC:\\Windows\\System32\\lsass.exe(964)as requested by subjectSYSTEM\\NT AUTHORITY4672' + 'With special privileges,SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54successfully logged inviaC:\\Windows\\System32\\lsass.exe(964)as requested by subjectSYSTEM\\NT AUTHORITY4672' ); }); @@ -800,14 +773,13 @@ describe('GenericRowRenderer', () => { explicitUserLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: explicitUserLogonEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children A login was attempted using explicit credentialsArun\\Anvi-AcertoHD-55b-3ec87f66viaC:\\Windows\\System32\\svchost.exe(1736)as requested by subjectANVI-ACER$\\WORKGROUP(subject logon ID0x3e7)4648' + 'A login was attempted using explicit credentialsArun\\Anvi-AcertoHD-55b-3ec87f66viaC:\\Windows\\System32\\svchost.exe(1736)as requested by subjectANVI-ACER$\\WORKGROUP(subject logon ID0x3e7)4648' ); }); @@ -825,14 +797,13 @@ describe('GenericRowRenderer', () => { userLogoffEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: userLogoffEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children Arun\\Anvi-Acer@HD-55b-3ec87f66logged offusing logon type2 - Interactive(target logon ID0x16db41e)viaC:\\Windows\\System32\\lsass.exe(964)4634' + 'Arun\\Anvi-Acer@HD-55b-3ec87f66logged offusing logon type2 - Interactive(target logon ID0x16db41e)viaC:\\Windows\\System32\\lsass.exe(964)4634' ); }); @@ -850,7 +821,6 @@ describe('GenericRowRenderer', () => { userLogonEventRowRenderer.renderRow({ browserFields: mockBrowserFields, data: userLogonEvent, - children: {'some children '}, timelineId: 'test', })} @@ -874,14 +844,13 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: requestEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54asked forupdate.googleapis.comwith question typeA, which resolved to10.100.197.67viaGoogleUpdate.exe(443192)3008dns' + 'SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54asked forupdate.googleapis.comwith question typeA, which resolved to10.100.197.67viaGoogleUpdate.exe(443192)3008dns' ); }); @@ -898,14 +867,13 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: dnsEvent, - children: {'some children '}, timelineId: 'test', })} ); expect(wrapper.text()).toEqual( - 'some children iot.example.comasked forlookup.example.comwith question typeA, which resolved to10.1.2.3(response code:NOERROR)viaan unknown process6.937500msOct 8, 2019 @ 10:05:23.241Oct 8, 2019 @ 10:05:23.248outbounddns177Budp1:network-community_idSource10.9.9.9:58732(22.60%)40B(77.40%)137BDestination10.1.1.1:53OceaniaAustralia🇦🇺AU' + 'iot.example.comasked forlookup.example.comwith question typeA, which resolved to10.1.2.3(response code:NOERROR)viaan unknown process6.937500msOct 8, 2019 @ 10:05:23.241Oct 8, 2019 @ 10:05:23.248outbounddns177Budp1:network-community_idSource10.9.9.9:58732(22.60%)40B(77.40%)137BDestination10.1.1.1:53OceaniaAustralia🇦🇺AU' ); }); @@ -928,7 +896,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: requestEvent, - children: {'some children '}, timelineId: 'test', })} @@ -956,7 +923,6 @@ describe('GenericRowRenderer', () => { dnsRowRenderer.renderRow({ browserFields: mockBrowserFields, data: requestEvent, - children: {'some children '}, timelineId: 'test', })} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx index 3e64248d39876..523d4f3a0cfb8 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx @@ -35,19 +35,16 @@ export const createGenericSystemRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -68,20 +65,17 @@ export const createEndgameProcessRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -102,20 +96,17 @@ export const createFimRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -136,19 +127,16 @@ export const createGenericFileRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -163,19 +151,16 @@ export const createSocketRowRenderer = ({ const action: string | null | undefined = get('event.action[0]', ecs); return action != null && action.toLowerCase() === actionName; }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -194,18 +179,15 @@ export const createSecurityEventRowRenderer = ({ action.toLowerCase() === actionName ); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); @@ -215,18 +197,15 @@ export const createDnsRowRenderer = (): RowRenderer => ({ const dnsQuestionName: string | null | undefined = get('dns.question.name[0]', ecs); return !isNillEmptyOrNotFinite(dnsQuestionType) && !isNillEmptyOrNotFinite(dnsQuestionName); }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap index 9b59f69cad3a3..460ad35b47678 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/__snapshots__/zeek_row_renderer.test.tsx.snap @@ -2,9 +2,6 @@ exports[`zeek_row_renderer renders correctly against snapshot 1`] = ` - - some children - { const children = zeekRowRenderer.renderRow({ browserFields: mockBrowserFields, data: nonZeek, - children: {'some children'}, timelineId: 'test', }); @@ -44,26 +43,10 @@ describe('zeek_row_renderer', () => { expect(zeekRowRenderer.isInstance(zeek)).toBe(true); }); - test('should render children normally if it does not have a zeek object', () => { - const children = zeekRowRenderer.renderRow({ - browserFields: mockBrowserFields, - data: nonZeek, - children: {'some children'}, - timelineId: 'test', - }); - const wrapper = mount( - - {children} - - ); - expect(wrapper.text()).toEqual('some children'); - }); - test('should render a zeek row', () => { const children = zeekRowRenderer.renderRow({ browserFields: mockBrowserFields, data: zeek, - children: {'some children '}, timelineId: 'test', }); const wrapper = mount( @@ -72,7 +55,7 @@ describe('zeek_row_renderer', () => { ); expect(wrapper.text()).toContain( - 'some children C8DRTq362Fios6hw16connectionREJSrConnection attempt rejectedtcpSource185.176.26.101:44059Destination207.154.238.205:11568' + 'C8DRTq362Fios6hw16connectionREJSrConnection attempt rejectedtcpSource185.176.26.101:44059Destination207.154.238.205:11568' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx index fc528e33b5ab6..0fca5cdd8b3d4 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_row_renderer.tsx @@ -17,12 +17,9 @@ export const zeekRowRenderer: RowRenderer = { const module: string | null | undefined = get('event.module[0]', ecs); return module != null && module.toLowerCase() === 'zeek'; }, - renderRow: ({ browserFields, data, children, timelineId }) => ( - <> - {children} - - - - + renderRow: ({ browserFields, data, timelineId }) => ( + + + ), }; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx index 57e5ff19eb815..f13a236e8ec36 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBadge, EuiBadgeProps, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { get } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; @@ -19,11 +19,9 @@ import { IS_OPERATOR } from '../../../data_providers/data_provider'; import * as i18n from './translations'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const Badge = styled(EuiBadge)` -// vertical-align: top; -// `; -const Badge = (props: EuiBadgeProps) => ; +const Badge = styled(EuiBadge)` + vertical-align: top; +`; Badge.displayName = 'Badge'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx index d06dcbb84ad78..76f26d3dda5af 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx @@ -8,6 +8,7 @@ import { noop } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import React, { useCallback, useEffect } from 'react'; import { connect, ConnectedProps } from 'react-redux'; +import deepEqual from 'fast-deep-equal'; import { BrowserFields } from '../../../containers/source'; import { TimelineItem } from '../../../graphql/types'; @@ -38,9 +39,9 @@ import { plainRowRenderer } from './renderers/plain_row_renderer'; interface OwnProps { browserFields: BrowserFields; data: TimelineItem[]; + height?: number; id: string; isEventViewer?: boolean; - height: number; sort: Sort; toggleColumn: (column: ColumnHeaderOptions) => void; } @@ -101,7 +102,7 @@ const StatefulBodyComponent = React.memo( isSelected && Object.keys(selectedEventIds).length + 1 === data.length, }); }, - [id, data, selectedEventIds, timelineTypeContext.queryFields] + [setSelected, id, data, selectedEventIds, timelineTypeContext.queryFields] ); const onSelectAll: OnSelectAll = useCallback( @@ -118,7 +119,7 @@ const StatefulBodyComponent = React.memo( isSelectAllChecked: isSelected, }) : clearSelected!({ id }), - [id, data, timelineTypeContext.queryFields] + [setSelected, clearSelected, id, data, timelineTypeContext.queryFields] ); const onColumnSorted: OnColumnSorted = useCallback( @@ -189,25 +190,22 @@ const StatefulBodyComponent = React.memo( /> ); }, - (prevProps, nextProps) => { - return ( - prevProps.browserFields === nextProps.browserFields && - prevProps.columnHeaders === nextProps.columnHeaders && - prevProps.data === nextProps.data && - prevProps.eventIdToNoteIds === nextProps.eventIdToNoteIds && - prevProps.notesById === nextProps.notesById && - prevProps.height === nextProps.height && - prevProps.id === nextProps.id && - prevProps.isEventViewer === nextProps.isEventViewer && - prevProps.isSelectAllChecked === nextProps.isSelectAllChecked && - prevProps.loadingEventIds === nextProps.loadingEventIds && - prevProps.pinnedEventIds === nextProps.pinnedEventIds && - prevProps.selectedEventIds === nextProps.selectedEventIds && - prevProps.showCheckboxes === nextProps.showCheckboxes && - prevProps.showRowRenderers === nextProps.showRowRenderers && - prevProps.sort === nextProps.sort - ); - } + (prevProps, nextProps) => + deepEqual(prevProps.browserFields, nextProps.browserFields) && + deepEqual(prevProps.columnHeaders, nextProps.columnHeaders) && + deepEqual(prevProps.data, nextProps.data) && + prevProps.eventIdToNoteIds === nextProps.eventIdToNoteIds && + deepEqual(prevProps.notesById, nextProps.notesById) && + prevProps.height === nextProps.height && + prevProps.id === nextProps.id && + prevProps.isEventViewer === nextProps.isEventViewer && + prevProps.isSelectAllChecked === nextProps.isSelectAllChecked && + prevProps.loadingEventIds === nextProps.loadingEventIds && + prevProps.pinnedEventIds === nextProps.pinnedEventIds && + prevProps.selectedEventIds === nextProps.selectedEventIds && + prevProps.showCheckboxes === nextProps.showCheckboxes && + prevProps.showRowRenderers === nextProps.showRowRenderers && + prevProps.sort === nextProps.sort ); StatefulBodyComponent.displayName = 'StatefulBodyComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx index a47fb932ed26c..56639f90c1464 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/empty.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBadge, EuiBadgeProps, EuiText } from '@elastic/eui'; +import { EuiBadge, EuiText } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; @@ -21,24 +21,12 @@ const Text = styled(EuiText)` Text.displayName = 'Text'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const BadgeHighlighted = styled(EuiBadge)` -// height: 20px; -// margin: 0 5px 0 5px; -// max-width: 70px; -// min-width: 70px; -// `; -const BadgeHighlighted = (props: EuiBadgeProps) => ( - -); +const BadgeHighlighted = styled(EuiBadge)` + height: 20px; + margin: 0 5px 0 5px; + maxwidth: 85px; + minwidth: 85px; +`; BadgeHighlighted.displayName = 'BadgeHighlighted'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx index 1a1e8292b7e02..663b3dd501341 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBadge, EuiBadgeProps, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { rgba } from 'polished'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; import { AndOrBadge } from '../../and_or_badge'; @@ -54,13 +54,9 @@ const DropAndTargetDataProviders = styled.div<{ hasAndItem: boolean }>` DropAndTargetDataProviders.displayName = 'DropAndTargetDataProviders'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const NumberProviderAndBadge = styled(EuiBadge)` -// margin: 0px 5px; -// `; -const NumberProviderAndBadge = (props: EuiBadgeProps) => ( - -); +const NumberProviderAndBadge = styled(EuiBadge)` + margin: 0px 5px; +`; NumberProviderAndBadge.displayName = 'NumberProviderAndBadge'; @@ -89,8 +85,13 @@ export const ProviderItemAndDragDrop = React.memo( onToggleDataProviderExcluded, timelineId, }) => { - const onMouseEnter = () => onChangeDroppableAndProvider(dataProvider.id); - const onMouseLeave = () => onChangeDroppableAndProvider(''); + const onMouseEnter = useCallback(() => onChangeDroppableAndProvider(dataProvider.id), [ + onChangeDroppableAndProvider, + dataProvider.id, + ]); + const onMouseLeave = useCallback(() => onChangeDroppableAndProvider(''), [ + onChangeDroppableAndProvider, + ]); const hasAndItem = dataProvider.and.length > 0; return ( ` ${({ hideExpandButton }) => @@ -50,33 +49,26 @@ export const ExpandableEvent = React.memo( timelineId, toggleColumn, onUpdateColumns, - }) => { - const width = useTimelineWidthContext(); - // Passing the styles directly to the component of LazyAccordion because the width is - // being calculated and is recommended by Styled Components for performance - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 - return ( - - ( - - )} - forceExpand={forceExpand} - paddingSize="none" - /> - - ); - } + }) => ( + + ( + + )} + forceExpand={forceExpand} + paddingSize="none" + /> + + ) ); ExpandableEvent.displayName = 'ExpandableEvent'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx index 65c539d77a16b..16eaa80308205 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx @@ -6,6 +6,7 @@ import { memo, useEffect } from 'react'; import { connect, ConnectedProps } from 'react-redux'; +import deepEqual from 'fast-deep-equal'; import { IIndexPattern } from 'src/plugins/data/public'; import { timelineSelectors, State } from '../../store'; @@ -39,7 +40,14 @@ const TimelineKqlFetchComponent = memo( }); }, [kueryFilterQueryDraft, kueryFilterQuery, id]); return null; - } + }, + (prevProps, nextProps) => + prevProps.id === nextProps.id && + prevProps.inputId === nextProps.inputId && + prevProps.setTimelineQuery === nextProps.setTimelineQuery && + deepEqual(prevProps.kueryFilterQuery, nextProps.kueryFilterQuery) && + deepEqual(prevProps.kueryFilterQueryDraft, nextProps.kueryFilterQueryDraft) && + deepEqual(prevProps.indexPattern, nextProps.indexPattern) ); const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/footer/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/footer/__snapshots__/index.test.tsx.snap index 9cdbda757d97e..eff487ceb7981 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/footer/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/footer/__snapshots__/index.test.tsx.snap @@ -1,94 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Footer Timeline Component rendering it renders the default timeline footer 1`] = ` - - + - - - - - 1 rows - , - - 5 rows - , - - 10 rows - , - - 20 rows - , - ] - } - itemsCount={2} - onClick={[Function]} - serverSideEventCount={15546} - /> - - - - + 1 rows + , + + 5 rows + , + + 10 rows + , + + 20 rows + , + ] + } + itemsCount={2} + onClick={[Function]} + serverSideEventCount={15546} /> - - - - - - - - - + + + + + + + + + + `; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx index cbad2d42cf8af..d54a4cee83e52 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.test.tsx @@ -17,7 +17,6 @@ describe('Footer Timeline Component', () => { const loadMore = jest.fn(); const onChangeItemsPerPage = jest.fn(); const getUpdatedAt = () => 1546878704036; - const compact = true; describe('rendering', () => { test('it renders the default timeline footer', () => { @@ -36,7 +35,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -59,7 +57,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -83,7 +80,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -140,7 +136,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -164,7 +159,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -195,7 +189,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -225,7 +218,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -259,7 +251,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); @@ -285,7 +276,6 @@ describe('Footer Timeline Component', () => { nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!} tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)} getUpdatedAt={getUpdatedAt} - compact={compact} /> ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.tsx index 1fcc4382c1798..7a025e96e57f2 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/footer/index.tsx @@ -19,7 +19,7 @@ import { EuiPopoverProps, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { FunctionComponent, useCallback, useEffect, useState } from 'react'; +import React, { FC, useCallback, useEffect, useState, useMemo } from 'react'; import styled from 'styled-components'; import { LoadingPanel } from '../../loading'; @@ -28,8 +28,30 @@ import { OnChangeItemsPerPage, OnLoadMore } from '../events'; import { LastUpdatedAt } from './last_updated'; import * as i18n from './translations'; import { useTimelineTypeContext } from '../timeline_context'; +import { useEventDetailsWidthContext } from '../../events_viewer/event_details_width_context'; -const FixedWidthLastUpdated = styled.div<{ compact: boolean }>` +export const isCompactFooter = (width: number): boolean => width < 600; + +interface FixedWidthLastUpdatedContainerProps { + updatedAt: number; +} + +const FixedWidthLastUpdatedContainer = React.memo( + ({ updatedAt }) => { + const width = useEventDetailsWidthContext(); + const compact = useMemo(() => isCompactFooter(width), [width]); + + return ( + + + + ); + } +); + +FixedWidthLastUpdatedContainer.displayName = 'FixedWidthLastUpdatedContainer'; + +const FixedWidthLastUpdated = styled.div<{ compact?: boolean }>` width: ${({ compact }) => (!compact ? 200 : 25)}px; overflow: hidden; text-align: end; @@ -37,8 +59,16 @@ const FixedWidthLastUpdated = styled.div<{ compact: boolean }>` FixedWidthLastUpdated.displayName = 'FixedWidthLastUpdated'; -const FooterContainer = styled(EuiFlexGroup)<{ height: number }>` - height: ${({ height }) => height}px; +interface HeightProp { + height: number; +} + +const FooterContainer = styled(EuiFlexGroup).attrs(({ height }) => ({ + style: { + height: `${height}px`, + }, +}))` + flex: 0; `; FooterContainer.displayName = 'FooterContainer'; @@ -56,7 +86,7 @@ const LoadingPanelContainer = styled.div` LoadingPanelContainer.displayName = 'LoadingPanelContainer'; -const PopoverRowItems = styled((EuiPopover as unknown) as FunctionComponent)< +const PopoverRowItems = styled((EuiPopover as unknown) as FC)< EuiPopoverProps & { className?: string; id?: string; @@ -173,11 +203,9 @@ export const PagingControl = React.memo(PagingControlComponent); PagingControl.displayName = 'PagingControl'; interface FooterProps { - compact: boolean; getUpdatedAt: () => number; hasNextPage: boolean; height: number; - isEventViewer?: boolean; isLive: boolean; isLoading: boolean; itemsCount: number; @@ -192,11 +220,9 @@ interface FooterProps { /** Renders a loading indicator and paging controls */ export const FooterComponent = ({ - compact, getUpdatedAt, hasNextPage, height, - isEventViewer, isLive, isLoading, itemsCount, @@ -216,11 +242,13 @@ export const FooterComponent = ({ const loadMore = useCallback(() => { setPaginationLoading(true); onLoadMore(nextCursor, tieBreaker); - }, [nextCursor, tieBreaker, onLoadMore]); + }, [nextCursor, tieBreaker, onLoadMore, setPaginationLoading]); - const onButtonClick = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); - - const closePopover = useCallback(() => setIsPopoverOpen(false), []); + const onButtonClick = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [ + isPopoverOpen, + setIsPopoverOpen, + ]); + const closePopover = useCallback(() => setIsPopoverOpen(false), [setIsPopoverOpen]); useEffect(() => { if (paginationLoading && !isLoading) { @@ -263,95 +291,78 @@ export const FooterComponent = ({ )); return ( - <> - + - - - - - - - - - {isLive ? ( - - - {i18n.AUTO_REFRESH_ACTIVE}{' '} - - } - type="iInCircle" - /> - - - ) : ( - - )} - - - - - - - - - - + + + + + + + + {isLive ? ( + + + {i18n.AUTO_REFRESH_ACTIVE}{' '} + + } + type="iInCircle" + /> + + + ) : ( + + )} + + + + + + + ); }; FooterComponent.displayName = 'FooterComponent'; -export const Footer = React.memo( - FooterComponent, - (prevProps, nextProps) => - prevProps.compact === nextProps.compact && - prevProps.hasNextPage === nextProps.hasNextPage && - prevProps.height === nextProps.height && - prevProps.isEventViewer === nextProps.isEventViewer && - prevProps.isLive === nextProps.isLive && - prevProps.isLoading === nextProps.isLoading && - prevProps.itemsCount === nextProps.itemsCount && - prevProps.itemsPerPage === nextProps.itemsPerPage && - prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions && - prevProps.serverSideEventCount === nextProps.serverSideEventCount -); +export const Footer = React.memo(FooterComponent); Footer.displayName = 'Footer'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap index 048ca080772f6..90d0dc1a8a66d 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap @@ -1,9 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Header rendering renders correctly against snapshot 1`] = ` - + - + `; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx index 5af7aff4f8795..317c68b63f691 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.test.tsx @@ -7,13 +7,12 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { Direction } from '../../../graphql/types'; import { mockIndexPattern } from '../../../mock'; import { TestProviders } from '../../../mock/test_providers'; import { mockDataProviders } from '../data_providers/mock/mock_data_providers'; import { useMountAppended } from '../../../utils/use_mount_appended'; -import { TimelineHeaderComponent } from '.'; +import { TimelineHeader } from '.'; jest.mock('../../../lib/kibana'); @@ -24,7 +23,7 @@ describe('Header', () => { describe('rendering', () => { test('renders correctly against snapshot', () => { const wrapper = shallow( - { onToggleDataProviderExcluded={jest.fn()} show={true} showCallOutUnauthorizedMsg={false} - sort={{ - columnId: '@timestamp', - sortDirection: Direction.desc, - }} /> ); expect(wrapper).toMatchSnapshot(); @@ -49,7 +44,7 @@ describe('Header', () => { test('it renders the data providers', () => { const wrapper = mount( - { onToggleDataProviderExcluded={jest.fn()} show={true} showCallOutUnauthorizedMsg={false} - sort={{ - columnId: '@timestamp', - sortDirection: Direction.desc, - }} /> ); @@ -76,7 +67,7 @@ describe('Header', () => { test('it renders the unauthorized call out providers', () => { const wrapper = mount( - { onToggleDataProviderExcluded={jest.fn()} show={true} showCallOutUnauthorizedMsg={true} - sort={{ - columnId: '@timestamp', - sortDirection: Direction.desc, - }} /> ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx index 81eef0efbfa5b..7cac03cec42b1 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/index.tsx @@ -6,10 +6,9 @@ import { EuiCallOut } from '@elastic/eui'; import React from 'react'; -import styled from 'styled-components'; import { IIndexPattern } from 'src/plugins/data/public'; +import deepEqual from 'fast-deep-equal'; -import { Sort } from '../body/sort'; import { DataProviders } from '../data_providers'; import { DataProvider } from '../data_providers/data_provider'; import { @@ -38,16 +37,9 @@ interface Props { onToggleDataProviderExcluded: OnToggleDataProviderExcluded; show: boolean; showCallOutUnauthorizedMsg: boolean; - sort: Sort; } -const TimelineHeaderContainer = styled.div` - width: 100%; -`; - -TimelineHeaderContainer.displayName = 'TimelineHeaderContainer'; - -export const TimelineHeaderComponent: React.FC = ({ +const TimelineHeaderComponent: React.FC = ({ browserFields, id, indexPattern, @@ -61,7 +53,7 @@ export const TimelineHeaderComponent: React.FC = ({ show, showCallOutUnauthorizedMsg, }) => ( - + <> {showCallOutUnauthorizedMsg && ( = ({ indexPattern={indexPattern} timelineId={id} /> - + ); -export const TimelineHeader = React.memo(TimelineHeaderComponent); +export const TimelineHeader = React.memo( + TimelineHeaderComponent, + (prevProps, nextProps) => + deepEqual(prevProps.browserFields, nextProps.browserFields) && + prevProps.id === nextProps.id && + deepEqual(prevProps.indexPattern, nextProps.indexPattern) && + deepEqual(prevProps.dataProviders, nextProps.dataProviders) && + prevProps.onChangeDataProviderKqlQuery === nextProps.onChangeDataProviderKqlQuery && + prevProps.onChangeDroppableAndProvider === nextProps.onChangeDroppableAndProvider && + prevProps.onDataProviderEdited === nextProps.onDataProviderEdited && + prevProps.onDataProviderRemoved === nextProps.onDataProviderRemoved && + prevProps.onToggleDataProviderEnabled === nextProps.onToggleDataProviderEnabled && + prevProps.onToggleDataProviderExcluded === nextProps.onToggleDataProviderExcluded && + prevProps.show === nextProps.show && + prevProps.showCallOutUnauthorizedMsg === nextProps.showCallOutUnauthorizedMsg +); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx index 611d08e61be22..f051bbe5b1af6 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx @@ -153,25 +153,6 @@ export const combineQueries = ({ }; }; -interface CalculateBodyHeightParams { - /** The the height of the flyout container, which is typically the entire "page", not including the standard Kibana navigation */ - flyoutHeight?: number; - /** The flyout header typically contains a title and a close button */ - flyoutHeaderHeight?: number; - /** All non-body timeline content (i.e. the providers drag and drop area, and the column headers) */ - timelineHeaderHeight?: number; - /** Footer content that appears below the body (i.e. paging controls) */ - timelineFooterHeight?: number; -} - -export const calculateBodyHeight = ({ - flyoutHeight = 0, - flyoutHeaderHeight = 0, - timelineHeaderHeight = 0, - timelineFooterHeight = 0, -}: CalculateBodyHeightParams): number => - flyoutHeight - (flyoutHeaderHeight + timelineHeaderHeight + timelineFooterHeight); - /** * The CSS class name of a "stateful event", which appears in both * the `Timeline` and the `Events Viewer` widget diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx index 0ce6bc16f1325..35099e3836fb4 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx @@ -28,8 +28,8 @@ import { Timeline } from './timeline'; export interface OwnProps { id: string; - flyoutHeaderHeight: number; - flyoutHeight: number; + onClose: () => void; + usersViewing: string[]; } type Props = OwnProps & PropsFromRedux; @@ -42,14 +42,13 @@ const StatefulTimelineComponent = React.memo( eventType, end, filters, - flyoutHeaderHeight, - flyoutHeight, id, isLive, itemsPerPage, itemsPerPageOptions, kqlMode, kqlQueryExpression, + onClose, onDataProviderEdited, removeColumn, removeProvider, @@ -63,6 +62,7 @@ const StatefulTimelineComponent = React.memo( updateHighlightedDropAndProviderId, updateItemsPerPage, upsertColumn, + usersViewing, }) => { const { loading, signalIndexExists, signalIndexName } = useSignalIndex(); @@ -173,8 +173,6 @@ const StatefulTimelineComponent = React.memo( end={end} eventType={eventType} filters={filters} - flyoutHeaderHeight={flyoutHeaderHeight} - flyoutHeight={flyoutHeight} id={id} indexPattern={indexPattern} indexToAdd={indexToAdd} @@ -187,6 +185,7 @@ const StatefulTimelineComponent = React.memo( onChangeDataProviderKqlQuery={onChangeDataProviderKqlQuery} onChangeDroppableAndProvider={onChangeDroppableAndProvider} onChangeItemsPerPage={onChangeItemsPerPage} + onClose={onClose} onDataProviderEdited={onDataProviderEditedLocal} onDataProviderRemoved={onDataProviderRemoved} onToggleDataProviderEnabled={onToggleDataProviderEnabled} @@ -196,6 +195,7 @@ const StatefulTimelineComponent = React.memo( sort={sort!} start={start} toggleColumn={toggleColumn} + usersViewing={usersViewing} /> )} @@ -205,8 +205,6 @@ const StatefulTimelineComponent = React.memo( return ( prevProps.eventType === nextProps.eventType && prevProps.end === nextProps.end && - prevProps.flyoutHeaderHeight === nextProps.flyoutHeaderHeight && - prevProps.flyoutHeight === nextProps.flyoutHeight && prevProps.id === nextProps.id && prevProps.isLive === nextProps.isLive && prevProps.itemsPerPage === nextProps.itemsPerPage && @@ -219,7 +217,8 @@ const StatefulTimelineComponent = React.memo( deepEqual(prevProps.dataProviders, nextProps.dataProviders) && deepEqual(prevProps.filters, nextProps.filters) && deepEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) && - deepEqual(prevProps.sort, nextProps.sort) + deepEqual(prevProps.sort, nextProps.sort) && + deepEqual(prevProps.usersViewing, nextProps.usersViewing) ); } ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx index ae139c24d0176..4b1fd4b5851c0 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/properties/helpers.tsx @@ -6,7 +6,6 @@ import { EuiBadge, - EuiBadgeProps, EuiButton, EuiButtonEmpty, EuiButtonIcon, @@ -18,8 +17,9 @@ import { EuiOverlayMask, EuiToolTip, } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import uuid from 'uuid'; +import styled from 'styled-components'; import { Note } from '../../../lib/note'; import { Notes } from '../../notes'; @@ -32,13 +32,10 @@ export const historyToolTip = 'The chronological history of actions related to t export const streamLiveToolTip = 'Update the Timeline as new data arrives'; export const newTimelineToolTip = 'Create a new timeline'; -// Ref: https://github.com/elastic/eui/issues/1655 -// const NotesCountBadge = styled(EuiBadge)` -// margin-left: 5px; -// `; -const NotesCountBadge = (props: EuiBadgeProps) => ( - -); +const NotesCountBadge = styled(EuiBadge)` + margin-left: 5px; +`; + NotesCountBadge.displayName = 'NotesCountBadge'; type CreateTimeline = ({ id, show }: { id: string; show?: boolean }) => void; @@ -121,20 +118,24 @@ interface NewTimelineProps { } export const NewTimeline = React.memo( - ({ createTimeline, onClosePopover, timelineId }) => ( - { - createTimeline({ id: timelineId, show: true }); - onClosePopover(); - }} - > - {i18n.NEW_TIMELINE} - - ) + ({ createTimeline, onClosePopover, timelineId }) => { + const handleClick = useCallback(() => { + createTimeline({ id: timelineId, show: true }); + onClosePopover(); + }, [createTimeline, timelineId, onClosePopover]); + + return ( + + {i18n.NEW_TIMELINE} + + ); + } ); NewTimeline.displayName = 'NewTimeline'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.test.tsx index 495b94f8c02e7..e942c8f36dc83 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.test.tsx @@ -10,11 +10,18 @@ import { Provider as ReduxStoreProvider } from 'react-redux'; import { mockGlobalState, apolloClientObservable } from '../../../mock'; import { createStore, State } from '../../../store'; +import { useThrottledResizeObserver } from '../../utils'; import { Properties, showDescriptionThreshold, showNotesThreshold } from '.'; jest.mock('../../../lib/kibana'); +let mockedWidth = 1000; +jest.mock('../../utils'); +(useThrottledResizeObserver as jest.Mock).mockImplementation(() => ({ + width: mockedWidth, +})); + describe('Properties', () => { const usersViewing = ['elastic']; @@ -24,6 +31,7 @@ describe('Properties', () => { beforeEach(() => { jest.clearAllMocks(); store = createStore(state, apolloClientObservable); + mockedWidth = 1000; }); test('renders correctly', () => { @@ -46,7 +54,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -73,7 +80,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -101,7 +107,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -131,7 +136,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -164,7 +168,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -197,7 +200,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -229,7 +231,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -243,7 +244,7 @@ describe('Properties', () => { test('it renders a description on the left when the width is at least as wide as the threshold', () => { const description = 'strange'; - const width = showDescriptionThreshold; + mockedWidth = showDescriptionThreshold; const wrapper = mount( @@ -264,7 +265,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={width} /> ); @@ -280,7 +280,7 @@ describe('Properties', () => { test('it does NOT render a description on the left when the width is less than the threshold', () => { const description = 'strange'; - const width = showDescriptionThreshold - 1; + mockedWidth = showDescriptionThreshold - 1; const wrapper = mount( @@ -301,7 +301,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={width} /> ); @@ -315,7 +314,7 @@ describe('Properties', () => { }); test('it renders a notes button on the left when the width is at least as wide as the threshold', () => { - const width = showNotesThreshold; + mockedWidth = showNotesThreshold; const wrapper = mount( @@ -336,7 +335,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={width} /> ); @@ -350,7 +348,7 @@ describe('Properties', () => { }); test('it does NOT render a a notes button on the left when the width is less than the threshold', () => { - const width = showNotesThreshold - 1; + mockedWidth = showNotesThreshold - 1; const wrapper = mount( @@ -371,7 +369,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={width} /> ); @@ -404,7 +401,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -434,7 +430,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); @@ -462,7 +457,6 @@ describe('Properties', () => { updateTitle={jest.fn()} updateNote={jest.fn()} usersViewing={usersViewing} - width={1000} /> ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.tsx index 7b69e006f48ad..8549784b8ecd6 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/properties/index.tsx @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; +import { useThrottledResizeObserver } from '../../utils'; import { Note } from '../../../lib/note'; import { InputsModelId } from '../../../store/inputs/constants'; import { AssociateNote, UpdateNote } from '../../notes/helpers'; @@ -37,7 +38,6 @@ interface Props { updateNote: UpdateNote; updateTitle: UpdateTitle; usersViewing: string[]; - width: number; } const rightGutter = 60; // px @@ -49,7 +49,7 @@ const starIconWidth = 30; const nameWidth = 155; const descriptionWidth = 165; const noteWidth = 130; -const settingsWidth = 50; +const settingsWidth = 55; /** Displays the properties of a timeline, i.e. name, description, notes, etc */ export const Properties = React.memo( @@ -70,47 +70,36 @@ export const Properties = React.memo( updateNote, updateTitle, usersViewing, - width, }) => { + const { ref, width = 0 } = useThrottledResizeObserver(300); const [showActions, setShowActions] = useState(false); const [showNotes, setShowNotes] = useState(false); const [showTimelineModal, setShowTimelineModal] = useState(false); - const onButtonClick = useCallback(() => { - setShowActions(!showActions); - }, [showActions]); - - const onToggleShowNotes = useCallback(() => { - setShowNotes(!showNotes); - }, [showNotes]); - - const onClosePopover = useCallback(() => { - setShowActions(false); - }, []); - + const onButtonClick = useCallback(() => setShowActions(!showActions), [showActions]); + const onToggleShowNotes = useCallback(() => setShowNotes(!showNotes), [showNotes]); + const onClosePopover = useCallback(() => setShowActions(false), []); + const onCloseTimelineModal = useCallback(() => setShowTimelineModal(false), []); + const onToggleLock = useCallback(() => toggleLock({ linkToId: 'timeline' }), [toggleLock]); const onOpenTimelineModal = useCallback(() => { onClosePopover(); setShowTimelineModal(true); }, []); - const onCloseTimelineModal = useCallback(() => { - setShowTimelineModal(false); - }, []); - - const datePickerWidth = - width - - rightGutter - - starIconWidth - - nameWidth - - (width >= showDescriptionThreshold ? descriptionWidth : 0) - - noteWidth - - settingsWidth; + const datePickerWidth = useMemo( + () => + width - + rightGutter - + starIconWidth - + nameWidth - + (width >= showDescriptionThreshold ? descriptionWidth : 0) - + noteWidth - + settingsWidth, + [width] + ); - // Passing the styles directly to the component because the width is - // being calculated and is recommended by Styled Components for performance - // https://github.com/styled-components/styled-components/issues/134#issuecomment-312415291 return ( - + ( showNotesFromWidth={width >= showNotesThreshold} timelineId={timelineId} title={title} - toggleLock={() => { - toggleLock({ linkToId: 'timeline' }); - }} + toggleLock={onToggleLock} updateDescription={updateDescription} updateIsFavorite={updateIsFavorite} updateNote={updateNote} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/properties/properties_left.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/properties/properties_left.tsx index 21800fefb21fb..3016def8a80b1 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/properties/properties_left.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/properties/properties_left.tsx @@ -52,7 +52,15 @@ export const LockIconContainer = styled(EuiFlexItem)` LockIconContainer.displayName = 'LockIconContainer'; -export const DatePicker = styled(EuiFlexItem)` +interface WidthProp { + width: number; +} + +export const DatePicker = styled(EuiFlexItem).attrs(({ width }) => ({ + style: { + width: `${width}px`, + }, +}))` .euiSuperDatePicker__flexWrapper { max-width: none; width: auto; @@ -151,7 +159,7 @@ export const PropertiesLeft = React.memo( /> - + diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/properties/styles.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/properties/styles.tsx index 3444875282ae7..74653fb6cb1ef 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/properties/styles.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/properties/styles.tsx @@ -12,17 +12,26 @@ const fadeInEffect = keyframes` to { opacity: 1; } `; +interface WidthProp { + width: number; +} + export const TimelineProperties = styled.div` + flex: 1; align-items: center; display: flex; flex-direction: row; justify-content: space-between; user-select: none; `; + TimelineProperties.displayName = 'TimelineProperties'; -export const DatePicker = styled(EuiFlexItem)<{ width: number }>` - width: ${({ width }) => `${width}px`}; +export const DatePicker = styled(EuiFlexItem).attrs(({ width }) => ({ + style: { + width: `${width}px`, + }, +}))` .euiSuperDatePicker__flexWrapper { max-width: none; width: auto; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx index 3d2ec0683f091..73c20d9b9b6b4 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx @@ -5,7 +5,7 @@ */ import React, { useEffect } from 'react'; -import { connect, ConnectedProps } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { inputsModel } from '../../store'; import { inputsActions } from '../../store/actions'; @@ -19,29 +19,20 @@ export interface TimelineRefetchProps { refetch: inputsModel.Refetch; } -type OwnProps = TimelineRefetchProps & PropsFromRedux; - -const TimelineRefetchComponent: React.FC = ({ +const TimelineRefetchComponent: React.FC = ({ id, inputId, inspect, loading, refetch, - setTimelineQuery, }) => { + const dispatch = useDispatch(); + useEffect(() => { - setTimelineQuery({ id, inputId, inspect, loading, refetch }); - }, [id, inputId, loading, refetch, inspect]); + dispatch(inputsActions.setQuery({ id, inputId, inspect, loading, refetch })); + }, [dispatch, id, inputId, loading, refetch, inspect]); return null; }; -const mapDispatchToProps = { - setTimelineQuery: inputsActions.setQuery, -}; - -const connector = connect(null, mapDispatchToProps); - -type PropsFromRedux = ConnectedProps; - -export const TimelineRefetch = connector(React.memo(TimelineRefetchComponent)); +export const TimelineRefetch = React.memo(TimelineRefetchComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/styles.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/styles.tsx index d5e5d15eb8ad2..16fb57714829c 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/styles.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/styles.tsx @@ -11,12 +11,6 @@ import styled, { createGlobalStyle } from 'styled-components'; import { EventType } from '../../store/timeline/model'; import { IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME } from '../drag_and_drop/helpers'; -/** - * OFFSET PIXEL VALUES - */ - -export const OFFSET_SCROLLBAR = 17; - /** * TIMELINE BODY */ @@ -30,10 +24,11 @@ export const TimelineBodyGlobalStyle = createGlobalStyle` export const TimelineBody = styled.div.attrs(({ className = '' }) => ({ className: `siemTimeline__body ${className}`, -}))<{ bodyHeight: number }>` - height: ${({ bodyHeight }) => `${bodyHeight}px`}; +}))<{ bodyHeight?: number }>` + height: ${({ bodyHeight }) => (bodyHeight ? `${bodyHeight}px` : 'auto')}; overflow: auto; scrollbar-width: thin; + flex: 1; &::-webkit-scrollbar { height: ${({ theme }) => theme.eui.euiScrollBar}; @@ -57,10 +52,19 @@ TimelineBody.displayName = 'TimelineBody'; * EVENTS TABLE */ -export const EventsTable = styled.div.attrs(({ className = '' }) => ({ - className: `siemEventsTable ${className}`, - role: 'table', -}))``; +interface EventsTableProps { + columnWidths: number; +} + +export const EventsTable = styled.div.attrs( + ({ className = '', columnWidths }) => ({ + className: `siemEventsTable ${className}`, + role: 'table', + style: { + minWidth: `${columnWidths}px`, + }, + }) +)``; /* EVENTS HEAD */ @@ -177,6 +181,14 @@ export const EventsTrData = styled.div.attrs(({ className = '' }) => ({ display: flex; `; +const TIMELINE_EVENT_DETAILS_OFFSET = 40; + +export const EventsTrSupplementContainer = styled.div.attrs(({ width }) => ({ + style: { + width: `${width! - TIMELINE_EVENT_DETAILS_OFFSET}px`, + }, +}))``; + export const EventsTrSupplement = styled.div.attrs(({ className = '' }) => ({ className: `siemEventsTable__trSupplement ${className}`, }))<{ className: string }>` @@ -200,11 +212,17 @@ export const EventsTdGroupData = styled.div.attrs(({ className = '' }) => ({ }))` display: flex; `; +interface WidthProp { + width?: number; +} -export const EventsTd = styled.div.attrs(({ className = '' }) => ({ +export const EventsTd = styled.div.attrs(({ className = '', width }) => ({ className: `siemEventsTable__td ${className}`, role: 'cell', -}))` + style: { + flexBasis: width ? `${width}px` : 'auto', + }, +}))` align-items: center; display: flex; flex-shrink: 0; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx index d66bc702bae43..ea4406311d7cc 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx @@ -14,20 +14,17 @@ import { mockBrowserFields } from '../../containers/source/mock'; import { Direction } from '../../graphql/types'; import { defaultHeaders, mockTimelineData, mockIndexPattern } from '../../mock'; import { TestProviders } from '../../mock/test_providers'; -import { flyoutHeaderHeight } from '../flyout'; import { DELETE_CLASS_NAME, ENABLE_CLASS_NAME, EXCLUDE_CLASS_NAME, } from './data_providers/provider_item_actions'; -import { TimelineComponent } from './timeline'; +import { TimelineComponent, Props as TimelineComponentProps } from './timeline'; import { Sort } from './body/sort'; import { mockDataProviders } from './data_providers/mock/mock_data_providers'; import { useMountAppended } from '../../utils/use_mount_appended'; -const testFlyoutHeight = 980; - jest.mock('../../lib/kibana'); const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; @@ -35,6 +32,7 @@ jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); describe('Timeline', () => { + let props = {} as TimelineComponentProps; const sort: Sort = { columnId: '@timestamp', sortDirection: Direction.desc, @@ -50,41 +48,44 @@ describe('Timeline', () => { const mount = useMountAppended(); + beforeEach(() => { + props = { + browserFields: mockBrowserFields, + columns: defaultHeaders, + id: 'foo', + dataProviders: mockDataProviders, + end: endDate, + eventType: 'raw' as TimelineComponentProps['eventType'], + filters: [], + indexPattern, + indexToAdd: [], + isLive: false, + itemsPerPage: 5, + itemsPerPageOptions: [5, 10, 20], + kqlMode: 'search' as TimelineComponentProps['kqlMode'], + kqlQueryExpression: '', + loadingIndexName: false, + onChangeDataProviderKqlQuery: jest.fn(), + onChangeDroppableAndProvider: jest.fn(), + onChangeItemsPerPage: jest.fn(), + onClose: jest.fn(), + onDataProviderEdited: jest.fn(), + onDataProviderRemoved: jest.fn(), + onToggleDataProviderEnabled: jest.fn(), + onToggleDataProviderExcluded: jest.fn(), + show: true, + showCallOutUnauthorizedMsg: false, + start: startDate, + sort, + toggleColumn: jest.fn(), + usersViewing: ['elastic'], + }; + }); + describe('rendering', () => { test('renders correctly against snapshot', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); }); @@ -92,37 +93,7 @@ describe('Timeline', () => { const wrapper = mount( - + ); @@ -130,41 +101,28 @@ describe('Timeline', () => { expect(wrapper.find('[data-test-subj="timelineHeader"]').exists()).toEqual(true); }); + test('it renders the title field', () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find('[data-test-subj="timeline-title"]') + .first() + .props().placeholder + ).toContain('Untitled Timeline'); + }); + test('it renders the timeline table', () => { const wrapper = mount( - + ); @@ -176,37 +134,7 @@ describe('Timeline', () => { const wrapper = mount( - + ); @@ -218,36 +146,7 @@ describe('Timeline', () => { const wrapper = mount( - + ); @@ -261,42 +160,10 @@ describe('Timeline', () => { describe('event wire up', () => { describe('onDataProviderRemoved', () => { test('it invokes the onDataProviderRemoved callback when the delete button on a provider is clicked', () => { - const mockOnDataProviderRemoved = jest.fn(); - const wrapper = mount( - + ); @@ -306,46 +173,16 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1'); + expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual( + 'id-Provider 1' + ); }); test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => { - const mockOnDataProviderRemoved = jest.fn(); - const wrapper = mount( - + ); @@ -361,48 +198,18 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1'); + expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual( + 'id-Provider 1' + ); }); }); describe('onToggleDataProviderEnabled', () => { test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => { - const mockOnToggleDataProviderEnabled = jest.fn(); - const wrapper = mount( - + ); @@ -419,7 +226,7 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({ + expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({ providerId: 'id-Provider 1', enabled: false, }); @@ -428,42 +235,10 @@ describe('Timeline', () => { describe('onToggleDataProviderExcluded', () => { test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => { - const mockOnToggleDataProviderExcluded = jest.fn(); - const wrapper = mount( - + ); @@ -482,7 +257,7 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({ + expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({ providerId: 'id-Provider 1', excluded: true, }); @@ -490,44 +265,14 @@ describe('Timeline', () => { }); describe('#ProviderWithAndProvider', () => { - test('Rendering And Provider', () => { - const dataProviders = mockDataProviders.slice(0, 1); - dataProviders[0].and = mockDataProviders.slice(1, 3); + const dataProviders = mockDataProviders.slice(0, 1); + dataProviders[0].and = mockDataProviders.slice(1, 3); + test('Rendering And Provider', () => { const wrapper = mount( - + ); @@ -544,44 +289,10 @@ describe('Timeline', () => { }); test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the accordion menu', () => { - const dataProviders = mockDataProviders.slice(0, 1); - dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnDataProviderRemoved = jest.fn(); - const wrapper = mount( - + ); @@ -600,48 +311,17 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnDataProviderRemoved.mock.calls[0]).toEqual(['id-Provider 1', 'id-Provider 2']); + expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0]).toEqual([ + 'id-Provider 1', + 'id-Provider 2', + ]); }); test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the accordion menu', () => { - const dataProviders = mockDataProviders.slice(0, 1); - dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnToggleDataProviderEnabled = jest.fn(); - const wrapper = mount( - + ); @@ -660,7 +340,7 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({ + expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({ andProviderId: 'id-Provider 2', enabled: false, providerId: 'id-Provider 1', @@ -668,44 +348,10 @@ describe('Timeline', () => { }); test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the accordion menu', () => { - const dataProviders = mockDataProviders.slice(0, 1); - dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnToggleDataProviderExcluded = jest.fn(); - const wrapper = mount( - + ); @@ -724,7 +370,7 @@ describe('Timeline', () => { .first() .simulate('click'); - expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({ + expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({ andProviderId: 'id-Provider 2', excluded: true, providerId: 'id-Provider 1', diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx index 58bbbef328ddf..098dd82791610 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiFlyoutBody, EuiFlyoutFooter } from '@elastic/eui'; import { getOr, isEmpty } from 'lodash/fp'; -import React from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; -import useResizeObserver from 'use-resize-observer/polyfilled'; +import { FlyoutHeaderWithCloseButton } from '../flyout/header_with_close_button'; import { BrowserFields } from '../../containers/source'; import { TimelineQuery } from '../../containers/timeline'; import { Direction } from '../../graphql/types'; @@ -31,38 +31,60 @@ import { import { TimelineKqlFetch } from './fetch_kql_timeline'; import { Footer, footerHeight } from './footer'; import { TimelineHeader } from './header'; -import { calculateBodyHeight, combineQueries } from './helpers'; +import { combineQueries } from './helpers'; import { TimelineRefetch } from './refetch_timeline'; import { ManageTimelineContext } from './timeline_context'; import { esQuery, Filter, IIndexPattern } from '../../../../../../../src/plugins/data/public'; -const WrappedByAutoSizer = styled.div` +const TimelineContainer = styled.div` + height: 100%; + display: flex; + flex-direction: column; +`; + +const TimelineHeaderContainer = styled.div` + margin-top: 6px; width: 100%; -`; // required by AutoSizer +`; -WrappedByAutoSizer.displayName = 'WrappedByAutoSizer'; +TimelineHeaderContainer.displayName = 'TimelineHeaderContainer'; -const TimelineContainer = styled(EuiFlexGroup)` - min-height: 500px; - overflow: hidden; - padding: 0 10px 0 12px; - user-select: none; - width: 100%; +const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` + align-items: center; + box-shadow: none; + display: flex; + flex-direction: column; + padding: 14px 10px 0 12px; `; -TimelineContainer.displayName = 'TimelineContainer'; +const StyledEuiFlyoutBody = styled(EuiFlyoutBody)` + overflow-y: hidden; + flex: 1; -export const isCompactFooter = (width: number): boolean => width < 600; + .euiFlyoutBody__overflow { + overflow: hidden; + mask-image: none; + } -interface Props { + .euiFlyoutBody__overflowContent { + padding: 0 10px 0 12px; + height: 100%; + display: flex; + } +`; + +const StyledEuiFlyoutFooter = styled(EuiFlyoutFooter)` + background: none; + padding: 0 10px 5px 12px; +`; + +export interface Props { browserFields: BrowserFields; columns: ColumnHeaderOptions[]; dataProviders: DataProvider[]; end: number; eventType?: EventType; filters: Filter[]; - flyoutHeaderHeight: number; - flyoutHeight: number; id: string; indexPattern: IIndexPattern; indexToAdd: string[]; @@ -75,6 +97,7 @@ interface Props { onChangeDataProviderKqlQuery: OnChangeDataProviderKqlQuery; onChangeDroppableAndProvider: OnChangeDroppableAndProvider; onChangeItemsPerPage: OnChangeItemsPerPage; + onClose: () => void; onDataProviderEdited: OnDataProviderEdited; onDataProviderRemoved: OnDataProviderRemoved; onToggleDataProviderEnabled: OnToggleDataProviderEnabled; @@ -84,6 +107,7 @@ interface Props { start: number; sort: Sort; toggleColumn: (column: ColumnHeaderOptions) => void; + usersViewing: string[]; } /** The parent Timeline component */ @@ -94,8 +118,6 @@ export const TimelineComponent: React.FC = ({ end, eventType, filters, - flyoutHeaderHeight, - flyoutHeight, id, indexPattern, indexToAdd, @@ -108,6 +130,7 @@ export const TimelineComponent: React.FC = ({ onChangeDataProviderKqlQuery, onChangeDroppableAndProvider, onChangeItemsPerPage, + onClose, onDataProviderEdited, onDataProviderRemoved, onToggleDataProviderEnabled, @@ -117,10 +140,8 @@ export const TimelineComponent: React.FC = ({ start, sort, toggleColumn, + usersViewing, }) => { - const { ref: measureRef, width = 0, height: timelineHeaderHeight = 0 } = useResizeObserver< - HTMLDivElement - >({}); const kibana = useKibana(); const combinedQueries = combineQueries({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), @@ -134,45 +155,51 @@ export const TimelineComponent: React.FC = ({ end, }); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; + const timelineQueryFields = useMemo(() => columnsHeader.map(c => c.id), [columnsHeader]); + const timelineQuerySortField = useMemo( + () => ({ + sortFieldId: sort.columnId, + direction: sort.sortDirection as Direction, + }), + [sort.columnId, sort.sortDirection] + ); return ( - - }> - + + - + + + + {combinedQueries != null ? ( c.id)} + fields={timelineQueryFields} sourceId="default" limit={itemsPerPage} filterQuery={combinedQueries.filterQuery} - sortField={{ - sortFieldId: sort.columnId, - direction: sort.sortDirection as Direction, - }} + sortField={timelineQuerySortField} > {({ events, @@ -184,7 +211,7 @@ export const TimelineComponent: React.FC = ({ getUpdatedAt, refetch, }) => ( - + = ({ loading={loading} refetch={refetch} /> - -
+ + + + +
+ )} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline_context.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline_context.tsx index 15759c2efff0b..f1100e17bd3cb 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline_context.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline_context.tsx @@ -11,10 +11,6 @@ const initTimelineContext = false; export const TimelineContext = createContext(initTimelineContext); export const useTimelineContext = () => useContext(TimelineContext); -const initTimelineWidth = 0; -export const TimelineWidthContext = createContext(initTimelineWidth); -export const useTimelineWidthContext = () => useContext(TimelineWidthContext); - export interface TimelineTypeContextProps { documentType?: string; footerText?: string; @@ -41,7 +37,6 @@ export const useTimelineTypeContext = () => useContext(TimelineTypeContext); interface ManageTimelineContextProps { children: React.ReactNode; loading: boolean; - width: number; type?: TimelineTypeContextProps; } @@ -50,11 +45,9 @@ interface ManageTimelineContextProps { const ManageTimelineContextComponent: React.FC = ({ children, loading, - width, type = initTimelineType, }) => { const [myLoading, setLoading] = useState(initTimelineContext); - const [myWidth, setWidth] = useState(initTimelineWidth); const [myType, setType] = useState(initTimelineType); useEffect(() => { @@ -65,15 +58,9 @@ const ManageTimelineContextComponent: React.FC = ({ setType(type); }, [type]); - useEffect(() => { - setWidth(width); - }, [width]); - return ( - - {children} - + {children} ); }; diff --git a/x-pack/legacy/plugins/siem/public/components/toasters/modal_all_errors.tsx b/x-pack/legacy/plugins/siem/public/components/toasters/modal_all_errors.tsx index dfbf09e555a76..06a46ddff1075 100644 --- a/x-pack/legacy/plugins/siem/public/components/toasters/modal_all_errors.tsx +++ b/x-pack/legacy/plugins/siem/public/components/toasters/modal_all_errors.tsx @@ -17,7 +17,7 @@ import { EuiModalFooter, EuiAccordion, } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; import { AppToast } from '.'; @@ -29,10 +29,14 @@ interface FullErrorProps { toggle: (toast: AppToast) => void; } -export const ModalAllErrors = ({ isShowing, toast, toggle }: FullErrorProps) => - isShowing && toast != null ? ( +const ModalAllErrorsComponent: React.FC = ({ isShowing, toast, toggle }) => { + const handleClose = useCallback(() => toggle(toast), [toggle, toast]); + + if (!isShowing || toast == null) return null; + + return ( - toggle(toast)}> + {i18n.TITLE_ERROR_MODAL} @@ -55,13 +59,16 @@ export const ModalAllErrors = ({ isShowing, toast, toggle }: FullErrorProps) => - toggle(toast)} fill data-test-subj="modal-all-errors-close"> + {i18n.CLOSE_ERROR_MODAL} - ) : null; + ); +}; + +export const ModalAllErrors = React.memo(ModalAllErrorsComponent); const MyEuiCodeBlock = styled(EuiCodeBlock)` margin-top: 4px; diff --git a/x-pack/legacy/plugins/siem/public/components/utils.ts b/x-pack/legacy/plugins/siem/public/components/utils.ts index 42dd5b7c011aa..ff022fd7d763d 100644 --- a/x-pack/legacy/plugins/siem/public/components/utils.ts +++ b/x-pack/legacy/plugins/siem/public/components/utils.ts @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { throttle } from 'lodash/fp'; +import { useMemo, useState } from 'react'; +import useResizeObserver from 'use-resize-observer/polyfilled'; import { niceTimeFormatByDay, timeFormatter } from '@elastic/charts'; import moment from 'moment-timezone'; @@ -22,3 +25,11 @@ export const histogramDateTimeFormatter = (domain: [number, number] | null, fixe const format = niceTimeFormatByDay(diff); return timeFormatter(format); }; + +export const useThrottledResizeObserver = (wait = 100) => { + const [size, setSize] = useState<{ width: number; height: number }>({ width: 0, height: 0 }); + const onResize = useMemo(() => throttle(wait, setSize), [wait]); + const { ref } = useResizeObserver({ onResize }); + + return { ref, ...size }; +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx index ccd8babd41e68..f726ec9779dc8 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getOr } from 'lodash/fp'; +import { getOr, uniqBy } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import React from 'react'; import { Query } from 'react-apollo'; @@ -137,10 +137,10 @@ class TimelineQueryComponent extends QueryTemplate< ...fetchMoreResult.source, Timeline: { ...fetchMoreResult.source.Timeline, - edges: [ + edges: uniqBy('node._id', [ ...prev.source.Timeline.edges, ...fetchMoreResult.source.Timeline.edges, - ], + ]), }, }, }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx index ef42b5097e364..49a181a1cd897 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx @@ -49,11 +49,11 @@ export const ImportRuleModalComponent = ({ const [overwrite, setOverwrite] = useState(false); const [, dispatchToaster] = useStateToaster(); - const cleanupAndCloseModal = () => { + const cleanupAndCloseModal = useCallback(() => { setIsImporting(false); setSelectedFiles(null); closeModal(); - }; + }, [setIsImporting, setSelectedFiles, closeModal]); const importRulesCallback = useCallback(async () => { if (selectedFiles != null) { diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index 605136a190c54..a8a34383585c6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -4,19 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; import styled from 'styled-components'; -import useResizeObserver from 'use-resize-observer/polyfilled'; +import { useThrottledResizeObserver } from '../../components/utils'; import { DragDropContextWrapper } from '../../components/drag_and_drop/drag_drop_context_wrapper'; -import { Flyout, flyoutHeaderHeight } from '../../components/flyout'; +import { Flyout } from '../../components/flyout'; import { HeaderGlobal } from '../../components/header_global'; import { HelpMenu } from '../../components/help_menu'; import { LinkToPage } from '../../components/link_to'; import { MlHostConditionalContainer } from '../../components/ml/conditional_links/ml_host_conditional_container'; import { MlNetworkConditionalContainer } from '../../components/ml/conditional_links/ml_network_conditional_container'; -import { StatefulTimeline } from '../../components/timeline'; import { AutoSaveWarningMsg } from '../../components/timeline/auto_save_warning'; import { UseUrlState } from '../../components/url_state'; import { WithSource, indicesExistOrDataTemporarilyUnavailable } from '../../containers/source'; @@ -63,11 +62,15 @@ const calculateFlyoutHeight = ({ }): number => Math.max(0, windowHeight - globalHeaderSize); export const HomePage: React.FC = () => { - const { ref: measureRef, height: windowHeight = 0 } = useResizeObserver({}); - const flyoutHeight = calculateFlyoutHeight({ - globalHeaderSize: globalHeaderHeightPx, - windowHeight, - }); + const { ref: measureRef, height: windowHeight = 0 } = useThrottledResizeObserver(); + const flyoutHeight = useMemo( + () => + calculateFlyoutHeight({ + globalHeaderSize: globalHeaderHeightPx, + windowHeight, + }), + [windowHeight] + ); const [showTimeline] = useShowTimeline(); @@ -85,16 +88,9 @@ export const HomePage: React.FC = () => { - - + /> )} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3fdcf9b815931..c155344c7534a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11330,7 +11330,7 @@ "xpack.siem.timeline.expandableEvent.copyToClipboardToolTip": "クリップボードにコピー", "xpack.siem.timeline.expandableEvent.eventToolTipTitle": "イベント", "xpack.siem.timeline.fieldTooltip": "フィールド", - "xpack.siem.timeline.flyout.pane.closeTimelineButtonLabel": "タイムラインを閉じる", + "xpack.siem.timeline.flyout.header.closeTimelineButtonLabel": "タイムラインを閉じる", "xpack.siem.timeline.flyout.pane.removeColumnButtonLabel": "列を削除", "xpack.siem.timeline.flyout.pane.timelinePropertiesAriaLabel": "タイムラインのプロパティ", "xpack.siem.timeline.properties.descriptionPlaceholder": "説明", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1bcbcca055c32..ed0ac8f6f7fef 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11330,7 +11330,7 @@ "xpack.siem.timeline.expandableEvent.copyToClipboardToolTip": "复制到剪贴板", "xpack.siem.timeline.expandableEvent.eventToolTipTitle": "时间", "xpack.siem.timeline.fieldTooltip": "字段", - "xpack.siem.timeline.flyout.pane.closeTimelineButtonLabel": "关闭时间线", + "xpack.siem.timeline.flyout.header.closeTimelineButtonLabel": "关闭时间线", "xpack.siem.timeline.flyout.pane.removeColumnButtonLabel": "删除列", "xpack.siem.timeline.flyout.pane.timelinePropertiesAriaLabel": "时间线属性", "xpack.siem.timeline.properties.descriptionPlaceholder": "描述", diff --git a/yarn.lock b/yarn.lock index dcb360587e4ed..1e5c160a7eb19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5018,10 +5018,10 @@ dependencies: "@types/react" "*" -"@types/react-beautiful-dnd@^11.0.4": - version "11.0.4" - resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-11.0.4.tgz#25cdf16864df8fd1d82f9416c8c0fd957e793024" - integrity sha512-a1Nvt1AcSEA962OuXrk1gu5bJQhzu0B3qFNO999/0nmF+oAD7HIAY0DwraS3L3XM1cVuRO1+PtpTkD4CfRK2QA== +"@types/react-beautiful-dnd@^12.1.1": + version "12.1.1" + resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-12.1.1.tgz#149e638c0f912eee6b74ea419b26bb43d0b1da60" + integrity sha512-CPKynKgGVRK+xmywLMD0qNWamdscxhgf1Um+2oEgN6Qibn1rye3M4p2bdxAMgtOTZ2L81bYl6KGKSzJVboJWeA== dependencies: "@types/react" "*" From eddbdc896ba6ebb548fc01d32b86cf56c2922764 Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:02:03 +0300 Subject: [PATCH 37/76] [NP] Get rid of usage redirectWhenMissing service (#59777) * Move redirect_when_missing to kibana utils * Replace redirectWhenMissing in dashboard * Replace redirectWhenMissing in discover * Remove redirect in monitoring * Remove extra import * Move invalid vistype check into editor.js * Mock the history folder * Fix redirect when missing index or saved object * Move history to discover services * Use redirect to listing page Co-authored-by: Elastic Machine --- .../kibana/public/dashboard/legacy_imports.ts | 2 +- .../public/dashboard/np_ready/application.ts | 4 +- .../public/dashboard/np_ready/legacy_app.js | 23 ++++-- .../kibana/public/discover/build_services.ts | 4 + .../public/discover/get_inner_angular.ts | 5 +- .../kibana/public/discover/kibana_services.ts | 2 +- .../discover/np_ready/angular/discover.js | 17 ++-- .../np_ready/angular/discover_state.test.ts | 2 +- .../np_ready/angular/discover_state.ts | 10 +-- .../kibana/public/visualize/legacy_imports.ts | 2 +- .../public/visualize/np_ready/application.ts | 4 +- .../visualize/np_ready/editor/editor.js | 30 +++++-- .../np_ready/editor/visualization.js | 4 +- .../public/visualize/np_ready/legacy_app.js | 35 +++++--- .../index_patterns/index_pattern.test.ts | 2 + .../kibana_utils/public/history/index.ts | 1 + .../public/history/redirect_when_missing.tsx | 80 +++++++++++++++++++ src/plugins/kibana_utils/public/index.ts | 2 +- .../public/np_imports/angular/modules.ts | 4 +- .../public/np_imports/legacy_imports.ts | 2 +- 20 files changed, 179 insertions(+), 56 deletions(-) create mode 100644 src/plugins/kibana_utils/public/history/redirect_when_missing.tsx diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts index 0c5329d8b259f..b497f73f3df2a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -28,7 +28,7 @@ export { npSetup, npStart } from 'ui/new_platform'; export { KbnUrl } from 'ui/url/kbn_url'; // @ts-ignore -export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url/index'; +export { KbnUrlProvider } from 'ui/url/index'; export { IInjector } from 'ui/chrome'; export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; export { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts index fe0e7a1d3e6d0..9447b5384d172 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts @@ -35,7 +35,6 @@ import { KbnUrlProvider, PrivateProvider, PromiseServiceCreator, - RedirectWhenMissingProvider, } from '../legacy_imports'; // @ts-ignore import { initDashboardApp } from './legacy_app'; @@ -146,8 +145,7 @@ function createLocalIconModule() { function createLocalKbnUrlModule() { angular .module('app/dashboard/KbnUrl', ['app/dashboard/Private', 'ngRoute']) - .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)) - .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider)); + .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)); } function createLocalConfigModule(core: AppMountContext['core']) { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js index 35b510894179d..f7baba663da75 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js @@ -28,6 +28,7 @@ import { initDashboardAppDirective } from './dashboard_app'; import { createDashboardEditUrl, DashboardConstants } from './dashboard_constants'; import { createKbnUrlStateStorage, + redirectWhenMissing, InvalidJSONProperty, SavedObjectNotFound, } from '../../../../../../plugins/kibana_utils/public'; @@ -136,7 +137,7 @@ export function initDashboardApp(app, deps) { }); }, resolve: { - dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, history) { + dash: function($rootScope, $route, kbnUrl, history) { return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl).then(() => { const savedObjectsClient = deps.savedObjectsClient; const title = $route.current.params.title; @@ -171,14 +172,18 @@ export function initDashboardApp(app, deps) { controller: createNewDashboardCtrl, requireUICapability: 'dashboard.createNew', resolve: { - dash: function(redirectWhenMissing, $rootScope, kbnUrl) { + dash: function($rootScope, kbnUrl, history) { return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl) .then(() => { return deps.savedDashboards.get(); }) .catch( redirectWhenMissing({ - dashboard: DashboardConstants.LANDING_PAGE_PATH, + history, + mapping: { + dashboard: DashboardConstants.LANDING_PAGE_PATH, + }, + toastNotifications: deps.core.notifications.toasts, }) ); }, @@ -189,7 +194,7 @@ export function initDashboardApp(app, deps) { template: dashboardTemplate, controller: createNewDashboardCtrl, resolve: { - dash: function($rootScope, $route, redirectWhenMissing, kbnUrl, history) { + dash: function($rootScope, $route, kbnUrl, history) { const id = $route.current.params.id; return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl) @@ -207,7 +212,7 @@ export function initDashboardApp(app, deps) { .catch(error => { // A corrupt dashboard was detected (e.g. with invalid JSON properties) if (error instanceof InvalidJSONProperty) { - deps.toastNotifications.addDanger(error.message); + deps.core.notifications.toasts.addDanger(error.message); kbnUrl.redirect(DashboardConstants.LANDING_PAGE_PATH); return; } @@ -221,7 +226,7 @@ export function initDashboardApp(app, deps) { pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL, }); - deps.toastNotifications.addWarning( + deps.core.notifications.toasts.addWarning( i18n.translate('kbn.dashboard.urlWasRemovedInSixZeroWarningMessage', { defaultMessage: 'The url "dashboard/create" was removed in 6.0. Please update your bookmarks.', @@ -234,7 +239,11 @@ export function initDashboardApp(app, deps) { }) .catch( redirectWhenMissing({ - dashboard: DashboardConstants.LANDING_PAGE_PATH, + history, + mapping: { + dashboard: DashboardConstants.LANDING_PAGE_PATH, + }, + toastNotifications: deps.core.notifications.toasts, }) ); }, diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts index c58307adaf38c..282eef0c983eb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import { createHashHistory, History } from 'history'; + import { Capabilities, ChromeStart, @@ -46,6 +48,7 @@ export interface DiscoverServices { data: DataPublicPluginStart; docLinks: DocLinksStart; docViewsRegistry: DocViewsRegistry; + history: History; theme: ChartsPluginStart['theme']; filterManager: FilterManager; indexPatterns: IndexPatternsContract; @@ -79,6 +82,7 @@ export async function buildServices( data: plugins.data, docLinks: core.docLinks, docViewsRegistry, + history: createHashHistory(), theme: plugins.charts.theme, filterManager: plugins.data.query.filterManager, getSavedSearchById: async (id: string) => savedObjectService.get(id), diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts index 76d475c4f7f96..4d871bcb7a858 100644 --- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -27,7 +27,7 @@ import { CoreStart, LegacyCoreStart, IUiSettingsClient } from 'kibana/public'; // @ts-ignore import { StateManagementConfigProvider } from 'ui/state_management/config_provider'; // @ts-ignore -import { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; +import { KbnUrlProvider } from 'ui/url'; import { DataPublicPluginStart } from '../../../../../plugins/data/public'; import { Storage } from '../../../../../plugins/kibana_utils/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; @@ -173,8 +173,7 @@ export function initializeInnerAngularModule( function createLocalKbnUrlModule() { angular .module('discoverKbnUrl', ['discoverPrivate', 'ngRoute']) - .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)) - .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider)); + .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)); } function createLocalConfigModule(uiSettings: IUiSettingsClient) { diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 57a9e4966d6d6..8202ba13b30cc 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -59,7 +59,7 @@ export { intervalOptions } from 'ui/agg_types'; export { subscribeWithScope } from '../../../../../plugins/kibana_legacy/public'; // @ts-ignore export { timezoneProvider } from 'ui/vis/lib/timezone'; -export { unhashUrl } from '../../../../../plugins/kibana_utils/public'; +export { unhashUrl, redirectWhenMissing } from '../../../../../plugins/kibana_utils/public'; export { ensureDefaultIndexPattern, formatMsg, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js index f3334c9211e4b..6978781fe6696 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js @@ -50,6 +50,7 @@ import { tabifyAggResponse, getAngularModule, ensureDefaultIndexPattern, + redirectWhenMissing, } from '../../kibana_services'; const { @@ -57,6 +58,7 @@ const { chrome, data, docTitle, + history, indexPatterns, filterManager, share, @@ -113,10 +115,10 @@ app.config($routeProvider => { template: indexTemplate, reloadOnSearch: false, resolve: { - savedObjects: function(redirectWhenMissing, $route, kbnUrl, Promise, $rootScope) { + savedObjects: function($route, kbnUrl, Promise, $rootScope) { const savedSearchId = $route.current.params.id; return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl).then(() => { - const { appStateContainer } = getState({}); + const { appStateContainer } = getState({ history }); const { index } = appStateContainer.getState(); return Promise.props({ ip: indexPatterns.getCache().then(indexPatternList => { @@ -151,9 +153,13 @@ app.config($routeProvider => { }) .catch( redirectWhenMissing({ - search: '/discover', - 'index-pattern': - '/management/kibana/objects/savedSearches/' + $route.current.params.id, + history, + mapping: { + search: '/discover', + 'index-pattern': + '/management/kibana/objects/savedSearches/' + $route.current.params.id, + }, + toastNotifications, }) ), }); @@ -207,6 +213,7 @@ function discoverController( } = getState({ defaultAppState: getStateDefaults(), storeInSessionStorage: config.get('state:storeInSessionStorage'), + history, }); if (appStateContainer.getState().index !== $scope.indexPattern.id) { //used index pattern is different than the given by url/state which is invalid diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts index af772cb5c76f1..3840fd0c2e3be 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.test.ts @@ -30,7 +30,7 @@ describe('Test discover state', () => { history.push('/'); state = getState({ defaultAppState: { index: 'test' }, - hashHistory: history, + history, }); await state.replaceUrlAppState({}); await state.startSync(); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts index 10e7cd1d0c49d..981855d1ee774 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover_state.ts @@ -17,7 +17,7 @@ * under the License. */ import { isEqual } from 'lodash'; -import { createHashHistory, History } from 'history'; +import { History } from 'history'; import { createStateContainer, createKbnUrlStateStorage, @@ -65,9 +65,9 @@ interface GetStateParams { */ storeInSessionStorage?: boolean; /** - * Browser history used for testing + * Browser history */ - hashHistory?: History; + history: History; } export interface GetStateReturn { @@ -121,11 +121,11 @@ const APP_STATE_URL_KEY = '_a'; export function getState({ defaultAppState = {}, storeInSessionStorage = false, - hashHistory, + history, }: GetStateParams): GetStateReturn { const stateStorage = createKbnUrlStateStorage({ useHash: storeInSessionStorage, - history: hashHistory ? hashHistory : createHashHistory(), + history, }); const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState; diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts index 0ddf3ee67aa94..69af466a03729 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -25,7 +25,7 @@ */ // @ts-ignore -export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; +export { KbnUrlProvider } from 'ui/url'; export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; export { KibanaParsedUrl } from 'ui/url/kibana_parsed_url'; export { wrapInI18nContext } from 'ui/i18n'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts index 8ef63ec5778e2..c7c3286bb5c71 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts @@ -24,7 +24,6 @@ import { AppMountContext } from 'kibana/public'; import { configureAppAngularModule, KbnUrlProvider, - RedirectWhenMissingProvider, IPrivate, PrivateProvider, PromiseServiceCreator, @@ -102,8 +101,7 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav function createLocalKbnUrlModule() { angular .module('app/visualize/KbnUrl', ['app/visualize/Private', 'ngRoute']) - .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)) - .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider)); + .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)); } function createLocalPromiseModule() { diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js index c023c402f5fea..1fab38027f65b 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js @@ -31,6 +31,7 @@ import { getEditBreadcrumbs } from '../breadcrumbs'; import { addHelpMenuToAppChrome } from '../help_menu/help_menu_util'; import { unhashUrl } from '../../../../../../../plugins/kibana_utils/public'; +import { MarkdownSimple, toMountPoint } from '../../../../../../../plugins/kibana_react/public'; import { addFatalError, kbnBaseUrl } from '../../../../../../../plugins/kibana_legacy/public'; import { SavedObjectSaveModal, @@ -75,7 +76,6 @@ function VisualizeAppController( $injector, $timeout, kbnUrl, - redirectWhenMissing, kbnUrlStateStorage, history ) { @@ -313,16 +313,33 @@ function VisualizeAppController( } ); + const stopAllSyncing = () => { + stopStateSync(); + stopSyncingQueryServiceStateWithUrl(); + stopSyncingAppFilters(); + }; + // The savedVis is pulled from elasticsearch, but the appState is pulled from the url, with the // defaults applied. If the url was from a previous session which included modifications to the // appState then they won't be equal. if (!_.isEqual(stateContainer.getState().vis, stateDefaults.vis)) { try { vis.setState(stateContainer.getState().vis); - } catch { - redirectWhenMissing({ - 'index-pattern-field': '/visualize', + } catch (error) { + // stop syncing url updtes with the state to prevent extra syncing + stopAllSyncing(); + + toastNotifications.addWarning({ + title: i18n.translate('kbn.visualize.visualizationTypeInvalidNotificationMessage', { + defaultMessage: 'Invalid visualization type', + }), + text: toMountPoint({error.message}), }); + + history.replace(`${VisualizeConstants.LANDING_PAGE_PATH}?notFound=visualization`); + + // prevent further controller execution + return; } } @@ -529,9 +546,8 @@ function VisualizeAppController( unsubscribePersisted(); unsubscribeStateUpdates(); - stopStateSync(); - stopSyncingQueryServiceStateWithUrl(); - stopSyncingAppFilters(); + + stopAllSyncing(); }); $timeout(() => { diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js index 6acdb0abdd0b5..c8acea168444f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/visualization.js @@ -59,7 +59,9 @@ export function initVisualizationDirective(app, deps) { }); $scope.$on('$destroy', () => { - $scope._handler.destroy(); + if ($scope._handler) { + $scope._handler.destroy(); + } }); }, }; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index b9409445166bc..1002f401706cd 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -21,7 +21,10 @@ import { find } from 'lodash'; import { i18n } from '@kbn/i18n'; import { createHashHistory } from 'history'; -import { createKbnUrlStateStorage } from '../../../../../../plugins/kibana_utils/public'; +import { + createKbnUrlStateStorage, + redirectWhenMissing, +} from '../../../../../../plugins/kibana_utils/public'; import editorTemplate from './editor/editor.html'; import visualizeListingTemplate from './listing/visualize_listing.html'; @@ -100,8 +103,8 @@ export function initVisualizeApp(app, deps) { template: editorTemplate, k7Breadcrumbs: getCreateBreadcrumbs, resolve: { - savedVis: function(redirectWhenMissing, $route, $rootScope, kbnUrl) { - const { core, data, savedVisualizations, visualizations } = deps; + savedVis: function($route, $rootScope, kbnUrl, history) { + const { core, data, savedVisualizations, visualizations, toastNotifications } = deps; const visTypes = visualizations.all(); const visType = find(visTypes, { name: $route.current.params.type }); const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection; @@ -128,7 +131,9 @@ export function initVisualizeApp(app, deps) { }) .catch( redirectWhenMissing({ - '*': '/visualize', + history, + mapping: VisualizeConstants.LANDING_PAGE_PATH, + toastNotifications, }) ); }, @@ -139,8 +144,8 @@ export function initVisualizeApp(app, deps) { template: editorTemplate, k7Breadcrumbs: getEditBreadcrumbs, resolve: { - savedVis: function(redirectWhenMissing, $route, $rootScope, kbnUrl) { - const { chrome, core, data, savedVisualizations } = deps; + savedVis: function($route, $rootScope, kbnUrl, history) { + const { chrome, core, data, savedVisualizations, toastNotifications } = deps; return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl) .then(() => savedVisualizations.get($route.current.params.id)) .then(savedVis => { @@ -155,13 +160,17 @@ export function initVisualizeApp(app, deps) { }) .catch( redirectWhenMissing({ - visualization: '/visualize', - search: - '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, - 'index-pattern': - '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, - 'index-pattern-field': - '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + history, + mapping: { + visualization: VisualizeConstants.LANDING_PAGE_PATH, + search: + '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + 'index-pattern': + '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + 'index-pattern-field': + '/management/kibana/objects/savedVisualizations/' + $route.current.params.id, + }, + toastNotifications, }) ); }, diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts index b6ca91169a933..305aa8575e4d7 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts @@ -18,6 +18,8 @@ */ import { defaults, pluck, last, get } from 'lodash'; + +jest.mock('../../../../kibana_utils/public/history'); import { IndexPattern } from './index_pattern'; import { DuplicateField } from '../../../../kibana_utils/public'; diff --git a/src/plugins/kibana_utils/public/history/index.ts b/src/plugins/kibana_utils/public/history/index.ts index b4b5658c1c886..bb13ea09f928a 100644 --- a/src/plugins/kibana_utils/public/history/index.ts +++ b/src/plugins/kibana_utils/public/history/index.ts @@ -18,3 +18,4 @@ */ export { removeQueryParam } from './remove_query_param'; +export { redirectWhenMissing } from './redirect_when_missing'; diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx new file mode 100644 index 0000000000000..cbdeef6fbe96c --- /dev/null +++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx @@ -0,0 +1,80 @@ +/* + * 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 React from 'react'; +import { History } from 'history'; +import { i18n } from '@kbn/i18n'; + +import { ToastsSetup } from 'kibana/public'; +import { MarkdownSimple, toMountPoint } from '../../../kibana_react/public'; +import { SavedObjectNotFound } from '../errors'; + +interface Mapping { + [key: string]: string; +} + +/** + * Creates an error handler that will redirect to a url when a SavedObjectNotFound + * error is thrown + */ +export function redirectWhenMissing({ + history, + mapping, + toastNotifications, +}: { + history: History; + /** + * a mapping of url's to redirect to based on the saved object that + * couldn't be found, or just a string that will be used for all types + */ + mapping: string | Mapping; + /** + * Toast notifications service to show toasts in error cases. + */ + toastNotifications: ToastsSetup; +}) { + let localMappingObject: Mapping; + + if (typeof mapping === 'string') { + localMappingObject = { '*': mapping }; + } else { + localMappingObject = mapping; + } + + return (error: SavedObjectNotFound) => { + // if this error is not "404", rethrow + // we can't check "error instanceof SavedObjectNotFound" since this class can live in a separate bundle + // and the error will be an instance of other class with the same interface (actually the copy of SavedObjectNotFound class) + if (!error.savedObjectType) { + throw error; + } + + let url = localMappingObject[error.savedObjectType] || localMappingObject['*'] || '/'; + url += (url.indexOf('?') >= 0 ? '&' : '?') + `notFound=${error.savedObjectType}`; + + toastNotifications.addWarning({ + title: i18n.translate('kibana_utils.history.savedObjectIsMissingNotificationMessage', { + defaultMessage: 'Saved object is missing', + }), + text: toMountPoint({error.message}), + }); + + history.replace(url); + }; +} diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index ee38d5e8111c9..47f90cbe2a627 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -73,5 +73,5 @@ export { StartSyncStateFnType, StopSyncStateFnType, } from './state_sync'; -export { removeQueryParam } from './history'; +export { removeQueryParam, redirectWhenMissing } from './history'; export { applyDiff } from './state_management/utils/diff_object'; diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts index c14b64a32fb5c..b506784bf15ee 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts +++ b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts @@ -19,7 +19,6 @@ import { StateManagementConfigProvider, AppStateProvider, KbnUrlProvider, - RedirectWhenMissingProvider, npStart, } from '../legacy_imports'; @@ -79,8 +78,7 @@ function createLocalStateModule() { function createLocalKbnUrlModule() { angular .module('monitoring/KbnUrl', ['monitoring/Private', 'ngRoute']) - .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)) - .service('redirectWhenMissing', (Private: IPrivate) => Private(RedirectWhenMissingProvider)); + .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)); } function createLocalConfigModule(core: AppMountContext['core']) { diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts index a2ebe8231456f..208b7e2acdb0f 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts +++ b/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts @@ -18,5 +18,5 @@ export { AppStateProvider } from 'ui/state_management/app_state'; // @ts-ignore export { EventsProvider } from 'ui/events'; // @ts-ignore -export { KbnUrlProvider, RedirectWhenMissingProvider } from 'ui/url'; +export { KbnUrlProvider } from 'ui/url'; export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router'; From a755e55907ef0b084c850814809e77df650d07bc Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Tue, 17 Mar 2020 12:16:11 +0100 Subject: [PATCH 38/76] Fix import to timefilter from in TSVB (#60296) --- .../vis_type_timeseries/public/components/vis_editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js index 0263f5b2c851c..ff2546f75c51a 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_editor.js @@ -50,7 +50,7 @@ export class VisEditor extends Component { visFields: props.visFields, extractedIndexPatterns: [''], }; - this.onBrush = createBrushHandler(getDataStart().query.timefilter); + this.onBrush = createBrushHandler(getDataStart().query.timefilter.timefilter); this.visDataSubject = new Rx.BehaviorSubject(this.props.visData); this.visData$ = this.visDataSubject.asObservable().pipe(share()); From e25430ba36bb450a3e93bf788258fb732bdad63b Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 17 Mar 2020 12:22:45 +0100 Subject: [PATCH 39/76] [TSVB] fix text color when using custom background color (#60261) When the user apply a background color manually from the UI, this commit adapt the current colors to have a better contrast with the chosen background color irrespective of the used dark/light theme --- package.json | 1 + .../components/vis_types/_vis_types.scss | 17 +++ .../components/vis_types/timeseries/vis.js | 7 +- .../timeseries/__mocks__/@elastic/charts.js | 2 + .../visualizations/views/timeseries/index.js | 19 ++- .../views/timeseries/utils/theme.test.ts | 44 ++++++ .../views/timeseries/utils/theme.ts | 139 ++++++++++++++++++ 7 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/theme.test.ts create mode 100644 src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/theme.ts diff --git a/package.json b/package.json index b3dcfb2aa3b0a..261b3ad74d9b7 100644 --- a/package.json +++ b/package.json @@ -314,6 +314,7 @@ "@types/cheerio": "^0.22.10", "@types/chromedriver": "^2.38.0", "@types/classnames": "^2.2.9", + "@types/color": "^3.0.0", "@types/d3": "^3.5.43", "@types/dedent": "^0.7.0", "@types/deep-freeze-strict": "^1.1.0", diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/_vis_types.scss b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/_vis_types.scss index 90c2007b1c94a..3db09bace079f 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/_vis_types.scss +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/_vis_types.scss @@ -7,4 +7,21 @@ .tvbVisTimeSeries { overflow: hidden; } + .tvbVisTimeSeriesDark { + .echReactiveChart_unavailable { + color: #DFE5EF; + } + .echLegendItem { + color: #DFE5EF; + } + } + .tvbVisTimeSeriesLight { + .echReactiveChart_unavailable { + color: #343741; + } + .echLegendItem { + color: #343741; + } + } } + diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js index 954d3d174bb8c..356ba08ac2427 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/vis_types/timeseries/vis.js @@ -33,9 +33,8 @@ import { getAxisLabelString } from '../../lib/get_axis_label_string'; import { getInterval } from '../../lib/get_interval'; import { areFieldsDifferent } from '../../lib/charts'; import { createXaxisFormatter } from '../../lib/create_xaxis_formatter'; -import { isBackgroundDark } from '../../../lib/set_is_reversed'; import { STACKED_OPTIONS } from '../../../visualizations/constants'; -import { getCoreStart } from '../../../services'; +import { getCoreStart, getUISettings } from '../../../services'; export class TimeseriesVisualization extends Component { static propTypes = { @@ -238,6 +237,7 @@ export class TimeseriesVisualization extends Component { } }); + const darkMode = getUISettings().get('theme:darkMode'); return (
null; export const AreaSeries = () => null; + +export { LIGHT_THEME, DARK_THEME } from '@elastic/charts'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js index 986111b462b35..75554a476bdea 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js @@ -19,14 +19,13 @@ import React, { useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { Axis, Chart, Position, Settings, - DARK_THEME, - LIGHT_THEME, AnnotationDomainTypes, LineAnnotation, TooltipType, @@ -40,6 +39,7 @@ import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constan import { AreaSeriesDecorator } from './decorators/area_decorator'; import { BarSeriesDecorator } from './decorators/bar_decorator'; import { getStackAccessors } from './utils/stack_format'; +import { getTheme, getChartClasses } from './utils/theme'; const generateAnnotationData = (values, formatter) => values.map(({ key, docs }) => ({ @@ -57,7 +57,8 @@ const handleCursorUpdate = cursor => { }; export const TimeSeries = ({ - isDarkMode, + darkMode, + backgroundColor, showGrid, legend, legendPosition, @@ -89,8 +90,13 @@ export const TimeSeries = ({ const timeZone = timezoneProvider(uiSettings)(); const hasBarChart = series.some(({ bars }) => bars.show); + // compute the theme based on the bg color + const theme = getTheme(darkMode, backgroundColor); + // apply legend style change if bgColor is configured + const classes = classNames('tvbVisTimeSeries', getChartClasses(backgroundColor)); + return ( - + { + it('should return the basic themes if no bg color is specified', () => { + // use original dark/light theme + expect(getTheme(false)).toEqual(LIGHT_THEME); + expect(getTheme(true)).toEqual(DARK_THEME); + + // discard any wrong/missing bg color + expect(getTheme(true, null)).toEqual(DARK_THEME); + expect(getTheme(true, '')).toEqual(DARK_THEME); + expect(getTheme(true, undefined)).toEqual(DARK_THEME); + }); + it('should return a highcontrast color theme for a different background', () => { + // red use a near full-black color + expect(getTheme(false, 'red').axes.axisTitleStyle.fill).toEqual('rgb(23,23,23)'); + + // violet increased the text color to full white for higer contrast + expect(getTheme(false, '#ba26ff').axes.axisTitleStyle.fill).toEqual('rgb(255,255,255)'); + + // light yellow, prefer the LIGHT_THEME fill color because already with a good contrast + expect(getTheme(false, '#fff49f').axes.axisTitleStyle.fill).toEqual('#333'); + }); +}); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/theme.ts b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/theme.ts new file mode 100644 index 0000000000000..a25d5e1ce1d35 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/theme.ts @@ -0,0 +1,139 @@ +/* + * 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 colorJS from 'color'; +import { Theme, LIGHT_THEME, DARK_THEME } from '@elastic/charts'; + +function computeRelativeLuminosity(rgb: string) { + return colorJS(rgb).luminosity(); +} + +function computeContrast(rgb1: string, rgb2: string) { + return colorJS(rgb1).contrast(colorJS(rgb2)); +} + +function getAAARelativeLum(bgColor: string, fgColor: string, ratio = 7) { + const relLum1 = computeRelativeLuminosity(bgColor); + const relLum2 = computeRelativeLuminosity(fgColor); + if (relLum1 > relLum2) { + // relLum1 is brighter, relLum2 is darker + return (relLum1 + 0.05 - ratio * 0.05) / ratio; + } else { + // relLum1 is darker, relLum2 is brighter + return Math.min(ratio * (relLum1 + 0.05) - 0.05, 1); + } +} + +function getGrayFromRelLum(relLum: number) { + if (relLum <= 0.0031308) { + return relLum * 12.92; + } else { + return (1.0 + 0.055) * Math.pow(relLum, 1.0 / 2.4) - 0.055; + } +} + +function getGrayRGBfromGray(gray: number) { + const g = Math.round(gray * 255); + return `rgb(${g},${g},${g})`; +} + +function getAAAGray(bgColor: string, fgColor: string, ratio = 7) { + const relLum = getAAARelativeLum(bgColor, fgColor, ratio); + const gray = getGrayFromRelLum(relLum); + return getGrayRGBfromGray(gray); +} + +function findBestContrastColor( + bgColor: string, + lightFgColor: string, + darkFgColor: string, + ratio = 4.5 +) { + const lc = computeContrast(bgColor, lightFgColor); + const dc = computeContrast(bgColor, darkFgColor); + if (lc >= dc) { + if (lc >= ratio) { + return lightFgColor; + } + return getAAAGray(bgColor, lightFgColor, ratio); + } + if (dc >= ratio) { + return darkFgColor; + } + return getAAAGray(bgColor, darkFgColor, ratio); +} + +function isValidColor(color: string | null | undefined): color is string { + if (typeof color !== 'string') { + return false; + } + if (color.length === 0) { + return false; + } + try { + colorJS(color); + return true; + } catch { + return false; + } +} + +export function getTheme(darkMode: boolean, bgColor?: string | null): Theme { + if (!isValidColor(bgColor)) { + return darkMode ? DARK_THEME : LIGHT_THEME; + } + + const bgLuminosity = computeRelativeLuminosity(bgColor); + const mainTheme = bgLuminosity <= 0.179 ? DARK_THEME : LIGHT_THEME; + const color = findBestContrastColor( + bgColor, + LIGHT_THEME.axes.axisTitleStyle.fill, + DARK_THEME.axes.axisTitleStyle.fill + ); + return { + ...mainTheme, + axes: { + ...mainTheme.axes, + axisTitleStyle: { + ...mainTheme.axes.axisTitleStyle, + fill: color, + }, + tickLabelStyle: { + ...mainTheme.axes.tickLabelStyle, + fill: color, + }, + axisLineStyle: { + ...mainTheme.axes.axisLineStyle, + stroke: color, + }, + tickLineStyle: { + ...mainTheme.axes.tickLineStyle, + stroke: color, + }, + }, + }; +} + +export function getChartClasses(bgColor?: string) { + // keep the original theme color if no bg color is specified + if (typeof bgColor !== 'string') { + return; + } + const bgLuminosity = computeRelativeLuminosity(bgColor); + return bgLuminosity <= 0.179 ? 'tvbVisTimeSeriesDark' : 'tvbVisTimeSeriesLight'; +} From dd680c790cf974ae9e48364c770485302c6eafa9 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 17 Mar 2020 13:34:33 +0100 Subject: [PATCH 40/76] [ML] Adds the class_assignment_objective to classification (#60358) * [ML] add maximize_minimum_recall to classification analysis * [ML] fix mutation of the original job during the cloning --- x-pack/plugins/ml/common/types/common.ts | 12 +++++ .../analytics_list/action_clone.tsx | 44 ++++++++++++------- .../components/analytics_list/actions.tsx | 3 +- .../use_create_analytics_form/actions.ts | 5 ++- .../hooks/use_create_analytics_form/state.ts | 6 +-- .../use_create_analytics_form.ts | 3 +- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/ml/common/types/common.ts b/x-pack/plugins/ml/common/types/common.ts index 3f3493863e0f5..691b3e9eb1163 100644 --- a/x-pack/plugins/ml/common/types/common.ts +++ b/x-pack/plugins/ml/common/types/common.ts @@ -19,3 +19,15 @@ export function dictionaryToArray(dict: Dictionary): TValue[] { export type DeepPartial = { [P in keyof T]?: DeepPartial; }; + +export type DeepReadonly = T extends Array + ? ReadonlyArray> + : T extends Function + ? T + : T extends object + ? DeepReadonlyObject + : T; + +type DeepReadonlyObject = { + readonly [P in keyof T]: DeepReadonly; +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx index 7199453a15d7f..3a0f98fc5acaa 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx @@ -6,8 +6,9 @@ import { EuiButtonEmpty } from '@elastic/eui'; import React, { FC } from 'react'; -import { isEqual } from 'lodash'; +import { isEqual, cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { DeepReadonly } from '../../../../../../../common/types/common'; import { DataFrameAnalyticsConfig, isOutlierAnalysis } from '../../../../common'; import { isClassificationAnalysis, isRegressionAnalysis } from '../../../../common/analytics'; import { CreateAnalyticsFormProps } from '../../hooks/use_create_analytics_form'; @@ -97,6 +98,10 @@ const getAnalyticsJobMeta = (config: CloneDataFrameAnalyticsConfig): AnalyticsJo num_top_feature_importance_values: { optional: true, }, + class_assignment_objective: { + optional: true, + defaultValue: 'maximize_minimum_recall', + }, }, } : {}), @@ -257,20 +262,25 @@ export type CloneDataFrameAnalyticsConfig = Omit< 'id' | 'version' | 'create_time' >; -export function extractCloningConfig( - originalConfig: DataFrameAnalyticsConfig -): CloneDataFrameAnalyticsConfig { - const { - // Omit non-relevant props from the configuration - id, - version, - create_time, - ...cloneConfig - } = originalConfig; - - // Reset the destination index - cloneConfig.dest.index = ''; - return cloneConfig; +/** + * Gets complete original configuration as an input + * and returns the config for cloning omitting + * non-relevant parameters and resetting the destination index. + */ +export function extractCloningConfig({ + id, + version, + create_time, + ...configToClone +}: DeepReadonly): CloneDataFrameAnalyticsConfig { + return (cloneDeep({ + ...configToClone, + dest: { + ...configToClone.dest, + // Reset the destination index + index: '', + }, + }) as unknown) as CloneDataFrameAnalyticsConfig; } export function getCloneAction(createAnalyticsForm: CreateAnalyticsFormProps) { @@ -280,7 +290,7 @@ export function getCloneAction(createAnalyticsForm: CreateAnalyticsFormProps) { const { actions } = createAnalyticsForm; - const onClick = async (item: DataFrameAnalyticsListRow) => { + const onClick = async (item: DeepReadonly) => { await actions.setJobClone(item.config); }; @@ -294,7 +304,7 @@ export function getCloneAction(createAnalyticsForm: CreateAnalyticsFormProps) { } interface CloneActionProps { - item: DataFrameAnalyticsListRow; + item: DeepReadonly; createAnalyticsForm: CreateAnalyticsFormProps; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx index 0436bcfc36847..425e3bc903d04 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/actions.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; +import { DeepReadonly } from '../../../../../../../common/types/common'; import { checkPermission, @@ -107,7 +108,7 @@ export const getActions = (createAnalyticsForm: CreateAnalyticsFormProps) => { }, }, { - render: (item: DataFrameAnalyticsListRow) => { + render: (item: DeepReadonly) => { return ; }, }, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts index 8cedc38b1b59b..66e4103f5bb41 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/actions.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { DeepReadonly } from '../../../../../../../common/types/common'; import { DataFrameAnalyticsConfig } from '../../../../common'; import { FormMessage, State, SourceIndexMap } from './state'; @@ -64,7 +65,7 @@ export type Action = | { type: ACTION.SET_JOB_CONFIG; payload: State['jobConfig'] } | { type: ACTION.SET_JOB_IDS; jobIds: State['jobIds'] } | { type: ACTION.SET_ESTIMATED_MODEL_MEMORY_LIMIT; value: State['estimatedModelMemoryLimit'] } - | { type: ACTION.SET_JOB_CLONE; cloneJob: DataFrameAnalyticsConfig }; + | { type: ACTION.SET_JOB_CLONE; cloneJob: DeepReadonly }; // Actions wrapping the dispatcher exposed by the custom hook export interface ActionDispatchers { @@ -79,5 +80,5 @@ export interface ActionDispatchers { startAnalyticsJob: () => void; switchToAdvancedEditor: () => void; setEstimatedModelMemoryLimit: (value: State['estimatedModelMemoryLimit']) => void; - setJobClone: (cloneJob: DataFrameAnalyticsConfig) => Promise; + setJobClone: (cloneJob: DeepReadonly) => Promise; } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index 515e0e42bd873..719bb6c5b07c7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -5,7 +5,7 @@ */ import { EuiComboBoxOptionOption } from '@elastic/eui'; -import { DeepPartial } from '../../../../../../../common/types/common'; +import { DeepPartial, DeepReadonly } from '../../../../../../../common/types/common'; import { checkPermission } from '../../../../../privilege/check_privilege'; import { mlNodesAvailable } from '../../../../../ml_nodes_check'; @@ -91,7 +91,7 @@ export interface State { jobIds: DataFrameAnalyticsId[]; requestMessages: FormMessage[]; estimatedModelMemoryLimit: string; - cloneJob?: DataFrameAnalyticsConfig; + cloneJob?: DeepReadonly; } export const getInitialState = (): State => ({ @@ -195,7 +195,7 @@ export const getJobConfigFromFormState = ( * For cloning we keep job id and destination index empty. */ export function getCloneFormStateFromJobConfig( - analyticsJobConfig: CloneDataFrameAnalyticsConfig + analyticsJobConfig: Readonly ): Partial { const jobType = Object.keys(analyticsJobConfig.analysis)[0] as ANALYSIS_CONFIG_TYPE; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index 3b6b8538c2ffd..86c43b232738c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { SimpleSavedObject } from 'kibana/public'; import { isErrorResponse } from '../../../../../../../common/types/errors'; +import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; @@ -313,7 +314,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { dispatch({ type: ACTION.SET_ESTIMATED_MODEL_MEMORY_LIMIT, value }); }; - const setJobClone = async (cloneJob: DataFrameAnalyticsConfig) => { + const setJobClone = async (cloneJob: DeepReadonly) => { resetForm(); await prepareFormValidation(); From 9c3c2a237278fc254bb59845cb5af155229128c2 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Tue, 17 Mar 2020 09:39:59 -0400 Subject: [PATCH 41/76] Changing default type to start and allowing it to be configured by the event category (#60323) --- .../endpoint/common/generate_data.test.ts | 6 +++-- .../plugins/endpoint/common/generate_data.ts | 24 ++++++++++++++++--- x-pack/plugins/endpoint/common/types.ts | 1 + 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts index e14f506c825f2..a687d7af1c590 100644 --- a/x-pack/plugins/endpoint/common/generate_data.test.ts +++ b/x-pack/plugins/endpoint/common/generate_data.test.ts @@ -62,10 +62,11 @@ describe('data generator', () => { expect(processEvent['@timestamp']).toEqual(timestamp); expect(processEvent.event.category).toEqual('process'); expect(processEvent.event.kind).toEqual('event'); - expect(processEvent.event.type).toEqual('creation'); + expect(processEvent.event.type).toEqual('start'); expect(processEvent.agent).not.toBeNull(); expect(processEvent.host).not.toBeNull(); expect(processEvent.process.entity_id).not.toBeNull(); + expect(processEvent.process.name).not.toBeNull(); }); it('creates other event documents', () => { @@ -74,10 +75,11 @@ describe('data generator', () => { expect(processEvent['@timestamp']).toEqual(timestamp); expect(processEvent.event.category).toEqual('dns'); expect(processEvent.event.kind).toEqual('event'); - expect(processEvent.event.type).toEqual('creation'); + expect(processEvent.event.type).toEqual('start'); expect(processEvent.agent).not.toBeNull(); expect(processEvent.host).not.toBeNull(); expect(processEvent.process.entity_id).not.toBeNull(); + expect(processEvent.process.name).not.toBeNull(); }); describe('creates alert ancestor tree', () => { diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index b539e309d76f7..36896e5af6810 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -16,6 +16,7 @@ interface EventOptions { parentEntityID?: string; eventType?: string; eventCategory?: string; + processName?: string; } const Windows: OSFields[] = [ @@ -64,8 +65,22 @@ const POLICIES: Array<{ name: string; id: string }> = [ const FILE_OPERATIONS: string[] = ['creation', 'open', 'rename', 'execution', 'deletion']; +interface EventInfo { + category: string; + /** + * This denotes the `event.type` field for when an event is created, this can be `start` or `creation` + */ + creationType: string; +} + // These are from the v1 schemas and aren't all valid ECS event categories, still in flux -const OTHER_EVENT_CATEGORIES: string[] = ['driver', 'file', 'library', 'network', 'registry']; +const OTHER_EVENT_CATEGORIES: EventInfo[] = [ + { category: 'driver', creationType: 'start' }, + { category: 'file', creationType: 'creation' }, + { category: 'library', creationType: 'start' }, + { category: 'network', creationType: 'start' }, + { category: 'registry', creationType: 'creation' }, +]; interface HostInfo { agent: { @@ -240,13 +255,14 @@ export class EndpointDocGenerator { event: { category: options.eventCategory ? options.eventCategory : 'process', kind: 'event', - type: options.eventType ? options.eventType : 'creation', + type: options.eventType ? options.eventType : 'start', id: this.seededUUIDv4(), }, host: this.commonInfo.host, process: { entity_id: options.entityID ? options.entityID : this.randomString(10), parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined, + name: options.processName ? options.processName : 'powershell.exe', }, }; } @@ -352,12 +368,14 @@ export class EndpointDocGenerator { const ts = node['@timestamp'] + 1000; const relatedEvents: EndpointEvent[] = []; for (let i = 0; i < numRelatedEvents; i++) { + const eventInfo = this.randomChoice(OTHER_EVENT_CATEGORIES); relatedEvents.push( this.generateEvent({ timestamp: ts, entityID: node.process.entity_id, parentEntityID: node.process.parent?.entity_id, - eventCategory: this.randomChoice(OTHER_EVENT_CATEGORIES), + eventCategory: eventInfo.category, + eventType: eventInfo.creationType, }) ); } diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 1cb000b0a0357..aa326c663965d 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -325,6 +325,7 @@ export interface EndpointEvent { }; process: { entity_id: string; + name: string; parent?: { entity_id: string; }; From caed9ba5ac9798762e07d236a55226dce355b386 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 17 Mar 2020 09:57:52 -0400 Subject: [PATCH 42/76] [Lens] Simplify state management from visualization (#58279) * [Lens] Declarative right panel * Fix memoized operations * Add error checking * Fix dimension panel tests * More updates * Fix all editor frame tests * Fix jest tests * Fix bug with removing dimension * Update tests * Fix frame tests * Fix all tests I could find * Remove debugger * Style config panels * Update i18n * Fix dashboard test * Fix bug when switching index patterns --- .../plugins/lens/public/_config_panel.scss | 21 - .../visualization.test.tsx | 193 +- .../datatable_visualization/visualization.tsx | 123 +- .../editor_frame/_config_panel_wrapper.scss | 50 + .../editor_frame/chart_switch.test.tsx | 2 +- .../editor_frame/config_panel_wrapper.tsx | 502 ++++- .../editor_frame/editor_frame.test.tsx | 106 +- .../editor_frame/editor_frame.tsx | 132 +- .../editor_frame/frame_layout.tsx | 27 +- .../editor_frame/index.scss | 1 + .../editor_frame/save.test.ts | 4 +- .../editor_frame/suggestion_helpers.test.ts | 8 +- .../editor_frame/suggestion_panel.test.tsx | 2 +- .../editor_frame/suggestion_panel.tsx | 1 - .../editor_frame/workspace_panel.test.tsx | 4 +- .../public/editor_frame_service/mocks.tsx | 29 +- .../editor_frame_service/service.test.tsx | 4 +- x-pack/legacy/plugins/lens/public/index.scss | 2 - .../dimension_panel/_dimension_panel.scss | 9 - .../dimension_panel/_index.scss | 1 - .../dimension_panel/_popover.scss | 29 +- .../dimension_panel/dimension_panel.test.tsx | 1838 ++++++++--------- .../dimension_panel/dimension_panel.tsx | 320 +-- .../dimension_panel/popover_editor.tsx | 423 ++-- .../indexpattern.test.ts | 1 - .../indexpattern_datasource/indexpattern.tsx | 168 +- .../layerpanel.test.tsx | 1 + .../indexpattern_datasource/layerpanel.tsx | 3 +- .../metric_config_panel.test.tsx | 69 - .../metric_config_panel.tsx | 39 - .../metric_expression.tsx | 5 + .../metric_visualization.test.ts | 49 +- .../metric_visualization.tsx | 46 +- .../lens/public/metric_visualization/types.ts | 2 +- .../lens/public/multi_column_editor/index.ts | 7 - .../multi_column_editor.test.tsx | 71 - .../multi_column_editor.tsx | 63 - x-pack/legacy/plugins/lens/public/plugin.tsx | 2 +- x-pack/legacy/plugins/lens/public/types.ts | 130 +- ...est.ts.snap => to_expression.test.ts.snap} | 2 +- .../xy_visualization/to_expression.test.ts | 133 ++ .../public/xy_visualization/to_expression.ts | 199 +- .../lens/public/xy_visualization/types.ts | 4 +- .../xy_visualization/xy_config_panel.test.tsx | 156 +- .../xy_visualization/xy_config_panel.tsx | 104 +- .../public/xy_visualization/xy_expression.tsx | 4 +- .../xy_visualization/xy_suggestions.test.ts | 71 +- .../public/xy_visualization/xy_suggestions.ts | 3 +- .../xy_visualization/xy_visualization.test.ts | 195 +- .../xy_visualization/xy_visualization.tsx | 105 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../dashboard_mode/dashboard_empty_screen.js | 6 +- .../test/functional/apps/lens/smokescreen.ts | 6 +- 54 files changed, 2777 insertions(+), 2700 deletions(-) delete mode 100644 x-pack/legacy/plugins/lens/public/_config_panel.scss create mode 100644 x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss delete mode 100644 x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_dimension_panel.scss delete mode 100644 x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.test.tsx delete mode 100644 x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.tsx delete mode 100644 x-pack/legacy/plugins/lens/public/multi_column_editor/index.ts delete mode 100644 x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.test.tsx delete mode 100644 x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.tsx rename x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/{xy_visualization.test.ts.snap => to_expression.test.ts.snap} (96%) create mode 100644 x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.test.ts diff --git a/x-pack/legacy/plugins/lens/public/_config_panel.scss b/x-pack/legacy/plugins/lens/public/_config_panel.scss deleted file mode 100644 index 5c6d25bf10818..0000000000000 --- a/x-pack/legacy/plugins/lens/public/_config_panel.scss +++ /dev/null @@ -1,21 +0,0 @@ -.lnsConfigPanel__panel { - margin-bottom: $euiSizeS; -} - -.lnsConfigPanel__axis { - background: $euiColorLightestShade; - padding: $euiSizeS; - border-radius: $euiBorderRadius; - - // Add margin to the top of the next same panel - & + & { - margin-top: $euiSizeS; - } -} - -.lnsConfigPanel__addLayerBtn { - color: transparentize($euiColorMediumShade, .3); - // sass-lint:disable-block no-important - box-shadow: none !important; - border: 1px dashed currentColor; -} diff --git a/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.test.tsx index 0cba22170df1f..e18190b6c2d69 100644 --- a/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.test.tsx +++ b/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.test.tsx @@ -4,18 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import { createMockDatasource } from '../editor_frame_service/mocks'; -import { - DatatableVisualizationState, - datatableVisualization, - DataTableLayer, -} from './visualization'; -import { mount } from 'enzyme'; +import { DatatableVisualizationState, datatableVisualization } from './visualization'; import { Operation, DataType, FramePublicAPI, TableSuggestionColumn } from '../types'; -import { generateId } from '../id_generator'; - -jest.mock('../id_generator'); function mockFrame(): FramePublicAPI { return { @@ -34,12 +25,11 @@ function mockFrame(): FramePublicAPI { describe('Datatable Visualization', () => { describe('#initialize', () => { it('should initialize from the empty state', () => { - (generateId as jest.Mock).mockReturnValueOnce('id'); expect(datatableVisualization.initialize(mockFrame(), undefined)).toEqual({ layers: [ { layerId: 'aaa', - columns: ['id'], + columns: [], }, ], }); @@ -88,7 +78,6 @@ describe('Datatable Visualization', () => { describe('#clearLayer', () => { it('should reset the layer', () => { - (generateId as jest.Mock).mockReturnValueOnce('testid'); const state: DatatableVisualizationState = { layers: [ { @@ -101,7 +90,7 @@ describe('Datatable Visualization', () => { layers: [ { layerId: 'baz', - columns: ['testid'], + columns: [], }, ], }); @@ -214,29 +203,35 @@ describe('Datatable Visualization', () => { }); }); - describe('DataTableLayer', () => { - it('allows all kinds of operations', () => { - const setState = jest.fn(); - const datasource = createMockDatasource(); - const layer = { layerId: 'a', columns: ['b', 'c'] }; + describe('#getConfiguration', () => { + it('returns a single layer option', () => { + const datasource = createMockDatasource('test'); const frame = mockFrame(); - frame.datasourceLayers = { a: datasource.publicAPIMock }; + frame.datasourceLayers = { first: datasource.publicAPIMock }; - mount( - {} }} - frame={frame} - layer={layer} - setState={setState} - state={{ layers: [layer] }} - /> - ); + expect( + datatableVisualization.getConfiguration({ + layerId: 'first', + state: { + layers: [{ layerId: 'first', columns: [] }], + }, + frame, + }).groups + ).toHaveLength(1); + }); - expect(datasource.publicAPIMock.renderDimensionPanel).toHaveBeenCalled(); + it('allows all kinds of operations', () => { + const datasource = createMockDatasource('test'); + const frame = mockFrame(); + frame.datasourceLayers = { first: datasource.publicAPIMock }; - const filterOperations = - datasource.publicAPIMock.renderDimensionPanel.mock.calls[0][1].filterOperations; + const filterOperations = datatableVisualization.getConfiguration({ + layerId: 'first', + state: { + layers: [{ layerId: 'first', columns: [] }], + }, + frame, + }).groups[0].filterOperations; const baseOperation: Operation = { dataType: 'string', @@ -253,108 +248,80 @@ describe('Datatable Visualization', () => { ); }); - it('allows columns to be removed', () => { - const setState = jest.fn(); - const datasource = createMockDatasource(); + it('reorders the rendered colums based on the order from the datasource', () => { + const datasource = createMockDatasource('test'); const layer = { layerId: 'a', columns: ['b', 'c'] }; const frame = mockFrame(); frame.datasourceLayers = { a: datasource.publicAPIMock }; - const component = mount( - {} }} - frame={frame} - layer={layer} - setState={setState} - state={{ layers: [layer] }} - /> - ); - - const onRemove = component - .find('[data-test-subj="datatable_multicolumnEditor"]') - .first() - .prop('onRemove') as (k: string) => {}; - - onRemove('b'); + datasource.publicAPIMock.getTableSpec.mockReturnValue([{ columnId: 'c' }, { columnId: 'b' }]); + + expect( + datatableVisualization.getConfiguration({ + layerId: 'a', + state: { layers: [layer] }, + frame, + }).groups[0].accessors + ).toEqual(['c', 'b']); + }); + }); - expect(setState).toHaveBeenCalledWith({ + describe('#removeDimension', () => { + it('allows columns to be removed', () => { + const layer = { layerId: 'layer1', columns: ['b', 'c'] }; + expect( + datatableVisualization.removeDimension({ + prevState: { layers: [layer] }, + layerId: 'layer1', + columnId: 'b', + }) + ).toEqual({ layers: [ { - layerId: 'a', + layerId: 'layer1', columns: ['c'], }, ], }); }); + }); + describe('#setDimension', () => { it('allows columns to be added', () => { - (generateId as jest.Mock).mockReturnValueOnce('d'); - const setState = jest.fn(); - const datasource = createMockDatasource(); - const layer = { layerId: 'a', columns: ['b', 'c'] }; - const frame = mockFrame(); - frame.datasourceLayers = { a: datasource.publicAPIMock }; - const component = mount( - {} }} - frame={frame} - layer={layer} - setState={setState} - state={{ layers: [layer] }} - /> - ); - - const onAdd = component - .find('[data-test-subj="datatable_multicolumnEditor"]') - .first() - .prop('onAdd') as () => {}; - - onAdd(); - - expect(setState).toHaveBeenCalledWith({ + const layer = { layerId: 'layer1', columns: ['b', 'c'] }; + expect( + datatableVisualization.setDimension({ + prevState: { layers: [layer] }, + layerId: 'layer1', + columnId: 'd', + groupId: '', + }) + ).toEqual({ layers: [ { - layerId: 'a', + layerId: 'layer1', columns: ['b', 'c', 'd'], }, ], }); }); - it('reorders the rendered colums based on the order from the datasource', () => { - const datasource = createMockDatasource(); - const layer = { layerId: 'a', columns: ['b', 'c'] }; - const frame = mockFrame(); - frame.datasourceLayers = { a: datasource.publicAPIMock }; - const component = mount( - {} }} - frame={frame} - layer={layer} - setState={jest.fn()} - state={{ layers: [layer] }} - /> - ); - - const accessors = component - .find('[data-test-subj="datatable_multicolumnEditor"]') - .first() - .prop('accessors') as string[]; - - expect(accessors).toEqual(['b', 'c']); - - component.setProps({ - layer: { layerId: 'a', columns: ['c', 'b'] }, + it('does not set a duplicate dimension', () => { + const layer = { layerId: 'layer1', columns: ['b', 'c'] }; + expect( + datatableVisualization.setDimension({ + prevState: { layers: [layer] }, + layerId: 'layer1', + columnId: 'b', + groupId: '', + }) + ).toEqual({ + layers: [ + { + layerId: 'layer1', + columns: ['b', 'c'], + }, + ], }); - - const newAccessors = component - .find('[data-test-subj="datatable_multicolumnEditor"]') - .first() - .prop('accessors') as string[]; - - expect(newAccessors).toEqual(['c', 'b']); }); }); }); diff --git a/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.tsx index 79a018635134f..4248d722d5540 100644 --- a/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/legacy/plugins/lens/public/datatable_visualization/visualization.tsx @@ -4,20 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { render } from 'react-dom'; -import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n/react'; -import { MultiColumnEditor } from '../multi_column_editor'; -import { - SuggestionRequest, - Visualization, - VisualizationLayerConfigProps, - VisualizationSuggestion, - Operation, -} from '../types'; -import { generateId } from '../id_generator'; +import { SuggestionRequest, Visualization, VisualizationSuggestion, Operation } from '../types'; import chartTableSVG from '../assets/chart_datatable.svg'; export interface LayerState { @@ -32,58 +20,10 @@ export interface DatatableVisualizationState { function newLayerState(layerId: string): LayerState { return { layerId, - columns: [generateId()], + columns: [], }; } -function updateColumns( - state: DatatableVisualizationState, - layer: LayerState, - fn: (columns: string[]) => string[] -) { - const columns = fn(layer.columns); - const updatedLayer = { ...layer, columns }; - const layers = state.layers.map(l => (l.layerId === layer.layerId ? updatedLayer : l)); - return { ...state, layers }; -} - -const allOperations = () => true; - -export function DataTableLayer({ - layer, - frame, - state, - setState, - dragDropContext, -}: { layer: LayerState } & VisualizationLayerConfigProps) { - const datasource = frame.datasourceLayers[layer.layerId]; - - const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); - // When we add a column it could be empty, and therefore have no order - const sortedColumns = Array.from(new Set(originalOrder.concat(layer.columns))); - - return ( - - setState(updateColumns(state, layer, columns => [...columns, generateId()]))} - onRemove={column => - setState(updateColumns(state, layer, columns => columns.filter(c => c !== column))) - } - testSubj="datatable_columns" - data-test-subj="datatable_multicolumnEditor" - /> - - ); -} - export const datatableVisualization: Visualization< DatatableVisualizationState, DatatableVisualizationState @@ -188,17 +128,56 @@ export const datatableVisualization: Visualization< ]; }, - renderLayerConfigPanel(domElement, props) { - const layer = props.state.layers.find(l => l.layerId === props.layerId); - - if (layer) { - render( - - - , - domElement - ); + getConfiguration({ state, frame, layerId }) { + const layer = state.layers.find(l => l.layerId === layerId); + if (!layer) { + return { groups: [] }; } + + const datasource = frame.datasourceLayers[layer.layerId]; + const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); + // When we add a column it could be empty, and therefore have no order + const sortedColumns = Array.from(new Set(originalOrder.concat(layer.columns))); + + return { + groups: [ + { + groupId: 'columns', + groupLabel: i18n.translate('xpack.lens.datatable.columns', { + defaultMessage: 'Columns', + }), + layerId: state.layers[0].layerId, + accessors: sortedColumns, + supportsMoreColumns: true, + filterOperations: () => true, + }, + ], + }; + }, + + setDimension({ prevState, layerId, columnId }) { + return { + ...prevState, + layers: prevState.layers.map(l => { + if (l.layerId !== layerId || l.columns.includes(columnId)) { + return l; + } + return { ...l, columns: [...l.columns, columnId] }; + }), + }; + }, + removeDimension({ prevState, layerId, columnId }) { + return { + ...prevState, + layers: prevState.layers.map(l => + l.layerId === layerId + ? { + ...l, + columns: l.columns.filter(c => c !== columnId), + } + : l + ), + }; }, toExpression(state, frame) { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss new file mode 100644 index 0000000000000..62a7f6b023f31 --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/_config_panel_wrapper.scss @@ -0,0 +1,50 @@ +.lnsConfigPanel__panel { + margin-bottom: $euiSizeS; +} + +.lnsConfigPanel__row { + background: $euiColorLightestShade; + padding: $euiSizeS; + border-radius: $euiBorderRadius; + + // Add margin to the top of the next same panel + & + & { + margin-top: $euiSizeS; + } +} + +.lnsConfigPanel__addLayerBtn { + color: transparentize($euiColorMediumShade, .3); + // Remove EuiButton's default shadow to make button more subtle + // sass-lint:disable-block no-important + box-shadow: none !important; + border: 1px dashed currentColor; +} + +.lnsConfigPanel__dimension { + @include euiFontSizeS; + background: lightOrDarkTheme($euiColorEmptyShade, $euiColorLightestShade); + border-radius: $euiBorderRadius; + display: flex; + align-items: center; + margin-top: $euiSizeXS; + overflow: hidden; +} + +.lnsConfigPanel__trigger { + max-width: 100%; + display: block; +} + +.lnsConfigPanel__triggerLink { + padding: $euiSizeS; + width: 100%; + display: flex; + align-items: center; + min-height: $euiSizeXXL; +} + +.lnsConfigPanel__popover { + line-height: 0; + flex-grow: 1; +} diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx index 1b60098fd45ad..6698c9e68b98c 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/chart_switch.test.tsx @@ -84,7 +84,7 @@ describe('chart_switch', () => { } function mockDatasourceMap() { - const datasource = createMockDatasource(); + const datasource = createMockDatasource('testDatasource'); datasource.getDatasourceSuggestionsFromCurrentState.mockReturnValue([ { state: {}, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx index 1422ee86be3e9..c2cd0485de67e 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/config_panel_wrapper.tsx @@ -16,17 +16,21 @@ import { EuiToolTip, EuiButton, EuiForm, + EuiFormRow, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { NativeRenderer } from '../../native_renderer'; import { Action } from './state_management'; import { Visualization, FramePublicAPI, Datasource, - VisualizationLayerConfigProps, + VisualizationLayerWidgetProps, + DatasourceDimensionEditorProps, + StateSetter, } from '../../types'; -import { DragContext } from '../../drag_drop'; +import { DragContext, DragDrop, ChildDragDropProvider } from '../../drag_drop'; import { ChartSwitch } from './chart_switch'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { generateId } from '../../id_generator'; @@ -47,6 +51,7 @@ interface ConfigPanelWrapperProps { state: unknown; } >; + core: DatasourceDimensionEditorProps['core']; } export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: ConfigPanelWrapperProps) { @@ -86,8 +91,7 @@ function LayerPanels( activeDatasourceId, datasourceMap, } = props; - const dragDropContext = useContext(DragContext); - const setState = useMemo( + const setVisualizationState = useMemo( () => (newState: unknown) => { props.dispatch({ type: 'UPDATE_VISUALIZATION_STATE', @@ -98,6 +102,43 @@ function LayerPanels( }, [props.dispatch, activeVisualization] ); + const updateDatasource = useMemo( + () => (datasourceId: string, newState: unknown) => { + props.dispatch({ + type: 'UPDATE_DATASOURCE_STATE', + updater: () => newState, + datasourceId, + clearStagedPreview: false, + }); + }, + [props.dispatch] + ); + const updateAll = useMemo( + () => (datasourceId: string, newDatasourceState: unknown, newVisualizationState: unknown) => { + props.dispatch({ + type: 'UPDATE_STATE', + subType: 'UPDATE_ALL_STATES', + updater: prevState => { + return { + ...prevState, + datasourceStates: { + ...prevState.datasourceStates, + [datasourceId]: { + state: newDatasourceState, + isLoading: false, + }, + }, + visualization: { + activeId: activeVisualization.id, + state: newVisualizationState, + }, + stagedPreview: undefined, + }; + }, + }); + }, + [props.dispatch] + ); const layerIds = activeVisualization.getLayerIds(visualizationState); return ( @@ -108,12 +149,13 @@ function LayerPanels( key={layerId} layerId={layerId} activeVisualization={activeVisualization} - dragDropContext={dragDropContext} - state={setState} - setState={setState} + visualizationState={visualizationState} + updateVisualization={setVisualizationState} + updateDatasource={updateDatasource} + updateAll={updateAll} frame={framePublicAPI} isOnlyLayer={layerIds.length === 1} - onRemove={() => { + onRemoveLayer={() => { dispatch({ type: 'UPDATE_STATE', subType: 'REMOVE_OR_CLEAR_LAYER', @@ -143,7 +185,7 @@ function LayerPanels( className="lnsConfigPanel__addLayerBtn" fullWidth size="s" - data-test-subj={`lnsXY_layer_add`} + data-test-subj="lnsXY_layer_add" aria-label={i18n.translate('xpack.lens.xyChart.addLayerButton', { defaultMessage: 'Add layer', })} @@ -174,85 +216,399 @@ function LayerPanels( } function LayerPanel( - props: ConfigPanelWrapperProps & - VisualizationLayerConfigProps & { - isOnlyLayer: boolean; - activeVisualization: Visualization; - onRemove: () => void; - } + props: Exclude & { + frame: FramePublicAPI; + layerId: string; + isOnlyLayer: boolean; + activeVisualization: Visualization; + visualizationState: unknown; + updateVisualization: StateSetter; + updateDatasource: (datasourceId: string, newState: unknown) => void; + updateAll: ( + datasourceId: string, + newDatasourcestate: unknown, + newVisualizationState: unknown + ) => void; + onRemoveLayer: () => void; + } ) { - const { framePublicAPI, layerId, activeVisualization, isOnlyLayer, onRemove } = props; + const dragDropContext = useContext(DragContext); + const { framePublicAPI, layerId, activeVisualization, isOnlyLayer, onRemoveLayer } = props; const datasourcePublicAPI = framePublicAPI.datasourceLayers[layerId]; - const layerConfigProps = { + if (!datasourcePublicAPI) { + return null; + } + const layerVisualizationConfigProps = { layerId, - dragDropContext: props.dragDropContext, + dragDropContext, state: props.visualizationState, - setState: props.setState, frame: props.framePublicAPI, + dateRange: props.framePublicAPI.dateRange, }; + const datasourceId = datasourcePublicAPI.datasourceId; + const layerDatasourceState = props.datasourceStates[datasourceId].state; + const layerDatasource = props.datasourceMap[datasourceId]; - return ( - - - - - + const layerDatasourceDropProps = { + layerId, + dragDropContext, + state: layerDatasourceState, + setState: (newState: unknown) => { + props.updateDatasource(datasourceId, newState); + }, + }; - {datasourcePublicAPI && ( - - ({ + isOpen: false, + openId: null, + addingToGroupId: null, + }); + + const { groups } = activeVisualization.getConfiguration(layerVisualizationConfigProps); + const isEmptyLayer = !groups.some(d => d.accessors.length > 0); + + function wrapInPopover( + id: string, + groupId: string, + trigger: React.ReactElement, + panel: React.ReactElement + ) { + const noMatch = popoverState.isOpen ? !groups.some(d => d.accessors.includes(id)) : false; + return ( + { + setPopoverState({ isOpen: false, openId: null, addingToGroupId: null }); + }} + button={trigger} + anchorPosition="leftUp" + withTitle + panelPaddingSize="s" + > + {panel} + + ); + } + + return ( + + + + + - )} - - - - + {layerDatasource && ( + + { + const newState = + typeof updater === 'function' ? updater(layerDatasourceState) : updater; + // Look for removed columns + const nextPublicAPI = layerDatasource.getPublicAPI({ + state: newState, + layerId, + dateRange: props.framePublicAPI.dateRange, + }); + const nextTable = new Set( + nextPublicAPI.getTableSpec().map(({ columnId }) => columnId) + ); + const removed = datasourcePublicAPI + .getTableSpec() + .map(({ columnId }) => columnId) + .filter(columnId => !nextTable.has(columnId)); + let nextVisState = props.visualizationState; + removed.forEach(columnId => { + nextVisState = activeVisualization.removeDimension({ + layerId, + columnId, + prevState: nextVisState, + }); + }); - + props.updateAll(datasourceId, newState, nextVisState); + }, + }} + /> + + )} + - - - { - // If we don't blur the remove / clear button, it remains focused - // which is a strange UX in this case. e.target.blur doesn't work - // due to who knows what, but probably event re-writing. Additionally, - // activeElement does not have blur so, we need to do some casting + safeguards. - const el = (document.activeElement as unknown) as { blur: () => void }; + - if (el && el.blur) { - el.blur(); + {groups.map((group, index) => { + const newId = generateId(); + const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0; + return ( + + <> + {group.accessors.map(accessor => ( + { + layerDatasource.onDrop({ + ...layerDatasourceDropProps, + droppedItem, + columnId: accessor, + filterOperations: group.filterOperations, + }); + }} + > + {wrapInPopover( + accessor, + group.groupId, + { + if (popoverState.isOpen) { + setPopoverState({ + isOpen: false, + openId: null, + addingToGroupId: null, + }); + } else { + setPopoverState({ + isOpen: true, + openId: accessor, + addingToGroupId: null, // not set for existing dimension + }); + } + }, + }} + />, + + )} - onRemove(); - }} - > - {isOnlyLayer - ? i18n.translate('xpack.lens.resetLayer', { - defaultMessage: 'Reset layer', - }) - : i18n.translate('xpack.lens.deleteLayer', { - defaultMessage: 'Delete layer', - })} - - - - + { + trackUiEvent('indexpattern_dimension_removed'); + props.updateAll( + datasourceId, + layerDatasource.removeColumn({ + layerId, + columnId: accessor, + prevState: layerDatasourceState, + }), + props.activeVisualization.removeDimension({ + layerId, + columnId: accessor, + prevState: props.visualizationState, + }) + ); + }} + /> + + ))} + {group.supportsMoreColumns ? ( + { + const dropSuccess = layerDatasource.onDrop({ + ...layerDatasourceDropProps, + droppedItem, + columnId: newId, + filterOperations: group.filterOperations, + }); + if (dropSuccess) { + props.updateVisualization( + activeVisualization.setDimension({ + layerId, + groupId: group.groupId, + columnId: newId, + prevState: props.visualizationState, + }) + ); + } + }} + > + {wrapInPopover( + newId, + group.groupId, +
+ { + if (popoverState.isOpen) { + setPopoverState({ + isOpen: false, + openId: null, + addingToGroupId: null, + }); + } else { + setPopoverState({ + isOpen: true, + openId: newId, + addingToGroupId: group.groupId, + }); + } + }} + size="xs" + > + + +
, + { + props.updateAll( + datasourceId, + newState, + activeVisualization.setDimension({ + layerId, + groupId: group.groupId, + columnId: newId, + prevState: props.visualizationState, + }) + ); + setPopoverState({ + isOpen: true, + openId: newId, + addingToGroupId: null, // clear now that dimension exists + }); + }, + }} + /> + )} +
+ ) : null} + + + ); + })} + + + + + + { + // If we don't blur the remove / clear button, it remains focused + // which is a strange UX in this case. e.target.blur doesn't work + // due to who knows what, but probably event re-writing. Additionally, + // activeElement does not have blur so, we need to do some casting + safeguards. + const el = (document.activeElement as unknown) as { blur: () => void }; + + if (el?.blur) { + el.blur(); + } + + onRemoveLayer(); + }} + > + {isOnlyLayer + ? i18n.translate('xpack.lens.resetLayer', { + defaultMessage: 'Reset layer', + }) + : i18n.translate('xpack.lens.deleteLayer', { + defaultMessage: 'Delete layer', + })} + + + + + ); } @@ -263,7 +619,7 @@ function LayerSettings({ }: { layerId: string; activeVisualization: Visualization; - layerConfigProps: VisualizationLayerConfigProps; + layerConfigProps: VisualizationLayerWidgetProps; }) { const [isOpen, setIsOpen] = useState(false); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index dd591b3992fe5..8d8d38944e18a 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -87,14 +87,15 @@ describe('editor_frame', () => { mockVisualization.getLayerIds.mockReturnValue(['first']); mockVisualization2.getLayerIds.mockReturnValue(['second']); - mockDatasource = createMockDatasource(); - mockDatasource2 = createMockDatasource(); + mockDatasource = createMockDatasource('testDatasource'); + mockDatasource2 = createMockDatasource('testDatasource2'); expressionRendererMock = createExpressionRendererMock(); }); describe('initialization', () => { it('should initialize initial datasource', async () => { + mockVisualization.getLayerIds.mockReturnValue([]); await act(async () => { mount( { }); it('should initialize all datasources with state from doc', async () => { - const mockDatasource3 = createMockDatasource(); + const mockDatasource3 = createMockDatasource('testDatasource3'); const datasource1State = { datasource1: '' }; const datasource2State = { datasource2: '' }; @@ -198,9 +199,9 @@ describe('editor_frame', () => { ExpressionRenderer={expressionRendererMock} /> ); - expect(mockVisualization.renderLayerConfigPanel).not.toHaveBeenCalled(); expect(mockDatasource.renderDataPanel).not.toHaveBeenCalled(); }); + expect(mockDatasource.renderDataPanel).toHaveBeenCalled(); }); it('should not initialize visualization before datasource is initialized', async () => { @@ -289,6 +290,7 @@ describe('editor_frame', () => { mockDatasource2.initialize.mockReturnValue(Promise.resolve(initialState)); mockDatasource2.getLayers.mockReturnValue(['abc', 'def']); mockDatasource2.removeLayer.mockReturnValue({ removed: true }); + mockVisualization.getLayerIds.mockReturnValue(['first', 'abc', 'def']); await act(async () => { mount( { ); }); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: initialState }) ); }); @@ -614,15 +615,14 @@ describe('editor_frame', () => { ); }); const updatedState = {}; - const setVisualizationState = (mockVisualization.renderLayerConfigPanel as jest.Mock).mock - .calls[0][1].setState; + const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1] + .setState; act(() => { - setVisualizationState(updatedState); + setDatasourceState(updatedState); }); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledTimes(2); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenLastCalledWith( - expect.any(Element), + expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(2); + expect(mockVisualization.getConfiguration).toHaveBeenLastCalledWith( expect.objectContaining({ state: updatedState, }) @@ -688,8 +688,7 @@ describe('editor_frame', () => { }); const updatedPublicAPI: DatasourcePublicAPI = { - renderLayerPanel: jest.fn(), - renderDimensionPanel: jest.fn(), + datasourceId: 'testDatasource', getOperationForColumnId: jest.fn(), getTableSpec: jest.fn(), }; @@ -701,9 +700,8 @@ describe('editor_frame', () => { setDatasourceState({}); }); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledTimes(2); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenLastCalledWith( - expect.any(Element), + expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(2); + expect(mockVisualization.getConfiguration).toHaveBeenLastCalledWith( expect.objectContaining({ frame: expect.objectContaining({ datasourceLayers: { @@ -719,6 +717,7 @@ describe('editor_frame', () => { it('should pass the datasource api for each layer to the visualization', async () => { mockDatasource.getLayers.mockReturnValue(['first']); mockDatasource2.getLayers.mockReturnValue(['second', 'third']); + mockVisualization.getLayerIds.mockReturnValue(['first', 'second', 'third']); await act(async () => { mount( @@ -755,10 +754,10 @@ describe('editor_frame', () => { ); }); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalled(); + expect(mockVisualization.getConfiguration).toHaveBeenCalled(); const datasourceLayers = - mockVisualization.renderLayerConfigPanel.mock.calls[0][1].frame.datasourceLayers; + mockVisualization.getConfiguration.mock.calls[0][0].frame.datasourceLayers; expect(datasourceLayers.first).toBe(mockDatasource.publicAPIMock); expect(datasourceLayers.second).toBe(mockDatasource2.publicAPIMock); expect(datasourceLayers.third).toBe(mockDatasource2.publicAPIMock); @@ -811,21 +810,18 @@ describe('editor_frame', () => { expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith( expect.objectContaining({ state: datasource1State, - setState: expect.anything(), layerId: 'first', }) ); expect(mockDatasource2.getPublicAPI).toHaveBeenCalledWith( expect.objectContaining({ state: datasource2State, - setState: expect.anything(), layerId: 'second', }) ); expect(mockDatasource2.getPublicAPI).toHaveBeenCalledWith( expect.objectContaining({ state: datasource2State, - setState: expect.anything(), layerId: 'third', }) ); @@ -858,45 +854,9 @@ describe('editor_frame', () => { expect(mockDatasource.getPublicAPI).toHaveBeenCalledWith({ dateRange, state: datasourceState, - setState: expect.any(Function), layerId: 'first', }); }); - - it('should re-create the public api after state has been set', async () => { - mockDatasource.getLayers.mockReturnValue(['first']); - - await act(async () => { - mount( - - ); - }); - - const updatedState = {}; - const setDatasourceState = mockDatasource.getPublicAPI.mock.calls[0][0].setState; - act(() => { - setDatasourceState(updatedState); - }); - - expect(mockDatasource.getPublicAPI).toHaveBeenLastCalledWith( - expect.objectContaining({ - state: updatedState, - setState: expect.any(Function), - layerId: 'first', - }) - ); - }); }); describe('switching', () => { @@ -1021,8 +981,7 @@ describe('editor_frame', () => { expect(mockVisualization2.getSuggestions).toHaveBeenCalled(); expect(mockVisualization2.initialize).toHaveBeenCalledWith(expect.anything(), initialState); - expect(mockVisualization2.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization2.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: { initial: true } }) ); }); @@ -1039,8 +998,7 @@ describe('editor_frame', () => { datasourceLayers: expect.objectContaining({ first: mockDatasource.publicAPIMock }), }) ); - expect(mockVisualization2.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization2.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: { initial: true } }) ); }); @@ -1239,9 +1197,8 @@ describe('editor_frame', () => { .simulate('click'); }); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledTimes(1); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(1); + expect(mockVisualization.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: suggestionVisState, }) @@ -1306,8 +1263,7 @@ describe('editor_frame', () => { .simulate('drop'); }); - expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: suggestionVisState, }) @@ -1375,14 +1331,16 @@ describe('editor_frame', () => { instance.update(); act(() => { - instance.find(DragDrop).prop('onDrop')!({ + instance + .find(DragDrop) + .filter('[data-test-subj="mockVisA"]') + .prop('onDrop')!({ indexPatternId: '1', field: {}, }); }); - expect(mockVisualization2.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization2.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: suggestionVisState, }) @@ -1472,14 +1430,16 @@ describe('editor_frame', () => { instance.update(); act(() => { - instance.find(DragDrop).prop('onDrop')!({ + instance + .find(DragDrop) + .filter('[data-test-subj="lnsWorkspace"]') + .prop('onDrop')!({ indexPatternId: '1', field: {}, }); }); - expect(mockVisualization3.renderLayerConfigPanel).toHaveBeenCalledWith( - expect.any(Element), + expect(mockVisualization3.getConfiguration).toHaveBeenCalledWith( expect.objectContaining({ state: suggestionVisState, }) diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index a456372c99c01..082519d9a8feb 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -21,6 +21,7 @@ import { FrameLayout } from './frame_layout'; import { SuggestionPanel } from './suggestion_panel'; import { WorkspacePanel } from './workspace_panel'; import { Document } from '../../persistence/saved_object_store'; +import { RootDragDropProvider } from '../../drag_drop'; import { getSavedObjectFormat } from './save'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; import { generateId } from '../../id_generator'; @@ -90,21 +91,11 @@ export function EditorFrame(props: EditorFrameProps) { const layers = datasource.getLayers(datasourceState); layers.forEach(layer => { - const publicAPI = props.datasourceMap[id].getPublicAPI({ + datasourceLayers[layer] = props.datasourceMap[id].getPublicAPI({ state: datasourceState, - setState: (newState: unknown) => { - dispatch({ - type: 'UPDATE_DATASOURCE_STATE', - datasourceId: id, - updater: newState, - clearStagedPreview: true, - }); - }, layerId: layer, dateRange: props.dateRange, }); - - datasourceLayers[layer] = publicAPI; }); }); @@ -235,74 +226,79 @@ export function EditorFrame(props: EditorFrameProps) { ]); return ( - - } - configPanel={ - allLoaded && ( - + - ) - } - workspacePanel={ - allLoaded && ( - - + ) + } + workspacePanel={ + allLoaded && ( + + + + ) + } + suggestionsPanel={ + allLoaded && ( + - - ) - } - suggestionsPanel={ - allLoaded && ( - - ) - } - /> + ) + } + /> + ); } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx index a69da8b49e233..56afe3ed69a73 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { EuiPage, EuiPageSideBar, EuiPageBody } from '@elastic/eui'; -import { RootDragDropProvider } from '../../drag_drop'; export interface FrameLayoutProps { dataPanel: React.ReactNode; @@ -17,19 +16,17 @@ export interface FrameLayoutProps { export function FrameLayout(props: FrameLayoutProps) { return ( - - -
- {props.dataPanel} - - {props.workspacePanel} - {props.suggestionsPanel} - - - {props.configPanel} - -
-
-
+ +
+ {props.dataPanel} + + {props.workspacePanel} + {props.suggestionsPanel} + + + {props.configPanel} + +
+
); } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/index.scss b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/index.scss index fee28c374ef7e..6c6a63c8c7eb6 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/index.scss +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/index.scss @@ -1,4 +1,5 @@ @import './chart_switch'; +@import './config_panel_wrapper'; @import './data_panel_wrapper'; @import './expression_renderer'; @import './frame_layout'; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/save.test.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/save.test.ts index 158a6cb8c979a..60bfbc493f61c 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/save.test.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/save.test.ts @@ -11,7 +11,7 @@ import { esFilters, IIndexPattern, IFieldType } from '../../../../../../../src/p describe('save editor frame state', () => { const mockVisualization = createMockVisualization(); mockVisualization.getPersistableState.mockImplementation(x => x); - const mockDatasource = createMockDatasource(); + const mockDatasource = createMockDatasource('a'); const mockIndexPattern = ({ id: 'indexpattern' } as unknown) as IIndexPattern; const mockField = ({ name: '@timestamp' } as unknown) as IFieldType; @@ -45,7 +45,7 @@ describe('save editor frame state', () => { }; it('transforms from internal state to persisted doc format', async () => { - const datasource = createMockDatasource(); + const datasource = createMockDatasource('a'); datasource.getPersistableState.mockImplementation(state => ({ stuff: `${state}_datasource_persisted`, })); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts index 487a91c22b5d5..63b8b1f048296 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts @@ -30,7 +30,7 @@ let datasourceStates: Record< beforeEach(() => { datasourceMap = { - mock: createMockDatasource(), + mock: createMockDatasource('a'), }; datasourceStates = { @@ -147,9 +147,9 @@ describe('suggestion helpers', () => { }, }; const multiDatasourceMap = { - mock: createMockDatasource(), - mock2: createMockDatasource(), - mock3: createMockDatasource(), + mock: createMockDatasource('a'), + mock2: createMockDatasource('a'), + mock3: createMockDatasource('a'), }; const droppedField = {}; getSuggestions({ diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx index 9729d6259f84a..b146f2467c46c 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx @@ -39,7 +39,7 @@ describe('suggestion_panel', () => { beforeEach(() => { mockVisualization = createMockVisualization(); - mockDatasource = createMockDatasource(); + mockDatasource = createMockDatasource('a'); expressionRendererMock = createExpressionRendererMock(); dispatchMock = jest.fn(); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 1115126792c86..93f6ea6ea67ac 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -373,7 +373,6 @@ function getPreviewExpression( layerId, dateRange: frame.dateRange, state: datasourceState, - setState: () => {}, }); } }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx index a51091d39f84c..748e5b876da95 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel.test.tsx @@ -36,7 +36,7 @@ describe('workspace_panel', () => { mockVisualization = createMockVisualization(); mockVisualization2 = createMockVisualization(); - mockDatasource = createMockDatasource(); + mockDatasource = createMockDatasource('a'); expressionRendererMock = createExpressionRendererMock(); }); @@ -199,7 +199,7 @@ describe('workspace_panel', () => { }); it('should include data fetching for each layer in the expression', () => { - const mockDatasource2 = createMockDatasource(); + const mockDatasource2 = createMockDatasource('a'); const framePublicAPI = createMockFramePublicAPI(); framePublicAPI.datasourceLayers = { first: mockDatasource.publicAPIMock, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx index e606c69c8c386..5d2f68a5567eb 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx @@ -33,9 +33,24 @@ export function createMockVisualization(): jest.Mocked { getPersistableState: jest.fn(_state => _state), getSuggestions: jest.fn(_options => []), initialize: jest.fn((_frame, _state?) => ({})), - renderLayerConfigPanel: jest.fn(), + getConfiguration: jest.fn(props => ({ + groups: [ + { + groupId: 'a', + groupLabel: 'a', + layerId: 'layer1', + supportsMoreColumns: true, + accessors: [], + filterOperations: jest.fn(() => true), + dataTestSubj: 'mockVisA', + }, + ], + })), toExpression: jest.fn((_state, _frame) => null), toPreviewExpression: jest.fn((_state, _frame) => null), + + setDimension: jest.fn(), + removeDimension: jest.fn(), }; } @@ -43,12 +58,11 @@ export type DatasourceMock = jest.Mocked & { publicAPIMock: jest.Mocked; }; -export function createMockDatasource(): DatasourceMock { +export function createMockDatasource(id: string): DatasourceMock { const publicAPIMock: jest.Mocked = { + datasourceId: id, getTableSpec: jest.fn(() => []), getOperationForColumnId: jest.fn(), - renderDimensionPanel: jest.fn(), - renderLayerPanel: jest.fn(), }; return { @@ -60,12 +74,19 @@ export function createMockDatasource(): DatasourceMock { getPublicAPI: jest.fn().mockReturnValue(publicAPIMock), initialize: jest.fn((_state?) => Promise.resolve()), renderDataPanel: jest.fn(), + renderLayerPanel: jest.fn(), toExpression: jest.fn((_frame, _state) => null), insertLayer: jest.fn((_state, _newLayerId) => {}), removeLayer: jest.fn((_state, _layerId) => {}), + removeColumn: jest.fn(props => {}), getLayers: jest.fn(_state => []), getMetaData: jest.fn(_state => ({ filterableIndexPatterns: [] })), + renderDimensionTrigger: jest.fn(), + renderDimensionEditor: jest.fn(), + canHandleDrop: jest.fn(), + onDrop: jest.fn(), + // this is an additional property which doesn't exist on real datasources // but can be used to validate whether specific API mock functions are called publicAPIMock, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx index 6b9dc88e7ed12..47fd810bb4c53 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx @@ -47,7 +47,7 @@ describe('editor_frame service', () => { pluginSetupDependencies ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); - const instance = await publicAPI.createInstance({}); + const instance = await publicAPI.createInstance(); instance.mount(mountpoint, { onError: jest.fn(), onChange: jest.fn(), @@ -66,7 +66,7 @@ describe('editor_frame service', () => { pluginSetupDependencies ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); - const instance = await publicAPI.createInstance({}); + const instance = await publicAPI.createInstance(); instance.mount(mountpoint, { onError: jest.fn(), onChange: jest.fn(), diff --git a/x-pack/legacy/plugins/lens/public/index.scss b/x-pack/legacy/plugins/lens/public/index.scss index 496573f6a1c9a..2f91d14c397c7 100644 --- a/x-pack/legacy/plugins/lens/public/index.scss +++ b/x-pack/legacy/plugins/lens/public/index.scss @@ -4,8 +4,6 @@ @import './variables'; @import './mixins'; -@import './config_panel'; - @import './app_plugin/index'; @import 'datatable_visualization/index'; @import './drag_drop/index'; diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_dimension_panel.scss b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_dimension_panel.scss deleted file mode 100644 index ddb37505f9985..0000000000000 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_dimension_panel.scss +++ /dev/null @@ -1,9 +0,0 @@ -.lnsIndexPatternDimensionPanel { - @include euiFontSizeS; - background-color: $euiColorEmptyShade; - border-radius: $euiBorderRadius; - display: flex; - align-items: center; - margin-top: $euiSizeXS; - overflow: hidden; -} diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_index.scss b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_index.scss index 2ce3e11171fc9..26f805fe735f0 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_index.scss +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_index.scss @@ -1,3 +1,2 @@ -@import './dimension_panel'; @import './field_select'; @import './popover'; diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_popover.scss b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_popover.scss index 8f26ab91e0f16..07a72ee1f66fc 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_popover.scss +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/_popover.scss @@ -1,37 +1,24 @@ -.lnsPopoverEditor { +.lnsIndexPatternDimensionEditor { flex-grow: 1; line-height: 0; overflow: hidden; } -.lnsPopoverEditor__anchor { - max-width: 100%; - display: block; -} - -.lnsPopoverEditor__link { - width: 100%; - display: flex; - align-items: center; - padding: $euiSizeS; - min-height: $euiSizeXXL; -} - -.lnsPopoverEditor__left, -.lnsPopoverEditor__right { +.lnsIndexPatternDimensionEditor__left, +.lnsIndexPatternDimensionEditor__right { padding: $euiSizeS; } -.lnsPopoverEditor__left { +.lnsIndexPatternDimensionEditor__left { padding-top: 0; background-color: $euiPageBackgroundColor; } -.lnsPopoverEditor__right { +.lnsIndexPatternDimensionEditor__right { width: $euiSize * 20; } -.lnsPopoverEditor__operation { +.lnsIndexPatternDimensionEditor__operation { @include euiFontSizeS; color: $euiColorPrimary; @@ -41,11 +28,11 @@ } } -.lnsPopoverEditor__operation--selected { +.lnsIndexPatternDimensionEditor__operation--selected { font-weight: bold; color: $euiTextColor; } -.lnsPopoverEditor__operation--incompatible { +.lnsIndexPatternDimensionEditor__operation--incompatible { color: $euiColorMediumShade; } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 56f75ae4b17be..41c317ccab290 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -7,27 +7,28 @@ import { ReactWrapper, ShallowWrapper } from 'enzyme'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import { - EuiComboBox, - EuiSideNav, - EuiSideNavItemType, - EuiPopover, - EuiFieldNumber, -} from '@elastic/eui'; +import { EuiComboBox, EuiSideNav, EuiSideNavItemType, EuiFieldNumber } from '@elastic/eui'; import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public'; import { changeColumn } from '../state_helpers'; import { - IndexPatternDimensionPanel, - IndexPatternDimensionPanelComponent, - IndexPatternDimensionPanelProps, + IndexPatternDimensionEditorComponent, + IndexPatternDimensionEditorProps, + onDrop, + canHandleDrop, } from './dimension_panel'; -import { DropHandler, DragContextState } from '../../drag_drop'; +import { DragContextState } from '../../drag_drop'; import { createMockedDragDropContext } from '../mocks'; import { mountWithIntl as mount, shallowWithIntl as shallow } from 'test_utils/enzyme_helpers'; -import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'src/core/public'; +import { + IUiSettingsClient, + SavedObjectsClientContract, + HttpSetup, + CoreSetup, +} from 'src/core/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IndexPatternPrivateState } from '../types'; import { documentField } from '../document_field'; +import { OperationMetadata } from '../../types'; jest.mock('ui/new_platform'); jest.mock('../loader'); @@ -79,20 +80,12 @@ const expectedIndexPatterns = { }, }; -describe('IndexPatternDimensionPanel', () => { - let wrapper: ReactWrapper | ShallowWrapper; +describe('IndexPatternDimensionEditorPanel', () => { let state: IndexPatternPrivateState; let setState: jest.Mock; - let defaultProps: IndexPatternDimensionPanelProps; + let defaultProps: IndexPatternDimensionEditorProps; let dragDropContext: DragContextState; - function openPopover() { - wrapper - .find('[data-test-subj="indexPattern-configure-dimension"]') - .first() - .simulate('click'); - } - beforeEach(() => { state = { indexPatternRefs: [], @@ -134,7 +127,6 @@ describe('IndexPatternDimensionPanel', () => { dragDropContext = createMockedDragDropContext(); defaultProps = { - dragDropContext, state, setState, dateRange: { fromDate: 'now-1d', toDate: 'now' }, @@ -158,475 +150,582 @@ describe('IndexPatternDimensionPanel', () => { }), } as unknown) as DataPublicPluginStart['fieldFormats'], } as unknown) as DataPublicPluginStart, + core: {} as CoreSetup, }; jest.clearAllMocks(); }); - afterEach(() => { - if (wrapper) { - wrapper.unmount(); - } - }); + describe('Editor component', () => { + let wrapper: ReactWrapper | ShallowWrapper; - it('should display a configure button if dimension has no column yet', () => { - wrapper = mount(); - expect( - wrapper - .find('[data-test-subj="indexPattern-configure-dimension"]') - .first() - .prop('iconType') - ).toEqual('plusInCircleFilled'); - }); + afterEach(() => { + if (wrapper) { + wrapper.unmount(); + } + }); - it('should call the filterOperations function', () => { - const filterOperations = jest.fn().mockReturnValue(true); + it('should call the filterOperations function', () => { + const filterOperations = jest.fn().mockReturnValue(true); - wrapper = shallow( - - ); + wrapper = shallow( + + ); - expect(filterOperations).toBeCalled(); - }); + expect(filterOperations).toBeCalled(); + }); - it('should show field select combo box on click', () => { - wrapper = mount(); + it('should show field select combo box on click', () => { + wrapper = mount(); - openPopover(); + expect( + wrapper.find(EuiComboBox).filter('[data-test-subj="indexPattern-dimension-field"]') + ).toHaveLength(1); + }); - expect( - wrapper.find(EuiComboBox).filter('[data-test-subj="indexPattern-dimension-field"]') - ).toHaveLength(1); - }); + it('should not show any choices if the filter returns false', () => { + wrapper = mount( + false} + /> + ); - it('should not show any choices if the filter returns false', () => { - wrapper = mount( - false} - /> - ); + expect( + wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]')! + .prop('options')! + ).toHaveLength(0); + }); - openPopover(); + it('should list all field names and document as a whole in prioritized order', () => { + wrapper = mount(); - expect( - wrapper + const options = wrapper .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]')! - .prop('options')! - ).toHaveLength(0); - }); - - it('should list all field names and document as a whole in prioritized order', () => { - wrapper = mount(); - - openPopover(); - - const options = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('options'); + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); - expect(options).toHaveLength(2); + expect(options).toHaveLength(2); - expect(options![0].label).toEqual('Records'); + expect(options![0].label).toEqual('Records'); - expect(options![1].options!.map(({ label }) => label)).toEqual([ - 'timestamp', - 'bytes', - 'memory', - 'source', - ]); - }); + expect(options![1].options!.map(({ label }) => label)).toEqual([ + 'timestamp', + 'bytes', + 'memory', + 'source', + ]); + }); - it('should hide fields that have no data', () => { - const props = { - ...defaultProps, - state: { - ...defaultProps.state, - existingFields: { - 'my-fake-index-pattern': { - timestamp: true, - source: true, + it('should hide fields that have no data', () => { + const props = { + ...defaultProps, + state: { + ...defaultProps.state, + existingFields: { + 'my-fake-index-pattern': { + timestamp: true, + source: true, + }, }, }, - }, - }; - wrapper = mount(); - - openPopover(); - - const options = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('options'); + }; + wrapper = mount(); - expect(options![1].options!.map(({ label }) => label)).toEqual(['timestamp', 'source']); - }); + const options = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); - it('should indicate fields which are incompatible for the operation of the current column', () => { - wrapper = mount( - label)).toEqual(['timestamp', 'source']); + }); - // Private - operationType: 'max', - sourceField: 'bytes', + it('should indicate fields which are incompatible for the operation of the current column', () => { + wrapper = mount( + - ); - - openPopover(); - - const options = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('options'); + }} + /> + ); - expect(options![0]['data-test-subj']).toEqual('lns-fieldOptionIncompatible-Records'); + const options = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); - expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] - ).toContain('Incompatible'); - expect( - options![1].options!.filter(({ label }) => label === 'memory')[0]['data-test-subj'] - ).not.toContain('Incompatible'); - }); + expect(options![0]['data-test-subj']).toEqual('lns-fieldOptionIncompatible-Records'); - it('should indicate operations which are incompatible for the field of the current column', () => { - wrapper = mount( - label === 'timestamp')[0]['data-test-subj'] + ).toContain('Incompatible'); + expect( + options![1].options!.filter(({ label }) => label === 'memory')[0]['data-test-subj'] + ).not.toContain('Incompatible'); + }); - // Private - operationType: 'max', - sourceField: 'bytes', + it('should indicate operations which are incompatible for the field of the current column', () => { + wrapper = mount( + - ); - - openPopover(); + }} + /> + ); - interface ItemType { - name: string; - 'data-test-subj': string; - } - const items: Array> = wrapper.find(EuiSideNav).prop('items'); - const options = (items[0].items as unknown) as ItemType[]; + interface ItemType { + name: string; + 'data-test-subj': string; + } + const items: Array> = wrapper.find(EuiSideNav).prop('items'); + const options = (items[0].items as unknown) as ItemType[]; - expect(options.find(({ name }) => name === 'Minimum')!['data-test-subj']).not.toContain( - 'Incompatible' - ); + expect(options.find(({ name }) => name === 'Minimum')!['data-test-subj']).not.toContain( + 'Incompatible' + ); - expect(options.find(({ name }) => name === 'Date histogram')!['data-test-subj']).toContain( - 'Incompatible' - ); - }); + expect(options.find(({ name }) => name === 'Date histogram')!['data-test-subj']).toContain( + 'Incompatible' + ); + }); - it('should keep the operation when switching to another field compatible with this operation', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - label: 'Max of bytes', - dataType: 'number', - isBucketed: false, + it('should keep the operation when switching to another field compatible with this operation', () => { + const initialState: IndexPatternPrivateState = { + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + label: 'Max of bytes', + dataType: 'number', + isBucketed: false, - // Private - operationType: 'max', - sourceField: 'bytes', - params: { format: { id: 'bytes' } }, + // Private + operationType: 'max', + sourceField: 'bytes', + params: { format: { id: 'bytes' } }, + }, }, }, }, - }, - }; - - wrapper = mount(); + }; - openPopover(); + wrapper = mount( + + ); - const comboBox = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]')!; - const option = comboBox.prop('options')![1].options!.find(({ label }) => label === 'memory')!; + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]')!; + const option = comboBox.prop('options')![1].options!.find(({ label }) => label === 'memory')!; - act(() => { - comboBox.prop('onChange')!([option]); - }); + act(() => { + comboBox.prop('onChange')!([option]); + }); - expect(setState).toHaveBeenCalledWith({ - ...initialState, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: expect.objectContaining({ - operationType: 'max', - sourceField: 'memory', - params: { format: { id: 'bytes' } }, - // Other parts of this don't matter for this test - }), + expect(setState).toHaveBeenCalledWith({ + ...initialState, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + operationType: 'max', + sourceField: 'memory', + params: { format: { id: 'bytes' } }, + // Other parts of this don't matter for this test + }), + }, }, }, - }, + }); }); - }); - - it('should switch operations when selecting a field that requires another operation', () => { - wrapper = mount(); - openPopover(); + it('should switch operations when selecting a field that requires another operation', () => { + wrapper = mount(); - const comboBox = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]')!; - const option = comboBox.prop('options')![1].options!.find(({ label }) => label === 'source')!; + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]')!; + const option = comboBox.prop('options')![1].options!.find(({ label }) => label === 'source')!; - act(() => { - comboBox.prop('onChange')!([option]); - }); + act(() => { + comboBox.prop('onChange')!([option]); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: expect.objectContaining({ - operationType: 'terms', - sourceField: 'source', - // Other parts of this don't matter for this test - }), + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + operationType: 'terms', + sourceField: 'source', + // Other parts of this don't matter for this test + }), + }, }, }, - }, + }); }); - }); - - it('should keep the field when switching to another operation compatible for this field', () => { - wrapper = mount( - { + wrapper = mount( + - ); - - openPopover(); + }} + /> + ); - act(() => { - wrapper.find('button[data-test-subj="lns-indexPatternDimension-min"]').simulate('click'); - }); + act(() => { + wrapper.find('button[data-test-subj="lns-indexPatternDimension-min"]').simulate('click'); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: expect.objectContaining({ - operationType: 'min', - sourceField: 'bytes', - params: { format: { id: 'bytes' } }, - // Other parts of this don't matter for this test - }), + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + operationType: 'min', + sourceField: 'bytes', + params: { format: { id: 'bytes' } }, + // Other parts of this don't matter for this test + }), + }, }, }, - }, + }); }); - }); - it('should not set the state if selecting the currently active operation', () => { - wrapper = mount(); + it('should not set the state if selecting the currently active operation', () => { + wrapper = mount(); - openPopover(); + act(() => { + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-date_histogram"]') + .simulate('click'); + }); - act(() => { - wrapper - .find('button[data-test-subj="lns-indexPatternDimension-date_histogram"]') - .simulate('click'); + expect(setState).not.toHaveBeenCalled(); }); - expect(setState).not.toHaveBeenCalled(); - }); - - it('should update label on label input changes', () => { - wrapper = mount(); + it('should update label on label input changes', () => { + wrapper = mount(); - openPopover(); - - act(() => { - wrapper - .find('input[data-test-subj="indexPattern-label-edit"]') - .simulate('change', { target: { value: 'New Label' } }); - }); + act(() => { + wrapper + .find('input[data-test-subj="indexPattern-label-edit"]') + .simulate('change', { target: { value: 'New Label' } }); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: expect.objectContaining({ - label: 'New Label', - // Other parts of this don't matter for this test - }), + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + label: 'New Label', + // Other parts of this don't matter for this test + }), + }, }, }, - }, + }); }); - }); - describe('transient invalid state', () => { - it('should not set the state if selecting an operation incompatible with the current field', () => { - wrapper = mount(); + describe('transient invalid state', () => { + it('should not set the state if selecting an operation incompatible with the current field', () => { + wrapper = mount(); - openPopover(); + act(() => { + wrapper + .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') + .simulate('click'); + }); + + expect(setState).not.toHaveBeenCalled(); + }); + + it('should show error message in invalid state', () => { + wrapper = mount(); - act(() => { wrapper .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') .simulate('click'); + + expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).not.toHaveLength( + 0 + ); + + expect(setState).not.toHaveBeenCalled(); }); - expect(setState).not.toHaveBeenCalled(); - }); + it('should leave error state if a compatible operation is selected', () => { + wrapper = mount(); - it('should show error message in invalid state', () => { - wrapper = mount(); + wrapper + .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') + .simulate('click'); - openPopover(); + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-date_histogram"]') + .simulate('click'); - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') - .simulate('click'); + expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); + }); - expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).not.toHaveLength(0); + it('should indicate fields compatible with selected operation', () => { + wrapper = mount(); - expect(setState).not.toHaveBeenCalled(); - }); + wrapper + .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') + .simulate('click'); - it('should leave error state if a compatible operation is selected', () => { - wrapper = mount(); + const options = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); - openPopover(); + expect(options![0]['data-test-subj']).toContain('Incompatible'); - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') - .simulate('click'); + expect( + options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + ).toContain('Incompatible'); + expect( + options![1].options!.filter(({ label }) => label === 'source')[0]['data-test-subj'] + ).not.toContain('Incompatible'); + }); - wrapper - .find('button[data-test-subj="lns-indexPatternDimension-date_histogram"]') - .simulate('click'); + it('should select compatible operation if field not compatible with selected operation', () => { + wrapper = mount( + + ); - expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); - }); + wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); - it('should leave error state if the popover gets closed', () => { - wrapper = mount(); + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]'); + const options = comboBox.prop('options'); - openPopover(); + // options[1][2] is a `source` field of type `string` which doesn't support `avg` operation + act(() => { + comboBox.prop('onChange')!([options![1].options![2]]); + }); - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') - .simulate('click'); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col2: expect.objectContaining({ + sourceField: 'source', + operationType: 'terms', + // Other parts of this don't matter for this test + }), + }, + columnOrder: ['col1', 'col2'], + }, + }, + }); + }); - act(() => { - wrapper.find(EuiPopover).prop('closePopover')!(); + it('should select the Records field when count is selected', () => { + const initialState: IndexPatternPrivateState = { + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col2: { + dataType: 'number', + isBucketed: false, + label: '', + operationType: 'avg', + sourceField: 'bytes', + }, + }, + }, + }, + }; + wrapper = mount( + + ); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-count"]') + .simulate('click'); + + const newColumnState = setState.mock.calls[0][0].layers.first.columns.col2; + expect(newColumnState.operationType).toEqual('count'); + expect(newColumnState.sourceField).toEqual('Records'); }); - openPopover(); + it('should indicate document and field compatibility with selected document operation', () => { + const initialState: IndexPatternPrivateState = { + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col2: { + dataType: 'number', + isBucketed: false, + label: '', + operationType: 'count', + sourceField: 'Records', + }, + }, + }, + }, + }; + wrapper = mount( + + ); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') + .simulate('click'); - expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); - }); + const options = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); - it('should indicate fields compatible with selected operation', () => { - wrapper = mount(); + expect(options![0]['data-test-subj']).toContain('Incompatible'); - openPopover(); + expect( + options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + ).toContain('Incompatible'); + expect( + options![1].options!.filter(({ label }) => label === 'source')[0]['data-test-subj'] + ).not.toContain('Incompatible'); + }); - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') - .simulate('click'); + it('should set datasource state if compatible field is selected for operation', () => { + wrapper = mount(); - const options = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('options'); + act(() => { + wrapper + .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') + .simulate('click'); + }); - expect(options![0]['data-test-subj']).toContain('Incompatible'); + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]')!; + const option = comboBox + .prop('options')![1] + .options!.find(({ label }) => label === 'source')!; - expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] - ).toContain('Incompatible'); - expect( - options![1].options!.filter(({ label }) => label === 'source')[0]['data-test-subj'] - ).not.toContain('Incompatible'); - }); + act(() => { + comboBox.prop('onChange')!([option]); + }); - it('should select compatible operation if field not compatible with selected operation', () => { - wrapper = mount(); + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + sourceField: 'source', + operationType: 'terms', + }), + }, + }, + }, + }); + }); + }); - openPopover(); + it('should support selecting the operation before the field', () => { + wrapper = mount(); wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); @@ -635,9 +734,8 @@ describe('IndexPatternDimensionPanel', () => { .filter('[data-test-subj="indexPattern-dimension-field"]'); const options = comboBox.prop('options'); - // options[1][2] is a `source` field of type `string` which doesn't support `avg` operation act(() => { - comboBox.prop('onChange')!([options![1].options![2]]); + comboBox.prop('onChange')!([options![1].options![0]]); }); expect(setState).toHaveBeenCalledWith({ @@ -648,8 +746,8 @@ describe('IndexPatternDimensionPanel', () => { columns: { ...state.layers.first.columns, col2: expect.objectContaining({ - sourceField: 'source', - operationType: 'terms', + sourceField: 'bytes', + operationType: 'avg', // Other parts of this don't matter for this test }), }, @@ -659,41 +757,93 @@ describe('IndexPatternDimensionPanel', () => { }); }); - it('should select the Records field when count is selected', () => { - const initialState: IndexPatternPrivateState = { + it('should select operation directly if only one field is possible', () => { + const initialState = { + ...state, + indexPatterns: { + 1: { + ...state.indexPatterns['1'], + fields: state.indexPatterns['1'].fields.filter(field => field.name !== 'memory'), + }, + }, + }; + + wrapper = mount( + + ); + + wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + + expect(setState).toHaveBeenCalledWith({ + ...initialState, + layers: { + first: { + ...initialState.layers.first, + columns: { + ...initialState.layers.first.columns, + col2: expect.objectContaining({ + sourceField: 'bytes', + operationType: 'avg', + // Other parts of this don't matter for this test + }), + }, + columnOrder: ['col1', 'col2'], + }, + }, + }); + }); + + it('should select operation directly if only document is possible', () => { + wrapper = mount(); + + wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); + + expect(setState).toHaveBeenCalledWith({ ...state, layers: { first: { ...state.layers.first, columns: { ...state.layers.first.columns, - col2: { - dataType: 'number', - isBucketed: false, - label: '', - operationType: 'avg', - sourceField: 'bytes', - }, + col2: expect.objectContaining({ + operationType: 'count', + // Other parts of this don't matter for this test + }), }, + columnOrder: ['col1', 'col2'], }, }, - }; - wrapper = mount( - - ); + }); + }); - openPopover(); + it('should indicate compatible fields when selecting the operation first', () => { + wrapper = mount(); - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-count"]') - .simulate('click'); + wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); - const newColumnState = setState.mock.calls[0][0].layers.first.columns.col2; - expect(newColumnState.operationType).toEqual('count'); - expect(newColumnState.sourceField).toEqual('Records'); + const options = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); + + expect(options![0]['data-test-subj']).toContain('Incompatible'); + + expect( + options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] + ).toContain('Incompatible'); + expect( + options![1].options!.filter(({ label }) => label === 'bytes')[0]['data-test-subj'] + ).not.toContain('Incompatible'); + expect( + options![1].options!.filter(({ label }) => label === 'memory')[0]['data-test-subj'] + ).not.toContain('Incompatible'); }); - it('should indicate document and field compatibility with selected document operation', () => { + it('should indicate document compatibility when document operation is selected', () => { const initialState: IndexPatternPrivateState = { ...state, layers: { @@ -713,45 +863,56 @@ describe('IndexPatternDimensionPanel', () => { }, }; wrapper = mount( - + ); - openPopover(); - - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') - .simulate('click'); - const options = wrapper .find(EuiComboBox) .filter('[data-test-subj="indexPattern-dimension-field"]') .prop('options'); - expect(options![0]['data-test-subj']).toContain('Incompatible'); + expect(options![0]['data-test-subj']).not.toContain('Incompatible'); - expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] - ).toContain('Incompatible'); - expect( - options![1].options!.filter(({ label }) => label === 'source')[0]['data-test-subj'] - ).not.toContain('Incompatible'); + options![1].options!.map(operation => + expect(operation['data-test-subj']).toContain('Incompatible') + ); }); - it('should set datasource state if compatible field is selected for operation', () => { - wrapper = mount(); + it('should show all operations that are not filtered out', () => { + wrapper = mount( + !op.isBucketed && op.dataType === 'number'} + /> + ); - openPopover(); + interface ItemType { + name: React.ReactNode; + } + const items: Array> = wrapper.find(EuiSideNav).prop('items'); + const options = (items[0].items as unknown) as ItemType[]; + + expect(options.map(({ name }: { name: React.ReactNode }) => name)).toEqual([ + 'Unique count', + 'Average', + 'Count', + 'Maximum', + 'Minimum', + 'Sum', + ]); + }); - act(() => { - wrapper - .find('button[data-test-subj="lns-indexPatternDimensionIncompatible-terms"]') - .simulate('click'); - }); + it('should add a column on selection of a field', () => { + wrapper = mount(); const comboBox = wrapper .find(EuiComboBox) .filter('[data-test-subj="indexPattern-dimension-field"]')!; - const option = comboBox.prop('options')![1].options!.find(({ label }) => label === 'source')!; + const option = comboBox.prop('options')![1].options![0]; act(() => { comboBox.prop('onChange')!([option]); @@ -764,479 +925,237 @@ describe('IndexPatternDimensionPanel', () => { ...state.layers.first, columns: { ...state.layers.first.columns, - col1: expect.objectContaining({ - sourceField: 'source', - operationType: 'terms', + col2: expect.objectContaining({ + sourceField: 'bytes', + // Other parts of this don't matter for this test }), }, + columnOrder: ['col1', 'col2'], }, }, }); }); - }); - - it('should support selecting the operation before the field', () => { - wrapper = mount(); - - openPopover(); - - wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); - - const comboBox = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]'); - const options = comboBox.prop('options'); - - act(() => { - comboBox.prop('onChange')!([options![1].options![0]]); - }); - - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: expect.objectContaining({ - sourceField: 'bytes', - operationType: 'avg', - // Other parts of this don't matter for this test - }), - }, - columnOrder: ['col1', 'col2'], - }, - }, - }); - }); - - it('should select operation directly if only one field is possible', () => { - const initialState = { - ...state, - indexPatterns: { - 1: { - ...state.indexPatterns['1'], - fields: state.indexPatterns['1'].fields.filter(field => field.name !== 'memory'), - }, - }, - }; - - wrapper = mount( - - ); - - openPopover(); - - wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); - - expect(setState).toHaveBeenCalledWith({ - ...initialState, - layers: { - first: { - ...initialState.layers.first, - columns: { - ...initialState.layers.first.columns, - col2: expect.objectContaining({ - sourceField: 'bytes', - operationType: 'avg', - // Other parts of this don't matter for this test - }), - }, - columnOrder: ['col1', 'col2'], - }, - }, - }); - }); - - it('should select operation directly if only document is possible', () => { - wrapper = mount(); - - openPopover(); - - wrapper.find('button[data-test-subj="lns-indexPatternDimension-count"]').simulate('click'); - - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: expect.objectContaining({ - operationType: 'count', - // Other parts of this don't matter for this test - }), - }, - columnOrder: ['col1', 'col2'], - }, - }, - }); - }); - it('should indicate compatible fields when selecting the operation first', () => { - wrapper = mount(); - - openPopover(); - - wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); - - const options = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('options'); - - expect(options![0]['data-test-subj']).toContain('Incompatible'); - - expect( - options![1].options!.filter(({ label }) => label === 'timestamp')[0]['data-test-subj'] - ).toContain('Incompatible'); - expect( - options![1].options!.filter(({ label }) => label === 'bytes')[0]['data-test-subj'] - ).not.toContain('Incompatible'); - expect( - options![1].options!.filter(({ label }) => label === 'memory')[0]['data-test-subj'] - ).not.toContain('Incompatible'); - }); - - it('should indicate document compatibility when document operation is selected', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: { - dataType: 'number', - isBucketed: false, - label: '', - operationType: 'count', - sourceField: 'Records', - }, - }, - }, - }, - }; - wrapper = mount( - - ); - - openPopover(); - - const options = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('options'); - - expect(options![0]['data-test-subj']).not.toContain('Incompatible'); - - options![1].options!.map(operation => - expect(operation['data-test-subj']).toContain('Incompatible') - ); - }); - - it('should show all operations that are not filtered out', () => { - wrapper = mount( - !op.isBucketed && op.dataType === 'number'} - /> - ); - - openPopover(); - - interface ItemType { - name: React.ReactNode; - } - const items: Array> = wrapper.find(EuiSideNav).prop('items'); - const options = (items[0].items as unknown) as ItemType[]; - - expect(options.map(({ name }: { name: React.ReactNode }) => name)).toEqual([ - 'Unique count', - 'Average', - 'Count', - 'Maximum', - 'Minimum', - 'Sum', - ]); - }); - - it('should add a column on selection of a field', () => { - wrapper = mount(); - - openPopover(); - - const comboBox = wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]')!; - const option = comboBox.prop('options')![1].options![0]; - - act(() => { - comboBox.prop('onChange')!([option]); - }); - - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: expect.objectContaining({ - sourceField: 'bytes', - // Other parts of this don't matter for this test - }), - }, - columnOrder: ['col1', 'col2'], - }, - }, - }); - }); - - it('should use helper function when changing the function', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - label: 'Max of bytes', - dataType: 'number', - isBucketed: false, + it('should use helper function when changing the function', () => { + const initialState: IndexPatternPrivateState = { + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + label: 'Max of bytes', + dataType: 'number', + isBucketed: false, - // Private - operationType: 'max', - sourceField: 'bytes', + // Private + operationType: 'max', + sourceField: 'bytes', + }, }, }, }, - }, - }; - wrapper = mount(); - - openPopover(); - - act(() => { - wrapper - .find('[data-test-subj="lns-indexPatternDimension-min"]') - .first() - .prop('onClick')!({} as React.MouseEvent<{}, MouseEvent>); - }); - - expect(changeColumn).toHaveBeenCalledWith({ - state: initialState, - columnId: 'col1', - layerId: 'first', - newColumn: expect.objectContaining({ - sourceField: 'bytes', - operationType: 'min', - }), - }); - }); - - it('should clear the dimension with the clear button', () => { - wrapper = mount(); - - const clearButton = wrapper.find( - 'EuiButtonIcon[data-test-subj="indexPattern-dimensionPopover-remove"]' - ); + }; + wrapper = mount( + + ); - act(() => { - clearButton.simulate('click'); - }); + act(() => { + wrapper + .find('[data-test-subj="lns-indexPatternDimension-min"]') + .first() + .prop('onClick')!({} as React.MouseEvent<{}, MouseEvent>); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - indexPatternId: '1', - columns: {}, - columnOrder: [], - }, - }, + expect(changeColumn).toHaveBeenCalledWith({ + state: initialState, + columnId: 'col1', + layerId: 'first', + newColumn: expect.objectContaining({ + sourceField: 'bytes', + operationType: 'min', + }), + }); }); - }); - - it('should clear the dimension when removing the selection in field combobox', () => { - wrapper = mount(); - openPopover(); + it('should clear the dimension when removing the selection in field combobox', () => { + wrapper = mount(); - act(() => { - wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-field"]') - .prop('onChange')!([]); - }); + act(() => { + wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('onChange')!([]); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - indexPatternId: '1', - columns: {}, - columnOrder: [], + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + indexPatternId: '1', + columns: {}, + columnOrder: [], + }, }, - }, + }); }); - }); - it('allows custom format', () => { - const stateWithNumberCol: IndexPatternPrivateState = { - ...state, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Average of bar', - dataType: 'number', - isBucketed: false, - // Private - operationType: 'avg', - sourceField: 'bar', + it('allows custom format', () => { + const stateWithNumberCol: IndexPatternPrivateState = { + ...state, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Average of bar', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'avg', + sourceField: 'bar', + }, }, }, }, - }, - }; - - wrapper = mount(); + }; - openPopover(); + wrapper = mount( + + ); - act(() => { - wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-format"]') - .prop('onChange')!([{ value: 'bytes', label: 'Bytes' }]); - }); + act(() => { + wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-format"]') + .prop('onChange')!([{ value: 'bytes', label: 'Bytes' }]); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: expect.objectContaining({ - params: { - format: { id: 'bytes', params: { decimals: 2 } }, - }, - }), + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + params: { + format: { id: 'bytes', params: { decimals: 2 } }, + }, + }), + }, }, }, - }, + }); }); - }); - it('keeps decimal places while switching', () => { - const stateWithNumberCol: IndexPatternPrivateState = { - ...state, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Average of bar', - dataType: 'number', - isBucketed: false, - // Private - operationType: 'avg', - sourceField: 'bar', - params: { - format: { id: 'bytes', params: { decimals: 0 } }, + it('keeps decimal places while switching', () => { + const stateWithNumberCol: IndexPatternPrivateState = { + ...state, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Average of bar', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'avg', + sourceField: 'bar', + params: { + format: { id: 'bytes', params: { decimals: 0 } }, + }, }, }, }, }, - }, - }; + }; - wrapper = mount(); + wrapper = mount( + + ); - openPopover(); + act(() => { + wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-format"]') + .prop('onChange')!([{ value: '', label: 'Default' }]); + }); - act(() => { - wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-format"]') - .prop('onChange')!([{ value: '', label: 'Default' }]); - }); + act(() => { + wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-format"]') + .prop('onChange')!([{ value: 'number', label: 'Number' }]); + }); - act(() => { - wrapper - .find(EuiComboBox) - .filter('[data-test-subj="indexPattern-dimension-format"]') - .prop('onChange')!([{ value: 'number', label: 'Number' }]); + expect( + wrapper + .find(EuiFieldNumber) + .filter('[data-test-subj="indexPattern-dimension-formatDecimals"]') + .prop('value') + ).toEqual(0); }); - expect( - wrapper - .find(EuiFieldNumber) - .filter('[data-test-subj="indexPattern-dimension-formatDecimals"]') - .prop('value') - ).toEqual(0); - }); - - it('allows custom format with number of decimal places', () => { - const stateWithNumberCol: IndexPatternPrivateState = { - ...state, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Average of bar', - dataType: 'number', - isBucketed: false, - // Private - operationType: 'avg', - sourceField: 'bar', - params: { - format: { id: 'bytes', params: { decimals: 2 } }, + it('allows custom format with number of decimal places', () => { + const stateWithNumberCol: IndexPatternPrivateState = { + ...state, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Average of bar', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'avg', + sourceField: 'bar', + params: { + format: { id: 'bytes', params: { decimals: 2 } }, + }, }, }, }, }, - }, - }; - - wrapper = mount(); + }; - openPopover(); + wrapper = mount( + + ); - act(() => { - wrapper - .find(EuiFieldNumber) - .filter('[data-test-subj="indexPattern-dimension-formatDecimals"]') - .prop('onChange')!({ target: { value: '0' } }); - }); + act(() => { + wrapper + .find(EuiFieldNumber) + .filter('[data-test-subj="indexPattern-dimension-formatDecimals"]') + .prop('onChange')!({ target: { value: '0' } }); + }); - expect(setState).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: expect.objectContaining({ - params: { - format: { id: 'bytes', params: { decimals: 0 } }, - }, - }), + expect(setState).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: expect.objectContaining({ + params: { + format: { id: 'bytes', params: { decimals: 0 } }, + }, + }), + }, }, }, - }, + }); }); }); - describe('drag and drop', () => { + describe('Drag and drop', () => { function dragDropState(): IndexPatternPrivateState { return { indexPatternRefs: [], @@ -1287,112 +1206,80 @@ describe('IndexPatternDimensionPanel', () => { } it('is not droppable if no drag is happening', () => { - wrapper = mount( - - ); - expect( - wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('droppable') - ).toBeFalsy(); + canHandleDrop({ + ...defaultProps, + dragDropContext, + state: dragDropState(), + layerId: 'myLayer', + }) + ).toBe(false); }); it('is not droppable if the dragged item has no field', () => { - wrapper = shallow( - - ); - - expect( - wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('droppable') - ).toBeFalsy(); + }, + }) + ).toBe(false); }); it('is not droppable if field is not supported by filterOperations', () => { - wrapper = shallow( - false} - layerId="myLayer" - /> - ); - - expect( - wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('droppable') - ).toBeFalsy(); + }, + state: dragDropState(), + filterOperations: () => false, + layerId: 'myLayer', + }) + ).toBe(false); }); it('is droppable if the field is supported by filterOperations', () => { - wrapper = shallow( - op.dataType === 'number'} - layerId="myLayer" - /> - ); - - expect( - wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('droppable') - ).toBeTruthy(); + }, + state: dragDropState(), + filterOperations: (op: OperationMetadata) => op.dataType === 'number', + layerId: 'myLayer', + }) + ).toBe(true); }); - it('is notdroppable if the field belongs to another index pattern', () => { - wrapper = shallow( - { + expect( + canHandleDrop({ + ...defaultProps, + dragDropContext: { ...dragDropContext, dragging: { field: { type: 'number', name: 'bar', aggregatable: true }, indexPatternId: 'foo2', }, - }} - state={dragDropState()} - filterOperations={op => op.dataType === 'number'} - layerId="myLayer" - /> - ); - - expect( - wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('droppable') - ).toBeFalsy(); + }, + state: dragDropState(), + filterOperations: (op: OperationMetadata) => op.dataType === 'number', + layerId: 'myLayer', + }) + ).toBe(false); }); it('appends the dropped column when a field is dropped', () => { @@ -1401,27 +1288,18 @@ describe('IndexPatternDimensionPanel', () => { indexPatternId: 'foo', }; const testState = dragDropState(); - wrapper = shallow( - op.dataType === 'number'} - layerId="myLayer" - /> - ); - - act(() => { - const onDrop = wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('onDrop') as DropHandler; - onDrop(dragging); + onDrop({ + ...defaultProps, + dragDropContext: { + ...dragDropContext, + dragging, + }, + droppedItem: dragging, + state: testState, + columnId: 'col2', + filterOperations: (op: OperationMetadata) => op.dataType === 'number', + layerId: 'myLayer', }); expect(setState).toBeCalledTimes(1); @@ -1449,27 +1327,17 @@ describe('IndexPatternDimensionPanel', () => { indexPatternId: 'foo', }; const testState = dragDropState(); - wrapper = shallow( - op.isBucketed} - layerId="myLayer" - /> - ); - - act(() => { - const onDrop = wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('onDrop') as DropHandler; - - onDrop(dragging); + onDrop({ + ...defaultProps, + dragDropContext: { + ...dragDropContext, + dragging, + }, + droppedItem: dragging, + state: testState, + columnId: 'col2', + filterOperations: (op: OperationMetadata) => op.isBucketed, + layerId: 'myLayer', }); expect(setState).toBeCalledTimes(1); @@ -1497,26 +1365,16 @@ describe('IndexPatternDimensionPanel', () => { indexPatternId: 'foo', }; const testState = dragDropState(); - wrapper = shallow( - op.dataType === 'number'} - layerId="myLayer" - /> - ); - - act(() => { - const onDrop = wrapper - .find('[data-test-subj="indexPattern-dropTarget"]') - .first() - .prop('onDrop') as DropHandler; - - onDrop(dragging); + onDrop({ + ...defaultProps, + dragDropContext: { + ...dragDropContext, + dragging, + }, + droppedItem: dragging, + state: testState, + filterOperations: (op: OperationMetadata) => op.dataType === 'number', + layerId: 'myLayer', }); expect(setState).toBeCalledTimes(1); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index 59350ff215c27..5d87137db3d39 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -5,27 +5,36 @@ */ import _ from 'lodash'; -import React, { memo, useMemo } from 'react'; -import { EuiButtonIcon } from '@elastic/eui'; +import React, { memo } from 'react'; import { i18n } from '@kbn/i18n'; +import { EuiLink } from '@elastic/eui'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'src/core/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { + DatasourceDimensionTriggerProps, + DatasourceDimensionEditorProps, + DatasourceDimensionDropProps, + DatasourceDimensionDropHandlerProps, +} from '../../types'; import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public'; -import { DatasourceDimensionPanelProps, StateSetter } from '../../types'; import { IndexPatternColumn, OperationType } from '../indexpattern'; import { getAvailableOperationsByMetadata, buildColumn, changeField } from '../operations'; import { PopoverEditor } from './popover_editor'; -import { DragContextState, ChildDragDropProvider, DragDrop } from '../../drag_drop'; -import { changeColumn, deleteColumn } from '../state_helpers'; +import { changeColumn } from '../state_helpers'; import { isDraggedField, hasField } from '../utils'; import { IndexPatternPrivateState, IndexPatternField } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { DateRange } from '../../../../../../plugins/lens/common'; -export type IndexPatternDimensionPanelProps = DatasourceDimensionPanelProps & { - state: IndexPatternPrivateState; - setState: StateSetter; - dragDropContext: DragContextState; +export type IndexPatternDimensionTriggerProps = DatasourceDimensionTriggerProps< + IndexPatternPrivateState +> & { + uniqueLabel: string; +}; + +export type IndexPatternDimensionEditorProps = DatasourceDimensionEditorProps< + IndexPatternPrivateState +> & { uiSettings: IUiSettingsClient; storage: IStorageWrapper; savedObjectsClient: SavedObjectsClientContract; @@ -41,152 +50,181 @@ export interface OperationFieldSupportMatrix { fieldByOperation: Partial>; } -export const IndexPatternDimensionPanelComponent = function IndexPatternDimensionPanel( - props: IndexPatternDimensionPanelProps -) { +type Props = Pick< + DatasourceDimensionDropProps, + 'layerId' | 'columnId' | 'state' | 'filterOperations' +>; + +// TODO: This code has historically been memoized, as a potentially performance +// sensitive task. If we can add memoization without breaking the behavior, we should. +const getOperationFieldSupportMatrix = (props: Props): OperationFieldSupportMatrix => { const layerId = props.layerId; const currentIndexPattern = props.state.indexPatterns[props.state.layers[layerId].indexPatternId]; - const operationFieldSupportMatrix = useMemo(() => { - const filteredOperationsByMetadata = getAvailableOperationsByMetadata( - currentIndexPattern - ).filter(operation => props.filterOperations(operation.operationMetaData)); - - const supportedOperationsByField: Partial> = {}; - const supportedFieldsByOperation: Partial> = {}; - - filteredOperationsByMetadata.forEach(({ operations }) => { - operations.forEach(operation => { - if (supportedOperationsByField[operation.field]) { - supportedOperationsByField[operation.field]!.push(operation.operationType); - } else { - supportedOperationsByField[operation.field] = [operation.operationType]; - } - - if (supportedFieldsByOperation[operation.operationType]) { - supportedFieldsByOperation[operation.operationType]!.push(operation.field); - } else { - supportedFieldsByOperation[operation.operationType] = [operation.field]; - } - }); + const filteredOperationsByMetadata = getAvailableOperationsByMetadata( + currentIndexPattern + ).filter(operation => props.filterOperations(operation.operationMetaData)); + + const supportedOperationsByField: Partial> = {}; + const supportedFieldsByOperation: Partial> = {}; + + filteredOperationsByMetadata.forEach(({ operations }) => { + operations.forEach(operation => { + if (supportedOperationsByField[operation.field]) { + supportedOperationsByField[operation.field]!.push(operation.operationType); + } else { + supportedOperationsByField[operation.field] = [operation.operationType]; + } + + if (supportedFieldsByOperation[operation.operationType]) { + supportedFieldsByOperation[operation.operationType]!.push(operation.field); + } else { + supportedFieldsByOperation[operation.operationType] = [operation.field]; + } }); - return { - operationByField: _.mapValues(supportedOperationsByField, _.uniq), - fieldByOperation: _.mapValues(supportedFieldsByOperation, _.uniq), - }; - }, [currentIndexPattern, props.filterOperations]); + }); + return { + operationByField: _.mapValues(supportedOperationsByField, _.uniq), + fieldByOperation: _.mapValues(supportedFieldsByOperation, _.uniq), + }; +}; - const selectedColumn: IndexPatternColumn | null = - props.state.layers[layerId].columns[props.columnId] || null; +export function canHandleDrop(props: DatasourceDimensionDropProps) { + const operationFieldSupportMatrix = getOperationFieldSupportMatrix(props); + + const { dragging } = props.dragDropContext; + const layerIndexPatternId = props.state.layers[props.layerId].indexPatternId; function hasOperationForField(field: IndexPatternField) { return Boolean(operationFieldSupportMatrix.operationByField[field.name]); } - function canHandleDrop() { - const { dragging } = props.dragDropContext; - const layerIndexPatternId = props.state.layers[props.layerId].indexPatternId; + return ( + isDraggedField(dragging) && + layerIndexPatternId === dragging.indexPatternId && + Boolean(hasOperationForField(dragging.field)) + ); +} + +export function onDrop( + props: DatasourceDimensionDropHandlerProps +): boolean { + const operationFieldSupportMatrix = getOperationFieldSupportMatrix(props); + const droppedItem = props.droppedItem; + + function hasOperationForField(field: IndexPatternField) { + return Boolean(operationFieldSupportMatrix.operationByField[field.name]); + } - return ( - isDraggedField(dragging) && - layerIndexPatternId === dragging.indexPatternId && - Boolean(hasOperationForField(dragging.field)) - ); + if (!isDraggedField(droppedItem) || !hasOperationForField(droppedItem.field)) { + // TODO: What do we do if we couldn't find a column? + return false; } + const operationsForNewField = + operationFieldSupportMatrix.operationByField[droppedItem.field.name]; + + const layerId = props.layerId; + const selectedColumn: IndexPatternColumn | null = + props.state.layers[layerId].columns[props.columnId] || null; + const currentIndexPattern = + props.state.indexPatterns[props.state.layers[layerId]?.indexPatternId]; + + // We need to check if dragging in a new field, was just a field change on the same + // index pattern and on the same operations (therefore checking if the new field supports + // our previous operation) + const hasFieldChanged = + selectedColumn && + hasField(selectedColumn) && + selectedColumn.sourceField !== droppedItem.field.name && + operationsForNewField && + operationsForNewField.includes(selectedColumn.operationType); + + // If only the field has changed use the onFieldChange method on the operation to get the + // new column, otherwise use the regular buildColumn to get a new column. + const newColumn = hasFieldChanged + ? changeField(selectedColumn, currentIndexPattern, droppedItem.field) + : buildColumn({ + op: operationsForNewField ? operationsForNewField[0] : undefined, + columns: props.state.layers[props.layerId].columns, + indexPattern: currentIndexPattern, + layerId, + suggestedPriority: props.suggestedPriority, + field: droppedItem.field, + previousColumn: selectedColumn, + }); + + trackUiEvent('drop_onto_dimension'); + const hasData = Object.values(props.state.layers).some(({ columns }) => columns.length); + trackUiEvent(hasData ? 'drop_non_empty' : 'drop_empty'); + + props.setState( + changeColumn({ + state: props.state, + layerId, + columnId: props.columnId, + newColumn, + // If the field has changed, the onFieldChange method needs to take care of everything including moving + // over params. If we create a new column above we want changeColumn to move over params. + keepParams: !hasFieldChanged, + }) + ); + + return true; +} + +export const IndexPatternDimensionTriggerComponent = function IndexPatternDimensionTrigger( + props: IndexPatternDimensionTriggerProps +) { + const layerId = props.layerId; + + const selectedColumn: IndexPatternColumn | null = + props.state.layers[layerId].columns[props.columnId] || null; + + const { columnId, uniqueLabel } = props; + if (!selectedColumn) { + return null; + } + return ( + { + props.togglePopover(); + }} + data-test-subj="lns-dimensionTrigger" + aria-label={i18n.translate('xpack.lens.configure.editConfig', { + defaultMessage: 'Edit configuration', + })} + title={i18n.translate('xpack.lens.configure.editConfig', { + defaultMessage: 'Edit configuration', + })} + > + {uniqueLabel} + + ); +}; + +export const IndexPatternDimensionEditorComponent = function IndexPatternDimensionPanel( + props: IndexPatternDimensionEditorProps +) { + const layerId = props.layerId; + const currentIndexPattern = + props.state.indexPatterns[props.state.layers[layerId]?.indexPatternId]; + const operationFieldSupportMatrix = getOperationFieldSupportMatrix(props); + + const selectedColumn: IndexPatternColumn | null = + props.state.layers[layerId].columns[props.columnId] || null; + return ( - - { - if (!isDraggedField(droppedItem) || !hasOperationForField(droppedItem.field)) { - // TODO: What do we do if we couldn't find a column? - return; - } - - const operationsForNewField = - operationFieldSupportMatrix.operationByField[droppedItem.field.name]; - - // We need to check if dragging in a new field, was just a field change on the same - // index pattern and on the same operations (therefore checking if the new field supports - // our previous operation) - const hasFieldChanged = - selectedColumn && - hasField(selectedColumn) && - selectedColumn.sourceField !== droppedItem.field.name && - operationsForNewField && - operationsForNewField.includes(selectedColumn.operationType); - - // If only the field has changed use the onFieldChange method on the operation to get the - // new column, otherwise use the regular buildColumn to get a new column. - const newColumn = hasFieldChanged - ? changeField(selectedColumn, currentIndexPattern, droppedItem.field) - : buildColumn({ - op: operationsForNewField ? operationsForNewField[0] : undefined, - columns: props.state.layers[props.layerId].columns, - indexPattern: currentIndexPattern, - layerId, - suggestedPriority: props.suggestedPriority, - field: droppedItem.field, - previousColumn: selectedColumn, - }); - - trackUiEvent('drop_onto_dimension'); - const hasData = Object.values(props.state.layers).some(({ columns }) => columns.length); - trackUiEvent(hasData ? 'drop_non_empty' : 'drop_empty'); - - props.setState( - changeColumn({ - state: props.state, - layerId, - columnId: props.columnId, - newColumn, - // If the field has changed, the onFieldChange method needs to take care of everything including moving - // over params. If we create a new column above we want changeColumn to move over params. - keepParams: !hasFieldChanged, - }) - ); - }} - > - - {selectedColumn && ( - { - trackUiEvent('indexpattern_dimension_removed'); - props.setState( - deleteColumn({ - state: props.state, - layerId, - columnId: props.columnId, - }) - ); - if (props.onRemove) { - props.onRemove(props.columnId); - } - }} - /> - )} - - + ); }; -export const IndexPatternDimensionPanel = memo(IndexPatternDimensionPanelComponent); +export const IndexPatternDimensionTrigger = memo(IndexPatternDimensionTriggerComponent); +export const IndexPatternDimensionEditor = memo(IndexPatternDimensionEditorComponent); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx index 056a8d177dfe8..e26c338b6e240 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx @@ -7,22 +7,18 @@ import _ from 'lodash'; import React, { useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiPopover, EuiFlexItem, EuiFlexGroup, EuiSideNav, EuiCallOut, EuiFormRow, EuiFieldText, - EuiLink, - EuiButtonEmpty, EuiSpacer, } from '@elastic/eui'; import classNames from 'classnames'; import { IndexPatternColumn, OperationType } from '../indexpattern'; -import { IndexPatternDimensionPanelProps, OperationFieldSupportMatrix } from './dimension_panel'; +import { IndexPatternDimensionEditorProps, OperationFieldSupportMatrix } from './dimension_panel'; import { operationDefinitionMap, getOperationDisplay, @@ -39,7 +35,7 @@ import { FormatSelector } from './format_selector'; const operationPanels = getOperationDisplay(); -export interface PopoverEditorProps extends IndexPatternDimensionPanelProps { +export interface PopoverEditorProps extends IndexPatternDimensionEditorProps { selectedColumn?: IndexPatternColumn; operationFieldSupportMatrix: OperationFieldSupportMatrix; currentIndexPattern: IndexPattern; @@ -67,11 +63,9 @@ export function PopoverEditor(props: PopoverEditorProps) { setState, layerId, currentIndexPattern, - uniqueLabel, hideGrouping, } = props; const { operationByField, fieldByOperation } = operationFieldSupportMatrix; - const [isPopoverOpen, setPopoverOpen] = useState(false); const [ incompatibleSelectedOperationType, setInvalidOperationType, @@ -115,14 +109,14 @@ export function PopoverEditor(props: PopoverEditorProps) { items: getOperationTypes().map(({ operationType, compatibleWithCurrentField }) => ({ name: operationPanels[operationType].displayName, id: operationType as string, - className: classNames('lnsPopoverEditor__operation', { - 'lnsPopoverEditor__operation--selected': Boolean( + className: classNames('lnsIndexPatternDimensionEditor__operation', { + 'lnsIndexPatternDimensionEditor__operation--selected': Boolean( incompatibleSelectedOperationType === operationType || (!incompatibleSelectedOperationType && selectedColumn && selectedColumn.operationType === operationType) ), - 'lnsPopoverEditor__operation--incompatible': !compatibleWithCurrentField, + 'lnsIndexPatternDimensionEditor__operation--incompatible': !compatibleWithCurrentField, }), 'data-test-subj': `lns-indexPatternDimension${ compatibleWithCurrentField ? '' : 'Incompatible' @@ -188,246 +182,193 @@ export function PopoverEditor(props: PopoverEditorProps) { } return ( - { - setPopoverOpen(!isPopoverOpen); +
+ + + { + setState( + deleteColumn({ + state, + layerId, + columnId, + }) + ); }} - data-test-subj="indexPattern-configure-dimension" - aria-label={i18n.translate('xpack.lens.configure.editConfig', { - defaultMessage: 'Edit configuration', - })} - title={i18n.translate('xpack.lens.configure.editConfig', { - defaultMessage: 'Edit configuration', - })} - > - {uniqueLabel} - - ) : ( - <> - setPopoverOpen(!isPopoverOpen)} - size="xs" - > - - - - ) - } - isOpen={isPopoverOpen} - closePopover={() => { - setPopoverOpen(false); - setInvalidOperationType(null); - }} - anchorPosition="leftUp" - withTitle - panelPaddingSize="s" - > - {isPopoverOpen && ( - - - { - setState( - deleteColumn({ - state, - layerId, - columnId, - }) - ); - }} - onChoose={choice => { - let column: IndexPatternColumn; - if ( - !incompatibleSelectedOperationType && - selectedColumn && - 'field' in choice && - choice.operationType === selectedColumn.operationType - ) { - // If we just changed the field are not in an error state and the operation didn't change, - // we use the operations onFieldChange method to calculate the new column. - column = changeField(selectedColumn, currentIndexPattern, fieldMap[choice.field]); - } else { - // Otherwise we'll use the buildColumn method to calculate a new column - const compatibleOperations = - ('field' in choice && - operationFieldSupportMatrix.operationByField[choice.field]) || - []; - let operation; - if (compatibleOperations.length > 0) { - operation = - incompatibleSelectedOperationType && - compatibleOperations.includes(incompatibleSelectedOperationType) - ? incompatibleSelectedOperationType - : compatibleOperations[0]; - } else if ('field' in choice) { - operation = choice.operationType; - } - column = buildColumn({ - columns: props.state.layers[props.layerId].columns, - field: fieldMap[choice.field], - indexPattern: currentIndexPattern, - layerId: props.layerId, - suggestedPriority: props.suggestedPriority, - op: operation as OperationType, - previousColumn: selectedColumn, - }); + onChoose={choice => { + let column: IndexPatternColumn; + if ( + !incompatibleSelectedOperationType && + selectedColumn && + 'field' in choice && + choice.operationType === selectedColumn.operationType + ) { + // If we just changed the field are not in an error state and the operation didn't change, + // we use the operations onFieldChange method to calculate the new column. + column = changeField(selectedColumn, currentIndexPattern, fieldMap[choice.field]); + } else { + // Otherwise we'll use the buildColumn method to calculate a new column + const compatibleOperations = + ('field' in choice && + operationFieldSupportMatrix.operationByField[choice.field]) || + []; + let operation; + if (compatibleOperations.length > 0) { + operation = + incompatibleSelectedOperationType && + compatibleOperations.includes(incompatibleSelectedOperationType) + ? incompatibleSelectedOperationType + : compatibleOperations[0]; + } else if ('field' in choice) { + operation = choice.operationType; } + column = buildColumn({ + columns: props.state.layers[props.layerId].columns, + field: fieldMap[choice.field], + indexPattern: currentIndexPattern, + layerId: props.layerId, + suggestedPriority: props.suggestedPriority, + op: operation as OperationType, + previousColumn: selectedColumn, + }); + } - setState( - changeColumn({ - state, - layerId, - columnId, - newColumn: column, - keepParams: false, - }) - ); - setInvalidOperationType(null); - }} - /> - - - - - - - - {incompatibleSelectedOperationType && selectedColumn && ( - - )} - {incompatibleSelectedOperationType && !selectedColumn && ( - - )} - {!incompatibleSelectedOperationType && ParamEditor && ( - <> - - - - )} - {!incompatibleSelectedOperationType && selectedColumn && ( - - { - setState( - changeColumn({ - state, - layerId, - columnId, - newColumn: { - ...selectedColumn, - label: e.target.value, - }, - }) - ); - }} - /> - - )} - - {!hideGrouping && ( - { - setState({ - ...state, - layers: { - ...state.layers, - [props.layerId]: { - ...state.layers[props.layerId], - columnOrder, - }, - }, - }); - }} + setState( + changeColumn({ + state, + layerId, + columnId, + newColumn: column, + keepParams: false, + }) + ); + setInvalidOperationType(null); + }} + /> + + + + + + + + {incompatibleSelectedOperationType && selectedColumn && ( + + )} + {incompatibleSelectedOperationType && !selectedColumn && ( + + )} + {!incompatibleSelectedOperationType && ParamEditor && ( + <> + - )} - - {selectedColumn && selectedColumn.dataType === 'number' ? ( - { + + + )} + {!incompatibleSelectedOperationType && selectedColumn && ( + + { setState( - updateColumnParam({ + changeColumn({ state, layerId, - currentColumn: selectedColumn, - paramName: 'format', - value: newFormat, + columnId, + newColumn: { + ...selectedColumn, + label: e.target.value, + }, }) ); }} /> - ) : null} - - - - - )} - + + )} + + {!hideGrouping && ( + { + setState({ + ...state, + layers: { + ...state.layers, + [props.layerId]: { + ...state.layers[props.layerId], + columnOrder, + }, + }, + }); + }} + /> + )} + + {selectedColumn && selectedColumn.dataType === 'number' ? ( + { + setState( + updateColumnParam({ + state, + layerId, + currentColumn: selectedColumn, + paramName: 'format', + value: newFormat, + }) + ); + }} + /> + ) : null} + + + + +
); } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 25121eec30f2a..76e59a170a9e9 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -408,7 +408,6 @@ describe('IndexPattern Data Source', () => { const initialState = stateFromPersistedState(persistedState); publicAPI = indexPatternDatasource.getPublicAPI({ state: initialState, - setState: () => {}, layerId: 'first', dateRange: { fromDate: 'now-30d', diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 00f52d6a1747f..9c2a9c9bf4a09 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -12,7 +12,8 @@ import { CoreStart } from 'src/core/public'; import { i18n } from '@kbn/i18n'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { - DatasourceDimensionPanelProps, + DatasourceDimensionEditorProps, + DatasourceDimensionTriggerProps, DatasourceDataPanelProps, Operation, DatasourceLayerPanelProps, @@ -20,7 +21,12 @@ import { } from '../types'; import { loadInitialState, changeIndexPattern, changeLayerIndexPattern } from './loader'; import { toExpression } from './to_expression'; -import { IndexPatternDimensionPanel } from './dimension_panel'; +import { + IndexPatternDimensionTrigger, + IndexPatternDimensionEditor, + canHandleDrop, + onDrop, +} from './dimension_panel'; import { IndexPatternDataPanel } from './datapanel'; import { getDatasourceSuggestionsForField, @@ -38,6 +44,7 @@ import { } from './types'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { Plugin as DataPlugin } from '../../../../../../src/plugins/data/public'; +import { deleteColumn } from './state_helpers'; import { Datasource, StateSetter } from '..'; export { OperationType, IndexPatternColumn } from './operations'; @@ -80,6 +87,9 @@ export function uniqueLabels(layers: Record) { }; Object.values(layers).forEach(layer => { + if (!layer.columns) { + return; + } Object.entries(layer.columns).forEach(([columnId, column]) => { columnLabelMap[columnId] = makeUnique(column.label); }); @@ -156,6 +166,14 @@ export function getIndexPatternDatasource({ return Object.keys(state.layers); }, + removeColumn({ prevState, layerId, columnId }) { + return deleteColumn({ + state: prevState, + layerId, + columnId, + }); + }, + toExpression, getMetaData(state: IndexPatternPrivateState) { @@ -198,15 +216,97 @@ export function getIndexPatternDatasource({ ); }, - getPublicAPI({ - state, - setState, - layerId, - dateRange, - }: PublicAPIProps) { + renderDimensionTrigger: ( + domElement: Element, + props: DatasourceDimensionTriggerProps + ) => { + const columnLabelMap = uniqueLabels(props.state.layers); + + render( + + + + + , + domElement + ); + }, + + renderDimensionEditor: ( + domElement: Element, + props: DatasourceDimensionEditorProps + ) => { + const columnLabelMap = uniqueLabels(props.state.layers); + + render( + + + + + , + domElement + ); + }, + + renderLayerPanel: ( + domElement: Element, + props: DatasourceLayerPanelProps + ) => { + render( + { + changeLayerIndexPattern({ + savedObjectsClient, + indexPatternId, + setState: props.setState, + state: props.state, + layerId: props.layerId, + onError: onIndexPatternLoadError, + replaceIfPossible: true, + }); + }} + {...props} + />, + domElement + ); + }, + + canHandleDrop, + onDrop, + + getPublicAPI({ state, layerId }: PublicAPIProps) { const columnLabelMap = uniqueLabels(state.layers); return { + datasourceId: 'indexpattern', + getTableSpec: () => { return state.layers[layerId].columnOrder.map(colId => ({ columnId: colId })); }, @@ -218,58 +318,6 @@ export function getIndexPatternDatasource({ } return null; }, - renderDimensionPanel: (domElement: Element, props: DatasourceDimensionPanelProps) => { - render( - - - - - , - domElement - ); - }, - - renderLayerPanel: (domElement: Element, props: DatasourceLayerPanelProps) => { - render( - { - changeLayerIndexPattern({ - savedObjectsClient, - indexPatternId, - setState, - state, - layerId: props.layerId, - onError: onIndexPatternLoadError, - replaceIfPossible: true, - }); - }} - {...props} - />, - domElement - ); - }, }; }, getDatasourceSuggestionsForField(state, draggedField) { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx index af7afb9cf9342..219a6d935e436 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx @@ -178,6 +178,7 @@ describe('Layer Data Panel', () => { defaultProps = { layerId: 'first', state: initialState, + setState: jest.fn(), onChangeIndexPattern: jest.fn(async () => {}), }; }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.tsx index ae346ecc72cbc..eea00d52a77f9 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_datasource/layerpanel.tsx @@ -11,7 +11,8 @@ import { DatasourceLayerPanelProps } from '../types'; import { IndexPatternPrivateState } from './types'; import { ChangeIndexPattern } from './change_indexpattern'; -export interface IndexPatternLayerPanelProps extends DatasourceLayerPanelProps { +export interface IndexPatternLayerPanelProps + extends DatasourceLayerPanelProps { state: IndexPatternPrivateState; onChangeIndexPattern: (newId: string) => void; } diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.test.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.test.tsx deleted file mode 100644 index eac35f82a50fa..0000000000000 --- a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { ReactWrapper } from 'enzyme'; -import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; -import { MetricConfigPanel } from './metric_config_panel'; -import { DatasourceDimensionPanelProps, Operation, DatasourcePublicAPI } from '../types'; -import { State } from './types'; -import { NativeRendererProps } from '../native_renderer'; -import { createMockFramePublicAPI, createMockDatasource } from '../editor_frame_service/mocks'; - -describe('MetricConfigPanel', () => { - const dragDropContext = { dragging: undefined, setDragging: jest.fn() }; - - function mockDatasource(): DatasourcePublicAPI { - return createMockDatasource().publicAPIMock; - } - - function testState(): State { - return { - accessor: 'foo', - layerId: 'bar', - }; - } - - function testSubj(component: ReactWrapper, subj: string) { - return component - .find(`[data-test-subj="${subj}"]`) - .first() - .props(); - } - - test('the value dimension panel only accepts singular numeric operations', () => { - const state = testState(); - const component = mount( - - ); - - const panel = testSubj(component, 'lns_metric_valueDimensionPanel'); - const nativeProps = (panel as NativeRendererProps).nativeProps; - const { columnId, filterOperations } = nativeProps; - const exampleOperation: Operation = { - dataType: 'number', - isBucketed: false, - label: 'bar', - }; - const ops: Operation[] = [ - { ...exampleOperation, dataType: 'number' }, - { ...exampleOperation, dataType: 'string' }, - { ...exampleOperation, dataType: 'boolean' }, - { ...exampleOperation, dataType: 'date' }, - ]; - expect(columnId).toEqual('shazm'); - expect(ops.filter(filterOperations)).toEqual([{ ...exampleOperation, dataType: 'number' }]); - }); -}); diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.tsx deleted file mode 100644 index 16e24f247fb68..0000000000000 --- a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_config_panel.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFormRow } from '@elastic/eui'; -import { State } from './types'; -import { VisualizationLayerConfigProps, OperationMetadata } from '../types'; -import { NativeRenderer } from '../native_renderer'; - -const isMetric = (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number'; - -export function MetricConfigPanel(props: VisualizationLayerConfigProps) { - const { state, frame, layerId } = props; - const datasource = frame.datasourceLayers[layerId]; - - return ( - - - - ); -} diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_expression.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_expression.tsx index 66ed963002f59..4d979a766cd2b 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_expression.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_expression.tsx @@ -91,6 +91,11 @@ export function MetricChart({ const { title, accessor, mode } = args; let value = '-'; const firstTable = Object.values(data.tables)[0]; + if (!accessor) { + return ( + + ); + } if (firstTable) { const column = firstTable.columns[0]; diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.test.ts b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.test.ts index 88964b95c2ac7..276f24433c670 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.test.ts +++ b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.test.ts @@ -24,8 +24,8 @@ function mockFrame(): FramePublicAPI { ...createMockFramePublicAPI(), addNewLayer: () => 'l42', datasourceLayers: { - l1: createMockDatasource().publicAPIMock, - l42: createMockDatasource().publicAPIMock, + l1: createMockDatasource('l1').publicAPIMock, + l42: createMockDatasource('l42').publicAPIMock, }, }; } @@ -36,10 +36,10 @@ describe('metric_visualization', () => { (generateId as jest.Mock).mockReturnValueOnce('test-id1'); const initialState = metricVisualization.initialize(mockFrame()); - expect(initialState.accessor).toBeDefined(); + expect(initialState.accessor).not.toBeDefined(); expect(initialState).toMatchInlineSnapshot(` Object { - "accessor": "test-id1", + "accessor": undefined, "layerId": "l42", } `); @@ -60,7 +60,7 @@ describe('metric_visualization', () => { it('returns a clean layer', () => { (generateId as jest.Mock).mockReturnValueOnce('test-id1'); expect(metricVisualization.clearLayer(exampleState(), 'l1')).toEqual({ - accessor: 'test-id1', + accessor: undefined, layerId: 'l1', }); }); @@ -72,10 +72,47 @@ describe('metric_visualization', () => { }); }); + describe('#setDimension', () => { + it('sets the accessor', () => { + expect( + metricVisualization.setDimension({ + prevState: { + accessor: undefined, + layerId: 'l1', + }, + layerId: 'l1', + groupId: '', + columnId: 'newDimension', + }) + ).toEqual({ + accessor: 'newDimension', + layerId: 'l1', + }); + }); + }); + + describe('#removeDimension', () => { + it('removes the accessor', () => { + expect( + metricVisualization.removeDimension({ + prevState: { + accessor: 'a', + layerId: 'l1', + }, + layerId: 'l1', + columnId: 'a', + }) + ).toEqual({ + accessor: undefined, + layerId: 'l1', + }); + }); + }); + describe('#toExpression', () => { it('should map to a valid AST', () => { const datasource: DatasourcePublicAPI = { - ...createMockDatasource().publicAPIMock, + ...createMockDatasource('l1').publicAPIMock, getOperationForColumnId(_: string) { return { id: 'a', diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.tsx index 6714c05787837..44256df5aed6d 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization/metric_visualization.tsx @@ -4,23 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { render } from 'react-dom'; -import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { Ast } from '@kbn/interpreter/target/common'; import { getSuggestions } from './metric_suggestions'; -import { MetricConfigPanel } from './metric_config_panel'; -import { Visualization, FramePublicAPI } from '../types'; +import { Visualization, FramePublicAPI, OperationMetadata } from '../types'; import { State, PersistableState } from './types'; -import { generateId } from '../id_generator'; import chartMetricSVG from '../assets/chart_metric.svg'; const toExpression = ( state: State, frame: FramePublicAPI, mode: 'reduced' | 'full' = 'full' -): Ast => { +): Ast | null => { + if (!state.accessor) { + return null; + } + const [datasource] = Object.values(frame.datasourceLayers); const operation = datasource && datasource.getOperationForColumnId(state.accessor); @@ -57,7 +56,7 @@ export const metricVisualization: Visualization = { clearLayer(state) { return { ...state, - accessor: generateId(), + accessor: undefined, }; }, @@ -80,22 +79,37 @@ export const metricVisualization: Visualization = { return ( state || { layerId: frame.addNewLayer(), - accessor: generateId(), + accessor: undefined, } ); }, getPersistableState: state => state, - renderLayerConfigPanel: (domElement, props) => - render( - - - , - domElement - ), + getConfiguration(props) { + return { + groups: [ + { + groupId: 'metric', + groupLabel: i18n.translate('xpack.lens.metric.label', { defaultMessage: 'Metric' }), + layerId: props.state.layerId, + accessors: props.state.accessor ? [props.state.accessor] : [], + supportsMoreColumns: false, + filterOperations: (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number', + }, + ], + }; + }, toExpression, toPreviewExpression: (state: State, frame: FramePublicAPI) => toExpression(state, frame, 'reduced'), + + setDimension({ prevState, columnId }) { + return { ...prevState, accessor: columnId }; + }, + + removeDimension({ prevState }) { + return { ...prevState, accessor: undefined }; + }, }; diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization/types.ts b/x-pack/legacy/plugins/lens/public/metric_visualization/types.ts index 6348d80b15e2f..53fc103934255 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization/types.ts +++ b/x-pack/legacy/plugins/lens/public/metric_visualization/types.ts @@ -6,7 +6,7 @@ export interface State { layerId: string; - accessor: string; + accessor?: string; } export interface MetricConfig extends State { diff --git a/x-pack/legacy/plugins/lens/public/multi_column_editor/index.ts b/x-pack/legacy/plugins/lens/public/multi_column_editor/index.ts deleted file mode 100644 index 92bad0dc90766..0000000000000 --- a/x-pack/legacy/plugins/lens/public/multi_column_editor/index.ts +++ /dev/null @@ -1,7 +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. - */ - -export * from './multi_column_editor'; diff --git a/x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.test.tsx b/x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.test.tsx deleted file mode 100644 index 38f48c9cdaf72..0000000000000 --- a/x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.test.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { createMockDatasource } from '../editor_frame_service/mocks'; -import { MultiColumnEditor } from './multi_column_editor'; -import { mount } from 'enzyme'; - -jest.useFakeTimers(); - -describe('MultiColumnEditor', () => { - it('should add a trailing accessor if the accessor list is empty', () => { - const onAdd = jest.fn(); - mount( - true} - layerId="foo" - onAdd={onAdd} - onRemove={jest.fn()} - testSubj="bar" - /> - ); - - expect(onAdd).toHaveBeenCalledTimes(0); - - jest.runAllTimers(); - - expect(onAdd).toHaveBeenCalledTimes(1); - }); - - it('should add a trailing accessor if the last accessor is configured', () => { - const onAdd = jest.fn(); - mount( - true} - layerId="foo" - onAdd={onAdd} - onRemove={jest.fn()} - testSubj="bar" - /> - ); - - expect(onAdd).toHaveBeenCalledTimes(0); - - jest.runAllTimers(); - - expect(onAdd).toHaveBeenCalledTimes(1); - }); -}); diff --git a/x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.tsx b/x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.tsx deleted file mode 100644 index 422f1dcf60f3c..0000000000000 --- a/x-pack/legacy/plugins/lens/public/multi_column_editor/multi_column_editor.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect } from 'react'; -import { NativeRenderer } from '../native_renderer'; -import { DatasourcePublicAPI, OperationMetadata } from '../types'; -import { DragContextState } from '../drag_drop'; - -interface Props { - accessors: string[]; - datasource: DatasourcePublicAPI; - dragDropContext: DragContextState; - onRemove: (accessor: string) => void; - onAdd: () => void; - filterOperations: (op: OperationMetadata) => boolean; - suggestedPriority?: 0 | 1 | 2 | undefined; - testSubj: string; - layerId: string; -} - -export function MultiColumnEditor({ - accessors, - datasource, - dragDropContext, - onRemove, - onAdd, - filterOperations, - suggestedPriority, - testSubj, - layerId, -}: Props) { - const lastOperation = datasource.getOperationForColumnId(accessors[accessors.length - 1]); - - useEffect(() => { - if (accessors.length === 0 || lastOperation !== null) { - setTimeout(onAdd); - } - }, [lastOperation]); - - return ( - <> - {accessors.map(accessor => ( -
- -
- ))} - - ); -} diff --git a/x-pack/legacy/plugins/lens/public/plugin.tsx b/x-pack/legacy/plugins/lens/public/plugin.tsx index cc029fee49d1d..c74653c70703c 100644 --- a/x-pack/legacy/plugins/lens/public/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/plugin.tsx @@ -114,7 +114,7 @@ export class LensPlugin { const savedObjectsClient = coreStart.savedObjects.client; addHelpMenuToAppChrome(coreStart.chrome); - const instance = await this.createEditorFrame!({}); + const instance = await this.createEditorFrame!(); setReportManager( new LensReportManager({ diff --git a/x-pack/legacy/plugins/lens/public/types.ts b/x-pack/legacy/plugins/lens/public/types.ts index b7983eeb8dbb8..c897979b06cfb 100644 --- a/x-pack/legacy/plugins/lens/public/types.ts +++ b/x-pack/legacy/plugins/lens/public/types.ts @@ -13,14 +13,10 @@ import { Document } from './persistence'; import { DateRange } from '../../../../plugins/lens/common'; import { Query, Filter, SavedQuery } from '../../../../../src/plugins/data/public'; -// eslint-disable-next-line -export interface EditorFrameOptions {} - export type ErrorCallback = (e: { message: string }) => void; export interface PublicAPIProps { state: T; - setState: StateSetter; layerId: string; dateRange: DateRange; } @@ -34,6 +30,7 @@ export interface EditorFrameProps { savedQuery?: SavedQuery; // Frame loader (app or embeddable) is expected to call this when it loads and updates + // This should be replaced with a top-down state onChange: (newState: { filterableIndexPatterns: DatasourceMetaData['filterableIndexPatterns']; doc: Document; @@ -53,7 +50,7 @@ export interface EditorFrameSetup { } export interface EditorFrameStart { - createInstance: (options: EditorFrameOptions) => Promise; + createInstance: () => Promise; } // Hints the default nesting to the data source. 0 is the highest priority @@ -138,8 +135,14 @@ export interface Datasource { removeLayer: (state: T, layerId: string) => T; clearLayer: (state: T, layerId: string) => T; getLayers: (state: T) => string[]; + removeColumn: (props: { prevState: T; layerId: string; columnId: string }) => T; renderDataPanel: (domElement: Element, props: DatasourceDataPanelProps) => void; + renderDimensionTrigger: (domElement: Element, props: DatasourceDimensionTriggerProps) => void; + renderDimensionEditor: (domElement: Element, props: DatasourceDimensionEditorProps) => void; + renderLayerPanel: (domElement: Element, props: DatasourceLayerPanelProps) => void; + canHandleDrop: (props: DatasourceDimensionDropProps) => boolean; + onDrop: (props: DatasourceDimensionDropHandlerProps) => boolean; toExpression: (state: T, layerId: string) => Ast | string | null; @@ -155,22 +158,11 @@ export interface Datasource { * This is an API provided to visualizations by the frame, which calls the publicAPI on the datasource */ export interface DatasourcePublicAPI { - getTableSpec: () => TableSpec; + datasourceId: string; + getTableSpec: () => Array<{ columnId: string }>; getOperationForColumnId: (columnId: string) => Operation | null; - - // Render can be called many times - renderDimensionPanel: (domElement: Element, props: DatasourceDimensionPanelProps) => void; - renderLayerPanel: (domElement: Element, props: DatasourceLayerPanelProps) => void; } -export interface TableSpecColumn { - // Column IDs are the keys for internal state in data sources and visualizations - columnId: string; -} - -// TableSpec is managed by visualizations -export type TableSpec = TableSpecColumn[]; - export interface DatasourceDataPanelProps { state: T; dragDropContext: DragContextState; @@ -181,31 +173,61 @@ export interface DatasourceDataPanelProps { filters: Filter[]; } -// The only way a visualization has to restrict the query building -export interface DatasourceDimensionPanelProps { - layerId: string; - columnId: string; - - dragDropContext: DragContextState; - - // Visualizations can restrict operations based on their own rules +interface SharedDimensionProps { + /** Visualizations can restrict operations based on their own rules. + * For example, limiting to only bucketed or only numeric operations. + */ filterOperations: (operation: OperationMetadata) => boolean; - // Visualizations can hint at the role this dimension would play, which - // affects the default ordering of the query + /** Visualizations can hint at the role this dimension would play, which + * affects the default ordering of the query + */ suggestedPriority?: DimensionPriority; - onRemove?: (accessor: string) => void; - // Some dimension editors will allow users to change the operation grouping - // from the panel, and this lets the visualization hint that it doesn't want - // users to have that level of control + /** Some dimension editors will allow users to change the operation grouping + * from the panel, and this lets the visualization hint that it doesn't want + * users to have that level of control + */ hideGrouping?: boolean; } -export interface DatasourceLayerPanelProps { +export type DatasourceDimensionProps = SharedDimensionProps & { layerId: string; + columnId: string; + onRemove?: (accessor: string) => void; + state: T; +}; + +// The only way a visualization has to restrict the query building +export type DatasourceDimensionEditorProps = DatasourceDimensionProps & { + setState: StateSetter; + core: Pick; + dateRange: DateRange; +}; + +export type DatasourceDimensionTriggerProps = DatasourceDimensionProps & { + dragDropContext: DragContextState; + togglePopover: () => void; +}; + +export interface DatasourceLayerPanelProps { + layerId: string; + state: T; + setState: StateSetter; } +export type DatasourceDimensionDropProps = SharedDimensionProps & { + layerId: string; + columnId: string; + state: T; + setState: StateSetter; + dragDropContext: DragContextState; +}; + +export type DatasourceDimensionDropHandlerProps = DatasourceDimensionDropProps & { + droppedItem: unknown; +}; + export type DataType = 'document' | 'string' | 'number' | 'date' | 'boolean' | 'ip'; // An operation represents a column in a table, not any information @@ -239,12 +261,32 @@ export interface LensMultiTable { }; } -export interface VisualizationLayerConfigProps { +export interface VisualizationConfigProps { layerId: string; - dragDropContext: DragContextState; frame: FramePublicAPI; state: T; +} + +export type VisualizationLayerWidgetProps = VisualizationConfigProps & { setState: (newState: T) => void; +}; + +type VisualizationDimensionGroupConfig = SharedDimensionProps & { + groupLabel: string; + + /** ID is passed back to visualization. For example, `x` */ + groupId: string; + accessors: string[]; + supportsMoreColumns: boolean; + /** If required, a warning will appear if accessors are empty */ + required?: boolean; + dataTestSubj?: string; +}; + +interface VisualizationDimensionChangeProps { + layerId: string; + columnId: string; + prevState: T; } /** @@ -329,16 +371,18 @@ export interface Visualization { visualizationTypes: VisualizationType[]; getLayerIds: (state: T) => string[]; - clearLayer: (state: T, layerId: string) => T; - removeLayer?: (state: T, layerId: string) => T; - appendLayer?: (state: T, layerId: string) => T; + // Layer context menu is used by visualizations for styling the entire layer + // For example, the XY visualization uses this to have multiple chart types getLayerContextMenuIcon?: (opts: { state: T; layerId: string }) => IconType | undefined; + renderLayerContextMenu?: (domElement: Element, props: VisualizationLayerWidgetProps) => void; - renderLayerContextMenu?: (domElement: Element, props: VisualizationLayerConfigProps) => void; + getConfiguration: ( + props: VisualizationConfigProps + ) => { groups: VisualizationDimensionGroupConfig[] }; getDescription: ( state: T @@ -354,7 +398,13 @@ export interface Visualization { getPersistableState: (state: T) => P; - renderLayerConfigPanel: (domElement: Element, props: VisualizationLayerConfigProps) => void; + // Actions triggered by the frame which tell the datasource that a dimension is being changed + setDimension: ( + props: VisualizationDimensionChangeProps & { + groupId: string; + } + ) => T; + removeDimension: (props: VisualizationDimensionChangeProps) => T; toExpression: (state: T, frame: FramePublicAPI) => Ast | string | null; diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/xy_visualization.test.ts.snap b/x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap similarity index 96% rename from x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/xy_visualization.test.ts.snap rename to x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index 76af8328673ad..6b68679bfd4ec 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/xy_visualization.test.ts.snap +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`xy_visualization #toExpression should map to a valid AST 1`] = ` +exports[`#toExpression should map to a valid AST 1`] = ` Object { "chain": Array [ Object { diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.test.ts new file mode 100644 index 0000000000000..6bc379ea33bca --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -0,0 +1,133 @@ +/* + * 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 { Ast } from '@kbn/interpreter/target/common'; +import { Position } from '@elastic/charts'; +import { xyVisualization } from './xy_visualization'; +import { Operation } from '../types'; +import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; + +describe('#toExpression', () => { + let mockDatasource: ReturnType; + let frame: ReturnType; + + beforeEach(() => { + frame = createMockFramePublicAPI(); + mockDatasource = createMockDatasource('testDatasource'); + + mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'd' }, + { columnId: 'a' }, + { columnId: 'b' }, + { columnId: 'c' }, + ]); + + mockDatasource.publicAPIMock.getOperationForColumnId.mockImplementation(col => { + return { label: `col_${col}`, dataType: 'number' } as Operation; + }); + + frame.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + }); + + it('should map to a valid AST', () => { + expect( + xyVisualization.toExpression( + { + legend: { position: Position.Bottom, isVisible: true }, + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + seriesType: 'area', + splitAccessor: 'd', + xAccessor: 'a', + accessors: ['b', 'c'], + }, + ], + }, + frame + ) + ).toMatchSnapshot(); + }); + + it('should not generate an expression when missing x', () => { + expect( + xyVisualization.toExpression( + { + legend: { position: Position.Bottom, isVisible: true }, + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + seriesType: 'area', + splitAccessor: undefined, + xAccessor: undefined, + accessors: ['a'], + }, + ], + }, + frame + ) + ).toBeNull(); + }); + + it('should not generate an expression when missing y', () => { + expect( + xyVisualization.toExpression( + { + legend: { position: Position.Bottom, isVisible: true }, + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + seriesType: 'area', + splitAccessor: undefined, + xAccessor: 'a', + accessors: [], + }, + ], + }, + frame + ) + ).toBeNull(); + }); + + it('should default to labeling all columns with their column label', () => { + const expression = xyVisualization.toExpression( + { + legend: { position: Position.Bottom, isVisible: true }, + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + seriesType: 'area', + splitAccessor: 'd', + xAccessor: 'a', + accessors: ['b', 'c'], + }, + ], + }, + frame + )! as Ast; + + expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b'); + expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('c'); + expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('d'); + expect(expression.chain[0].arguments.xTitle).toEqual(['col_a']); + expect(expression.chain[0].arguments.yTitle).toEqual(['col_b']); + expect( + (expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.columnToLabel + ).toEqual([ + JSON.stringify({ + b: 'col_b', + c: 'col_c', + d: 'col_d', + }), + ]); + }); +}); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.ts index f0e932d14f281..9b068b0ca5ef0 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/to_expression.ts @@ -9,6 +9,10 @@ import { ScaleType } from '@elastic/charts'; import { State, LayerConfig } from './types'; import { FramePublicAPI, OperationMetadata } from '../types'; +interface ValidLayer extends LayerConfig { + xAccessor: NonNullable; +} + function xyTitles(layer: LayerConfig, frame: FramePublicAPI) { const defaults = { xTitle: 'x', @@ -22,8 +26,8 @@ function xyTitles(layer: LayerConfig, frame: FramePublicAPI) { if (!datasource) { return defaults; } - const x = datasource.getOperationForColumnId(layer.xAccessor); - const y = datasource.getOperationForColumnId(layer.accessors[0]); + const x = layer.xAccessor ? datasource.getOperationForColumnId(layer.xAccessor) : null; + const y = layer.accessors[0] ? datasource.getOperationForColumnId(layer.accessors[0]) : null; return { xTitle: x ? x.label : defaults.xTitle, @@ -36,26 +40,6 @@ export const toExpression = (state: State, frame: FramePublicAPI): Ast | null => return null; } - const stateWithValidAccessors = { - ...state, - layers: state.layers.map(layer => { - const datasource = frame.datasourceLayers[layer.layerId]; - - const newLayer = { ...layer }; - - if (!datasource.getOperationForColumnId(layer.splitAccessor)) { - delete newLayer.splitAccessor; - } - - return { - ...newLayer, - accessors: layer.accessors.filter(accessor => - Boolean(datasource.getOperationForColumnId(accessor)) - ), - }; - }), - }; - const metadata: Record> = {}; state.layers.forEach(layer => { metadata[layer.layerId] = {}; @@ -68,12 +52,7 @@ export const toExpression = (state: State, frame: FramePublicAPI): Ast | null => }); }); - return buildExpression( - stateWithValidAccessors, - metadata, - frame, - xyTitles(state.layers[0], frame) - ); + return buildExpression(state, metadata, frame, xyTitles(state.layers[0], frame)); }; export function toPreviewExpression(state: State, frame: FramePublicAPI) { @@ -122,82 +101,94 @@ export const buildExpression = ( metadata: Record>, frame?: FramePublicAPI, { xTitle, yTitle }: { xTitle: string; yTitle: string } = { xTitle: '', yTitle: '' } -): Ast => ({ - type: 'expression', - chain: [ - { - type: 'function', - function: 'lens_xy_chart', - arguments: { - xTitle: [xTitle], - yTitle: [yTitle], - legend: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'lens_xy_legendConfig', - arguments: { - isVisible: [state.legend.isVisible], - position: [state.legend.position], +): Ast | null => { + const validLayers = state.layers.filter((layer): layer is ValidLayer => + Boolean(layer.xAccessor && layer.accessors.length) + ); + if (!validLayers.length) { + return null; + } + + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_chart', + arguments: { + xTitle: [xTitle], + yTitle: [yTitle], + legend: [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_legendConfig', + arguments: { + isVisible: [state.legend.isVisible], + position: [state.legend.position], + }, }, - }, - ], - }, - ], - layers: state.layers.map(layer => { - const columnToLabel: Record = {}; - - if (frame) { - const datasource = frame.datasourceLayers[layer.layerId]; - layer.accessors.concat([layer.splitAccessor]).forEach(accessor => { - const operation = datasource.getOperationForColumnId(accessor); - if (operation && operation.label) { - columnToLabel[accessor] = operation.label; - } - }); - } - - const xAxisOperation = - frame && frame.datasourceLayers[layer.layerId].getOperationForColumnId(layer.xAccessor); - - const isHistogramDimension = Boolean( - xAxisOperation && - xAxisOperation.isBucketed && - xAxisOperation.scale && - xAxisOperation.scale !== 'ordinal' - ); - - return { - type: 'expression', - chain: [ - { - type: 'function', - function: 'lens_xy_layer', - arguments: { - layerId: [layer.layerId], - - hide: [Boolean(layer.hide)], - - xAccessor: [layer.xAccessor], - yScaleType: [ - getScaleType(metadata[layer.layerId][layer.accessors[0]], ScaleType.Ordinal), - ], - xScaleType: [ - getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear), - ], - isHistogram: [isHistogramDimension], - splitAccessor: [layer.splitAccessor], - seriesType: [layer.seriesType], - accessors: layer.accessors, - columnToLabel: [JSON.stringify(columnToLabel)], + ], + }, + ], + layers: validLayers.map(layer => { + const columnToLabel: Record = {}; + + if (frame) { + const datasource = frame.datasourceLayers[layer.layerId]; + layer.accessors + .concat(layer.splitAccessor ? [layer.splitAccessor] : []) + .forEach(accessor => { + const operation = datasource.getOperationForColumnId(accessor); + if (operation && operation.label) { + columnToLabel[accessor] = operation.label; + } + }); + } + + const xAxisOperation = + frame && + frame.datasourceLayers[layer.layerId].getOperationForColumnId(layer.xAccessor); + + const isHistogramDimension = Boolean( + xAxisOperation && + xAxisOperation.isBucketed && + xAxisOperation.scale && + xAxisOperation.scale !== 'ordinal' + ); + + return { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_layer', + arguments: { + layerId: [layer.layerId], + + hide: [Boolean(layer.hide)], + + xAccessor: [layer.xAccessor], + yScaleType: [ + getScaleType(metadata[layer.layerId][layer.accessors[0]], ScaleType.Ordinal), + ], + xScaleType: [ + getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear), + ], + isHistogram: [isHistogramDimension], + splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [], + seriesType: [layer.seriesType], + accessors: layer.accessors, + columnToLabel: [JSON.stringify(columnToLabel)], + }, }, - }, - ], - }; - }), + ], + }; + }), + }, }, - }, - ], -}); + ], + }; +}; diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/types.ts b/x-pack/legacy/plugins/lens/public/xy_visualization/types.ts index b49e6fa6b4b6f..f7b4afc76ec4b 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/types.ts @@ -191,10 +191,10 @@ export type SeriesType = export interface LayerConfig { hide?: boolean; layerId: string; - xAccessor: string; + xAccessor?: string; accessors: string[]; seriesType: SeriesType; - splitAccessor: string; + splitAccessor?: string; } export type LayerArgs = LayerConfig & { diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx index 301c4a58a0ffd..7544ed0f87b7d 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx @@ -5,22 +5,15 @@ */ import React from 'react'; -import { ReactWrapper } from 'enzyme'; import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { EuiButtonGroupProps } from '@elastic/eui'; -import { XYConfigPanel, LayerContextMenu } from './xy_config_panel'; -import { DatasourceDimensionPanelProps, Operation, FramePublicAPI } from '../types'; +import { LayerContextMenu } from './xy_config_panel'; +import { FramePublicAPI } from '../types'; import { State } from './types'; import { Position } from '@elastic/charts'; -import { NativeRendererProps } from '../native_renderer'; -import { generateId } from '../id_generator'; import { createMockFramePublicAPI, createMockDatasource } from '../editor_frame_service/mocks'; -jest.mock('../id_generator'); - -describe('XYConfigPanel', () => { - const dragDropContext = { dragging: undefined, setDragging: jest.fn() }; - +describe('LayerContextMenu', () => { let frame: FramePublicAPI; function testState(): State { @@ -39,17 +32,10 @@ describe('XYConfigPanel', () => { }; } - function testSubj(component: ReactWrapper, subj: string) { - return component - .find(`[data-test-subj="${subj}"]`) - .first() - .props(); - } - beforeEach(() => { frame = createMockFramePublicAPI(); frame.datasourceLayers = { - first: createMockDatasource().publicAPIMock, + first: createMockDatasource('test').publicAPIMock, }; }); @@ -64,7 +50,6 @@ describe('XYConfigPanel', () => { const component = mount( { const component = mount( { expect(options!.filter(({ isDisabled }) => isDisabled).map(({ id }) => id)).toEqual([]); }); }); - - test('the x dimension panel accepts only bucketed operations', () => { - // TODO: this should eventually also accept raw operation - const state = testState(); - const component = mount( - - ); - - const panel = testSubj(component, 'lnsXY_xDimensionPanel'); - const nativeProps = (panel as NativeRendererProps).nativeProps; - const { columnId, filterOperations } = nativeProps; - const exampleOperation: Operation = { - dataType: 'number', - isBucketed: false, - label: 'bar', - }; - const bucketedOps: Operation[] = [ - { ...exampleOperation, isBucketed: true, dataType: 'number' }, - { ...exampleOperation, isBucketed: true, dataType: 'string' }, - { ...exampleOperation, isBucketed: true, dataType: 'boolean' }, - { ...exampleOperation, isBucketed: true, dataType: 'date' }, - ]; - const ops: Operation[] = [ - ...bucketedOps, - { ...exampleOperation, dataType: 'number' }, - { ...exampleOperation, dataType: 'string' }, - { ...exampleOperation, dataType: 'boolean' }, - { ...exampleOperation, dataType: 'date' }, - ]; - expect(columnId).toEqual('shazm'); - expect(ops.filter(filterOperations)).toEqual(bucketedOps); - }); - - test('the y dimension panel accepts numeric operations', () => { - const state = testState(); - const component = mount( - - ); - - const filterOperations = component - .find('[data-test-subj="lensXY_yDimensionPanel"]') - .first() - .prop('filterOperations') as (op: Operation) => boolean; - - const exampleOperation: Operation = { - dataType: 'number', - isBucketed: false, - label: 'bar', - }; - const ops: Operation[] = [ - { ...exampleOperation, dataType: 'number' }, - { ...exampleOperation, dataType: 'string' }, - { ...exampleOperation, dataType: 'boolean' }, - { ...exampleOperation, dataType: 'date' }, - ]; - expect(ops.filter(filterOperations).map(x => x.dataType)).toEqual(['number']); - }); - - test('allows removal of y dimensions', () => { - const setState = jest.fn(); - const state = testState(); - const component = mount( - - ); - - const onRemove = component - .find('[data-test-subj="lensXY_yDimensionPanel"]') - .first() - .prop('onRemove') as (accessor: string) => {}; - - onRemove('b'); - - expect(setState).toHaveBeenCalledTimes(1); - expect(setState.mock.calls[0][0]).toMatchObject({ - layers: [ - { - ...state.layers[0], - accessors: ['a', 'c'], - }, - ], - }); - }); - - test('allows adding a y axis dimension', () => { - (generateId as jest.Mock).mockReturnValueOnce('zed'); - const setState = jest.fn(); - const state = testState(); - const component = mount( - - ); - - const onAdd = component - .find('[data-test-subj="lensXY_yDimensionPanel"]') - .first() - .prop('onAdd') as () => {}; - - onAdd(); - - expect(setState).toHaveBeenCalledTimes(1); - expect(setState.mock.calls[0][0]).toMatchObject({ - layers: [ - { - ...state.layers[0], - accessors: ['a', 'b', 'c', 'zed'], - }, - ], - }); - }); }); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.tsx index dbcfa24395001..5e85680cc2b2c 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_config_panel.tsx @@ -9,16 +9,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; import { State, SeriesType, visualizationTypes } from './types'; -import { VisualizationLayerConfigProps, OperationMetadata } from '../types'; -import { NativeRenderer } from '../native_renderer'; -import { MultiColumnEditor } from '../multi_column_editor'; -import { generateId } from '../id_generator'; +import { VisualizationLayerWidgetProps } from '../types'; import { isHorizontalChart, isHorizontalSeries } from './state_helpers'; import { trackUiEvent } from '../lens_ui_telemetry'; -const isNumericMetric = (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number'; -const isBucketed = (op: OperationMetadata) => op.isBucketed; - type UnwrapArray = T extends Array ? P : T; function updateLayer(state: State, layer: UnwrapArray, index: number): State { @@ -31,7 +25,7 @@ function updateLayer(state: State, layer: UnwrapArray, index: n }; } -export function LayerContextMenu(props: VisualizationLayerConfigProps) { +export function LayerContextMenu(props: VisualizationLayerWidgetProps) { const { state, layerId } = props; const horizontalOnly = isHorizontalChart(state.layers); const index = state.layers.findIndex(l => l.layerId === layerId); @@ -74,97 +68,3 @@ export function LayerContextMenu(props: VisualizationLayerConfigProps) { ); } - -export function XYConfigPanel(props: VisualizationLayerConfigProps) { - const { state, setState, frame, layerId } = props; - const index = props.state.layers.findIndex(l => l.layerId === layerId); - - if (index < 0) { - return null; - } - - const layer = props.state.layers[index]; - - return ( - <> - - - - - - setState( - updateLayer( - state, - { - ...layer, - accessors: [...layer.accessors, generateId()], - }, - index - ) - ) - } - onRemove={accessor => - setState( - updateLayer( - state, - { - ...layer, - accessors: layer.accessors.filter(col => col !== accessor), - }, - index - ) - ) - } - filterOperations={isNumericMetric} - data-test-subj="lensXY_yDimensionPanel" - testSubj="lensXY_yDimensionPanel" - layerId={layer.layerId} - /> - - - - - - ); -} diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx index 27fd6e7064042..15aaf289eebf9 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -238,6 +238,8 @@ export function XYChart({ data, args, formatFactory, timeZone, chartTheme }: XYC index ) => { if ( + !xAccessor || + !accessors.length || !data.tables[layerId] || data.tables[layerId].rows.length === 0 || data.tables[layerId].rows.every(row => typeof row[xAccessor] === 'undefined') @@ -246,7 +248,7 @@ export function XYChart({ data, args, formatFactory, timeZone, chartTheme }: XYC } const columnToLabelMap = columnToLabel ? JSON.parse(columnToLabel) : {}; - const splitAccessorLabel = columnToLabelMap[splitAccessor]; + const splitAccessorLabel = splitAccessor ? columnToLabelMap[splitAccessor] : ''; const yAccessors = accessors.map(accessor => columnToLabelMap[accessor] || accessor); const idForLegend = splitAccessorLabel || yAccessors; const sanitized = sanitizeRows({ diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index 04ff720309d62..ddbd9d11b5fad 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -123,7 +123,7 @@ describe('xy_suggestions', () => { Array [ Object { "seriesType": "bar_stacked", - "splitAccessor": "aaa", + "splitAccessor": undefined, "x": "date", "y": Array [ "bytes", @@ -240,7 +240,6 @@ describe('xy_suggestions', () => { }); test('only makes a seriesType suggestion for unchanged table without split', () => { - (generateId as jest.Mock).mockReturnValueOnce('dummyCol'); const currentState: XYState = { legend: { isVisible: true, position: 'bottom' }, preferredSeriesType: 'bar', @@ -249,7 +248,7 @@ describe('xy_suggestions', () => { accessors: ['price'], layerId: 'first', seriesType: 'bar', - splitAccessor: 'dummyCol', + splitAccessor: undefined, xAccessor: 'date', }, ], @@ -472,17 +471,17 @@ describe('xy_suggestions', () => { }); expect(suggestionSubset(suggestion)).toMatchInlineSnapshot(` - Array [ - Object { - "seriesType": "bar_stacked", - "splitAccessor": "ddd", - "x": "quantity", - "y": Array [ - "price", - ], - }, - ] - `); + Array [ + Object { + "seriesType": "bar_stacked", + "splitAccessor": undefined, + "x": "quantity", + "y": Array [ + "price", + ], + }, + ] + `); }); test('handles ip', () => { @@ -509,17 +508,17 @@ describe('xy_suggestions', () => { }); expect(suggestionSubset(suggestion)).toMatchInlineSnapshot(` - Array [ - Object { - "seriesType": "bar_stacked", - "splitAccessor": "ddd", - "x": "myip", - "y": Array [ - "quantity", - ], - }, - ] - `); + Array [ + Object { + "seriesType": "bar_stacked", + "splitAccessor": undefined, + "x": "myip", + "y": Array [ + "quantity", + ], + }, + ] + `); }); test('handles unbucketed suggestions', () => { @@ -545,16 +544,16 @@ describe('xy_suggestions', () => { }); expect(suggestionSubset(suggestion)).toMatchInlineSnapshot(` - Array [ - Object { - "seriesType": "bar_stacked", - "splitAccessor": "eee", - "x": "mybool", - "y": Array [ - "num votes", - ], - }, - ] - `); + Array [ + Object { + "seriesType": "bar_stacked", + "splitAccessor": undefined, + "x": "mybool", + "y": Array [ + "num votes", + ], + }, + ] + `); }); }); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.ts index 33181b7f3a467..5e9311bb1e928 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -15,7 +15,6 @@ import { TableChangeType, } from '../types'; import { State, SeriesType, XYState } from './types'; -import { generateId } from '../id_generator'; import { getIconForSeries } from './state_helpers'; const columnSortOrder = { @@ -356,7 +355,7 @@ function buildSuggestion({ layerId, seriesType, xAccessor: xValue.columnId, - splitAccessor: splitBy ? splitBy.columnId : generateId(), + splitAccessor: splitBy?.columnId, accessors: yValues.map(col => col.columnId), }; diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.test.ts b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.test.ts index a27a8e7754b86..beccf0dc46eb4 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.test.ts +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.test.ts @@ -9,10 +9,6 @@ import { Position } from '@elastic/charts'; import { Operation } from '../types'; import { State, SeriesType } from './types'; import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; -import { generateId } from '../id_generator'; -import { Ast } from '@kbn/interpreter/target/common'; - -jest.mock('../id_generator'); function exampleState(): State { return { @@ -87,31 +83,22 @@ describe('xy_visualization', () => { describe('#initialize', () => { it('loads default state', () => { - (generateId as jest.Mock) - .mockReturnValueOnce('test-id1') - .mockReturnValueOnce('test-id2') - .mockReturnValue('test-id3'); const mockFrame = createMockFramePublicAPI(); const initialState = xyVisualization.initialize(mockFrame); expect(initialState.layers).toHaveLength(1); - expect(initialState.layers[0].xAccessor).toBeDefined(); - expect(initialState.layers[0].accessors[0]).toBeDefined(); - expect(initialState.layers[0].xAccessor).not.toEqual(initialState.layers[0].accessors[0]); + expect(initialState.layers[0].xAccessor).not.toBeDefined(); + expect(initialState.layers[0].accessors).toHaveLength(0); expect(initialState).toMatchInlineSnapshot(` Object { "layers": Array [ Object { - "accessors": Array [ - "test-id1", - ], + "accessors": Array [], "layerId": "", "position": "top", "seriesType": "bar_stacked", "showGridlines": false, - "splitAccessor": "test-id2", - "xAccessor": "test-id3", }, ], "legend": Object { @@ -167,14 +154,11 @@ describe('xy_visualization', () => { describe('#clearLayer', () => { it('clears the specified layer', () => { - (generateId as jest.Mock).mockReturnValue('test_empty_id'); const layer = xyVisualization.clearLayer(exampleState(), 'first').layers[0]; expect(layer).toMatchObject({ - accessors: ['test_empty_id'], + accessors: [], layerId: 'first', seriesType: 'bar', - splitAccessor: 'test_empty_id', - xAccessor: 'test_empty_id', }); }); }); @@ -185,13 +169,94 @@ describe('xy_visualization', () => { }); }); - describe('#toExpression', () => { + describe('#setDimension', () => { + it('sets the x axis', () => { + expect( + xyVisualization.setDimension({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: undefined, + accessors: [], + }, + ], + }, + layerId: 'first', + groupId: 'x', + columnId: 'newCol', + }).layers[0] + ).toEqual({ + layerId: 'first', + seriesType: 'area', + xAccessor: 'newCol', + accessors: [], + }); + }); + + it('replaces the x axis', () => { + expect( + xyVisualization.setDimension({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: 'a', + accessors: [], + }, + ], + }, + layerId: 'first', + groupId: 'x', + columnId: 'newCol', + }).layers[0] + ).toEqual({ + layerId: 'first', + seriesType: 'area', + xAccessor: 'newCol', + accessors: [], + }); + }); + }); + + describe('#removeDimension', () => { + it('removes the x axis', () => { + expect( + xyVisualization.removeDimension({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: 'a', + accessors: [], + }, + ], + }, + layerId: 'first', + columnId: 'a', + }).layers[0] + ).toEqual({ + layerId: 'first', + seriesType: 'area', + xAccessor: undefined, + accessors: [], + }); + }); + }); + + describe('#getConfiguration', () => { let mockDatasource: ReturnType; let frame: ReturnType; beforeEach(() => { frame = createMockFramePublicAPI(); - mockDatasource = createMockDatasource(); + mockDatasource = createMockDatasource('testDatasource'); mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ { columnId: 'd' }, @@ -200,36 +265,78 @@ describe('xy_visualization', () => { { columnId: 'c' }, ]); - mockDatasource.publicAPIMock.getOperationForColumnId.mockImplementation(col => { - return { label: `col_${col}`, dataType: 'number' } as Operation; - }); - frame.datasourceLayers = { first: mockDatasource.publicAPIMock, }; }); - it('should map to a valid AST', () => { - expect(xyVisualization.toExpression(exampleState(), frame)).toMatchSnapshot(); + it('should return options for 3 dimensions', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + expect(options).toHaveLength(3); + expect(options.map(o => o.groupId)).toEqual(['x', 'y', 'breakdown']); }); - it('should default to labeling all columns with their column label', () => { - const expression = xyVisualization.toExpression(exampleState(), frame)! as Ast; + it('should only accept bucketed operations for x', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + const filterOperations = options.find(o => o.groupId === 'x')!.filterOperations; - expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('b'); - expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('c'); - expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('d'); - expect(expression.chain[0].arguments.xTitle).toEqual(['col_a']); - expect(expression.chain[0].arguments.yTitle).toEqual(['col_b']); - expect( - (expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.columnToLabel - ).toEqual([ - JSON.stringify({ - b: 'col_b', - c: 'col_c', - d: 'col_d', - }), - ]); + const exampleOperation: Operation = { + dataType: 'number', + isBucketed: false, + label: 'bar', + }; + const bucketedOps: Operation[] = [ + { ...exampleOperation, isBucketed: true, dataType: 'number' }, + { ...exampleOperation, isBucketed: true, dataType: 'string' }, + { ...exampleOperation, isBucketed: true, dataType: 'boolean' }, + { ...exampleOperation, isBucketed: true, dataType: 'date' }, + ]; + const ops: Operation[] = [ + ...bucketedOps, + { ...exampleOperation, dataType: 'number' }, + { ...exampleOperation, dataType: 'string' }, + { ...exampleOperation, dataType: 'boolean' }, + { ...exampleOperation, dataType: 'date' }, + ]; + expect(ops.filter(filterOperations)).toEqual(bucketedOps); + }); + + it('should not allow anything to be added to x', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + expect(options.find(o => o.groupId === 'x')?.supportsMoreColumns).toBe(false); + }); + + it('should allow number operations on y', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + const filterOperations = options.find(o => o.groupId === 'y')!.filterOperations; + const exampleOperation: Operation = { + dataType: 'number', + isBucketed: false, + label: 'bar', + }; + const ops: Operation[] = [ + { ...exampleOperation, dataType: 'number' }, + { ...exampleOperation, dataType: 'string' }, + { ...exampleOperation, dataType: 'boolean' }, + { ...exampleOperation, dataType: 'date' }, + ]; + expect(ops.filter(filterOperations).map(x => x.dataType)).toEqual(['number']); }); }); }); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.tsx index 75d6fcc7d160b..c72fa0fec24d7 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_visualization.tsx @@ -11,17 +11,18 @@ import { Position } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { getSuggestions } from './xy_suggestions'; -import { XYConfigPanel, LayerContextMenu } from './xy_config_panel'; -import { Visualization } from '../types'; +import { LayerContextMenu } from './xy_config_panel'; +import { Visualization, OperationMetadata } from '../types'; import { State, PersistableState, SeriesType, visualizationTypes, LayerConfig } from './types'; import { toExpression, toPreviewExpression } from './to_expression'; -import { generateId } from '../id_generator'; import chartBarStackedSVG from '../assets/chart_bar_stacked.svg'; import chartMixedSVG from '../assets/chart_mixed_xy.svg'; import { isHorizontalChart } from './state_helpers'; const defaultIcon = chartBarStackedSVG; const defaultSeriesType = 'bar_stacked'; +const isNumericMetric = (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number'; +const isBucketed = (op: OperationMetadata) => op.isBucketed; function getDescription(state?: State) { if (!state) { @@ -133,12 +134,10 @@ export const xyVisualization: Visualization = { layers: [ { layerId: frame.addNewLayer(), - accessors: [generateId()], + accessors: [], position: Position.Top, seriesType: defaultSeriesType, showGridlines: false, - splitAccessor: generateId(), - xAccessor: generateId(), }, ], } @@ -147,13 +146,89 @@ export const xyVisualization: Visualization = { getPersistableState: state => state, - renderLayerConfigPanel: (domElement, props) => - render( - - - , - domElement - ), + getConfiguration(props) { + const layer = props.state.layers.find(l => l.layerId === props.layerId)!; + return { + groups: [ + { + groupId: 'x', + groupLabel: i18n.translate('xpack.lens.xyChart.xAxisLabel', { + defaultMessage: 'X-axis', + }), + accessors: layer.xAccessor ? [layer.xAccessor] : [], + filterOperations: isBucketed, + suggestedPriority: 1, + supportsMoreColumns: !layer.xAccessor, + required: true, + dataTestSubj: 'lnsXY_xDimensionPanel', + }, + { + groupId: 'y', + groupLabel: i18n.translate('xpack.lens.xyChart.yAxisLabel', { + defaultMessage: 'Y-axis', + }), + accessors: layer.accessors, + filterOperations: isNumericMetric, + supportsMoreColumns: true, + required: true, + dataTestSubj: 'lnsXY_yDimensionPanel', + }, + { + groupId: 'breakdown', + groupLabel: i18n.translate('xpack.lens.xyChart.splitSeries', { + defaultMessage: 'Break down by', + }), + accessors: layer.splitAccessor ? [layer.splitAccessor] : [], + filterOperations: isBucketed, + suggestedPriority: 0, + supportsMoreColumns: !layer.splitAccessor, + dataTestSubj: 'lnsXY_splitDimensionPanel', + }, + ], + }; + }, + + setDimension({ prevState, layerId, columnId, groupId }) { + const newLayer = prevState.layers.find(l => l.layerId === layerId); + if (!newLayer) { + return prevState; + } + + if (groupId === 'x') { + newLayer.xAccessor = columnId; + } + if (groupId === 'y') { + newLayer.accessors = [...newLayer.accessors.filter(a => a !== columnId), columnId]; + } + if (groupId === 'breakdown') { + newLayer.splitAccessor = columnId; + } + + return { + ...prevState, + layers: prevState.layers.map(l => (l.layerId === layerId ? newLayer : l)), + }; + }, + + removeDimension({ prevState, layerId, columnId }) { + const newLayer = prevState.layers.find(l => l.layerId === layerId); + if (!newLayer) { + return prevState; + } + + if (newLayer.xAccessor === columnId) { + delete newLayer.xAccessor; + } else if (newLayer.splitAccessor === columnId) { + delete newLayer.splitAccessor; + } else if (newLayer.accessors.includes(columnId)) { + newLayer.accessors = newLayer.accessors.filter(a => a !== columnId); + } + + return { + ...prevState, + layers: prevState.layers.map(l => (l.layerId === layerId ? newLayer : l)), + }; + }, getLayerContextMenuIcon({ state, layerId }) { const layer = state.layers.find(l => l.layerId === layerId); @@ -177,8 +252,6 @@ function newLayerState(seriesType: SeriesType, layerId: string): LayerConfig { return { layerId, seriesType, - xAccessor: generateId(), - accessors: [generateId()], - splitAccessor: generateId(), + accessors: [], }; } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c155344c7534a..eb208e67ccfec 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6831,7 +6831,6 @@ "xpack.lens.indexPatternSuggestion.removeLayerPositionLabel": "レイヤー{layerNumber}のみを表示", "xpack.lens.lensSavedObjectLabel": "レンズビジュアライゼーション", "xpack.lens.metric.label": "メトリック", - "xpack.lens.metric.valueLabel": "値", "xpack.lens.sugegstion.refreshSuggestionLabel": "更新", "xpack.lens.suggestion.refreshSuggestionTooltip": "選択したビジュアライゼーションに基づいて、候補を更新します。", "xpack.lens.suggestions.currentVisLabel": "現在", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ed0ac8f6f7fef..f85714a5913ad 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6831,7 +6831,6 @@ "xpack.lens.indexPatternSuggestion.removeLayerPositionLabel": "仅显示图层 {layerNumber}", "xpack.lens.lensSavedObjectLabel": "Lens 可视化", "xpack.lens.metric.label": "指标", - "xpack.lens.metric.valueLabel": "值", "xpack.lens.sugegstion.refreshSuggestionLabel": "刷新", "xpack.lens.suggestion.refreshSuggestionTooltip": "基于选定可视化刷新建议。", "xpack.lens.suggestions.currentVisLabel": "当前", diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js index c90a0ae6d19fc..19eebb3ba501c 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_empty_screen.js @@ -34,21 +34,21 @@ export default function({ getPageObjects, getService }) { await PageObjects.lens.goToTimeRange(); await PageObjects.lens.configureDimension({ dimension: - '[data-test-subj="lnsXY_xDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + '[data-test-subj="lnsXY_xDimensionPanel"] [data-test-subj="lns-empty-dimension"]', operation: 'date_histogram', field: '@timestamp', }); await PageObjects.lens.configureDimension({ dimension: - '[data-test-subj="lnsXY_yDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + '[data-test-subj="lnsXY_yDimensionPanel"] [data-test-subj="lns-empty-dimension"]', operation: 'avg', field: 'bytes', }); await PageObjects.lens.configureDimension({ dimension: - '[data-test-subj="lnsXY_splitDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + '[data-test-subj="lnsXY_splitDimensionPanel"] [data-test-subj="lns-empty-dimension"]', operation: 'terms', field: 'ip', }); diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 3346f2ff77036..317bb0b27e972 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -77,21 +77,21 @@ export default function({ getService, getPageObjects, ...rest }: FtrProviderCont await PageObjects.lens.configureDimension({ dimension: - '[data-test-subj="lnsXY_xDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + '[data-test-subj="lnsXY_xDimensionPanel"] [data-test-subj="lns-empty-dimension"]', operation: 'date_histogram', field: '@timestamp', }); await PageObjects.lens.configureDimension({ dimension: - '[data-test-subj="lnsXY_yDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + '[data-test-subj="lnsXY_yDimensionPanel"] [data-test-subj="lns-empty-dimension"]', operation: 'avg', field: 'bytes', }); await PageObjects.lens.configureDimension({ dimension: - '[data-test-subj="lnsXY_splitDimensionPanel"] [data-test-subj="indexPattern-configure-dimension"]', + '[data-test-subj="lnsXY_splitDimensionPanel"] [data-test-subj="lns-empty-dimension"]', operation: 'terms', field: 'ip', }); From 53d23fcb3be3102d2b0c746766b6500d7c6db4fc Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Tue, 17 Mar 2020 08:34:39 -0600 Subject: [PATCH 43/76] [Endpoint] Adds take action dropdown and tests to alert details flyout (#59242) * adds dropdown * changes i18n fields * switches to buttons * adds tests for alert details flyout * updates es archiver data * finishes functional and react tests * cleanup tests for alerts * updates alert esarchive data * replaces es archives and fixes tests * rebase * fixes functional tests * suggested changes to take action button * addresses comments Co-authored-by: oatkiller --- .../view/alerts/alert_details.test.tsx | 81 + .../details/metadata/file_accordion.tsx | 1 + .../details/metadata/general_accordion.tsx | 1 + .../details/metadata/hash_accordion.tsx | 1 + .../details/metadata/host_accordion.tsx | 1 + .../metadata/source_process_accordion.tsx | 1 + .../source_process_token_accordion.tsx | 1 + .../view/alerts/details/overview/index.tsx | 15 +- .../details/overview/take_action_dropdown.tsx | 71 + .../endpoint/view/alerts/index.test.tsx | 57 +- .../alerts/test_helpers/render_alert_page.tsx | 59 + .../api_integration/apis/endpoint/alerts.ts | 155 +- .../api_integration/apis/endpoint/metadata.ts | 38 +- .../endpoint/{alert_list.ts => alerts.ts} | 33 +- x-pack/test/functional/apps/endpoint/index.ts | 2 +- .../functional/apps/endpoint/management.ts | 16 +- .../endpoint/alerts/api_feature/data.json.gz | Bin 2063322 -> 15777 bytes .../endpoint/alerts/api_feature/mappings.json | 5460 ++++------------- .../endpoint/metadata/api_feature/data.json | 382 -- .../metadata/api_feature/data.json.gz | Bin 0 -> 732 bytes .../metadata/api_feature/mappings.json | 33 +- .../page_objects/endpoint_alerts_page.ts | 11 +- 22 files changed, 1700 insertions(+), 4719 deletions(-) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/take_action_dropdown.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/test_helpers/render_alert_page.tsx rename x-pack/test/functional/apps/endpoint/{alert_list.ts => alerts.ts} (55%) delete mode 100644 x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json create mode 100644 x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json.gz diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx new file mode 100644 index 0000000000000..0f5a9dd7fed17 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as reactTestingLibrary from '@testing-library/react'; +import { appStoreFactory } from '../../store'; +import { fireEvent } from '@testing-library/react'; +import { MemoryHistory } from 'history'; +import { AppAction } from '../../types'; +import { mockAlertResultList } from '../../store/alerts/mock_alert_result_list'; +import { alertPageTestRender } from './test_helpers/render_alert_page'; + +describe('when the alert details flyout is open', () => { + let render: () => reactTestingLibrary.RenderResult; + let history: MemoryHistory; + let store: ReturnType; + + beforeEach(async () => { + // Creates the render elements for the tests to use + ({ render, history, store } = alertPageTestRender); + }); + describe('when the alerts details flyout is open', () => { + beforeEach(() => { + reactTestingLibrary.act(() => { + history.push({ + search: '?selected_alert=1', + }); + }); + }); + describe('when the data loads', () => { + beforeEach(() => { + reactTestingLibrary.act(() => { + const action: AppAction = { + type: 'serverReturnedAlertDetailsData', + payload: mockAlertResultList().alerts[0], + }; + store.dispatch(action); + }); + }); + it('should display take action button', async () => { + await render().findByTestId('alertDetailTakeActionDropdownButton'); + }); + describe('when the user clicks the take action button on the flyout', () => { + let renderResult: reactTestingLibrary.RenderResult; + beforeEach(async () => { + renderResult = render(); + const takeActionButton = await renderResult.findByTestId( + 'alertDetailTakeActionDropdownButton' + ); + if (takeActionButton) { + fireEvent.click(takeActionButton); + } + }); + it('should display the correct fields in the dropdown', async () => { + await renderResult.findByTestId('alertDetailTakeActionCloseAlertButton'); + await renderResult.findByTestId('alertDetailTakeActionWhitelistButton'); + }); + }); + describe('when the user navigates to the overview tab', () => { + let renderResult: reactTestingLibrary.RenderResult; + beforeEach(async () => { + renderResult = render(); + const overviewTab = await renderResult.findByTestId('overviewMetadata'); + if (overviewTab) { + fireEvent.click(overviewTab); + } + }); + it('should render all accordion panels', async () => { + await renderResult.findAllByTestId('alertDetailsAlertAccordion'); + await renderResult.findAllByTestId('alertDetailsHostAccordion'); + await renderResult.findAllByTestId('alertDetailsFileAccordion'); + await renderResult.findAllByTestId('alertDetailsHashAccordion'); + await renderResult.findAllByTestId('alertDetailsSourceProcessAccordion'); + await renderResult.findAllByTestId('alertDetailsSourceProcessTokenAccordion'); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx index ac67e54f38779..26f1985368465 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx @@ -73,6 +73,7 @@ export const FileAccordion = memo(({ alertData }: { alertData: Immutable diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx index 070c78c968585..0183e9663bb44 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx @@ -61,6 +61,7 @@ export const GeneralAccordion = memo(({ alertData }: { alertData: Immutable diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx index b2be083ce8f59..4a2f7378a36ed 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx @@ -42,6 +42,7 @@ export const HashAccordion = memo(({ alertData }: { alertData: Immutable diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx index 4108781f0a79b..edaba3725e027 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx @@ -48,6 +48,7 @@ export const HostAccordion = memo(({ alertData }: { alertData: Immutable diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx index 4d921ee39d95b..4134bc35747d6 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx @@ -90,6 +90,7 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl } )} paddingSize="l" + data-test-subj="alertDetailsSourceProcessAccordion" > diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx index 7d75d4478afb3..00755673d3f82 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx @@ -37,6 +37,7 @@ export const SourceProcessTokenAccordion = memo( } )} paddingSize="l" + data-test-subj="alertDetailsSourceProcessTokenAccordion" > diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx index 080c70ca43bae..82a4bc00a4396 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx @@ -6,12 +6,20 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiSpacer, EuiTitle, EuiText, EuiHealth, EuiTabbedContent } from '@elastic/eui'; +import { + EuiSpacer, + EuiTitle, + EuiText, + EuiHealth, + EuiTabbedContent, + EuiTabbedContentTab, +} from '@elastic/eui'; import { useAlertListSelector } from '../../hooks/use_alerts_selector'; import * as selectors from '../../../../store/alerts/selectors'; import { MetadataPanel } from './metadata_panel'; import { FormattedDate } from '../../formatted_date'; import { AlertDetailResolver } from '../../resolver'; +import { TakeActionDropdown } from './take_action_dropdown'; export const AlertDetailsOverview = memo(() => { const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); @@ -22,10 +30,11 @@ export const AlertDetailsOverview = memo(() => { selectors.selectedAlertIsLegacyEndpointEvent ); - const tabs = useMemo(() => { + const tabs: EuiTabbedContentTab[] = useMemo(() => { return [ { id: 'overviewMetadata', + 'data-test-subj': 'overviewMetadata', name: i18n.translate( 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', { @@ -87,6 +96,8 @@ export const AlertDetailsOverview = memo(() => { Alert Status: Open + +

diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/take_action_dropdown.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/take_action_dropdown.tsx new file mode 100644 index 0000000000000..8d8468b4df4a3 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/take_action_dropdown.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { memo, useState, useCallback } from 'react'; +import { EuiPopover, EuiFormRow, EuiButton, EuiButtonEmpty } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +const TakeActionButton = memo(({ onClick }: { onClick: () => void }) => ( + + + +)); + +export const TakeActionDropdown = memo(() => { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + const onClick = useCallback(() => { + setIsDropdownOpen(!isDropdownOpen); + }, [isDropdownOpen]); + + const closePopover = useCallback(() => { + setIsDropdownOpen(false); + }, []); + + return ( + } + isOpen={isDropdownOpen} + anchorPosition="downRight" + closePopover={closePopover} + data-test-subj="alertListTakeActionDropdownContent" + > + + + + + + + + + + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx index 7fc5e18a6ba88..336c16b2c9332 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx @@ -4,21 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { I18nProvider } from '@kbn/i18n/react'; -import { AlertIndex } from './index'; import { IIndexPattern } from 'src/plugins/data/public'; import { appStoreFactory } from '../../store'; -import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { fireEvent, act } from '@testing-library/react'; -import { RouteCapture } from '../route_capture'; -import { createMemoryHistory, MemoryHistory } from 'history'; -import { Router } from 'react-router-dom'; +import { MemoryHistory } from 'history'; import { AppAction } from '../../types'; import { mockAlertResultList } from '../../store/alerts/mock_alert_result_list'; -import { DepsStartMock, depsStartMock } from '../../mocks'; +import { DepsStartMock } from '../../mocks'; +import { alertPageTestRender } from './test_helpers/render_alert_page'; describe('when on the alerting page', () => { let render: () => reactTestingLibrary.RenderResult; @@ -27,42 +21,8 @@ describe('when on the alerting page', () => { let depsStart: DepsStartMock; beforeEach(async () => { - /** - * Create a 'history' instance that is only in-memory and causes no side effects to the testing environment. - */ - history = createMemoryHistory(); - /** - * Create a store, with the middleware disabled. We don't want side effects being created by our code in this test. - */ - store = appStoreFactory(); - - depsStart = depsStartMock(); - depsStart.data.ui.SearchBar.mockImplementation(() =>
); - - /** - * Render the test component, use this after setting up anything in `beforeEach`. - */ - render = () => { - /** - * Provide the store via `Provider`, and i18n APIs via `I18nProvider`. - * Use react-router via `Router`, passing our in-memory `history` instance. - * Use `RouteCapture` to emit url-change actions when the URL is changed. - * Finally, render the `AlertIndex` component which we are testing. - */ - return reactTestingLibrary.render( - - - - - - - - - - - - ); - }; + // Creates the render elements for the tests to use + ({ render, history, store, depsStart } = alertPageTestRender); }); it('should show a data grid', async () => { await render().findByTestId('alertListGrid'); @@ -80,7 +40,7 @@ describe('when on the alerting page', () => { reactTestingLibrary.act(() => { const action: AppAction = { type: 'serverReturnedAlertsData', - payload: mockAlertResultList(), + payload: mockAlertResultList({ total: 11 }), }; store.dispatch(action); }); @@ -93,16 +53,17 @@ describe('when on the alerting page', () => { * There should be a 'row' which is the header, and * row which is the alert item. */ - expect(rows).toHaveLength(2); + expect(rows).toHaveLength(11); }); describe('when the user has clicked the alert type in the grid', () => { let renderResult: reactTestingLibrary.RenderResult; beforeEach(async () => { renderResult = render(); + const alertLinks = await renderResult.findAllByTestId('alertTypeCellLink'); /** * This is the cell with the alert type, it has a link. */ - fireEvent.click(await renderResult.findByTestId('alertTypeCellLink')); + fireEvent.click(alertLinks[0]); }); it('should show the flyout', async () => { await renderResult.findByTestId('alertDetailFlyout'); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/test_helpers/render_alert_page.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/test_helpers/render_alert_page.tsx new file mode 100644 index 0000000000000..6311671407610 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/test_helpers/render_alert_page.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import * as reactTestingLibrary from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { I18nProvider } from '@kbn/i18n/react'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; +import { AlertIndex } from '../index'; +import { appStoreFactory } from '../../../store'; +import { KibanaContextProvider } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { RouteCapture } from '../../route_capture'; +import { depsStartMock } from '../../../mocks'; + +/** + * Create a 'history' instance that is only in-memory and causes no side effects to the testing environment. + */ +const history = createMemoryHistory(); +/** + * Create a store, with the middleware disabled. We don't want side effects being created by our code in this test. + */ +const store = appStoreFactory(); + +const depsStart = depsStartMock(); +depsStart.data.ui.SearchBar.mockImplementation(() =>
); + +export const alertPageTestRender = { + store, + history, + depsStart, + + /** + * Render the test component, use this after setting up anything in `beforeEach`. + */ + render: () => { + /** + * Provide the store via `Provider`, and i18n APIs via `I18nProvider`. + * Use react-router via `Router`, passing our in-memory `history` instance. + * Use `RouteCapture` to emit url-change actions when the URL is changed. + * Finally, render the `AlertIndex` component which we are testing. + */ + return reactTestingLibrary.render( + + + + + + + + + + + + ); + }, +}; diff --git a/x-pack/test/api_integration/apis/endpoint/alerts.ts b/x-pack/test/api_integration/apis/endpoint/alerts.ts index 06134a093f7a5..140d8ca813694 100644 --- a/x-pack/test/api_integration/apis/endpoint/alerts.ts +++ b/x-pack/test/api_integration/apis/endpoint/alerts.ts @@ -6,6 +6,16 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; +/** + * The number of alert documents in the es archive. + */ +const numberOfAlertsInFixture = 12; + +/** + * The default number of entries returned when no page_size is specified. + */ +const defaultPageSize = 10; + export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); @@ -17,12 +27,12 @@ export default function({ getService }: FtrProviderContext) { const nextPrevPrefixPageSize = 'page_size=10'; const nextPrevPrefix = `${nextPrevPrefixQuery}&${nextPrevPrefixDateRange}&${nextPrevPrefixSort}&${nextPrevPrefixOrder}&${nextPrevPrefixPageSize}`; - describe('test alerts api', () => { - describe('Tests for alerts API', () => { + describe('Endpoint alert API', () => { + describe('when data is in elasticsearch', () => { before(() => esArchiver.load('endpoint/alerts/api_feature')); after(() => esArchiver.unload('endpoint/alerts/api_feature')); - it('alerts api should not support post', async () => { + it('should not support POST requests', async () => { await supertest .post('/api/endpoint/alerts') .send({}) @@ -30,43 +40,66 @@ export default function({ getService }: FtrProviderContext) { .expect(404); }); - it('alerts api should return one entry for each alert with default paging', async () => { + it('should return one entry for each alert with default paging', async () => { const { body } = await supertest .get('/api/endpoint/alerts') .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.total).to.eql(132); - expect(body.alerts.length).to.eql(10); - expect(body.request_page_size).to.eql(10); + expect(body.total).to.eql(numberOfAlertsInFixture); + expect(body.alerts.length).to.eql(defaultPageSize); + expect(body.request_page_size).to.eql(defaultPageSize); + /** + * No page_index was specified. It should return page 0. + */ expect(body.request_page_index).to.eql(0); + /** + * The total offset: page_index * page_size + */ expect(body.result_from_index).to.eql(0); }); - it('alerts api should return page based on paging properties passed.', async () => { + it('should return the page_size and page_index specified in the query params', async () => { + const pageSize = 1; + const pageIndex = 1; const { body } = await supertest - .get('/api/endpoint/alerts?page_size=1&page_index=1') + .get(`/api/endpoint/alerts?page_size=${pageSize}&page_index=${pageIndex}`) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.total).to.eql(132); - expect(body.alerts.length).to.eql(1); - expect(body.request_page_size).to.eql(1); - expect(body.request_page_index).to.eql(1); - expect(body.result_from_index).to.eql(1); - }); - - it('alerts api should return accurate total alerts if page index produces no result', async () => { - const { body } = await supertest - .get('/api/endpoint/alerts?page_size=100&page_index=3') - .set('kbn-xsrf', 'xxx') - .expect(200); - expect(body.total).to.eql(132); - expect(body.alerts.length).to.eql(0); - expect(body.request_page_size).to.eql(100); - expect(body.request_page_index).to.eql(3); - expect(body.result_from_index).to.eql(300); - }); - - it('alerts api should return 400 when paging properties are below boundaries.', async () => { + expect(body.total).to.eql(numberOfAlertsInFixture); + /** + * Skipping the first page (with a size of 1). + */ + const expectedToBeSkipped = 1; + expect(body.alerts.length).to.eql(pageSize); + expect(body.request_page_size).to.eql(pageSize); + expect(body.request_page_index).to.eql(pageIndex); + expect(body.result_from_index).to.eql(expectedToBeSkipped); + }); + + describe('when the query params specify a page_index and page_size that return no results', () => { + let body: any; + const requestPageSize = 100; + const requestPageIndex = 3; + beforeEach(async () => { + const response = await supertest + .get(`/api/endpoint/alerts?page_size=${requestPageSize}&page_index=${requestPageIndex}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + body = response.body; + }); + it('should return accurate total counts', async () => { + expect(body.total).to.eql(numberOfAlertsInFixture); + /** + * Nothing was returned due to pagination. + */ + expect(body.alerts.length).to.eql(0); + expect(body.request_page_size).to.eql(requestPageSize); + expect(body.request_page_index).to.eql(requestPageIndex); + expect(body.result_from_index).to.eql(requestPageIndex * requestPageSize); + }); + }); + + it('should return 400 when paging properties are less than 1', async () => { const { body } = await supertest .get('/api/endpoint/alerts?page_size=0') .set('kbn-xsrf', 'xxx') @@ -74,12 +107,12 @@ export default function({ getService }: FtrProviderContext) { expect(body.message).to.contain('Value must be equal to or greater than [1]'); }); - it('alerts api should return links to the next and previous pages using cursor-based pagination', async () => { + it('should return links to the next and previous pages using cursor-based pagination', async () => { const { body } = await supertest .get('/api/endpoint/alerts?page_index=0') .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.alerts.length).to.eql(10); + expect(body.alerts.length).to.eql(defaultPageSize); const lastTimestampFirstPage = body.alerts[9]['@timestamp']; const lastEventIdFirstPage = body.alerts[9].event.id; expect(body.next).to.eql( @@ -87,14 +120,14 @@ export default function({ getService }: FtrProviderContext) { ); }); - it('alerts api should return data using `next` link', async () => { + it('should return data using `next` link', async () => { const { body } = await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefix}&after=1542789412000&after=c710bf2d-8686-4038-a2a1-43bdecc06b2a` + `/api/endpoint/alerts?${nextPrevPrefix}&after=1584044338719&after=66008e21-2493-4b15-a937-939ea228064a` ) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.alerts.length).to.eql(10); + expect(body.alerts.length).to.eql(defaultPageSize); const firstTimestampNextPage = body.alerts[0]['@timestamp']; const firstEventIdNextPage = body.alerts[0].event.id; expect(body.prev).to.eql( @@ -102,7 +135,7 @@ export default function({ getService }: FtrProviderContext) { ); }); - it('alerts api should return data using `prev` link', async () => { + it('should return data using `prev` link', async () => { const { body } = await supertest .get( `/api/endpoint/alerts?${nextPrevPrefix}&before=1542789412000&before=823d814d-fa0c-4e53-a94c-f6b296bb965b` @@ -112,85 +145,89 @@ export default function({ getService }: FtrProviderContext) { expect(body.alerts.length).to.eql(10); }); - it('alerts api should return no results when `before` is requested past beginning of first page', async () => { + it('should return no results when `before` is requested past beginning of first page', async () => { const { body } = await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefix}&before=1542789473000&before=ffae628e-6236-45ce-ba24-7351e0af219e` + `/api/endpoint/alerts?${nextPrevPrefix}&before=1584044338726&before=5ff1a4ec-758e-49e7-89aa-2c6821fe6b54` ) .set('kbn-xsrf', 'xxx') .expect(200); expect(body.alerts.length).to.eql(0); }); - it('alerts api should return no results when `after` is requested past end of last page', async () => { + it('should return no results when `after` is requested past end of last page', async () => { const { body } = await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefix}&after=1542341895000&after=01911945-48aa-478e-9712-f49c92a15f20` + `/api/endpoint/alerts?${nextPrevPrefix}&after=1584044338612&after=6d75d498-3cca-45ad-a304-525b95ae0412` ) .set('kbn-xsrf', 'xxx') .expect(200); expect(body.alerts.length).to.eql(0); }); - it('alerts api should return 400 when using `before` by custom sort parameter', async () => { + it('should return 400 when using `before` by custom sort parameter', async () => { await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefixDateRange}&${nextPrevPrefixPageSize}&${nextPrevPrefixOrder}&sort=thread.id&before=2180&before=8362fcde-0b10-476f-97a8-8d6a43865226` + `/api/endpoint/alerts?${nextPrevPrefixDateRange}&${nextPrevPrefixPageSize}&${nextPrevPrefixOrder}&sort=process.pid&before=1&before=66008e21-2493-4b15-a937-939ea228064a` ) .set('kbn-xsrf', 'xxx') .expect(400); }); - it('alerts api should return data using `after` by custom sort parameter', async () => { + it('should return data using `after` by custom sort parameter', async () => { const { body } = await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefixDateRange}&${nextPrevPrefixPageSize}&${nextPrevPrefixOrder}&sort=thread.id&after=2180&after=8362fcde-0b10-476f-97a8-8d6a43865226` + `/api/endpoint/alerts?${nextPrevPrefixDateRange}&${nextPrevPrefixPageSize}&${nextPrevPrefixOrder}&sort=process.pid&after=3&after=66008e21-2493-4b15-a937-939ea228064a` ) .set('kbn-xsrf', 'xxx') .expect(200); expect(body.alerts.length).to.eql(10); - expect(body.alerts[0].thread.id).to.eql(1912); + expect(body.alerts[0].process.pid).to.eql(2); }); - it('alerts api should filter results of alert data using rison-encoded filters', async () => { + it('should filter results of alert data using rison-encoded filters', async () => { + const hostname = 'Host-abmfhmc5ku'; const { body } = await supertest .get( - `/api/endpoint/alerts?filters=!((%27%24state%27%3A(store%3AappState)%2Cmeta%3A(alias%3A!n%2Cdisabled%3A!f%2Ckey%3Ahost.hostname%2Cnegate%3A!f%2Cparams%3A(query%3AHD-m3z-4c803698)%2Ctype%3Aphrase)%2Cquery%3A(match_phrase%3A(host.hostname%3AHD-m3z-4c803698))))` + `/api/endpoint/alerts?filters=!((%27%24state%27%3A(store%3AappState)%2Cmeta%3A(alias%3A!n%2Cdisabled%3A!f%2Ckey%3Ahost.hostname%2Cnegate%3A!f%2Cparams%3A(query%3A${hostname})%2Ctype%3Aphrase)%2Cquery%3A(match_phrase%3A(host.hostname%3A${hostname}))))` ) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.total).to.eql(72); - expect(body.alerts.length).to.eql(10); - expect(body.request_page_size).to.eql(10); + expect(body.total).to.eql(4); + expect(body.alerts.length).to.eql(4); + expect(body.request_page_size).to.eql(defaultPageSize); expect(body.request_page_index).to.eql(0); expect(body.result_from_index).to.eql(0); }); - it('alerts api should filter results of alert data using KQL', async () => { + it('should filter results of alert data using KQL', async () => { + const agentID = '7cf9f7a3-28a6-4d1e-bb45-005aa28f18d0'; const { body } = await supertest .get( - `/api/endpoint/alerts?query=(language%3Akuery%2Cquery%3A%27agent.id%20%3A%20"c89dc040-2350-4d59-baea-9ff2e369136f"%27)` + `/api/endpoint/alerts?query=(language%3Akuery%2Cquery%3A%27agent.id%20%3A%20"${agentID}"%27)` ) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.total).to.eql(72); - expect(body.alerts.length).to.eql(10); - expect(body.request_page_size).to.eql(10); + expect(body.total).to.eql(4); + expect(body.alerts.length).to.eql(4); + expect(body.request_page_size).to.eql(defaultPageSize); expect(body.request_page_index).to.eql(0); expect(body.result_from_index).to.eql(0); }); - it('alerts api should return alert details by id', async () => { + it('should return alert details by id', async () => { + const documentID = 'zbNm0HABdD75WLjLYgcB'; + const prevDocumentID = '2rNm0HABdD75WLjLYgcU'; const { body } = await supertest - .get('/api/endpoint/alerts/YjUYMHABAJk0XnHd6bqU') + .get(`/api/endpoint/alerts/${documentID}`) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.id).to.eql('YjUYMHABAJk0XnHd6bqU'); + expect(body.id).to.eql(documentID); + expect(body.prev).to.eql(`/api/endpoint/alerts/${prevDocumentID}`); expect(body.next).to.eql(null); // last alert, no more beyond this - expect(body.prev).to.eql('/api/endpoint/alerts/XjUYMHABAJk0XnHd6boX'); }); - it('alerts api should return 404 when alert is not found', async () => { + it('should return 404 when alert is not found', async () => { await supertest .get('/api/endpoint/alerts/does-not-exist') .set('kbn-xsrf', 'xxx') diff --git a/x-pack/test/api_integration/apis/endpoint/metadata.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts index 516891d84dc91..5f18bdd9bea02 100644 --- a/x-pack/test/api_integration/apis/endpoint/metadata.ts +++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts @@ -6,6 +6,11 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; +/** + * The number of alert documents in the es archive. + */ +const numberOfEndpointsInFixture = 3; + export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); @@ -34,8 +39,8 @@ export default function({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'xxx') .send() .expect(200); - expect(body.total).to.eql(3); - expect(body.endpoints.length).to.eql(3); + expect(body.total).to.eql(numberOfEndpointsInFixture); + expect(body.endpoints.length).to.eql(numberOfEndpointsInFixture); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -55,7 +60,7 @@ export default function({ getService }: FtrProviderContext) { ], }) .expect(200); - expect(body.total).to.eql(3); + expect(body.total).to.eql(numberOfEndpointsInFixture); expect(body.endpoints.length).to.eql(1); expect(body.request_page_size).to.eql(1); expect(body.request_page_index).to.eql(1); @@ -79,7 +84,7 @@ export default function({ getService }: FtrProviderContext) { ], }) .expect(200); - expect(body.total).to.eql(3); + expect(body.total).to.eql(numberOfEndpointsInFixture); expect(body.endpoints.length).to.eql(0); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(30); @@ -107,7 +112,7 @@ export default function({ getService }: FtrProviderContext) { const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') - .send({ filter: 'not host.ip:10.101.149.26' }) + .send({ filter: 'not host.ip:10.100.170.247' }) .expect(200); expect(body.total).to.eql(2); expect(body.endpoints.length).to.eql(2); @@ -116,7 +121,7 @@ export default function({ getService }: FtrProviderContext) { }); it('metadata api should return page based on filters and paging passed.', async () => { - const notIncludedIp = '10.101.149.26'; + const notIncludedIp = '10.100.170.247'; const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') @@ -136,7 +141,14 @@ export default function({ getService }: FtrProviderContext) { const resultIps: string[] = [].concat( ...body.endpoints.map((metadata: Record) => metadata.host.ip) ); - expect(resultIps).to.eql(['10.192.213.130', '10.70.28.129', '10.46.229.234']); + expect(resultIps).to.eql([ + '10.48.181.222', + '10.116.62.62', + '10.102.83.30', + '10.198.70.21', + '10.252.10.66', + '10.128.235.38', + ]); expect(resultIps).not.include.eql(notIncludedIp); expect(body.endpoints.length).to.eql(2); expect(body.request_page_size).to.eql(10); @@ -152,18 +164,18 @@ export default function({ getService }: FtrProviderContext) { filter: `host.os.variant.keyword:${variantValue}`, }) .expect(200); - expect(body.total).to.eql(2); + expect(body.total).to.eql(1); const resultOsVariantValue: Set = new Set( body.endpoints.map((metadata: Record) => metadata.host.os.variant) ); expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); - expect(body.endpoints.length).to.eql(2); + expect(body.endpoints.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); it('metadata api should return the latest event for all the events for an endpoint', async () => { - const targetEndpointIp = '10.192.213.130'; + const targetEndpointIp = '10.100.170.247'; const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') @@ -176,7 +188,7 @@ export default function({ getService }: FtrProviderContext) { (ip: string) => ip === targetEndpointIp ); expect(resultIp).to.eql([targetEndpointIp]); - expect(body.endpoints[0].event.created).to.eql('2020-01-24T16:06:09.541Z'); + expect(body.endpoints[0].event.created).to.eql(1584044335459); expect(body.endpoints.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); @@ -190,8 +202,8 @@ export default function({ getService }: FtrProviderContext) { filter: '', }) .expect(200); - expect(body.total).to.eql(3); - expect(body.endpoints.length).to.eql(3); + expect(body.total).to.eql(numberOfEndpointsInFixture); + expect(body.endpoints.length).to.eql(numberOfEndpointsInFixture); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); diff --git a/x-pack/test/functional/apps/endpoint/alert_list.ts b/x-pack/test/functional/apps/endpoint/alerts.ts similarity index 55% rename from x-pack/test/functional/apps/endpoint/alert_list.ts rename to x-pack/test/functional/apps/endpoint/alerts.ts index ac00258ff9c02..1ce7eb41e6690 100644 --- a/x-pack/test/functional/apps/endpoint/alert_list.ts +++ b/x-pack/test/functional/apps/endpoint/alerts.ts @@ -12,7 +12,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); - describe('Endpoint Alert List page', function() { + describe('Endpoint Alert Page: when es has data and user has navigated to the page', function() { this.tags(['ciGroup7']); before(async () => { await esArchiver.load('endpoint/alerts/api_feature'); @@ -32,12 +32,31 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('includes Alert list data grid', async () => { await testSubjects.existOrFail('alertListGrid'); }); - it('updates the url upon submitting a new search bar query', async () => { - await pageObjects.endpointAlerts.enterSearchBarQuery(); - await pageObjects.endpointAlerts.submitSearchBarFilter(); - const currentUrl = await browser.getCurrentUrl(); - expect(currentUrl).to.contain('query='); - expect(currentUrl).to.contain('date_range='); + describe('when submitting a new bar query', () => { + before(async () => { + await pageObjects.endpointAlerts.enterSearchBarQuery('test query'); + await pageObjects.endpointAlerts.submitSearchBarFilter(); + }); + it('should update the url correctly', async () => { + const currentUrl = await browser.getCurrentUrl(); + expect(currentUrl).to.contain('query='); + expect(currentUrl).to.contain('date_range='); + }); + after(async () => { + await pageObjects.endpointAlerts.enterSearchBarQuery(''); + await pageObjects.endpointAlerts.submitSearchBarFilter(); + }); + }); + + describe('and user has clicked details view link', () => { + before(async () => { + await pageObjects.endpointAlerts.setSearchBarDate('Mar 10, 2020 @ 19:33:40.767'); // A timestamp that encompases our es-archive data + await testSubjects.click('alertTypeCellLink'); + }); + + it('loads the Alert List Flyout correctly', async () => { + await testSubjects.existOrFail('alertDetailFlyout'); + }); }); after(async () => { diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts index c6a7f723bfa2d..15ce522ce56ba 100644 --- a/x-pack/test/functional/apps/endpoint/index.ts +++ b/x-pack/test/functional/apps/endpoint/index.ts @@ -15,6 +15,6 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./management')); loadTestFile(require.resolve('./policy_list')); loadTestFile(require.resolve('./policy_details')); - loadTestFile(require.resolve('./alert_list')); + loadTestFile(require.resolve('./alerts')); }); } diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/management.ts index 4925fa7678ab0..640f6264c3a09 100644 --- a/x-pack/test/functional/apps/endpoint/management.ts +++ b/x-pack/test/functional/apps/endpoint/management.ts @@ -37,32 +37,32 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'Last Active', ], [ - 'cadmann-4.example.com', + 'Host-cxz5glsoup', 'Policy Name', 'Policy Status', '0', - 'windows 10.0', - '10.192.213.130, 10.70.28.129', + 'windows 6.2', + '10.48.181.222, 10.116.62.62, 10.102.83.30', 'version', 'xxxx', ], [ - 'thurlow-9.example.com', + 'Host-frl2otafoa', 'Policy Name', 'Policy Status', '0', 'windows 10.0', - '10.46.229.234', + '10.198.70.21, 10.252.10.66, 10.128.235.38', 'version', 'xxxx', ], [ - 'rezzani-7.example.com', + 'Host-abmfhmc5ku', 'Policy Name', 'Policy Status', '0', - 'windows 10.0', - '10.101.149.26, 2606:a000:ffc0:39:11ef:37b9:3371:578c', + 'windows 6.2', + '10.100.170.247, 10.113.203.29, 10.83.81.146', 'version', 'xxxx', ], diff --git a/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/data.json.gz b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/data.json.gz index 0788e40326bb3a6d22a80023d60a44d23743e051..05fc7d79faf46b6389d3a8c6f9eb2df2b1b0db30 100644 GIT binary patch literal 15777 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 A$^ZZW literal 2063322 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

(8myW43Yz1d%^ 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

pvJU6- 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

# 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 MP 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 z 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

yo$bQ3}kLh|SdW7WRtY$S+?D+jZa-r9W=|L2X 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%0AaP+x~edTq-LNqLv%OvDOeQDf~``}3UiF^ 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≈qsi%PEak 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 z%0A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

l&Yy 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%0A-haHku>6SZvkx2O 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%0AbBIfDUgkm8O@0HuJVT=WLFWRluyrqj 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

@*Mn6-d-}v1Ti*M#m%q$q zMDh}6$Xpxa`5Ru>?q4;eu>ZYmPNcwxSIB&HKoC~Kk%^_L+)r;gxr%WN_LA*E*Jep# z$V4zLBr7Xtwg83_=N}cdV$>A-1tely?VqCn@?bP;)-MrjIwEp}Tr({7hevem_z9Rb zi(P{sEwD1fl_w}61=eUI%A?%>IG6zmRk5=7=Suj`@$cuvz;D<899tZ*%P^BECdzVF zeY08`K8x4K`&LUV+NbUD)(PNta?^x?GPb@6+L}colRMXm=Ok4XnRPvyQPYZ9Tb?Gs zN+ex*oZZ7eHWjb{^B$O}5(TLPiHl-2El@(DF}pioY4PIMBiB@ zM~R-M@Q}8Q9rGwZf+nsz{u-2g(1E?kP|T{*TzEj~SGov~vB7lsT3FHA`Ysy$BU00! zCdB6G)~y}@rslJ%;m#JGCbY<2W0i7gH(F+O9y_ZDGX>{>bL?JaK{SQt2} z=4Crxm*_fgIn;C@{JGifU7#FoJ9=BgdY|94i zwj8vG!%{~xND5?*mjXx66D+Mdw4QMRWpb^Bhe<*rzlSu;+%L5{_ z>We_T*(_Xg8>F!Ka6}ZT0xe11_8o<(|4uBDZ_L8gQcb!dMus?P}(woLa4)GryZ0jygt4?x_DGzt!iNQui#r znPf-%L~wP;EP=EU9T9>*2yAM^sdNC&zQEZv4e=zGr#L}O94tcLtgqp$6N4|cbP|k4 zb8YpNBV8BVAt7?D3eLq1TC^4UJ4h8k5^M2xmzzD6N_q^Fzv+^^q=!0dB}*em-sBgu z9G*&b$mTEy8W~iuxrGJr^4tv<#$3F5VS!&Sw#HfsZ5S0d7Y`RJXHOIqNcCq)bTl3pSD72&~v6-5*&?#CY+D1Yd`DB$(gch?{n0ECgO1cq&yfZBw zw|9E1Ryr~8{XvB*c%Q2K^Y^mr>n}2AK_N1%EQ4!&k`;U7*XdbXt;Zt^eDvV^chl1T zK|W{udbJg9hoic*Z=tRuO>RQ^gU`{ z-99!<1^pzaaG|tAvcJ|3jW-E05QpV>4$EDy=aU@WJ3fGwdzCid+D%ZMjZO-}r@NlT z&%aiulQdU$L*pIGYl6#T>sL>Z#b=p%Yq8p4uIYFA7q(Gr-ru;I&M14<8K+(MXwRY? z-*fw>nl^WeV;#d{B_1Xq#S6>?A>U7tBJ=S!Y2tFCY|IDnX( zNsHKLZH^}5(eoDmeB^R;3)nqSnFah4jd@g@y7)ws+*3txVcVP%l&)Q?ZsNc_QQ2u& z%ZF9Lf-kI&__!LJ#h=dau6A(Ni0Ud4=w8LRU;6ZDeR49~T7&I+5N4xa!m0>0?U;V# zTqCP(I?79}Pxaz}@Nv)i&#h^D)u1D3?x2YDv~~CF;f^9D|Dp&K{NZtR=@A1on~|6C zt2%?8v+y6|83Ce!>b%-oqPC=a+v4a7KsiailmB+%XMWkzRwR^8RenYl<;6?y+)q~l z*i*ub?9$nyqR~ZvXd2f@J5p=yCktsD=cG3hBzfJzdY59-{nf)6!)jxMR1)Rm@l{bm=-QQm)u^T%WJ^wW`CunUh zd1@M!J^lM~r@XMSJJ1`eZTtAp{-oDjh6b)dM?v-2!6K2BD1Dw z;|I29KgS4ibk1M&VaS(@2VC7-ASBqqi~;krW-glU?b4d zu-+`;JU=qYd3R9kD7<)n{ue8E>XL~BSC)d z^|dXnP0t6pi>@`+tEIUF%F{cU3)l0F%x_etqY{Ucx94kOv-YvOVD-(Cpg$w@-W zPvtAo28^P`JC5%`1FC8S4XE^(hWsj(@Hl+RN}J~qmjv$KnZ z;*Y`w%quklZ?_lFE8BL~p*yRq%_Pn%!cI@;y60UVpZ3oC^Suyx+N(cDM_U`JK6~~% z$KMGo&xXbu@Md+jO@5Db%&Sj!^R8~4nJc6tJsWHFN6p525XS!FL>`Dl4}y#2ZB z@V<6N<$S~t_|9LJ%McQD)JP1$Wy|;80--|SZdm#C^itm(ulENMo{x_|M#}T|*acR2 zd~RQFq`&$eB|PGjFNVzJU(c-I+;8T3@&$U*i>qb3pOBvH-?}S?o~XNcdP3g!S@CTv z?ruMO&FddRmol=`ecwzyN$G7*)Zw15m3_K+%>}pgHGHHG4~psQK6mmZuGE)O-r%YQ zDo>xyZuSQ{v)0^imVgVcSDn@wcn>Lk>8}nxXEO=%|EiC6FBgj+Vm^C1naA!--zMrQ z^_%p)@)9y%ip?HSJ6Hbd``o%c?AzO(Rbw(@UR_?$ld;BRAL_Sj^RHJ;yzC&H!*c}`n;(JGM-UdIoVQo_rk4rra#=ZBA0o3)wPR0EX>_5y-or@ z9vW(cPcH}am;Ob-{fqcxsxN2A5v}&PJGu^RECjZvXER>VG2l<~zhD2nWDw}&h5D5F z+BW!bgW0U`Gd+$`E%pOl@zwt0+x1C%$f{MY!uxbD2etmA^izqNj&oAIb zDs2$=T#fm8VfEc(mdD)8^jeMPji7|X9c%blT}SOiYEKIxZ6y*)cu;dQO*ckwQL z-5&0Fy|nD>GqQFGYP{dG(czt`A? zH+Guz>bvTn92C|yew}=deE?q@1XjIEUhm#aKR1dq2U4<^y4n@fFFRjr{>E@{W`Cdd zvTWB&+m$8x?27}+sn@4$f~5{l`>%xr6ZPkhh{mUCeLnpyudjjRF11Tfoor9sE5`OJ z#^|ly)4{WYAtXdM_k-3={Ta^>PM^1#NhM#0yXqHA{nL$sq!%&IcJ1gbuO9gmAHC)G zdn&@SgI4G78_~k{<>b=ytoc<{(9`o>tjo7)L*FfZlc37vv&B93>A`2KLz~Bafqn>J9ho=JV$AQ~z%0ZOn;s z>U{9@ee`DU>EhYpOEB9rt}koddhPSUUO+H=i=VqEMW3L3LxW#OGvdm@bMJG}(3k!3 zTc#nh>RsRR?BRNcO(>8hxU%Ze@h=l&HXS(N;bvd*km7i2);xu@b)3S8mvLMva84*N z{Z`#qEOB~$nX>nKx;@r&LlOJxcKbN7einqjON~Bn=QFx2*(Wn060QkfHRrv>2wisy)%<*$*4i_^&CXvJ$@*ID z|88IV7TaIW6uuL_f|Xkz20u@#RRgCE;5NIeUGL{GF`MjfOgApNB4WldHyCN<7rorx z951LWK-TY#Y=09A4DB81>v>O8-;FKl>s=J1E_*mM_&hE2Wvc%AePTe!Az&wg4+S;-x%cDLwsQCP02|?7`N?4?GbO+L0Xc|~=m5JLSGb@Y;n`LG`%Z<|(Re3zT@lCA zt9q!|+XQ#6u7|xHROfpA-^A%0nFmbmH?Yn;pSjHsv&i>?h|0N39pkW$a*Ej{q$rxQ z*7`1dq;$qwe5Nv!3zV2P6^;As+%bU|Dm^pwy)inXm>#hUQD1Z!DASw{pZdJp_pp2L+iy3u%A*V5R# zNxP}FTjgEUHSHd?zMlBLluHXw5=ny5DSukVZ%&`S9AWm7Tq)6L^xhF8E?2|;72>mN z9K?L*SiCBCU-xF8KJDge#pj*9udo@bI24%kQ?Ic77Z#xH(DhE@%dmrZVHqPzq>}c( zg`waa5&&c3at&SQMJvPXpP7zp7L%+6K1OfhC zZAP}EUe)R}JZ-YhUSY?9x6?8N;r=owc9u7v{-dwyC7*LPX?3rHy7#Nd_NKm!dLK^< ztNLfFRitMww0m|+Fkeqemvhy>?fSway}s7M@0FPxBX37KYY2CbD;-k}#&wTWL` zb(I!%sET5ty*(d7Tr}6f%T>R7j+R#Y7*S|vkBgJit;tN6b2-Nqp2a*rkShNUJg{Yn zsMgS>N)b{mHPNyTNrd(1Qm-3~AzVQStuiPeON~`*T-1bd*;yZ?Hsu7t7Uj085GafX zgh9ksPC_eat^Mhx3Ej_`KK}F1d>EMx2u+{8$W!x9mlPhRn6m37bP*}3Z-w44Z(Xk* zz`gyqLSC(A*KXmg3kq8cKTb(*2CdB?(~KA1ql7T|;-U&l4GLUWqCXo6T4?VHOc=K} zA#nu6`KAg%)TK(Hwvsqx(c_&Dfe;@{Js<~!N=B9FMMhDK@VKf~PAB&o{A>uKIVIlmby2~v3nxgQj#KR{wotW@bJCe z%IPWNre{Z?I#Ro*XKrBOC}Ql+$tnV$$k}fiq<0Dmy65|_nnz%gAh&{*uwwGIX%9!w z%WeDO2R*}RJXaL_a`Nk80r29OGwtjqgmYAC4v5ediHPJHRwPa|vGMIJ9AE<$WjRu| zP2QQ3h-Hl87&j%|#dsxc=K)QzS_DnXx17h)Fe?@lG-~q!%mFOXY&fXqV>0{ggfmm; ziygDPmea(-A>e|fD4_u#K4jgC;T6d^c_lKK+(EmR^9#rO{hb6c;FViWkrs}V26S-c z!7La%NX7#|#|#n%a=sh5P}iXh0hB-@aMhTOns^?l zF~Ary1NTfQE$6YAxq;S<2&&SyRY~cffnPqtI|vhWs;J4#M=eFdYJ13h zu#>TPh(=j?QxN)Y|A>bidJ}SjLnlVL6Y>TU1DUmrcR-1lf(~7}-wIB};$lTbwZ1V#5fA~Cf?X6zv$P;CEnZ>=?$+=qzY-QK z>hG$D9JaLW46^#KrntzW*gD3U`HXl*Vj2uYG3F9K62R?BJ}GaXATH=pb&Pa4=9(WSDv{^aW06BC)4UMy=R&BV4N;eG=n zgIq4mj3zO1tIQf)V;ef+d9v)N9uqlYO@`sWT(Rb0M%?vwHE|uc^%xEqgMOQ`q&3%- zl9f}fjU=Zzxo`~cO4CfFJrKUAY{+XXycnu!{W;Nc|CE%ZdBBx3IP!Ib%`I{=7S*gj zl0O)ANWn8@YD`pc$xqI}3FDU^6$U~%gJjBy>XS7%@o2O}qHlTBl|TAt19OZfRz!<` zpdUdJ69lbmz~jKHtL76|1#9u8TOJyr!ua=K`8kEv2SWxoD9%-?XzOZf=r*$=Lh8Ea zp6RT8uN+sgd^NVgtq}k5LC9QJ;<6$c%SkNX?#7U%5mhuwotPH+_(hp-jCn9eqaJeW z;;Ny2Dwi~c3Q+`u(RPPMOgqz?1RsoX^-C<$sWw-6?2DL@qA}YdH==Pu3$6AT#|Xn^ z(m-sZO~G+|#?KmBMQj*auN6!a?Z3KJEsM>};p zN_#={qpqr?-`Y%ZsSv%f%*4E;x$h(=TL|Nvo~EU$H0NMwM~|m0GwpKze%~rxjNQgR z(W2NM*e_MnWUU;13RhXNj(S$9|RWap_J3|J^uzde*tH6=9Uw~pl788_{ z%!uUvZGE7?V+iL1nJxASYII6YKc>2t_7-WoZogTWP*GxNP$*OXDkIBq8V?eRhPDIN zYs0J8pY8*_Fqe%@kh1QtA<;M;t6sWFWoD~ZcbM_ z%g!aeiiy@=rRpk3N{sV*)IhNNLej1&=Jp!bnl4xS-%2aP-#g?GUt&nl1N=;-P`OY= z=A{sU2`2~Bdg?8wc$`apg{53W3*M&bPIw-J@3wvP8GBnNSawntZ>y2g3R$4Gq0-Qc zv3FZBxME`PMmtWj0Yny3>5BkXT*VC|tQgL#AT+KdkaD=qSUF92#~*}$dvFsd=Jzpb z|JkUi&Ht?i#hDS2rM4$)&|-r37a7i%^Jd-=P(?THg~lcwTUgtt3z>ZtKm*C7BI|RX zZE`~_5_9l|6;QB=cI37C#5^z!3CJpwe_vTW3rd=ax!-adV5F%<AkSGn)?Er@Ih-kTsH#np$LQz~#Rj5?*fFi??kG|f$o$%xVI4or4 z=0*$O2E(0sCoel!9y=LH%aGjF>afAqefA8-uk~YAD=dH`H7lW?6G$jlh$9Mb64i&0 zvYzH3Rp6MB;6~R-2{b##F_|j$g~iS z96+?i}$ zkR>Z~)zJ0|K9euPBz5jn0)PZJqYws6w1v7`!|2|Zsmi^w+=ABTwXzL+%zu8K5#>I}0aTPPK3kh7bSZp7An-BXGc@dha?_h@qd@^qL76mp zw7;l@-4}4GYH{^bO@FCsfR&bInu;}rPGmsg^MjDNOAY5!g(dKo^DW!r5KB5t0spx~HfUF-t zu3ss~9Cg`czxp|c<5v;Bz+hs^0)x`f%S1m;ipk{9GD1W8I-wg%YC*|D!mGAGdtDdm zNUULA=MXC0dK{rpjOS&uv6(oArPbXRykC~qSY$^4Gc3#Ldh)Wmhls%weJ#_|8)*;T zi+g`tD>ZLXCfB%Hw(K%@sPhDbm)W$tY>I%B8MwltDY_0HU82`JDq zZ)F-5wj?)T5K4d7?w?oBrO|GO%$s38x${Agx|Vp+YSAzTS#I?!n}`j>v2qdnRKnoL zPErZX%GoNfiqhk#Q@3CB&)>Z*V2?aD39&kF*;^R15Oy~AYg+LnJZu())17^hX zD`-JFe%5D|p?CaT$sypT_*^$e^D5I3y$Cqo!ElDwy!R3pMakrs#?Ok0rwc7Li9&2= zLYa`lRremHfW@DYlbBeD#1?nIWF{F`7K{qSb=lZXb`NU zGA_`xli+BZ#;*5+{`Z(17{dO*YH_3BXDIK$ieQ&F`ee(k9j>XB?_0@Nai8Carj5;H zz|e#INBZYP#LP|lBLM7cePiuF@D~e4k7+f;e9w^JC)b-B6f=ImGxFV>F#Jh+Ly{VG zL@pv)yiDlk5iGYvzD=|RQM9}vd)TUI0!or~Ma(uWZb~MrN&ACkz*!heZpO4`QcIEm z6*{ax%7y&M;dVDf^$!Wx@ED1q9DGfC} zE#uH*-&AmJ__NzG%JH)&Bu6QX{3U>FDOSX)%;Z2TZ%3!_41>@3DC%Bco8nQZ<;9j&Sy5?vXv$Efn zv{v}~$7zJRNB^C<8($gc-X-T_C|jg*evp{vK9@c?MT0T*C9uET*k7 zWvmV?FTCa~Wd83}=F8_@+LaCB5<+<5fB~aG*o-W52o|lCzU*YDJqj}O9zHK~T1CmP zC|77H303&D5-CQa_Z{F9Eq9M;PE1r9s!!N@HCa%zXeRk%daLv&?GyNJwc?nTCQ=WJRjNoV61x> zUQqD(Oz-#Ie$rbeI#@U*jh0H>*v9xx@3A>+Z0H!I-tYG>*20`f3ek$@EWHqse#&fk zs2btbBLJkm$SU}xH zIG5et4Z}Y>6mb+1hOom`4Gl6PB`R#2*~6K12PhyK6$Y$`SnIY$I8@G*+5};wf+d+a z(bEGPgyqRZwrA>oE{_^6?jkr!08>6PDex*B1bB`;H2?eTl@Eh-19L>K0}9jsXvK>c z*QD$-FtEEvyN9m)qbIVxY&Aq~@H0BL+&EIRQS{-*!R-%(0rFw9IYdKKQgq=qiH`OKq-^6X4X)4MAF`8Z&1v=%t1oe1S04N z$ON6z$||9RA5AaK^XIExTVsuZoRfhGm<@&qTn1Yb**Jtyv8EO#e$sgGI!2!(J%%`U zUjm*@$fs`c(CN50UklsWxV(h?=wCg`Y@tmTP?+xU53o(~DawCyeChD)km#d_7QgV{ z)vM~T^c3ol?HKJ)@$};#n%Gk*IWl5YkT1@P=5OC5c+d2*0;X@6;58Xg{*botBPoIG z!o3?ap^mFdtLW+pZIbp+qB>TXu94c7*@NrMn}Mc@xeC#8ah9!w^Nlp>#+{jOw1CXi z`pRb(-kZC*gCGHq>EE1RRM&RTR;9~Dt41qBMo&eNkPZ3AB$cp&AYN5^OuCX7rMQiv zP1*$pA@$w6%W*-Jb*SN0gvo-Y$#Zo%6u-c_`W~POUHHDX7kt7(ydKGQme*PtB6Sul zzF?70sKg|weWa!RcOVl1=bmLYy^eSVK{TP76%Of=xsXYN=2Y(Hz`W76!EH-FwdcZw zyIKS;hJ2;;&SY|B#k_x55o%gLz%q(axEh7+E7>8`!zYtRlvi}DqJJuvo2kXdNwrFH zi`UO@R1VAi+)vskM_K2CjKiyd^jA% z&C$569ARw+g}}QYb_*(|aJsE_al)^Wj+D?& zsF+EPICfgeMPRlkaPU%z)`FBLWL6LQ^7|?;s(vf}_Z=|M9yu6qbjc73qP@f@JrGwz zz2^jvJv``&Ta{+-UjbmQo?5L~RjF`!dsWokR5f5E1!~E9Ti&cK!p@sXt2lVg0BR~CNvlbWBJ)nmDFfG8 zNK~i!K(O;FO==t06+qy^4%LQNX$~sQF%IM^%CQ$>_i(>k^bnC5Fk;`%yfi7MyoMst z?bYRGl>J5X74%FUtMiM4*Srz>Pr7rGahzW@YF25t?-NTA=@GM0ru^76U}DN%%61juZpsbFQSLiVFZ!=osEk7MroCAkU28RN-bdcKh4VCOQile)Yn1qlnF$#5$dA z<9FzURP5K$Y}X^hf)Jh&S%S6Nmw*bHB#kVBS!Ff$WQ2Mh{lu}8SsW2Zj(wS2HIueEzm@j}}z#9-2;;{W^H%K~3ZGc@4i2WYdAqT# zaaZ6wjuSbu_3lhULwzDXrU)aVh!93TY{lRWpwq2@E4ThyQW4HgJ))mjpjOCt>uWVG zJ58k3jQ(j|Q98evU5cYz&tR+cX1z!?4LZPsL`IiCz-Mi>4^=CQDg0AK+Glq{pz(M4nr^0B^>xFbb0oT(pteED-W(itP*9Rz!F*&){(^xV@nw zaPFP~k$}QWczQSjjmF*Y(ZaY`BSGdk^eaJ6Rx%zJ>V$s4lII>?n{78v0?=l&h5~fK z?OT=$lrv+YWI)6&hUUxG@~P`$EVo-IIk#l zY5nlxsLO3991ZM>R;Ec&$Z%Z^={AbworUgJT}nfdJsmLYhzWo$P!1cPOw~ympmGLk zro&|Z6Q|N$w6CX3J;T`F3f}FR#Wu!A5lBRDiMeH3!J40 z!O4)T>c~|r32_i5$t^Q6nq;!d{2Qo7oW`^u9TpT4lf|!<`O@18rH!i%cl&oU&+jkryzf_74@oG5}3L$=|^aJ;FY8iU^DPfBjMeFFq+KAj{E1Kr)2JR zqYAh6n_9171#tZ59yQ&=@)rYGgu8sda>w7mY9$0H=nVOq)ubV=rv5q3L~gJOFgV`g$h4 zBuqcFd#x`J{KRUvOyQV2&%NWDpwj6sJsRo0!tudwQLy0q?G3~$wGKc!$La_m3QuIx zWFC+zx9k_qMn1=|xSnv9Unwc56OlJMv#PylT)xUb>dTCVSK=P&IY%HOfii>y(1ngr zUHt;2tbm_MfK9rC*(=AR+~$&~(Q_$EldS*)dQ{?IiOzJilbJ?+i%R;j_`_T>D;Wl+ zG(-?37GJ~}e7uH@IH%aGo#l!rPwtg@G6B$ANQk3GxP)3-yo)AUzD50Xhi*zEv44@L zf^o{i*e$!uGJwg@<%zUn@NWP^fJQ)m{V}wN5(l^CuMVf5%zj$8)^8Ai39j1dUN?1T z#1&=RNi@aRU|O`RBqW0h*8O1z>kY(ABt59YpelQrj@e4!khMS1byb0>c^f4^L$3Tv zZP9%5Xg*gs1X7R`2YJ0K<_3q;gQD!hyJ4&FGhP zMz9KuVkwM!Exp5n8ab>^Ie4Njm5Pf@_$^0xW(-3zw53sOT6)WBc{ev3j5WP@YienU zMOXx;ABf6!+3l?W4EBzW7KSZRg8N{=d~qYkD@I?51Y%sewr3SD(=lwFFEvx@NPu;l zaCx*wqvEuZD%|e{t(U(-$9&X|mG8E6EHyRYO|kv`uFQ=!VvvI-@rcuAFS0_A;A)`o zVk;=1ev|N8Ys*EF5xbbQn^a&Pj^o0yqOGvEzE;o{iZw#Z(T?XyU?A{}ZW6?XGJ&(` z6_g4TfPqz57BlPc3{q%Ec{@dBW>D-+KA+EX&F>jR=DE<%P+SAR#987mX#ih_q6(tg zL77m45Ko&|#Cpo)6POXUul0|A2euYkUr3)nIZ`eN%`4RpqLPVGJ)EZuukv?$ujN*o zXxODKYK5{Gxge#H@Ii0oMestbYE&v7S^+5WqLzvaHVT~64DpKz4YwUIIMwoo7}CZJ z4vx=bRT<}fr?f04-ojfe$f8MRXuiDoSoMn5k>^PSy%L*c2+K^Ns2V~wf z?^4ZJLq67N2zJAUm3k)-u`oDA3)F1kNM}D7i)ragK5#=cYcwt0f#H}*99>7;%Y5Bv zm8kUPet9@r=uy)KRZK>Wc@MS)wIuVue%^=+d5xeyvn++LjF|^$jWrr?Td<8G3D~i& zE>yh5c|Q_9^**N^fRn%PWpaD0d7^BE&@Aos*jNnu^AesAlm5W!l38IHc;(TsdF%9O z&=O8bi?PA>-^PHC6f$YmBIWcz!C54U4-2?kB6PYm2!}-@`Bf;edD1h1CNccEvD$H_ z-BwL%5C01W)0D$m#59l02PZFiORMa!<$_NoZzA1TG_Ye1k5hr5=bi!ZU23%Z(0xk} zard?A>I+81<*|Nlt?dc62YOgAj%qFlaj!1=hDB0YYimjQY221Z%k$CDq^HHx_CyqB zgDYmBqw>EEi#y+1O!%a^kaTrE)>{M(ny`d#QpN z4NvCj_)TbXqu1BK3<=3${d#c3)=Le&vuS8jroi|fiw;3^AH_!&X3YiNrDlS&Il58O3lD2XjPVXK9K-hb0H6p&{ zP+DvoCRBW=7|>F;)}joT$_*-EWv(%9YTDnIlwC2h!KGE9KN?44L5`9$r|S>VDf>5)XH40 zHk-{K*g7DjkDiD;V`D{)fy`8lU9-7lK%y>%eeo2Y3}O+hIyBX)AZ27rWNjnscqHuN zb|dmSE^V8_W3c^EKLZW7KqZ0LQFUJ9kFz+Ckx`pW1{V0|-uLxYBPc}O<(CxDkQHd& zw;bDP@chOYgObKth;e#UHutk2W5-U{TO)` zUSk2coyRzl7;v35R(=uEqsNJ0t!ShD;uV#9Zp%c$xDLL;4+GBREf$aLk&Z7CHeHdp|MKw4VHT%qUy>MH5M`975xg!Ng! zfy%rX)S8Eyg_62mn2!IC)#0s1TPEjB!vx4U7Ks7>1c@HZD-+7|$3yLvVBLwPb35ZpTl|iI>8uh!=gz(tTH4`xf3T~@gtNeXq%?aMJy_mINKPgu|291 zXc0-Rp`|u*ya_X^=mSV2C^thD=x2UlKGxhD#ZY#igRVdad}w z5F9cG0SCo_UR6zi`E3@(?HAKI_}gktHZ%w=B&OAYX8OfmCNq<%wOdPj=et|y|M~|E z!oHppWLddcqQ*C!cEFu2xh6^#-k%G=VQ;@^-)&_lyZ!MKs$Ptq#@M-}m%Z*Nk&+1L z(5<5U(96x@L`M^nNZKUlS&Hc4%L|uawcbsUA1_npwr4|IC6gsI4L~C*q638>ft|## zW-W%mmQ_`gD0D=ZW;4cb<_|tpQBp?W81B>C`&7fUIR;F1p|OpNA9I)FenKuuDY0j0 zFNH2}JC~M%_!$i2)?D{hR*xPrRH)!Q>EzJ=NO)6KP z{}1xcLMyIl39z`kTX1(8m*CpCYtY7BgC)U()400_cY?cXW5L}C?v~-bnKf%>%_qzs z+KMh6KvMLq#$@-XTJA6e6=cA#5 zi7f&lm9B7~3uzL!oG5IYyof9K!*3BW2_rvLn!xqek>W*?`2IGYXmCb*8X7}b=Fx+$ zO|qQwdFG2kWeA96eKThztHtH))8nvj^}%7WO#i7#lPXDc-SekaV|h}t%V=YA;pavX zt)~2r=~8MtzZ)!f)VjqHbKw z(Z<~y(H=pV9Y7%3!kgD~BR$Gnaj>pp)24zBWFxiq*QN%e%<3}`h9QJb9O*Tx(h+{P zpS40;5#Z5`VXQnqT6kj7%xabhNrgK*p2`ODW}c3# zmEazqmDx_mr>Q@`Xhb;krbQD~fxO;hDVc1Qo@l=%YlXBDN9sne3%v;t;$08g_L*cb z;Au)Dkopo9RnBnM+oULjq+sl(J+vDbu z7t}*x^I5RAH>w+Jrb5;>T+Pr?9g(GTv>z!*g5{2^ug15V`?n^vR(Hibn4&FIr$vRD zu5KVc7!sa>mO(q#74y$kM&CqZa&e5b7eP}R7&jU+5vo0r4~fRF`;DbU+QqjZ--ldU z)%m|Vol5&*DEiP*G6av6iiswxS_L$4G=+Ur8~Z0F2Srne(_jkPphqJVHcL3e)hXPg ziN#qynpOYu43{k^M@{i9n=8kLLR#TO2^hKuBNs@d>kb*r1LsDOZQ!gEdZnS7o3T-e zvA`C%(wHl|bdf>=VOwr+1R?g{YX$5ldVHyVV3d*wjvyL6zr_-JNbH-SqFZs6MTa@5 z&64SHLh%g2v2m43`-I8|puruE3D?C{aHUFZBxeXQa2Kz=yXn1Jn*hdX{d&8O@l-wFISe2;?L*> z)mv}r(bjslTj@C{<(S1aN7U+G9(TXkbqdb^&#Cm?4TOdM@-_li)*4z~q+WQ`O6G7F zmATK#p7oXf*1Hrq={5wCjv-A%Ko5&7&syt2r;Ge90)T?T$8=--%{&2@c#U8*fq+Mo zX_pNtVkjkT!u~v%YT7Urn4}BG6u1j^2&-fhR_p`1@s!X1d3FO^V0I-fv_ z3*cP;US3Bre&97sB18eH2}N(%mZFC6A34qXR-6RM=1+xexY|x$4kS$xIqqTaLmw(% zc_4+(u4r)yXM3`I;{Kg1O}r~xT=jiYG!)05e*cfGRKdWvd?oQ_7pCG~n87SL-o@4r zamu~}koFY6H8S}NQ$C7wxTusmQ3Y~_2;O*nldVh#oiKg9kngVsctl55+X~^VUzp{& z{JZDuA7g(?KWRS}ZbE-qr>1%^oW76$)H!u2P*kOn9bhg!Sb zOVgDxqb3LR@s5O>3FVvp!h+SJ4KPQMj;8t*6y#VngO=?jG(@JYh1+hJ6vQQ4rRP&A z8;UdXcm`jq#99UGJ!_zYcdw-(PtC>2rDSAQSCH+5_ z%?VU%ylL7fS%zhCouyPY!Bj9k8j-wQfPlaDcuv4H5a1?$YUUUdD?0<9j1j=w%s3d5 zrB!s4xn!bWHUJKCJophF17B1`HIKIYYom)=s$S5HRGAD7O4kheH}97na8eAl4^Ys0 ziC)$Qf}aqP^W_(;NkZ;tkXkRQX9hi>2!pufOcG|*{eBVHAQe`V`;U|`p>GrYP56ua z#&YJwKAhVkL7?(M88?H63d5%c6!WmvD>{2Nt5XFT7e^cS$ltOYSU8$xLV>y9gkQ4( zFxC+mDWEy4#^}hEmPAzUPayUCg7>5RW+H8R4`_`OJ{-ECB-j3mfZ`g@qRuIaS^^7&4q=KB8rJy_; z4r`b_9-`aI6}bJA7^t8>k6n5UMHUl1IX5|SS|g}Y*dluZSf)jmw2Jt$- zejS(=x>c)F{BAW^v7xAxq_#pQ$hzbg{-WpI)ZE(k`0Du+A^YVh_0y6F%Q;OSX=|Lv zSbWu=MLsPxVtXtwR%M_K%E-mAq|^v>(ua_n@H=)^xf)pu*^)ff;4J)TX`-N~TKYGf zzqwp`sUBD4(SznAGLu9~GRnC5`8KGOK#rf%wlikh;92)YEvPhvD)uBkpf`;yhYo!K!luJFI7r~h7>d$MmXR@bIhT;F5FzW6`x0WkK+zx?+3 zx|iqgK{UsQ`DLdCZtcAsa%`f1-AByv>FO@vpOMI$PJ5L>eCOl%+~!Ku(ArYdUd+Ev z@9NO$0t3PO2d~3DeLt^fhYjN2*l)e{Ul-4h{k$(U2sTIBuO0U{cGpD?j`CMsuWI|f zXPR{id7adRer!+P>kYfzxmD-=UL&E(@iPn-4%s4ET9R7EKJ@dxgU<1I?}e6ou@-_5 zQB>QS8sU^ngfC)T{#yRVoIvh2`aMfOUy<88q5`ky z%S3c_b&X_VK8Rjw|C`FLZ^3Or_?~)r+WA}eRFOl6qUJ*Zj)-;avuk>Kd6T>9Kd~Cj z0RR6j;rpQi;div7v0V)7&Na!u)a{dU)F1cv9WcKiWJP#=4k=7w1!E_$M)2;uUpS}e zO;Cf-GTc`S?+1v8%PSfb*M0VrS=M14SH3*^VYPbCeN&sNbM=dK{j4jB?seC=z{-J3 z&|=OuyW^3;|Jh7*5?zNlE-!Q4YxzT0*dY7B{MneHi>eHXvlje|S4UFz}+la+mfla}lqH&}Ueo{o<5<$gy0GPoG|%GD_? z*r~L#8ahmK9?dW>_IIreQc$8%s2Bv{4|B$DuJjY4M{f)LoPapYzB}2ri|zJu)M%mz zr}}_5m+661rJF(;=iyGIG9LUHDZJZq8UZy%t_0oz-yP{k8YwKz9vfs~9LAzPYNb4$ z<2dIuCke^~-OxJ5sohh9{Fr-FKjDxZ{l)F`(IpNH-w-?pnrq^Il5o_Dsxl}@@P#=-im&^ zk8e;@pZE(c^0GAV4#lu@8)7xvg=%o}Fx&wHoN%YtDG8-TgSwMW?l}GCzUiBX-;b~} zu_-Q{9ubG;VCPtLdP0Qpm}S}L-u>y-_0v`S7mM12`)b|QA+GX3KLR%{JA2)~i-r?L z2@iMaDMY6a>+K@Qf30X1fw2p#-4}l|_jPQW4);19YW{V0Z-tA*>_oSnp-s(qyEfsq zjQ6W%-B;i6H)A$`Jy;4m%+6{i*;KukA}Saypgu5u7T;c&2+}dD9DRm9~hG}v9IV$?K>Xfe8Mjq`GXIUnXenx@E7p%1Wn0-tB6qv2;^jknSZ|3 zuEiO8{26uPzc~kdyn8^AXxk&qf3V;eZ87g81pCkb?!h9E7r`PONyu7pR(fK={3 zwedTEiX0jP`GduU!_EQDGG%?-AJq4{J+$cCpQ!(0!Rx)9k9YS0(ZR)j z7vzUa8076W+dPM-=mXE@-A&`GQBm#fhf$qzR*q)Xb&ZX}rM^*K&Q=HJzcMU+vQ(9dgvg_Bg<%?T&<7NF$VDAEG>c%EeEt$K0=XtAh>-W!m^Y_}{-BGe`m@`ss zq!~V5^=dmau`3k4?Jf4~KNRZChOQ?2s0};~I(IH(?)WqPuyd-qC1~Np;3JF`bLyGj z4iv~-78Q{9%M~MI&6;*r8ZOQrY`PdG%tWk8PY-6Y!gy&YrC8s-m)vzB&dAI}M167n zeq#9L%c$>8?#tc!(Av4>*YDqNwoYDj>}~fisg<2S$+~;{v@VC;qy$giAGa;{JOx{J zwQuT)t_caA4)#yoo(ySQ96x>#c-AN8r<(Tvjv6uy1%vwU&#ts-9NEY@9LnYzmdD*& zg!q?o?^3;NJ)6HTeA&>uM=i~r{;E3NeAVIF{Az34@OACgDYgICb64})>T*uZ4dHrd zPO=|MdtdJ%rlXD5VC+A#opV9|o_)ntv9;cXsk34m)~#l~yY#n;1G6;e%XR%n7i_VX zlM^>zFVDfqI70M_iJzgN(}uzooy+S{Z08RL)BHjm-|Md&oldv5PA*>?TTbq;AJT{V zu4PU&*IP&kg*PXS5d{@f&{H*=) z9p>=n{`@OM>q|z3aZ9^~xCmDyeWP_ot~TjsZ83SDyC$xD&h1b{^rH*Ln4? zj@`Pco2OBVZ=W?_ZV0*^)815Z;!1R_FsC>&-a+sF_bv;3OM_1;6}jzlh;!{{E+jf9 z#||wG+}amk{(j3<*d&}R)gG8$e~QBA_Z#|(nR=jea_=H!oECTYG+F+v{@MAdDVeyg zwa%Z1x8cp~&40zz%x z7c%I7hfMesUVT*N8_a5ib6?g)4|R|IYZm%3{a$y&QVu(26SLpRF8tjHQ7gqfJyX86 z>#jG@*e*ZZymda8F1vpNo%LxLfB0i{Z!L393VA$_rE_xEpIvVMdZ>rJ!M9g~{C<3o z*Yj%=T1EeM>rN<_>*aOT9`O2R6L@jz+n;kQc9j16jm5Q#H@UhQ6LVpBL#6>yIP0Q@ zO-bL?mN1sEBuLPB;e2N%%YSQ?Vk&k_=N5yY^nujM^}F58oU4Vb|LINb`;)o#i5~PH zXG2PkIeaD68{DpXS7+2iu>+lp9*%I94aLmy)uF#X)(>`5`0(L~Wqw(Y?`n zS!9gT#>NbEuj9L446RU6>3h7bOpcnn#`*R-aK1UTr zh8F(O(1a**B4IZBBgg38QTmFi z!n)WrP^jn#tYLZ~Cj^4hZ(X{Hd#JdB5Ze2AJGnateO?^C{z*1R?ZIna~ z5N=;)2V6v3-Rrfp+#^;waMq*w*>@d zE25iDs#+1$ovHyAPg6k{S2?OGL??X|S!fCs)4~=?c?z!Frlm0#^IUSCIRffg<%x(^ zCfo8uq{=&mx}&tEEQWHfKWCgs$)7csXYw~((t{cS)=<+$53GuiuPU6A7jgTo|GvG2 zY{`u#fLZm(W3=Qel0Pox0RZy3LhAP)yYE==UG9S&DNkLMS{~l%M>li@G-FZ}-i!$B z#kuo4Z-mUJCWX&6Ew&RU|9oc&rFshAI)B`C>)8J*u=k6o$yGRyjqI=^s7*DY!Jx?u}T^cPR`)y*H8%6{eHSmO6rr>HBNro=`jPc=!J zVxs2MhPg@~9IrNNeaiNzOvx{?o%N4ap^#z|?kEnX90#Q-tDzvqx=-V2?gSRiz_dd- zFGZ;Ao>lQQtia?&eQ57Ky&)!<&oIRDg6<>qA$UgD3NXy%5@T8`&4wc(@&@$69qZ@l z0D;(qkdi{dukvD=%?jSrnvfyrrJ$l|L0khjty#`KGTRwuTs*Tu)&G*bz)#o~8OvNA zy=_xoM7L@erB)3mf!fd#3)oi=urQzR9v2>N2&F0~3EW+NxWMIQ z_$R5LF9-#dnf38g-InYAv*N+IWulaAwgE|;pW0$V`gGxZ4?urT>cC->Px)abf&Eaz zi~tUfJ&5Hf5Aid;ne?2;j7lM!#8_I>juO+M8Yd4|APja-!gh5KMJRlbpUDcfCFIPe zacc;MKSrS+s}ZgbX%b&n!mSq>~-<^^+f5L zUl&XW3Kp~YsOWLv4NZ; ze`7@$gW0b)$VN$Nu|`*#bQw-HiglE%rfzT{L(da2*gQ=$D#52PLyeHeLnV~yIUmbH z3?y&1x^3B8MiB261mp(d4q=Vij7uPi=^Il!PJq8#IiDvRs|TYXGt)kPiVwg$tD%8* zDWxnu6De2x%hg)caHxNoH`?``92wYceIYSGRIDanNuAZMI8#`9U~*&;baj^&QJP-)e~PsTt#n^SRR!1$#CQUO6jJA;M+^ zr*eYMkI`|$m7Xc+3gF-s5H9&7%m~TTk^2$8ZlbF}K&|%UM{0^;->?Wmwuh`$4mxGP z68Y#}k4Pn}M}evGf8s4KiIo8g!OhJ{UDk=*L*YQK9SneCag61d&0#P~`k_NdW@CJX z0gj2U5W#NDdx7#4Giw=R-^XsmmB$Vk)t%UBj-Rk2*Js}{g-xH=NSswQ?FFdzo42BsGi1&KH2v@K6xdkw<|0BY3^iw6ktmMa&`gri__|IgM98 za5=MXr_5VSsH8&C@ByzIBYBN}GXBJZ1@d9SwF)kv5vk@yTEIXP!_A96XeMHYCITEy zt#m2EI5J^1HCE|JGl&FIi|wHmb_Ri1KW1!>!d}4(>5AiKN!SzF-R)w-ypAOA_Ehz}KTKc5=buA|lq4HxQoGHkV*hK9gnKPjnZY zsoRJM?}ZOV<}~nF%maf23z=%M(YX@|T|e;vglasE#eQ9V1!Q8ViEuXjWyHi9@2$B_)`CJ<0*7cvARS#lUn+4pu8O zMUmBma0PfpIE0QwqQqPt9;_5pz71eg)#P&3C*EySHBUm{(WXx6#Z=}Sv5I!EZ{L(j zUZNt3HHJ<;NXfdq7>Jd2Q#3#s$-sYynT6V{;qzd85?cf-ILaCvfgEWVWR%*5s1tEqcO=)-xNZ z&Vp(e}~x?WA_qV9pU>V2Mi3 zL6+5bCi)BkK*=FMinRedG#GTWZ^*)zmvra99wLujpcg>;JkX! zC%h^oloJZDrE1%rb9cD*JEp2s$fGNQMDQ!bp7K7cssruQl?D*A_!6MYzo#UWVyT3JU-91A-?jAMHgyA9afgvGXlEEJV78N&2Q%oniKH29y9 z5pwhw*9irb4`>Z9|Y~?A&md*O>t_CleqTbmt<> z-u#w40W%_b4|WXUySI=RaQXvuNAy;FwK06vOflqv3j4^Lns`O8IzfT~h_?i1oi#bA zUQIvwuMh1nc-P`CqQ>z@#yZ(|#eV}V-%TR4|EQbEg?_?}B@O$u{)0q#o~5?m%1J$Z zM{<-;zPt_!nS{-~=O`zxw9SS$t|&>PzBpBH1{-f1K>#;;n+Cwq6fOa)4!5fYK2Xh( z!3CLd3)PxM^2)$*8}UG#1!j%5+tt@Yyuw?Xc^fy^lnuY ze@=Ms&&bM4aql=MUKLt_Cgq%yaMGj{P8y_T{Au^69xWL9Kg(Z|E2889bdqjW1{;te zq%)KD6%}!T1DMvQU(9QT=gE^# z;lV*o+lR~m#^9_P5G>^P>6{eLt#5@a6LRzd7_Xcupk}GIv*(mdVF>y|%mV3GwEF-UkPyDc-6|r!phZ;5+($3-eRYja22Dz0N+H#@n^B6=yf~QORH!keJhw!4e8( zVudmY_3=KCHF#SQ?N<$t0B+ZW7|2FrBYR3>&lBNQ7i<^5WXbE4W(`; zdN>K&c8I4GvGHQ?%O0I@(NAPZRnz71nC%+6Cual=)M#F z8wbx`5%NpJb~k@`Jeh;kC_Qo8Rea71jSn1IVL?lAcncy>FT?&_sbGmYQ3_yAFZ)S<>_|PXWzCMp z(m~}~Ge6>*Y)KvD8XLN&$%)MRMarYNJfXn;3dTK>nOeW85sN`IDI)JrLRztVtr_I# z=S(xyfS&d28CzBAi1dHrKdq5VVVYphOA-(go%~i4GFGLwjD+wlaWu+c#?M38#Zi(y zB=!`mg5_nnq7y24R95Eu3GruWfXg6Fe*hPqSrM8g9M`nvQE}4%bdU}T&vCu_2$TlP zx#W~IZ?r;V07~&^B%13j5h^1EkX!sn!~)PfE%A)s8Q-!x6NAw-Nqw$^P)C2pICY?t-jo%) zO6F;e+C@>($E0IEtIG4QP5xLfjFmH%?N;osyi_v3#pX@z;h~a{g_N#{E$yE-Lnu>r zEX`3>z~(9HdLuclvcyujm9U2IL@K`PM6?7JfqsyuQKC?x6NQ@{&dGFF=Q1Hbv$(du zaTSfQMUigAi_2_v=kJ5U2Tuq)q@`oR-VZo@5AGKJSp_5w^!$k8w zwnr2qX4A*NfzU)hCR;hN0EZK7Q4&bS`FlSFj7!QXiI1mMbK`*5JJ69EH#YFD&=OmF z@XwUVcm4n2eFK0Wyf2p=q0zBFwmD}8IMmvp>hT|4!nzoMBuk^&W3aEJ3_XwDQdMMR zTzZ^{Rwj*jS{7KUz(WP(=-9&JL>|k}=x{YFQFFeo38oRzj7niF@;qRLl!WhXdw!`px^Tadv=clc^k3 zBuju~8T~(kow!krdL`G22^QH#%TsWCkQXvsB&9O5e=i7&j5cceL27c%zOfj!`vKI0 z3YiylO%h3Zmz#!*sTrPTI4_YVSayAHQK?az{j|xE zhOa`x`Y7GrGefD+!?t>Mb6@o9m7_ffC3m86P30XO+=#{~TOk=R4R;2`vzp5uwz1n% zw|zKCD1}6M+Aq%(SCCuGthC0(<;JG0uC+Ri$h)WP=L5)q zdn5`5v%ur$?Jb4E%vA3*4+2ZK;>KCxUH1_HF3ku;Cgm1MUq6V`L}Fu6bM9dQX7pZ4G^zr z+d6oLbA?$I%y8@=4i#b4ES!TFG za#ll%b`vK8uyQYCs0w!>*wKuJ)YHgt-l)A$wV-}RPRdI0TxDu}mB45N zB;_a15+f>|VS;aVjo}frXZvyy5ODPxp^9{Vd?IcgNbCHt>M&?-raU^@!v88j=j*?b zK~44n+b(AqLAv#JVgs~4VcPpFpeGYrfAI<=w1E$|&c8O+)6?7TGE|HD{3t!sEk#8qSV#!6R z8d1npu2|?IDS1t6hZRugad{tB9jpV1r?aKTMuDLyEqlck`2V@i7R+>;lQi0q4q zwrrz!M;j60u&vHe;EU#86Dk2zliV9gBwr9E6~t;BDQb*Q(UXnv zDT9otKJPW9PK-GO6AOAUTrm=x#f_?umjS|i(g^r~Fga*prBKw?TxoZvQfOOEtm`R9 z%BPj-ta~$4#HOk*+_9eoC7-{awBx&I^{7J+AANi44A{l@sYOwENkIy^o`rTnV6wa@ zY^2A&^UiV8E7Z7(wLAqrWkFG%NCpHez>{Bxi|CyO%y(fHw@o%MezdJ4%K85s3Fcxq za6Ybyk1-?;c1uGFqnyGTzZ!x&pd1!A8UckqP$(<*Y6`P?hmc#NrZnnl!>uHi4rfX4T+3~{D{zGrcZ60T@&~Tinbq@08WS; zV(pa~lZ_FnhMW4xldGd*b8BuzI8nNB6>uklm?a6={VLF0apaDYqOFU^8Fkl|x5hXl z<5if3MBHbsHT*ED#=rJZrN2k#Ep%rSS02omYs#|A&(96U`ON`qqQi{0xKgrKSPfCZ1rYR8%4@J*%p zGsDZbNB`E-cnMa7XHF6-XsAzIu+#(uRi;DXP(lSRV`zw3s|Z`v@UnZsJ|kH!)O60a zm^cFQ3d~G2alb*xux|`Fs7M=n_0@$tOzFt>Bl%U35^&THQP6B8E;?!QT)CUjTZ#(9 z;aW{Ftftz~&k{&1Z#X2Ei84WuTn|epI^25vtp0M@9XZ{WO_7?_LWmAc@q-YPxXY)B!$v3AAows2XXT(`sHGL6-RGXwQ>fV6dhvvh8;_h4$^C5xG-Y-a1LG%SnB+<`V_Ef&@9 z=#p2nS>I1^#LN1rUMtgx1KkOVVpixf zeH0=AV~N?AD-0aVBb2-ZuD{JN3R!TCveI0j>NSnMLKo&;-!dWUFy=rPLQ(sn{HU#H{1xEm)B zr?}Q2)d9jD5G+%W6}!N$tf>UkMAWkHx&AE>&p5x8=}Uq9*Hq+1D%iqYnmS60K8Tl; zE3y7PR-=+>qKD|=Kz%HLl8f*>2$|Z@F=-d_Gb{QgHfppF26o7;*7*Yo=96^tbGP;K zJg)h@pxzsRg8tvN>kdE|>+G{GYMdggFX~Q5KuXA`5hhU{iwAG}SoPq#pc=~r8wgX< zsYvH5`KN%2J}i{zz|rjZSR!~7gh*?^b*D~Hsb1KN%csss$vB6d;Rt+z5}LA!L@HM1 z&lzszW12Q}8AtUn#YoiAlgQCm3bg$p;yPoy^wGpbTN*@#Lnquq$Jq}nSFJk;$#X|> zlEI@MB%MKwpi*>O5HdLq=8%o45%jxc#PiroN zq5%lINpOP#BQ`;G#l8FwV_c9c6&L{JWKJSk5b&Y>G1;Y!`f@DV_$hn}r#S$dI)7q{ zm^KINNS9jC@Yg9V_hMvHGNbDwaxg-1mKnm(%%fOVbullJ?Ee&K%4)YCh7k7G zSWfcMnXFT?V~msE6?MRUI^{`lU^EDRi$-ZnT3rTexFxRJsC9w`ifN-f52#Nh;Dr&K5v}1v`&&1fvzL#7z;QY?Gve zTR0lV-j9VlTN1svsPqUb%xwHE-V1YvFOuv#?upD&XxxpA8dx&| z86ws5;in5_B1qKB@q(K{WAw#idij-{vW@IO`=6zp>-X+5hrKSgl8cg$4s=yz8n#cv z>e8|%Lf9EZozOB`^24l+w>b*ZgvuF>GePy@nIanzSnD~%i|X(w_>5Zq+bxHvHAvhb z0#B6{;5jhYxi%15f3v7YA0-5RnmV|S*)P35i2!~D?YF4{WLb``l466;IRH&aLE_%e zLj*Ej&=k(aDb>Bh4iBC1y|KA2|5Gh3=osXRmUB{h*NS7>gR4AoUfwyoukn z@Zog0JVch^T=KG&5}zr&?kR&})fr1Hqjge906Ifm=ZcvXUO8k~k`${j8X1H1H%&u*!#uX}tf<_F?l&7|fvnp~q?r21TGnzBZ+3R;j)POE0IHN4yb zQoa^X-*NnicL;)B80^tm((W4QM zI5Aqh4`qOpV$B)urwXcZv{+oqdbV2-f9lTH?E{o6Cx&L*ZfQSx6h$EfCFp0Uzp(>s zGL>0*L43BlvX`_K$0Jw*+yVuocC<(V6Tn@kY`RbVf||@Ou?0!Z$quxnA>B zH8ZJDbbP+ogFmJwysHdP5GiHu#L(p8m-x`Ftf>GX10QXee-t_>kUL(&mr6RQGFw9d z89rdVCvbP9t_eAFT*P%5*%Wt&uCUr%%+kqsS)Em? zWc5^2*x299XG4$jJyuYCW?l?g1lxv`e^Khq1k%w&GY@+9>M7VrpkPVT24*!a+9u~s zWYL4L(wWYQqtY2?Lp;~`wjwnnVtD_E^ZVUr%i=qhNlyR3<57}5TCQVwLpM$+V&+V# z#x54dru`|RccG9=qBtfRcKBif{@}y#kbi@P#5LNKM7AQz!pt6(9wJY}RZV#y+m3jH*DF69Z2%34r@Az~0B3}tK&6njV5+Wj^9YghGO%QT+ z!Tx!qJd~PiLp`C;(Y=y5K);`ZLJzW71tM_>z~lr&iwFYgOe&Ii?h(iew>wMWuo9?$ zB_5r#$O`a+H!hB-V}jP>K4o^7-+>Eg_(TplzDn#Y>V-r89rWc7^QrQys+ z_w&nU@+5;%(7#LZfa7xSQQ}RG(c-&G$Epv43fZL_uJ9#zt39%}gQ` zzWWma(V?;WOmv`JX2OaH$|wQz zOpQd*IE=dJUaBtisZ(0AbqDb#aR@Dm>blgdGCugv;QXofL>>zBKYbg{KnY8%dwl#! z3}jeZL<*3?ghGxnw4NjaW;FWTbPKZF>;Sl7E--nAE4^s3e!?c);0aIeR&;EGu}08$ zW&=*>w_A#bfP69a<=K}RanzWQu*y&L@I;)0JLKlkXFup|Q33soNK{D2tS;%uqC|E~ z*^;4oK9Jc*h0{nxBO9tAE|)_IioddQx*w<0N7GBUAOIZp<_QY#kgvQYt0C45A@c5Q{C{6VPzyd-lOOt0ey%^byBx>fs#89*q zJ&P=l-M^_PexjdvSr*lv!0)UMX=oG)O|SUtY(Qu29C6OA*`V1pAh!shn8thGRo=nbrBjzYGPAAtx?Bnn#ATZ+>!>XnP_CZ^R^XH*p|I*v?A8vE zJZw=m5XoHY5Yu~xMGMiTM(}&%TAFV)xU7++3;exV4R+CO3@3>)wHZx@6ft}=i5cNG+9{J#U10Qm@ zK?wDx?1C&xJaBq_r4VQlR}~f_ig7`gh7KL5$BDj~*O%{z@2`!^?0*7}*Lu_-MTHqo;RLxh9rMHvP19~Vs zKx-bjkIa!7uAVPhD=}MRnU}f@Z4N$W zZwNwsaG3{bMx>;25K>2}TtF_laV!y*lsI+iTLh%RMc^Ux;YDw^C2M5zC+?YKAtx3> z@fGD9lQv#^7A(OM@d8H1aTcOrMQN?4B=aeYGKX}+z@QYjTRYz77S~|V={8Op{l*#g zDp;r@3x6ss{F@^S?#XzqmsBdC_1a|8nh|9Z!aL-N+dx)+ltMorSulPu?=eM_$mI9Y za!J}(KfECMdNL<>ETRdd{UbP~15d_Cp&$vXJx6dN1GgigA#o)oDZS`|4TBD=gp39w zw@FI0gH{)eivZ4m5Y`igU}XdkU9M!z*olLN;@BH+lZ%DT|HdG;yVw8wV}WGU2gdGCx5K5_Sf|J*-)qkOHL zS#|%H*S}Twjk&wE=#BsH{<&r^NwuQywOe(%*VeA@&38jLqBmIf-S&I_9^OC5;v4#{ z{8+iR)QZo1`pMUE|6_gMEnCY$2|XyCzxQUjt$liYIgiksz8WH)?Z)660;vnv=TEXD zZuju<4Ls`ngAbf`ZgixOn!nJ%cpy@N5YDxP$7c7nNc@RQ_LoFruMz3GEY(UUgc&ZC z5RXs#D^h`Zi&Q0q@SIeS1DpRxQdwoV;7_#QwNy{F;;%@h+&?7M6Pou+QrS021xh_$ z{8p;B-0g3Z%9LQ$aH*b1`_D@?Pz&_@>c_YAwMdG;>Fd+4-6+EhsU-Cz!{k~j^Q5x% zSEOREzSRw>1Xm_A4wcG283XuxNc9%A@Q+FLru*ujlj^^CU)ks2#=Z#z-zXI=|D06+ z&Aa_M6>8rEf^Yo>-81^iz6k_hCsi>)Rk?SD* z2J=Iy#M9>NKcpf z6FvV>DzfpPl?wg|drCaj@sA{W8V$XZNOEysqPy4kd2_#gjF0_0?^653HwJ!}^_^OM zcV}^9YiZ};p&x*x$HQ);fBChEXV@HkEz+t{pSkl6Ub|8ruGi@{&KgZTeBuRoynRJJ z?8R=QbK{zNw}qXik681yPPZMg*Q*UqSG&HpCvGKd73W9U-Rzq^eDeOSl-pSE6>q?1 zw{haTmpf&(&08r)`l5a&@M=3A*E*iAwbp3WPVl7Ny&NTHzk51L%(C3tV3F~NS5Mzg zd;J@^YTr-Wec!9)cK62yzhzUGTCsRu@MBqbOajM#jYRCwgHdbeTK@=Z?GAQ*ztK9Z z4Y}EVZF_fO7>}51zD3`8&7Y>mKr-J-wI0UP@`Tiey2oF>tJ^p0j7QQPe@t6=-okDv z#4mfAY0+Md?twP#F8w_ST)gQ%DCgL?J;k`5E1w3tdfIB1mkEnov6g6YI?bD&FrVfI ztBgm`&2GDF=*_)ZqkEMeuM^)I66Sfikk{VZo2ADi=j@Sh(H&bJs2foS(DIi1bg)cW zWz0KC4;1})fqweQF5WD-Jy>t^db#n4daL+qy?BJ_v{_UDp#lcG(&FfAeO! zF@5{AZhru)q_)bYyg|zAwKi`S8moNt^w6V2MpdnMYsLQ zRB2IDsnI1t=^Mkh2wT=Y9g_y#!XC+kW0&z}-SG&yeRSHdC4#4Wis?-{iPkI2jYqmU z45#J69t6bSvS)*Z#v{`Lwu(7~Kg^AR5%L!4>Scj3*+uQt$d`1_bx%rwuZFF&Mz`HM zDUbA-Z=R06^InEH^FP)c)6;y}@x(OxU0-YqtdUNS$0Y~j4O(Y_w(IRa?Z&ts=acps z$<{vUoc76V%iZ<~JvjqGYbCx}KKF*b-EEb{#)Rb8Mo{gn-8?Qpx-JOiPUQ`|e$ygm$wL zYbD3(?cewI&2p=>+H-Q=``7>8UvNa8MsY@6b_Qqa-?&GL|9G8mSHY_xBo8fZj0UMs#a{qu) z`6YulpA2t!5~zCkxvwW}A{}>0KbzMEO6^0j^uBz#mk?8rr4EAL5BTNF;Z<(-PfrHf zPEW4>W_Rvg`(d|z`Y_e<_Exz5S^oad|NQ5F{-=E4?)xSX8C?6j0ovake!Y|LPQUFQ ztS#1O>dVJ$zqOcb=$_te)w@P3;YIlno{`G!y~kD>I9A`^=Fq+CwNJb8elG00|93wp zwCu03PBM4TE&q@UXNLm}=R!vEq!4>@}xxsSK{ps2t{nDmZN3J;6_@$b9rzx!_|PvDFF=dR&; zR}xw&qSEid#Qa|Igzrn@;d^gfl3JjO>V6;Sccg&_$N469A1wFd79Do7+s(YYj3-w= zAgPZozJSO;x!ru#;=!XdSnK(>hvbpo&mpxElIAYulx2)Yn1~@cE*bMdD*bR;*=_!) z@)2e5-UD-q?kCO^1LT2ghf+N1Xz`=zwaG#7m>~rRIY+N#5K{S^y>kI$R7}Y5sA18) zyitvbM=Ny-nUw>KRS=R7MjQIRvWUD?-btf*03m=QQ|N+5qzjpB0eGR8M zUvD7R7yGSU-dlU}>BU?B}y99~Wmjo1Nx*q8&L--?r0b|56+E zp${&t8$Q~?{BZ-O4iCiH%=W=S+WY+B zVBvgzV>&MFEUtY&zSuly&CDE3F>~0TJ^know-*|R%h251JXqL|^HbmI>PprV|J<+d zHs*FOyYus#2Up*>T1zX37ioIyFq*^5!_79Fw;XJ?PuI-`&L1rE-TlV$;nvAnFCKQL zS2wO2OUK9CmpfAzTc29{N1xT)$`Ks4XOFKMX>0L#VMCps&0e0n)w#L;tiQBNtF!g1 z?()X6UpW1IzI%GMjpvQC4_qFBT&h~7azvwRXtlV4K6%IasXhX9r zcKqiMchBJiE??UI;SR2LW-mo!0k^fO&+K0{55s(e@6N)9i}<-!SC`YXt1DJ~yl7Zu zJ}fMJ-r8C1&F)@oalbczzR}_JlZ|h4TL%YMhv%ogRkxSF&oma6FIu1KzSX$c`EKSq zLaffD=HA-!RdeIQo}bLDh^-6QJDFKsUfMeE*Bjq9KF&|I+p|H;z{SkQ!B)SS*KxnI z+NqoNC7ts7;?)ASj!%}BH(N(DOKaV$o%8LwUUYJH1DaEjuh*T+dg*+1(dONi^lj&4 zuCo&kw}DL^E_`au&$i~(W`|B84o^4RezQKez1WSdxW0HYzu2#TIQ}lD=9i}G(p>er z-JP>VxwmtCw!GTj*twkPaCUwnmgC{4*)4xmSL@xS*?Hxsj=F36y}JBfUtH?%krJ4< z_-(`Nf0B4Ky)t`ne$n(N^XIdNtG2l=&Uae<+&wy&-erf=uu@NFyPch>dhcrQ`%L=2 zXg;kXe(ug+thm`TzI(Vdw^Fx<)A7Q5tj}*w)sO0V%df4>h{cWNqp6wg58K~YzHMLj z*B54*QEzrT7iTNx|7Y*an%YX1c76Z-3WaY8HWpWZmch8|7l_amKO5P;n&Hxhe!2Qo!@_bw`oq_~xwqfe&)=(UJE=Zz zr<3jNmmgM7>bJd#9rNzh>x1<2b$9aBX+3z4lN&FiSt%ditbKg#o&dAfi2Y5MeV zY3l5?*;<`mIbHnN-hbbJ%TsTXKaelmwEufCotCHf&(7%W`RwM_>ha22_(;23lRL-V z)zIDD`iOD={oLHU{q5J~cyan{XXoAiZk&nlkLK#}(J{bzK6}64Eic}~+cy@^V5f_R z$Fp$w&VQX(#}ntLb>ry#=yV&M`f|4Ue(K%KncZeLzgXw_X+?#*tAnSZtwVoou1u1*JrB} z`Fw3_%e?J=-R;skz~<}u38%lg`Ootckv}h4|8H)9`k)dltK{eEu|f3e#KXFBYa3 z7rWn9CKl@1i~RcV{N$D%6=(7qDK7ypz6IVcKwtLLLB33SI^W&7(VgjrPQI0@a^+bb z-b!qck8HlPko&J2X(@kLd()-5%x1YUpJxLPx#j62_IIZGB`VzTf6Lxs&sF(rfZLQx z!caXu*O}kxFO#!kYHrB22U6!C^T#dHjIxO7VHRPHc$sI-)HdFP!0Z{;A~%8a@9q545~ zeYp8RPw6Xu#C1uSAB`v)SygdIW#a}j_9yfqM$GY zRnh4plGMWWo*dV|uOF!Ugkp4ux*B2VrmNre{U;@~6IyK? z>L(OBd4gwfappghOMUx@k*s?%8db1NCt|14os-Pt;)vf58A=r_`(~qY^LC%Fd>m@b+aNtE~)iWN_GGl#5e<0jxk4{Lq;kF5+UYR zv)xe@B3aC?f34k1GI%H`la`|ae38F%bye|UTA>IxGD%BqjS|_!&#j2`$0fS=}?vU>PdTFN&NZfaxfY> z)#P=-;?eklAONS4fLJ-Q!i`dL_PMf1o9J2uL(C?-DgcNDAY{s|51&aj3lcOWJ5nGp zjv4Rdw?3)h)#q}wUv^QCSuke7m<7Kg3vQ>aKcwki|# zG}*qYm>#SXTMp5bss#i-k#BUcy@HA~pkjLSP>V}eY<0B;lri2HtqP}1hIAw+j=PCr z5DPSf&}t%8vFwGd26hCjWlElmWsUNKmS2S|uv4*CIio1Bky$!K9<`=>R_=XIcBu6a zU(Ky9&n-<)t<8;rFb2XH2)`Z>ynaMeZk-}2KyDo=G?N(kN~xB@6*#lOCv1g+R}2&7X=s7(=7ky@$O+HAVkdcb}` z1T58S!^+j*9EpdIP_j^k#e^5HLQc8%YxjCR#l^E0WuZ;wq;hfKl|d6RqSEX`tGX^) z0Z?4x>N)x%(uZqE9<5;!E+a#No-}Z(Rd;qn?YTNXyEtaSxM#+1#)47onc>Z2TCRcz zvGIfn#6yR+H!GYGScsX9koy?8t2HC7#Qux5VFD+%KC{lYHLi$Uu=Z=V@qHs1Y`|0; zxvE&C0JpXOkQBs1sr6FwRp#LaHsfwRwi%+cHY<`j$0)OqaeQ=~C`b7j3Ma2v)DK1P zd?;LejaDEEWz>3RIA!I#8m>xMT6s0b!dN2wR#+GjiQw@u-IF+0W5wBy)h5>#{+$@) z$Yg;6QlF-$mn6_t?0>;hkPKyv6kMjJE<#Zp`*rR9-aW~R<8GajL!CJGQ9IHM94d%= z?+Rfw#YNY@|I*d&LBJyHs2D>noH<*v&b7Sv%C(l1iZM#G16DLv454A`Lo~5MtsMNQ z^;;DoMtw>UKXJWi1=N@YV~Oxvv0wxx0x5h%BW{<(t%js(gM*9WQlk~8Utpms>;+!+?2=?M`jjHYT#P09!+l1#0pQOZK-fq}`i&HJH( zVXu3dj*iD<7?WX4hF_Zu#`;I>FBd39X9iC$YbdHftj=*3N8AQ+zwaf3%|ef?V`d{v z2oMcblqf*fo)n5xLTds?<$Nrs%-ZiuZqncy@y z9~0-GYl@Mx52}cUa=|N4=(S*rD9Zbcbwwr1JYr;Uz-WybYQWLd?7OLz@y5cK4P!R^ z@@ydRk5CO!oOki`t@9}-mt7PeTO4e!bQSMg_c8I8Y;b7A4^J_PV9*q8pun66ST$xi zdp{er043O#ohH6~m7}A{8ALIdsFE0UF1;Otfg20iT5rK*6^*f?ZnQBWdtaj*8@x^> z`e;g~l3ekQi$=DlaKlDo#iO?4%&b8!)DU)@|4vftmelGHjnOdPT==cfFd~}^{1_UD z@65J{DUIS#2Madm7$bPEvjyF6Dd>_0$^sbi^;`76XbtZnWpA8Tt&b$v0c%L&+hY|c zUgwfq2AO!U1saDk0Hz>6r>ZLtOil1A)Rc6v3Q3jJ5W*zl#*hcprK%dVD^H`#?TPDXgx{tgf38hrzhI&_mAcA#;7GG5@`)3mLw>ZuVTz)3Wa0lc!R)l6XH z0xKs^bSCj^6ZToKzFOM|IutX zSsOuytELtA>0h|vs!FFbVurCO$j$4|A=&^rAv&ipiyrV+u z?!5tSyz}Q4hG!0r4iAoQv?=R*J^#K0|G^dT>(@ZX4*Cx2j&W^6IO>&<;WO3s4>cHv}8rXlME7xsg>MFCKmgQ3yiD zfo>%mCEwS$<%AxGp;3bNL5Ku&HJzuP2b#GG*zXavR(qc6dc z0bv+n@jmtY=<5OZv8oUv1xyr*oV|(G6rWq&hb+(uA~GyHFqwqczMjxjsN?6`4UKXx4byV+qL!46l!6|LJ6N_7#@ zmIji-7HcfY-@l*gle3T%`96MjhVd#0;RY+nym_+E34jkm8(AX$4D~%jG z6ey;lvtxD4j+>`84zYQ_NsdEo{$n9F_=xS?8ba2YT8TH6g16|4;$VDniE`hF_^V+2 zWR-aHDX=lR)X3f_CsHING42X(FEU(1LI?%q-G-fR;hhF5M+{7sP)G6mv5FWF%nU+8 zO^`ikL}?aWKZg7ze*1 z4*X-fg+WPZI0Is?x)zfI7OLV%niNXt7qEGSgBWWduH;SS;({wVxr)r-Ou{;#$XjlE z#O+ucjS5uISs{^+QJ4U#g)NP~+kK&q3HrgN-YdxvfLlSGK)n@ra8Dh$|LJNPY1xvIgm1J_JJYpnp#(}kF2u+3m zCc)am()+2EF%8Bv7}Ma_qk*`*kI)4*yI_M>#%52f#jSVMXz{s=Cg|HH{%Xa@bF3wc z*+sF#ZPURgvGa+DoY?Gs=J2YOWG0F^0a;{GBv2w~TRA2jM2>)k0Hx}Cno(Y@7&%n} zG*{Qlyh}t|klJpBKp?rcJu@M5M)Jej*R?TY2Yj_oVxmB~Q^BFeWA8as3PafInJG%Rhic2L82wuwVYtQ5!;= z7zFCb;mTEemTTAcuZMo6_YsX=zO%1h&feEDQGY$kj8L-2&;|`n8mYK za$z|j*P?B*Ry)mx)qtfZmmJ9livJr+OMOtvXNO9xzl$b$i`KJ3>MvQYmY%EticscW z+Wxqf2~$=oN1tl;A~>1^X$x`@lOYp9te6gD1!BRQJZd4Vnvh#3>R}3w#g&Dnm8Ljm zmzKsj7;j7bHaHl?Z3+FD4eP?KnsY`~H5%Kx$$Lo77m*TH>D(tX`bvHzuR%4xz9`bw z22-LFU%$~JX)_>}HJchph1^;ti}bKEdzZyCwG}k0rW-N@V+OXXY#kBUdlhrYkuy0t zhvZvPB_rn&bg4O+RK%;5fk5<76@lVfV3w`X#Zi+VA|;`fYZ~f?_2P2dnHZB{OolNT zeq}OfrpJ&WwoQUyIZ|}aGz0BRsKh2VZ=UYou+EMP6!NJR6RqB55>rnR9jRp0Jq_u5 zWB>z6l~_{Fnh|4bK@7Duy>w1AK(xk?)wX}xyGt?}@*qmYgAYQu z7Rcn2h!bbAhFldFbXt+9BxmXfZBxiIz^!84aO0R(*2a2aT>R)E=k}1fKGdrJHpP#` z{IJ8vY!qN{$)S#|o^$Zni!gNwa?qd=`n7Jn+BSw{TU`WQ&}3&_i7DGsp;vzbeg9?t ziyC#1ptXKSR!#?nC52QhASo*(s736l4fj>KyUH|3EqPhgg4bf0F=-$Kd?sK+S?_$0%D42Tn}mNzK`AaU)Oza zeYhTxq%015<|t0H3(1F23vkvcaV}9HqVHeW2|*|2TGx0>7$K;vi)oRaTm!Hju&|>5 zeA1Ym(_U~RK`mg_K74}S6WYt$p|!H>Z(2E#8822${tc44Hk)(TFg zsI{@w;zK;NZYo8b^!vNdCxg&dfKmvMoE9yh1x`Vi5Yf2MKaHRVhGKMY-#OLfqi>Zj zVv1~qEKp^%w*HSr{QiNv!8unaXRKXLmZ?}V_M(FVW*LTyb*3t)jOeXQP3u+*FM2}? z$@pSMu3eY~C8s{*f<`mzFUFBUV=(;0Nt#Xy9qaF$q#J|b->4WI-}VR}00yfeAVf{# zPFKicX-jBDPf-kfi%h*gLroc#T6VBA!4(oTw90o?4w3`W4d@&eQ*9+!v9gaSzP@%z z=m05!Kp}hUxj4&%n;=jTX%mTrC`xBAp)f`8TH9PSq^w)Dj2vvTrIKS^vq&NaSB>zZ zWEe3pG~yc?ZiZStJ3L9d`{PEE;*1wuWTUwFM5-q(aCp zGy#&U?cc89N+)<_F;tf+F@{#9J8Rntotl)>5zEB?V(@$i9t|3tT|ny1#; zOvI5xYLzTXO`M`}2<>9DtmiDcLl<8x6mM9`=&iwzd>}+$MlDX%f)eTux2>?;ue-Y= zPlMrw1YvH?0(|J8?GUtn0hL-vkWZbWI_x`HcN}&+b=Y?BVf_=PcaC$ZWj~jr&f4nI zzXcF$-}b`3T=brS?(__FJM~K&-IdetJnzikEkV%l8mQ7qwNp(8T_?FtEuC09NF7_+ zic@wS8#>-~Aa)>h=wa93NAp|jec5e}cRknvK=BPILN)xC*1t`Zl2Znp;YYM+tIiiC z2HI!RMMvb^^a5AXAg*S*zaDCZV2kr41~`ZVuf0SFRSQ!Q1MwC4ND)99o0p+%L50ds?!~ zN@*^XL4g1RExYVXY>nVrX^K)^uqF@g%4S<}j1XHkv%*BhDH~i$7@1Ts9+dIz1GFZY zO_nLyR{scE(LeHf)JkV-Wyv}})Y93@bL&$pizBZo#yA+`;Mc^#ohk$mSz)qOx6YO5 zl4lMzR;;23D*B|d9e@LyN(v^~(DJ%`)Ws^sP;&@HMo@#cpS66V)yi3;Q}nIJLCRHU zEHMWiT}-A%r3W@z11)CXX2=np{Aj_XSfVnV0Fl9!>a*w%Yh#evxmbuwY8{?iFq8mq z}(9xf{H3<<1LGUS4KsQR)+!V0JY+fxH?b0m{zMXdvD0N%&p)mnOLy!!0qSa ziWePXbc9k^q(RB8425zbC~brUtbww!DIjo9f5hBUmV+n+%o;}w2Lzzip*JO>)syx<6^bo1(OJtO8L8V(Ke9!Xz%KQ69Kx1s_@DK=b+7V5?|{w71zRbXVb&!A(=X?DVh+=>#RZEfGOm#;p_Re@+* zVQ-f63p%y^XHge^)MSDIu-@9Ct{T0YTU}dxvpnKx7{?`zU|f=dNpMYSa7X!nY5V)c zCACxlG0{KIiHKM&`v^MwtSGl~NaAoNRWvwoeZq?_IRS-AVBP9XCu;o>WEcvDSlMQ; z2eetJ6of9(Fl!XyV{B+!)j}HMN-U5w6mFqQgE)bdgG@DQDydcQ4lL&(BiW=fauPuy z@G%wxXiT~IDgbgu>0}rSlo4ypX?@dY>IpTl9~hU^O<{keBaz0~7(r~b6)Jrh&g%Z9 z+;4-8)~{E*^GAfU5{PNF7MY7uE^Cnrx@axqiTTEUE!VCVo(!q%y(_sSN;zhQQROPG zJjhUbP+bQ%XveUQ$uWZDXoACD z3J&@gn%{;TP8@yIhJT(M*=EW~dtFQGuBp5tanD4vL?sR}VwXG#cYLXEk$ z`b<^pnjqUj<+BTrparwBj80TW+w(B3kROH|Y(V9(y2t~Y?qo=)ggso7#bhe!sFc+% zp@63nLyn|t)xBO1E~ZGqVym_!FFLWKqec!ZG*(aO^7%k=#QfzX9F4&-2FDm2gRkqa z1&&9=!I~o0x9vcfkUSA6%DIM+&~X;&alhnnY;s#2tIoIdIZY(eMSxSXi4g06I9TUN z%zlV1yDg}MptZ8mi{26O!0eL?PW~8JxK$}?hv~?II2M%?n4|JkQxq-a(18-!6SG#i zxLk@#-q*^kKrCp=BevJw`fqEn>fa8I>DjftaCAI7J<{wLM??K?8{nf64aH2i7abn5 zD3!9$mMyUNMVxW#fDlWCT4TgPwKxoO5F$8;Ih10xPE{ESA~#9_J)km^__&Fx_BA-e zqDj2aDRWSUL4yJBqHoP81};h|6_ih*dO7#vOf4mNSA)V-yaJtk^%kOSF93=3(AGJb zp{h=YlyKxKGqLtScS5J1wC6qZV*d5~>c}U~&F*aN%*ZjFDf#_T$(_a7n`&Za>iONk zzP@gB?L_LB)bX}cbH|Em!1e>jPV`MsWawno$Lq7M)n%e?glbdfU{DNqO{MNF6{gw0+`iP>-*{`@l>p6a&V^jD65{t4}; zc4{N79>yzABZ>>WSb5U;FTmdh7m74HfJekgCGFW4TZk2UF{F;75m(%Ss#;w10h`@| z25zoiEU^<`AH=r@@!$LRhr4JQq*9bO7uHBb#fvf!Vd09?idjdpsc3JwKX9%J zfa@HUhz}L0f^Z_|;N%))^18~b5EG=@qJ&BQ)mgY>XG3#*0qByQm(YjXDJ7t*$x)wa!enD6h4NSDqY%?UE*ooi~^+K(| z1ZHN%9<{OBS!YXV)eglnGI#|dS4s=Lf>yy}wBW!b;Ppf9CpWYDVP$>f*PO;Q7}MZa zq=Cc7lzG*KR7{k8E!_T5$pss=IN8;WGvY5Et75 zS4~x7z(GZH$@=Unw4f+9LHvBOAqP|gH>pdTF0k??`8ESnvsN}FmsK#XWEQ|6DYR9w zyzWGdl$2W(t~kx9jMyf1+k(*KpU{lDZxgwxwPhH?V4RTsTftx?6SD8Lg?RuOLItg` zg{o!;s+IE7-jQXeoNeiM_mZKAN+<@SxcD~2wr!EHP}C7Twq8C1$Us<>fFUcBy+?#p z5Ws}iMOqBMc=1|kgu#0bLK0;w%73jOpdn#_s2ybsD~kdQ7T!4@sRcqoMZ*r24p^zS zgp3jEB4>N$I)>aR;{D?C>(^s2j8(#K1%?q(30^-U2O^^Qk17Y1QB`KABeh0ySAW*^8yj?XAgI^qBtw3ItWFN&gQMFy%k`N0|uIko0KU{i2a+kr*=KvaQO zHj$~2SyGsBJA6vf2U+wIjq)H!UN1aio1v;?Yqk!-w>sbCq9XCW#a^o%g$?(Tft{%Vb47GT7gwn^mSkH)GTMdy zW%GJuuo<=Z@5w`wAH|m?Q4EUZSKxEhgz^jJZ2we;OER<~OF^ZYOY|ZQLJN$Ia*(!) zRZ%h*YpSU(u*Xyj66zv4LMir6rQ4OU{_$iW;iIMDlA)&;rl;2C#&8&KJO5U27>#Xb z_UaKuFk1PDg0(iaK&eH7Mm8*p0hOo*bN{w;ARTQ=+9-=DIczy(s^>@5Vb z_@)RqI79Us#DlNOXe?Ppr6DHoZ1j0xA=XxLJnN{DY+)HC7Sc5s_Q{KC0ZyiMEAlQz z<5M=lbJeK4)h;_-aKy5j*$Cc=?NHZ`W|!BdRu;!F7%PR}3I?O76vPDlJQ>JGYy_yT zhQg7Y^05`Zmj4mwx?d*QB^gYLDIq&C%|R>lNpsLOS_?S`RR)wtO3B*Rp(djN>x@By%Jhlp~bnA^{ZL3Mzt4NoaqQQzh zsNO{->nf*~L*}yetp!hZR*sAj6qf--R+Aurs%nM1ZACc-Kp7JOif*ZGEgH#1ZTqB6 z{0)adHMC}NqEejqK3JEM{9lZuoKOSAID!izMFc%Qc;z5C(GrkC2x!v}E$lxI4n|vi zLNTNPa2T^;%!V-=esMOqNA>o}!7^K`xN3*3`jMmPy{%M?&8p8H@)aAz16R5d2Ca3b zYP~Vphz?;)Ee3V~8`?&KZryxJLK8%8h=Pb#CQ)QAA%LiuXu`lPhT<#a)_fFE6oE!; zjfP~p#?_)YmcqFO@M^D%7524eKLI^e@;203My5OolNT zer+-k;3GwD$h*jKXxwazj?o>y45*g%XuiX&*d3@Q86KPl~k4PZSKj4D=<2fnne zbXmP^)x1~+hj!7}I2B#HcMRohbL;sQFSd=);lJO+m)%c;af-iHvfE!DKd zUqQvFRZ#T+j6+Fzz80psG%Yvu+c6r(O5wLc!)Pc4dF&t2T_J#XC1+I`L(H7AINPR_ z*7`C+O?~2lE@>dHxJwZ#pEhg{D9z8d6$X7v_&4EYOVFTM7E+gT+28!#i!P0 zVPLg9Xroos7zaS0p_S625?rYG3IEyjJDYz_-e)dM3ZU8aKUiC4_mo$jU8|Pc_ zDkr0DHp;Xn2^EZKMXLuyqdG9vCo#&t6cS@yE7oTJORI2KG^-d|RO!G}g(PA|fj~w3 zK*nj(QH*_S|6{W6_>tsVGyT2q!1D! zDC*xi?IOmgSv#?0*(@=SgW^Dwd66(Xf3uL4au62(}3DXHLwX5 zN+26J%7+{8#D)_ZlL%_?(UJ@_5DnG(k`0wua%#&%N{RokP%c4LC21jqkj@cc6?~{- z5xpsz`Tj5|7rGmOs5c2_Sut7ZjtO^+IFbfj0^DX7IDrJQa_b(#p;u49@ zD2biRUR4<=dlIeTbZhT2fDNokIRT2!7%2FlG6~ySq8VAR5JCznsDafja;6XhsG^vv zRz7>DIk5mshukWFX8;*d)KiW=Qb>jhMLsJ1U0{WWpzO1+JWQZn3Vv=`j#8O2iq*ipP_^3>fna7w? zvaMqTIH(wP;9_I#ttIj3kpYZTcOny>M20vf!;qJd&~&VMve6uq;rsDFSVB_tFYW%B zOGs?ZWBPKUv&Lk>W+?$uiDWHA2uXQy!_ECEYi-Y#oeF4M)8-gb4J-%=(N~Db142@P zG}?s5#{vlKRTh@Ced<=Xh{S^H2(d!{q_?Y-HF5bf1cfTrtYU0yMYX1EIoP106YD3s zKxvOv>&A*!TF;;gx$4%3bi`m_j6-jSTk7Zn28TyGdppNFr=>f_!gy!lcg4a#d1t}j zsax=nOoift>vRVXDGP}jm? zZk3z^Qi;7r&#HA$uB-(J;^T8RRz$}=4STL?9=YsTRE#JM0Sk)jpP&^-6?eFZ+OVW_ ztg0q&g0i+rA33?2TWcSxA|gf&3#eOS;!v@$u{O6lxBPBQgYojwZ`-bURF;n%J!1I? zVhY8GZ*6={VD5GIB}aGGcMPQWs(jBY7sRT@VSO zS_>XIT~vzQ$e}^C&KW~3axuU1-)O}4{pbJp_W;kXp0}yJwbS%R`Fj24lhZho@95T- z_e|cDPL4y|Eq6}w?*QEHh0*EW8uHpF-gw9NH~jDQkKbuuD=4w6)(y7IAr`8uw@ z?Km8@OABN)J7?6jd#HC?hkW~VZqZ!68WElcJH2m+(6)NZ{!SS6S#*B2!=e4Zwc-f;*4Y&&)_aW=0oDspyp0{Qi@=JK3L@6!|AiimjGw z)qhfVYWDwUCdJ;he7M^melaQZPqZzML(prifmP2aeVKpRI@&q@_SbGX zExQ*^)y-S%_Itjs0bVSa+#K)lXy1rdwe;s&X<#eQ?pJ z?&_+p?t1#1=kq-m^TCDP3|A`jr@MaEXEXk$0rt(g9i3-XXcieY{UG0?Y{x_oZx-a|wmG_!B0kb=P}DYhcezdK9*RZD*-nFQ zU&E{O{OrXVTNSfq%g=|NKwZ7Zh8Di-qUSuoP-aCO?xG8pGq^nG9NbLeVigsZO&dHu z%zJ`}?A6RM=19@*>^e<^$5If9hP$ zSI>LEe}OtDj(3xi3@`oW9RPUhRqG^ltm4=l8+7bXKTX*=bDeV^rR zIO0jy(qP5+{j1qzc9s;6Skr==n3Pi4<`^GlGURsccII5>J1+fLo-Ch;*{4`+FJNE7kS=q7?w!XK8%f5a~S$<7C`Prz2F z<>p(ZCm|ePz+K$y^l+qh;aTS4<-%^MA|^T?O`6NJEWI#pe&E|#wLfp^4HdmXrYv*u zuJx(c;Fb#{9+w@UJPm*Q_fq7u-FNG^MBpXKxb^M);|s*_Dc#*;bpqeIV`S@Rm7k}V zzvz9pfJs@;`YK9{(Mc|frneA&|FS=*7sO_z9jvh}cf;3&hq3b^gO_kRaJ1;7+Ats2j_^lwrU z8PGR1nx$rfIZyOOtttVUI=Ma@0`2>z{zv0dk7rlTD-BaLI1FuPHM&s|t#4-_l84uR z`hG5Z+FZ!mcn6ZnBk0qjfbYQ*RB(`_H^U}y6kuU73_8H!FOR}A#CZH_>0u8z(uIX9>Bt^Yb+AG_agKMsKl z)U9rNBT4XjF2wNt^(u)U|FL znF&0otLGZPj`5ulXAu^kLDfvXO+k95#@SS1o>osESd0d%j?2c|8u$I}^Z=7X+b$}B zIW79?Qzo$A+-mtZ5D4G>LN#vN-&O%kw*CfNiR~PeP8VPP!8{Q=ds`fm>ZJ#pLphM1 zqzjhYHXRK61JJKK-kj0;Hl2O9xO8=~N||^VAH7hi`8YzZysjwtYS^tEOz!T*nL;g$ z1AK-$fJbM!LRaLe2=D;M8aH%%Tic!7{z=s&=ay;oFDP0i(HPMu@W!P0F5PgE2^LrKT> zrh`@NGVwUMdo*RUwMYr72CZ$azge0a z*nK3M{yn!Ghn4U=fx|ic|gaP^J z*6jUub9X%D_2Oi3N#f|t!bZitckeI3YPMu8?*X7G%0dRY+P-rvck#g-eQ9qI9~e>f z=<)2r^6X-MpvW)mkX=dmI|aDX6jIoj`v>R#5IeS1-}f!(n@iL;m64;3!a~R#tZ(<* z%B8tU@Le`d~Yx7)wClwenxc6ZE`B3-qE|g=- zXeGy&?RMw<%zinOCv!#Hxnumv7VzFmZ0Dc0uBelAXHA`JPgt!W>o&FJpFU}T{cl5= ziX`q31iE1p_z7oz7i-IaUFV|^~#jhXq}M=VJUwk(I~ zO_gsl&D716`hAeE>1}q%y8p%q-(&x3CjElh*jUUXFM;O>odQW+H_HM8!A|JA&OMq> z^_T(7I)ylM5vhR3@t%u`5Fd`)UJg(7SgWxz`;rQb5B_CME5Yp1IAONT`(Z3c6JWZF zoOacr`twEK`K`SNxGj>}hQNMbaZPYU01$=`YNQR|IMjNZ~T3e>!@O5ATx3IO>`_!D!GLPNvC~3qqB)+?qvwg-Q2WC z{#j1v;*{KOONya0QyZaO#e~vmSeeoUKGL&4+n5`UN0*!2gW4mFM^$^Xt@Cy=palcf zp}2efS@HFI$j$xe+ZC@LYt35CgN$RRf3wFoJO_anjwDQnF7EGG*H19Cp*9TXNB7m6 zzx?0+N{>JARtY%p-U*-#o+dRx8ge}WjX8i5N*{&=8dbUiHPPajate%{vKd(DQfE_< zX@jnkZAk)IYE^qaYi+I8P0zij2MCN?uA=SN^5gGJ89xe}*lWd>eh9N}NzeVSq!(pX z;OpHR*)$Yl-v1=MBhAkY`;-5#q?i5wBE6T-Ce9oFS>yaKm5{~Q|3P}4TmQdEuUSukJrq3R) zH@2x(TwqVYBTog^lUl%=c;Gk?|B*@6`b$=_NZYAWGG4|SH7Suu5RPe5FvaBW%u#$o zWT0qx8(pb@f-vzQB*EOWGCF7?Dzyzqv*R z@nXd_uSIU3rbtD`t(8q*v&s5~8&}UlxbMn~NRBo(oKh;75Qf;${rSFppPmZt2`>IW zRj-ZLf2!AV8tv01IIXw0pm_xuRS{1E7RuDMn(iuikgA*Hb8<48T0~7w<3q|oZX7*) zVSaUXN>e~Mc>UEt3QAhpXQEzz! zYaZ?Y1NE-6)BWUfT9-o!*32XQ+<#u7dEciF!vnp@Os-IZMV>^`_(E+Om>tKNof|b?exnx!G8FSrsEicBN(^5?wnGC=45E$c$0TE z?}7|)#xavCWQjC~l{J+#xXSf%R?6)TTP2afLkp+hyA%kIFhp(#B^=XyzBF?@Q3@lc z4?u@Y#R%dRCSOC1tg;$Zh%OTlPBu?j*M#y*1_yqIK?F%)lm))JV|)>nsA^7|*L@=6 zQs2U0MP8}!2QVS_d``t!QAX0HET={a4q3EoZ*m{=6M{>|t}^+ijTAWFFA-m^YQ(mK zAy;19EXAj%P}(ZmTuxhQqbr;8X@=7kj|G362{L|I_1LFj3@;=k;{?Sm zD3C{srC$7^TbqCa;iEpXcIx91`8UW;St4eNV08N%HX>$*Q3~|>(whoXe2BC|8fNC? zPuYvs4FW5c{cQkSk;|l(MgHCZVXNZA6aH@**2>c`XA~wx8QT>aQK7>sCUHjn?uwQ2 z-v=pSFxYSrS0YA6@?^pT6^)X+>scBWJRAnX2LI5l<5`m6r6s>{XA&p%us6L2{oqpo z8kb;FySXI{IOfb}JiA1Z)x>t7i=(5SE?W?cFgsf3T>`%Jos;b|wvEn?UJ3~-xwHNS zB5G9U_#*IuyPRy4BRMTpc??C4+8(}LGnu6lG!9Ws>Zqt;8Jq6t8tvLt<5UIUX);Rw zCW(Y~`So>V8>`(etiYZPbB>!(SUnZLi~AJ`c`i?Wb&U^^NjRJ<_yk$5H!3Qqi533X ztxPhB=!FRvlOjUY&fj|F5I5b~p<6D5SIG|42C9lLxu3i(iR@3VI#MS}E^8(M{N4X) zsM-)NUjB^$$SE_?rXH;#iyGqUI}~NXcd<6>^`KwKVkRuoLGAL7}IX)s~GO zOhdtJPoP{t_O3nJ&&bE6KOXn~foFZvK?+*gRj&P*538GGM{ZHggr|gxrOcYZs6B|S zwD`b9GJrY7YhjAebG*@Zuebp!C#wyZvH0w!bAYg4e6Jmr^cl<%l*ZK1I?a|*K6<>u0l`>WKjxa*t$DDw}w?i zq0cN3&o=8cVRSRe_a@|EYNz1v22lrSmXW1lq(G}OW z*Ec@RPjI1xo(iXOwT%CT)DOK3m(%Z0x?Oesmb)a)qJyWjjCMGlQ5&P6U^7-dZ_9j& zlA~bYKba|ERe-B1T@>-iASMw`W*iiGAjH5fRBwfqsRKV8mQS`lO}7ode^yV;DD^%* zwL>l=ZH5X8&u|ecJzN~tR+hqs1R1gf{*I4fn30HNAGB;`&pf2RE2WIOcAFif*DeI15E-yQ0s#Y8g2*MZ6@`^I6 z&I8;RE#gAb%lDz(rJw#t9Sck0I@4Ei)FVg|GP@ux{`47RGlv9};#rQQ&{$0JBgMDB zfKtqvwUXxX>yBwI15dw7uS1*L0Vh zf13}&4eBlT{_dS}TU4?&pK&*lZ=s4;KXXh~a$7Gj=(n}yDzx3;`K(h;o2-(-24 z!UZ}%W!OzU*j!)ImjFTrFXAN@q8a0i2^P5hV0~;ch3RPWb$b zplr-MUKsmd>;(A&<;q-w+VIuy7M0- zm@LTFE+;WbX94U!$jN50j)@<^bU;WB>zs%3GZ}ZhD2|`VF*j3+N~KR&>h4_cUf%e^ zjig+d5;$_T-T_}Py5`};B@vqs3329d_@G3C;k;|DV-hw~sp=G4v;Ga#eyicA=eWyE zwR;tN6m=%xT=TFFXddCPR{ZIvP~m(?%Yp7all7fysY?3hAGeY~t$ijtsTfjbc$RQa?id~e zT~e32kxWJ(MDsg;c|b}4DSL{#VQjswKfox1i$@*a-4|dj5#8HDiL=KPrY8ayk(HFUjy_H^%#xyv{{k`|wy+kt#6~-vh=12Tz`qlpG4s zvS;YySsFZq_brLCQR~&umKqL|WiEaiX&zw__9jBkrg?bb&aLzmfQ<-eM`l*kQXM{c zUS5~C3#;<;Sq^A}_cRh<<=;}%`DTcpcZ%+_730C971D|Kif9a~(l~dEP zvWW?mV`!y#+Z~{_$jrwMw%X24+b(~4S)mrOcC@4*^y~}T)>9={{&h5~_o6vS#we#2 z94y$k?@I4RjV$}glxWC_J>Sb%-A9A;Zh0!v*N)pq4XPRJUn=`UDcNI&fF?zD)MY3r zfK)uqPA!*myagwRkI0d6+pwqiAw1YK#op*UERlv_P^aM_>d{PSs$3@0mw-m`u>$f)T0RCN%a*R;gT2BnATyBN#dX)eR!ISC9(DD4Ib z3Y<%N@mcjWe>5k1eN3x{k|c;tB?kW~9$#&aBIQINLeIIJk+4Y}0KTV5Yd}IyYU3KA zQmF2}0=|?sgb%91^JAOHC}P|VFe)A>QLo}kPb*Q(XYw?~ebPUY+#O_PAK?zaX5w~; zf`|_N8&E4&iCFh;;@CqT1nuk?-%tG2nQ1}j4aHl!_v&=_@9E3?>i3KGZ-h94Ylt>K zeFfgcN6e~D3JSzmgVmTBQ#sSui9M|-w0U1~9ufX$Y+!u_9Wp%idI>F_5qnp?F5&QZ z2qq&*oyS|%>1pP2bOj1--#*_-k~gZ>qe-h8aFSc}nm_S`kF2hE(RV>0`e(^0!QE9U z#jh2agk9@mZVNI5`hW*}IJuDSc*Bd$o2ZbKKsXpwIDB7!R>jl@r1rdpc;EV=NBoGY zc1S)~1%HcXP=wvCf_n6iBMP+%`f{SnOtiYPYz9%xd2s8K_} zp02Gj7c?=3WCIRsA+Ht_k~oH|2FGb9c_B^_C^P@duY3mumoh%n1?FI85d~^2`L4$z zKld~ay?`P3tkK8>kfu-f{WI9A!uw%3Rboez=Dl8@!<_^D;r$Ae$_IWjrGVGozlY&U z#=+_lxpu5apxLAO06nwn=Us?Uer%TC`|H0eC?*(VxB*{}a74$H`dJDbw|S<4bC$0z zR+`fF+tNRxPIkYjXTmU6SUBAEISbnx1FkpW#$zXvi%S)uPvFbnGmA_4alhFJ7A^{y zY7|J2tbCXILrW>6pbsTh=8_5$uy^ZIjRA7~Q7=S_r5Ab%(jD3zF;k>5|K=5<8CgB4 zgHS=fzZv2ECur|&tm`19n$w<+0i6@KncNST@b6yp@2;`XCdZmX-5J)X$p`zJuhQWK z%6$Ynl&0djBXJmXX_re;q-(y;iNQ}#+1P{aRCSdLO`;SFo77sxVM{AvgZX?_Vb`CSj~mh;Fhg@CHG zFvAk-9O~I~-1FpsU}zu;3?9gWS+k@fp7cR3XrD_3t=A2Ph@8zhwSz+e$3b(c2U@u9 z&bWXsRNDHK%H+FBR$CtFYZ}US9`2~JbyIYj53;dk*NGWIIaIwzY$qgXe3K|CaGEE) zjV%TLuNu5#TPi#gHLz9NmJsCZncEd18=_OQT%=)Tf6?w}u!KZ7qVk zKE;<~<|Fs?HvPr&cgDfE`4`8YiVp{jM4mU%mE#i08Ow+YYh7>nm7>X>cZEH4*GKO& z&9U{BkTXypKl)6;Fst+LJ5Zbm^SEj&L9TW*FIjZ}Ch=N2h{9R{&|WsSAyp!DGCE$4 zU7*$j1NX+ zAycVpC0-e&gLH8Q<1OAHA#U?rzl4#I5VP)XN)T7Z*PSXmPhNsBt47) zJ00*ac&*YMjP=Xf!G_Y!i(aSKq+h)`u#_lYcQ0=TN$;@o5;2ku)sQtqRO%C%+v6!p zzee|dpNxITmZ4!9)_?qw`d5DxT2A8Z?ETm2gTe-CbrUR~34!&s?C!#H1)8*`8!k4< z$DAr$ChzQwrY>y#*jSXrsNKU41PotT%D)#RE~5ZONf3g;1?`Y+loch!9mzAJ82*FD z64`Db-XZVVrX%}pE46U~r$$#*q<@FG(DN`dAH^~s%~EUu51$$oUy!c>vhj{54mXlM zs@2tI=#7jq5r;tReo4dsg2hB)dm6#uRd5IrESRPEMx_*8wVlXl7QXmIjV;~Or7bQ8 z^0dNSZZ1y@CuJ?|j7M^T3zhekXqZs60lTA*AKp((I)~%G)`|9KiN=Jw&Z1zBbgsJNaA~@&^(r_nDy$-@9Cwr}(J`B)s(hWRbs!ZLiwdTCM%}eb9uw*m zG!VJLeS!`SDaqrl zT$>LsAE{=W!s+mg`Wvn{Y^4M!#kEdgt%bshYk8$N8&Lfn&G?#g5*7h8fKs%FNyu?7 zNBBdw)|!&*vp#O;yrdeNu$BHVti8_NPhAQsiGLQHC2BzJ#Chi0be>QKc9EnItar>z z?|(f`&IM~y6DlOWM5mu&Sx-rY>Y@jtIb+L-UqvGEo+xSU z301A?^~@X^o7-RFJ{e#1tFJCb@v~1C)&*Xc`q>sfK<*?2;x2O-gcwioaq&{vt?hps zsrHqP{%ZL;J0fG?RBp6C_bc@4{@0?fdYAYw8!wAP2JX3gUg+*fipKDf%BcQL_hAvr zNJB!7z9HClbfQewc_!pE>_3;Paat2#-PpzlA&LZK45W$p+eK)dYBC$t2*Q_;IQnJQ zw|PI1ao-5$Z@V+MCR7(D;E?tTE;AEeBb(eYnn!t3NjOQ1E=z=pW1FVUw1tzpe+j*_ zI@|&-lyq|ZkOn2*L4+_6C<4*X^(3L7gdg8}9hMFh?tkvwB=Eo0&JP zFL-2w->E<{QbN~AiNdeX)81hRtZ2w=X_4N0^zb496b6L(1Dj&sG6ZF2TEh9Rk?W7< z2?&>@^5t^p5=GdC>J?#OkWlUjt&`UYdLgrzv%)vk^5C&UAE)SKANGciIXuw{`IvuY zDcTT*2H&6-6DLbflYMLaKX)CIi19cSH&k8ki)2}yoDa9;(pRqM`p7Z;kL6ZGu!|Z3 zeM8@V=0!FQQ}Rfbw*9i|SJvo3RRLIdkfc=gzxmsC4^OCLIL@7biGn z-Tc92w)QBDxQ2{9+a19|^S96Lxc`bmIk`mIJXosvg~|%pLB>Dxot@~AXumR2i|Zcq zOr_8k3#LcNqI%(@Bx5DwYq0HDmq7uPsR|0I!n}*gD5*6tuI7t>Y?T8;>dcj7mXhW> z@p+uPR-BRPqHLI`pd%a5oJ-Jn7;?(x_?X={6IUvk#;kLwP&T_ZNDLXt=w2)t)B1~G zl^dQeOg|;hVo(qu%rBq=-kVdQ-N^T+k9Uhgjt4YJmdOf|=jS=lev9cTTl^ToWF}h* zD%5xG7cCc}YaxZQK;gkw+9Up)k*ylPhm2QmhxF6I1UA!0-bGP-UOyQD;P9+o=m!z2 zK{DM!_G!p5s4oT!RJ4cRQhXx}h})cUT{wQcSZI1yAfDpF4{a^tik4w^{3e`=&&zzR zaps@ZT9kv7NAa}AmGCw5D6Mx<6Lmo!O^RsJctALSk+kzT?b~;<2)h~%y|M?1X`^3U z#(N!IIkV>ZO3Lk~!?sExakoGRH5x13cv?jogoo*3t1Q8CsnQHOsS~WuZ)aY|lAyFm zT~)@eZ|W#(V>zLI;WC(uDy$`$3>xJ1pKLiig@?2$-cj($Yi43=n?-7A>6 zi^3*lj1*nv|H&$~%s@h#O)vMvkPeM_=`<2C%MpEqD#(Q5S-}TJ=ZJRpj{d5toGzfpQ+yA7N|6_Jbx)F}CbO zYl@1!bK&{s^;IiQHDU2GCHr+6I?tOUE}}QqKENOGc{%Jxl>6pN8+w zo&bS+NKUo#^H29g2&vL!&>~IE$w+Y3)O_@R2m|#lDop4Ga`-JGRNH?!7PX_a$wdUC zCd{9gt~abDQXwqj=N6t4VWI!6`;B=Q2Mp1P$@fc}s0=qz2ZK+*yITIw z+4)wzCCZMMAL=(o@JP6{rs=55_2{y&qrxSZ)5FFDU?LTVM|V8+P!xN)^`tFFOZVc- zU+YjnupyonPGB-)9%w-Lq2d{x0F z5SgslvZ`YWg8nt2?)DMQKYpUR@pA>0cBnB4K%p)*!Ge1o9t6_=ITE(_H=a9< z*ZD(jJ`@@Smq_H7#v9XXjUEy>oKRZ$0Ga_wECQ}b9OGamRI9x6c`S6p3^g3Un0ZS< zV8>Zb6x#}>0^$4;Jmf;?^gbpJ?R1J=rHt4fWnjM9~fcS7ypo{XS z^+j7%-!xIwpd0uw1;uhFnknyyDbL z7tBk`xY*gDVx?f`8LFXw7mkdv-p|vk<8|h1XDnu4-G&=R*_WxFSC)+%faHSKLq6w$ zwL|~37wT$AWoU7OV`9dmCQie^*VSWSut5>wS<6 z4$z>wwGNS`S2g)|`JRT6IHjI_e(1w>5yb%vb`fmjJ~LtB8{`g$1hm2aFew=ryzfz4 zipF z1trUHU}5ros`vBO9GIRgxJ>#>n3kSzuJcy&uV2Y_q2XPrQ<%(OB9$0b_pGDC6+c7i zp6V4XTvgyW#E7MVj`GNuK>2S=$k5!%*k!9s`pcfGhP5~J1G0Qyknp+DEN%P5t0+zX zYT4Q7NjJygoAUOhjG=E!rAuofJwQX4T>@jmoT8B>B?N~0d!R?(LLlgxReyJ zE^(DJEiZX!q|{|XuF~-BX=bj4WZ>#|CVl*O|(@*P1dJ; zOUB$Nag8rXbl&Pw(aB}P=J%#O1UFQiG~qb#<;E?;s8ufBo=g-8Y-KtANNIrBB-CeI zR6?Re7C}u;mt$BEX`AKzkOUc1(Vs50EWzSAwU;ONb`?*y!W!iS++;eyN+(h?u9hmL z>1%==uD3f8K3ebMq3u>!E(h!J0dyQo8Mytv75{$5U%$*xwTh&P1gjqS+T| zz_*w){xu4DlIbn)b#E&DTt=C@HW(8wW)nOU{H3 zRh@jdt!43ks03d+s|6%zudA?+AHqtH^dI+~X3X0mke`i_#((s@B+&R0U;M64T(I%`u#4b($|%{vvaK82mv;V||g zkF?T3MwI@7$*`+Sw*p_Xsaep1Wi2nA@C7-Pk?Z$KQBK8xD9T8imyHT?+0XV>nYX}Y z<2xA*Ar)!kkM69m)uM?soS5+rw}TGyM=|3Q%LPJRORnkj#HSD_q+tDjmykZJX1ok4 zkQ&JsYHAEoFer;7UA2>I6YjuSVl(cPfII zK{lzIUuT%+Zj(-M-ZeD760OTXfj4$7T0J%WB2Xg{g60Y^y3@ zsrfKi8%)$tELGWBqgt}Rt(<=hq4lICH=H76>AJ|Q_%SHEq9bX2o&AfWRX7TJKGk1cn{K5qzn zL|c)by_>x}<@$QOxYrbt@(hClS9~SXYe<|Dlo8-S2{Ra-{fVxTEQ=~F&1c8vmBg)@ z(kziEsW(Lz$ysqOtFl*O-hmDaU$zTksDD@o_aNm&N~+>jsD&_FnAZLY->~(- zKFzGxxsQt^fmVg*L~;Cgx#JkZ+YD!G3y#?X)JSnxOKPOm_lNRUZHoOQ!B5Q*!7h*M zf>l7(kNb;p<2{k!fCcbUre zm;kZa<2E6P9=NUuo6sdM>`G4+Se|x1J5L7}foko*|B-0W^W$Dzgj)|M19$R=uFt2M z81>Hj8jfcvT%gh&kS)DslfUu^_1HXP)Y0Pc{?b)3?;}rxXUl??$W#m@ z%V>yPt6Wk)*l0Wbwe%x+^PKL&9;E}dfAJPQB=m9FJN8hwDs%iZP@AgDTC_%9lnrS= zK5&6g8lYj2maYrlPKry^uQi zWk$GSJ-q5|Q?^h@(!SFq?}Pa^lwB*t=uUpdpye{~79sAUW;@XlL4~8uGj)7g;a>47 z6jJ?qVxD>Zvir7IS~0blgC=dHh$MiCcBn|jc}VlYd?Dt%ze-34gHVLx3nA03$?n&O z4Ma4buj|&_ms)atUS}oy6s>8Kl4kkqpnN~~+B&OveJOSzvZy}Y^vRaHqFtcI@0)*x zozhiFC3~vj0LY}1lcDqul=vQdp+FU%0{4ZPN|MV{550G53XC_Y%fvqor5Ew$BB|)< z&l5bgK%oXJ|I43dB#{;(d8TpIko*!nZ;fvwkjZXd=&Cwd=I{6Lp3OGvWi{HN-{ z0&%X$c>J@)TDND0uD)zdb}mTSED(q!HW&>R5*ZVrTWi{Y|GfD&tg+X6sGc=vAc|wR zpv7!P5T(il9Kb&O5WZI1;eqDu+IR9dLz6v0fv_;hARcVTXbnlrKPcq$)ufk@AH03AaNcwtDn;}BkPpZn);f9vZM~wU$b$RDf8(SpFy|us>lNm!aj+D_*8dFvYCAja3W6Llu*0UO-LZk5`X;Sx( zWZPwqqg7(Latg%#k((yq8O2VSCRre09;0Jl2;*@;OJIF4k3DO;O?X=QZ;opo(9)7$ z+Ko)h`ea;WsO;TAkwJ(>xe`NI3J0NUbMo^=^>D$3hr3E#Ba&&8Wf8xY>HBYGMysWPyFu zuM?#>^v#(0yK!0c)8cj}^|!H5x+hgyeA2oJFBqtjFaiiRUy6+@tTalfTu;`F%-?E5 zD9TM(HFEM5U7b@mt{@!o>Zb4pW&_hJLe;Qd+m>(lvwr0c?C=YWm87qlwrO!}MPIg& z7U;a~)XGdzg%Ahytr-ILa4NDUW;lMre;bt zf2JCCwweaxJ5}d@MyZyrYBC`$Ql`LKXCv4@a!neaYCdrnl$U0hl&A{gTVNys zq2{@gkYwSqm0e4ejXKT&%Y3-M`~K@5ZE@fc_G4+&41`jd{jt^gHX6}tI%VH!Gd*pf zyYXWfQ)+A4Mw175lv-~@P}CX-R+f#BDvochc_Ij-LpVHce%m+<3d598S~2t>(^d(R{%6X{;P0_zGJgR5s#zXf|#Q?rX2pGfzIs?5w63B zK!*UHK%!p2&}dofpHkhG!=_gF(J8hrg^G~y&HB7GcCWQvB=kp&K0|jN1P4s(c$z?` zimc)GsDc<^y^&4VB}~Q-pQ?s8HPZmeOm$@m_R;Tv8EN*JS3yTPc^_&ang=uZ5 z##GLVNn2L!AE-~Lwqc~yv@^QAE=w~*z6VI<_jazjU*w-x75z(qJ$*kgkZ!!QvU;(w z7MF|8iu)(dZ`pm~w%!->7-?K0UIwES)D(p(myeBwF@`VOT=4cH%sPNE;~2581rfVz zHiFEW=&FQs$;nH;&8L4Wpz71A^3Ol^4IPVKRBGy_{1Vyx^*7M6Z2QZ?aY~v9v4*A^ zb3G;kmQr8qd-qU_}dSYgZ88{g-NDEsloZi{L;b+{XLn`tn;PQ;~wK=$PiGeP@fiQ@r^fLe2F8g$4j!VSl)mqi_(l)xA zRCw!*ZrH_Wet79B%Yo(McG!V)ov$uoXpfvIx~9a`N4UnqXY%5Wm7McI7BH85u2ynK zMTZrvR3YG{eq`}X*HGXYqgB`9-9#~Us=M;`K3&Nfn)A)$eokNlY0s@X<$2iL%^mnI zz)54w|9p~b?)E0~5hM}G9*TTP<6+g#7M+h+6e~Sms{DW%@a*1oK(EBTuUKHEXzssN# zsP8zfc{^ajJmtg{nLk=8CBN;R)ZyxAyj9s)ktWz9PaRf}K%UCk z^r`oLKdGom{dD))6yUJ8-A|I~OqQ8TSXl$EYXCoXGXAr|Iyk#_e_)SgXE%FYhwfRS zSw5nCHghm}@Kif3Ra@DR6%)5=>}XLj_4j9eYn>@|;b|!#TLWwq8f^Swe>hkNE10)Y zAU^|04L?qzxd3>sO6YKP(oKtUfOej=6x${LZ{sqX*9=TChw=Sm|`%Dsfj5?4=i9aIdC1FXzV345VC-8!R;K zIhnJ}m7Y}T_j#<9fhi!Kh zc4tIuBxQTR*cs>SXIEZX60;!ino2nZO)e(2SqgPl#hf z=BMIKd4Jm)rkx}s97uP{-j4a*#aS!7NoD!UDb4|Y!wc);*tH-_b}m2P({Y&r+p2%k zeUbhUr=EQ?CCrq0dz2K=ztzW9x+A@uz=vDub!eYE>0*y~awMpF3^XZW&Of~8WWh4p z#1RHxSnkPfmR4<+&}keVmgm?95^WoC`dSsYguZC=l9y28vZ$jaHtcR{hEED5I~zr@MKpa%ilgY1bI=F=UurFYO$Y?#jC41LBC@cr4p6g~^1 zUkWXM!vzW8#$I7+8@948vBl7t1x@@@gIq<(yXT43F z2u92afgG;Gf$Y$fh+F0!uydc=kYald4s3*>oaA@V0s#G zuk9}J(_+{?3s0Cp2i%ezvAsw z2|v#5x`y(1d=453s^o%5WfePZ2p!HSThOKqRpG4x2Q?2OksA=~y!JoZatC^@IL+T|{?mWi+Yg}F%YX3aCeY^bJAWGqs{*tK)H{L=pYhJuJ08wg zI((kHuM0a`*e|#I-N!GskQuOXwgfnrS2i>^z3#`(L92B&xrgxRf5Wx{TGnH#UNhq~ z1jcT0I>M&(Y3Vv%aWY#Bh&CSSczv%j{X6gO?uFwQbS&?|;B9HWwKdJn?SRf3clgxX zuBH30b{n3LWLs$SHrv50i0{sD8#qZVR{BYr*a6I%x*^^Cl0xL&LS$ujbj2i)uvSCd(~wGlzNpa> zdq4hkIdMQq!g1LR=(yiGiFv4)YB2zLUD)&aik$JtI0)F!FZeis`JKWfWJop#4|H14 z{>+9s`g>XJ>}>{9qR(#hFM)H2h^t0BVhZ7>z%}RH8!t4xBjcAf5+6YV)pRLq;D^gg zc;D-*^q9M+Ni@65mC_S8`l-zsBCjh_!b*XZgN_baO8HPgPXBQRu>-bjBT7AdtVnk;E@UZ z`5!OtW3sc0ZE;RscatGY9P0g7;jDe2BF}8hs>p1Yq=kw8g_icBxd9czq_~~NudU<=XCbQPEF;?yOTTeiD zMT!QwW%2SN<>Kbw=Hi79JhI2;+yBAdT?V!N_HBa)T3m}m(c%<$hvHD&-3t_VmliKl zio3hJ6WoitLvasof#ga5*LB~|y|cT|&d$#4+&i-~nY_sSUSuZ8ndCgbpW_JSb=c~z z`}X1-=mr=#ng&?xwEV}>$~U* zbO&xY)<3#D-H)m5b~BOL4fP|2zo*?YGze^JR~dUp-nHJ*ri6MPw&DHa=IHtQwcWcB z06N^6xw!7@ka6&Lciuk&pMt?rVBgNm!U59PdP7LJVATHA&V7gH?b@(D^veTNYjM_g z)u6l0xwbp~o_EREd^5lqbboWs8@sm+-sy;mIb6%|X6Rn-*uc2QfDu{rWNm)fpG18; z|1s=YTNm4b*kbbF?H9O2vi|5bpMCDXF6a~7`*L@&9~FA|8Tj%Z!xPxj@#1q+5ReuQ zRs>v8u6p=>y_?Js#RQrLc=HN*wBB6b3u~U=?anm#Q5-LVc&Bni61th1x;NKDQvyt> z>`2$IqFRgs8i87l^Rc$4rxz`+4;i3a-0l5>4&Ip<+I(qT4QapN@u^PwN=x%@D2g>PL2%1#RbFUIe<`uy_Q4robva@Slk)n(K_ucp`6otQ^<%eP7NtZg?F}HfSa@exFMR1T?(5JKY}42ncoE9jfsg z7>8^bU3C8jecA+j-yq>HiEg&FctOD6@TB(-s~v)OGZ(}C4P{uGN^#eOmnZt$E2}s4 zh8`!*U30?|F*U@T{zVB>7G;@U$sJZM#J5x(z{)-FZbfvWaVKQ8NI=Qr$wov}QFt%9 zB$jU_Ej_W|G70igzr$R6KbFBkoq3U~#=&b0JJ4e?BS+%r@nO7JU^<7S$a(Tl?sal+ z&ZL&%G{Ir;1i$0S#3Y!=bSBW{gEesD**h@s)G9%-NyI-1I!q$C`0*emH+`_fxQnsm za*hUJng5i+U&LCCXF|Cn<2bj5`kq%|OjGdH{n1zn8Zx%!dgxc5_1IL{$_H?d%&JD|RGhcJFo$DWD;Bnh%^!=@=?oKa9tiq9X|+Z4zSSyXGqAgHv_%qITGG8sLZ* zun{sNi@UXR^FM5Zohl^HmWyn=%zMrl7;B&dwH#h~-Uxs|a$jQ!rtbZGc2~k7r}KlH z(9FeLuV+}T{-&g6XpXQ~;#T*UqJ7uW_i88g|pz4PNgP3nD^8GgCzDAumQF+~wjf6MI75tCvgVu#?gC415NPO!&?3@DE4ejp zl4#lC;N{;bT!Xu>o=d^6ofxi~E52>GGo#0O1SMfga;UF1-e#TIx4isbkkIJoid3+2 z%GaR-$XsrUbWhr;@AFwV^DtQLp9y3hDlj9_Wnj)95s<^tp097w2`Nd9iAdo^V*Aem4TjiBy?01ZyO{4`7ae0Hu8yJIincxJOQ(FzhGg07>@R1`FDQecS%D5MY80}q- z-l4U%6Q{#bpo*1i_@atv>uFN!&<9kDZNqV9fBpi{s;X$IZ(+``hAL4eLY+(D`fa=T z$1l(4+zvz|twv5laj2g*{OFHHx$cVTZkI$DlC%9(^f6ZC8c>3C=sg4;otdT5)tCUj zrPz_$`Wu~Zb`7GPTOC(-ty`y>=bY5z#CHBuQs8Wjv?mqOKKc8n@Q%v3lYW*!o&oZ-)qbQ#RN zOLB*okZ)#Clz%v1({P?{)gvwRjNLUbv+jCC!f?gxyO)MYT2m(Iz_6FSqc>0F5P{Ln zIIvAZ@?Gm+@i0E;o<;o1_RE9O2 zTkV6s{HIe%O)1mb>2}4IMWq$ik#Dp4?snpzDS!DqBB>=YR?-^e%QemOl+B$UUM{+qiL31MDt#u=HC$)j2l{%_vye8_<4!&F5ZevRh`nbNfkQ6)&y=U+j($BlW9Oy7Br&h`S@&Ni_tc2QuWPmuX#pddlx4U;8=~ePNM&5iTroWIxaq$l% zKS*}U?@~`rc)K^d-q75JLUIxKIlXg3xbn7+*^%wkL9Fyad!_#c_11!1cmK~(@7@0u z>LvW2sJG%@sQ2st5$g4E`hSFa$NmfI^_c&E74^pcFH!H}|83MOZTQ~|^*;FhcSF62 z|7+BH_5Y*Z|MsZY|No_ zP8Ii=Uq&vPEYb9cdWhp7-!dvuy;X zzdU$orHz7=TD-T3iN*+!LyU?@A7)mChHHlb4{&)y_2okJQz`$@>U>4APzpPHx6T$;k^UwkNHi_Z8DIqTB)K{4P(#! zD=<-&I`WzY!MrG+!Fx!j=cjqdvDTe;dp#G#&%CGiTQDr5=m(4)_x8~5xQAsyu1zbn zU9@z|^KrK{a>UTG;Ge;n`rjttF9-CJ2Z;?E5o3M2WcRZ?C-LV+`>XATp2 z#+F{D1@UWh2!x^xi({|_Tk0s2VPMR?scc=Iy(}P79HlDFW`5e$Mo00zb}>ZAW3;00 za#;@@uvXKWsi~8yFd?Dj?e`iwcb&5-psjF+WyaoI8=p%T_MI_SEi1-AM}|n<@uE)z z?rj#8Jl5O@-|Q!ah#5qEJ25U=>T<>``NG8tK$;5MJvt`2tF`4~WS5l5Z}sZ)8w-E$ ze{?V-(|ClmNvAY3dCo%g0lvail{|uPR|O|j2Ww>bxkGjEf!(OVUmctc(@?{V%8qY? zlN@dEj}9J$>fl-|L*R@4!UAs{kufsK&7<(~%|AN0RI_Z;=a4!iqWIyL8*>wWIoMPP zdvQ89T$I~6J{XswORJJo8txvA@{xfH4=f&Zhxn28P#cl>AE&lEBWAd1dlaO_A8HYU z$^Ajs{j|;duMQ5Wv)`H0j{dL@)xp*3g6q7bW~ze5SaA!Mv7F(DP#tVm4bR4>CIuJY z8z$YhT_t43I;Zc&^h{Hf=Rj2V;NdkYHuRlZWcy>=hfjW>{@S5Y-5(Ee+rKT@U3+&$DaPCM>4klwW!yxNqmGWK>2si$-v^jgN zO_B5+ntcl0zUI-7Wx<6DZ7;Z8bF>P_j3C0|q&NtdK{nAMV2wg@(!SO+^v$KlF?=L3Qp5mbYy$ z^fN)n0RX~AW@^^7g(YAk{i1Tjm#QuruCjZrQAXjQ$?OWB;=J!PbM6wMd#?i?NdPLi zL2DO9Zgr_eTlzWz&1)a#QTd8N0@WHVM7RYBMR=5lp2}&CL)`>&Mw+}7+jMA6@O45L zC;Q*X7qKw%G_>SP0gDm;1Pb`E)HHwIX;n3e>o(sTue&h!FW6YoMFcqZaUaXehR+BU zuMQ%^ZmBrFg_}`-E?bCqZEK+jTg))xs6ZI$^+n@}X7%-yMsVwu7_TaurzjWrHU$uh zP`$!Rz^+E*7|h=Z3AQcck=R)#)<_!YnwaJZ4L~f_uFP;CnLByTB?#M**>bht;#>kY zoro%4QE`h7TnM~}XK^;Qa;-Vc%zrPH>he9ENe6LPk2l-#y#z9VB?#~EBVw|F%`b$4 zXB?KF#s{KRs)+DDr=Bul_8dy`jMzPS$p7eIiT3ib4e$S{gU=$r*!@cf^M7~4(4off0n1~;>yqf55p!h~YI(8I+ zs?dn8f3TD@liffPM-JGpA^f;M9GjKpj2Qsa4--aChGxAjaNalFzDQilu%bs^I~*=~ zw4ouHbz9LfMF7mLfk{ftt<_{U!BZNQADeU~&XHz=3-%IR^<=3UAo1OgGT?xxLFFQ? zs(}cqb_eb_I90{mMKUj8Bhi%BX_F=zESSo|fh=?5T-lH2E6oV+bQ;MK% zoo_XxOz}y&u#sSOJMqnjPafe0&Dz9LoFB>Awf!3h+k()KqJw+p6x9=4{MhE}{|+&3 zhZn*cNstU+uhSEJllVcTGR)vGU(5SWhR}Rr(iJHP>uuS)3Y(DGAQQ$R#KG^Vl!~%? zTE9typHFaO!@?AozP~l%VIO%NVQ#aASaWKs{ zN#%dx;E-Ph<9eCaFa`5j4*I74MlCH?*$8u}$S(ij;K@+_kWJQHQ%vN@FG}MigKaj4 z%M((W1#E|1HGzG7i>gVq9hw*ii%VMIcVd-+>vA1Fb211%4i>qfqMVf@3BXI+n(G5= z3I(AjX7qRx&(cfgZd;$L;(<$sD<45&4WDK<>Q|O)l0AM|*GK<{?V5uYk>^?v39D}{ z&Npm9l|-8h<3%u+s=S?0ZHaB8wJphD>Gz*_Rm*3k9sn_P=F$q?>gw=S3iv+}Hs2Bi zF1485D=08rY69H`i?6AjPmKvrCvA@2z-qUQT@%G zKwmdCsJAPQ?|A2V^1qM>CY@o0Dv)e_U~Q(fY2Hv#{$}fx9aiS3whX=M z<%|=KHA5J_q4G-*3H=?yu)Bfv++m|d^^gvBrHR=0KXA@5@xD~8M(N_jCEKTa)pmIV z!?JBwobR<&mO}=LS{1D0V@>?d=xXuZc{EFKeO{@Kq#$gox*xOb6?=M|%ZnOvnC!=E zSf)CQ388^3+rtCBMT~E^8v1b4p+1;2MK)2-p}OpYH4DCa|67uf4>V8MYE!3?EAni6 zY84DWewxtQi|PxT*M?vQt#Iy0J6h*kC`OK4z z!Y9)+Yo_`7PFzVqa59L#$kXehR|Tartsl13Bh`WSx|)QP+}<8jDhh!FjqefG8SA{o z_z0+qxDc0amZSOpaoXTpJj_Br-Du3|f~J~bO9Ous3~h#!b7rzIzprX1r63WUr40m6hzqdGOz6sS6i^rNsbxBmvh zP7Ccs{jhL)qWf$M{lN+K0PY@>Z@nHYvk(V2{B4? zg{^8UcCXZ6#9`Ns!Hk*Nc!)LQp&XlvmaL7&LP$j(0ztg|J5ojd`HwW-s%`P#Xj$+= zwG^56Z19`hgyon^>m;@^n0Xy^{i5XFsdV1`;`CUCN7k`FiK}Z!9=D89)tb{Vy##Ok zff}!`f2@Lg(P)`-(-*px5uDWYTt+#9+Mm}S9DNy7jQL#qD-!VY3C!Ol$VD)el{pR^ zMYb1iO8!E&knO4){tJXFE0fo9zww3%{U~*|`ffRlxA!O>W|z@NzpT@HkUcj(7O@(I z-8Q|}wNOg#Khpunf7`cNxRhdd44Nuu*{K|Ti-~21q;AOVQ`gws^>RBJU0@pVIlZ@) z`i`E9LSgcDV&sfb=Y}Hh`*A>pYy1Vmeon}hA2rR?Vd{9ByuV!ihd z9m@`Fxb8-Sa4jFb`YZ@`OY?mUN?*hxi6iV^DA%YptNK}Tuoh$6=FxSzH#1MDTaL@b zt-V`crfBFrop)AvAv6+a_B*!~DO39WEc}3`Y*o1p<)LAecHvBxX+bH>m59Jq)iN&X zVu03%SRDg3oHSgK<#*}7$?8!-|JpKw{ zqQ64;kAl;7cP8ZPNbkv!;OQ|ip&%@>Y1umC_9C5@SmU8HybWP@oNvRai*11-Wyna) zMWo3RYOD{29LS=S?s6gms?@XE!3wr4RzVcTHDO8W^-96gG662o3IV8 z(Cv9>by$>o=YHi{Hdd{u)*w#On4?rfi7p>Gk z$;$~g@uhC%iP3bm+mtRp0YrlXQViztKWLgtmsrj+WFyz(ou%?&_06JSU5;2`%5Q7G z>snb8uW3YPw2diPP;D1YAHP5MX5H+7Jc3up=_WTeR@e3B%?}Rl9uUGLw-+BXQNp8kZT4d!utl_XMGacPP&`ZnNlONG-F_!W~1@;kZ@Pj&>&vX z8r2Gm~KiBj{VQ?&ZcIQI>PQ4__sb(yU7Bt!KL z2CX0qrIy2VaQ+D0-3@zTdS+Uy9J7DB7_Iyde}pbgTX9AM#cQS{TsDjt)QMkRlZ?`Z z^JJkFO0IE4YlRp3Sopi>>8i2zA9%aLZxIO5kXJ0oRUkvPYM)o1lUo+Z2V$o`000G{Qi>& zyR&&ueUdX1cYQWyMLJT|&9xbRONo;S7o6xx!g()7Z#{`4Xr5rZ>?1xD?H-3(;6keI zfP3ZgG`c(dh7cj?QvJKBw1#-&(#ZD_rB@mCgtAGl56wirRBh#!?GytQ`Xeg?c`0k^ zRlmwl;0vX7P!Vuk3;Djo3$xd7Dg9EA+w?H}^W7(jzG(vx?CxNF3;e-ktyYw>ak(A& z71`AZudO{*ucA0~r@qwXdn&`)(09MLDJ~Vb>Td=0nR2^U$otUatB0F@=^Gmw8R&xV zF2)KczWpoe>;=(!dzX7L+7Ai6mj89`?Z8>IQ#w?liVa!BtMA}Uml zNOQ6Tjwcm!KPk6S5&Sj6M|n^q9MtlU5zeD_;89@OK`ckfM~3$gmLJPEDk!_F6k$0M;#( z!et2^0q{Sy1u@MHACB(SsHr*SvT-Y#%{LZ zAsYMIYUerVvLOrsV2HJjd(u%ISO)ZYye~a-JjaAOMYh^M!L!Ly17 zCJCycC87Aj)o5i25C4dvkVOxq|Cz2Dk@q&%2eu0rM}e`NHi!?S+?x3K%P+<_F+h_C z8;>T7^Wrnyld5psd)(Pp#!G8|cd`C&VcpJg0o8?VSgeNB5SeK)$n}!Ea4SDCm^4a` zY7hptx~{2bFx^7TFQ%&*<^w+Q{Wj@OW+THXypNnu1OHniEC)5hC{YO%JO0f4Y^IJ( zGixZ^tVKw}JE_CrY*|aLNvXejR2_O(=a-Koy6)oxDVhmir!}V&ra`f?*}fwZ7|xAi$BU$mSx3TlLpYoJDW=9m>N{HhGY>VId1 z(Ziw%vp!!=@G9XNe%+bYRV6h=lj7N58`FW@S%~eM_Z-KY782keETtgup{-4d;*nUI zuchU;NxzFW5)n7BHDoq9j>}CGDl>DE)(r0US>w-}$iqEVbSkPn<+mtq@xRe5OhxuZ zm1FKA*hkQ`o(b}~obWm=Ot)j&!{*=9p~E*j#~ZGgSTx0&RjtppA5~{k)kIaf0_dym zW%@HMl*mp9-~{iFX{|?p;ZD%*qzWyoCg1R{7wK*n>M?<8O6V%3%20~@2(su&HKY^U zO%CCx|ItR!wFpr?^f91aAtLe{E#fodo){%2h$^zs$AGm_l%`FTVWbJ!%qtea{0`to zW|Z8r)%uoN8gd053iRs@N)6qBY;$kZIfapNzA3eP@QQ zTxFY5{XH@AQ@!N!x;I%TjnB;OO{-$^Me%&<{wF~ian?TJ5qMg<5Gr5?6WY?7kz1{}|yg7G2wq&Cq;L zBX^{>S#uM*+B`?<<;@A1$Qx)_3S$I`8+N{{9J0osMGmb4yR4mx?~UrA#yg!up90FP z>O2@?#M*9pWM8eLV#m26%K)T=iESx)7KlTsQ;;oGAeycF z(&hY-vgO4C2H5xVzHKo!frvob!&d&Bs!_@e`D8!k>fZxOUrn=yp-4C`&HNJ2cj_xm zu>Ft(M=@8DB&U{h#_eW#XNLyqzO==3IH2gY%CyCF*#}%U&Gue@YQ~`4{YLmxxt4ql z@k|AdX$)MNx|*+i5RCy60&@GBM4vPtw(;M$URF9kP{8-;kB; zeaCFd8=mWb7BoyVa#L5-WXN%C@5u-vlxzJmz~=^+u*O_0+;D20>M-Fvnr&=J!6`d)uicUW6hx`p_H{M9xxNp&9PwZF;B?R#I>!jG{7e z#i5!O7P;Fkljmd(WV&HHGj)+$Ap{S#J2jVHLUnm=*&E?2ZB=$#JPj(C zwz5JYW9UCO-D$+z3?P}w4hUVE;`m%=te6Cc+>XQVj{L)`emR6Cvv@BKIqs!6Mf!RMyi zFpS~{4}{Bgjkl6lX2C_4ZsN4X+rdWA3S9O#WpMj?jN_{goCp-xb5-f>&BlvA7cH!S zM;#fB=#s5oRd3!Qe0b?^Qa>h^goXf+u9}EVw0_V0L7xPt^a?b4U7(5kmGLmMFKzUC zJ-~h|MlojN5);Qq$)~xs?|SVYhgchmWtODy=?>u`nv=EemIoCFbS~SQP5McRL@lo` zG3MBd(I`j)UNW+%?TC^mgFk}#S(`=`Ue#%_d>`JQ3#K3qS_53Z2QK=z5yq5#^Kjns z>f2uP33CS*NSZXDN%{&6THk=Ni7y~BkDs#s_4$^>_mUCMQQ~gsffgCP6DU_j)N(I8eyaB#m%07jBo%SzDo4cUn3l9+e9JU#^C#BgSYGvQF+vK@~vGM$(9O4zg*&wHd>~J zV{}w73dzVY#!Qwqz-`uS!5L%LPsBtMs)?`|Y+siCBHZR z=HlN26Y`cV>@ES4^$5qm%RIr;VY> z^(5LZ&srFYNywHGi=i5ooIz$8mxA5{75@CjuEVAQWt#C#`R{yDl`H=X_ zn<82`^evDE%KJ^)*N_p9*%n&Xf^7&Fe+97cj1LiGa~c%1OVw1N;8lI1kH#&$H}g7? zCr}U>O}+hglkbIPj(01<%LI#ZJI}6<1nn`2hE@QR_@CGm_R&TtCoe>(@Av)mPtvm; zhAUAU!dqnFA`SpMjm}?+jg|$Lgerh0kbCmLIeN44m`xh zh7Zi@%KRDppjpQdpDR0rMIpgRTmJr{MoIV7w|RiJZ90SJP($y7l$BpUym_9bTVrfnVx2R4!8bD=MahG3LYzW-Fh=!xPSX$#L~ENndO|5n0T za!o6n;ci1F+rk}$#nAp=Tm%rJzM#I0KAj76ZVjr0b?Yv-IvcQ97nmQj72yBimX<9r z?UXk$plEDY8U;A>7yuf?%4C{Nm^Bvml|y^W+#i~6QHqdoPV{Ok0P(Ia^xpGFE4LV9 zF_mzi{Qb&BN{?FeO{csksHzpp%{0E!>twcr;eDJn+{2|7q{Yl#Ijbad6p##`j|Q7o z^+6`;@7Qg1oSNxqmGDrfmQogzvpb3!>Aj()##&ZtFQ}a{aIlIu;1oX~P#S|C;31+9Fsp zwYocJb(2!ZyW_-l#(LWFhi4Pa80mOca{XnmZGXdMu!Gf~gkZ}f5#zZE{x&%k&~}Ly zLw2gs4ajt~t|pH`nF3VJ(1ozv`ux69BfMJ??%Z2b5P%$^4}%VQW`9AH5O*sWRB7f}4{Za04to3pUL04bPY&=lCoe2{*d0pq%@Qps?&}=fOl4 z_9IczqW5(N2sxw~WH2tFsAGZqgu@k9Md0DvG&^SHfFXBr?a2{+5T+OMEkTY!NQKqj z<+IDd36WwzE8XO+{)3R(D4_ z`3r&ecc4}M03LWWXLKAo?@ zRS8x0E1iunm*=EsHwpbNwM~J|jCa4&&m2oWxPEnjnAD8?`LllH=327k@l{IB1Jo|l z-N9^6K323eW!1)V6AmT9cP6%Uq<-s11y$KoE;}6a#)@8FYC1F4lzpmP^*dYz>l-{T zoo9t&F@Dl1Hefz@agDeY^BP0=M1%w~mezBIePxcFs?SON+Io50%6@kgv9^yih+HJn z8vHzPiv&Fnrmoq#$~b;;fNn0%&MaOWtfz*b12;Bf;!8LuhQDfy-n6+Hv`BM)?r^_Q z8FPk&jmH=gLBJjD0X&m#mhEwy$IO1+9}$HSySL_wgUKe3IcrNiwIL+uy6Z>$M~7ZM z0g+$*Yt%?L7Irtz!RAR_`Fz^$PQnv0U8ZH5q%j;>KdQ;j?%w;{+f5!flpEJ%H40jW zAA;z-+ns!QZru*r9(m4BaMrq2tkg;K_Gzb%ty4wD5`l#QdABvdTIE1zf+++8@|YGM zfpxcHABV>SjNkfY!)qIF>F@5Jm+ld8M94ipLG*>+JILpB{q*JPD^ITqPnmFw;qpQRPXJA2g%MuxAwAQ5Yi_zFMd0C zhVMmnutSsIIXa$1dJ*M+pz0^Yjk^Z><2;s>q7qYzKQ)&CTTIk4Y#GhCIq{G<(#x6l`VA zNFF{fS_W>RU!31;h%6_{2AsT|$U3tp2aXtM=gJ7+-2FJv2<+^b;dDCj?k0>8qDckP zdtf|xD8uSDw>vfOj^w)r|@4&BY#d^iByzejI9GMj$iNBLUmG~X4NTf@0hw@oBug30Y<}Ru^7ItY_vI@XCV3x z8_Vcsi=vbSK8Qjd0>RK*N%xuGw6n$E@r)A6?hX;syx2xbmw-wF7bYk(H~x)13f#1Lrj9*XBCSnLi| z)$zmObmvLwtHa-&us@w%zsK7>&!pb?S*7bt8H7rJsvgkQcjIm7cU}%P!VlVGf0hzj zoseEo%rW`fxNbBi&wDq)+NXKl^m!HyHNxOi*Xgan6kD~+u^G)9Q2-Usz-(>t+B4e0 zT6XKE64BWC_{^#bs@XFm*5@kVQEMYJqA~iz{uQ*p`sRJwnCzBu`$e44>p?w8P{y(< zaD>w5VIie7I(j9EH5QpnIQI3nEaEo<4Obm8*|}n#*dj2O52VN)$qHXl_67CTXg{JV zY0t2EvSd*$T37yRsQpE;E)SJG=My8YN8VozJV55Emb?&#Xl-rC5So`s7r$ZL{c zzLE`U2a5U%bzR{=8{1!AKbLNKf7{w`@z)kRqi1?DxgB{82oOt{nGWyvdoL*X?C_c$ zt1k%X<$!EkKk%OkdtJUffe^!|j;>xi^nd@J0zUU6zPOnF>6ZY6>VNc|GR}Bi^1Yvs z4tSjQ^FwIdbv!58^sc{`GwNIy4FJ114??M*k&nL%kVw?$kx58*T_@d2x8rpD&vmwb z)hVL>NBx1HT3hFBNiW+m{?URhM#2-A9vAb1H5W&sXW6slQI1L?Mb{>#f$o7Ixdp-N zYH*#AD49Rn9+1?uQW@#u;?OJT`iQwk$jIk%_|J|oD4-3nY~<_qXmWmhx&Q)Z3lHjN z)SQp@J)bV!?PvJb0K1KSd`g%cg}h!4J1{G1JPGiPwm2HS8@*nb9ICgrR{bK@Dk`9t zF@A0Vr#|GY`-Sq#+)TAxh(Nq*8ylaW=W=X2e#i2N&I{URgz|_wRA2l7ZA4V~4l5s( zF#!UQri|@{*A3SA`I$06o?6)ft9k(s4zIgIRhYJ{9>xIwU7(0hREBR9P{e+r2@!Z9 zdN)iLMZPth)ZrP6KPO{kq?-wGySzN^cY{1mfDVW0POk$__x?ofFZ6q^1zz`e?m+xr zE}hK%>zMrcS_4Ne_6rVqb~1c@Tm4OVXEO}4yN7+RFR94w9Y)5$J2D;a#*1$zSfOpS zfV7n6?WYf$AeGItNl>gOzBXB%|GjCH@J2@M-=8d2-q_yMz z`L1N*VL1Hb)fbSf=_$A$vxD_u?qV@KM)dfitLY=EJ7|A5Jf}G)VEaK3HTG;3(XXwc z_S(t&{B`DKkz6^4d1G}=f7}CBlu5`R;L*5|JyP+S^y<9|DlK`r=`8`(gB+}${+wK_ zh3*d{=J0IROob!PFpCVH>%(V{4=?v!gOU)}vqj4T?atOWwnj2etsU$)1M4~(TRZl& zAMS8HZoJ%Ct0uP$1D#YnekULbe#ZQH00e;MHuUtmxPeD=e|md|M&CTR>~4(qCjm;{ zlM~$cc0x?ZlP3tGY>&@716^u6TleOwS`cH6Nhdo*56PLx0UA`b1J}dqK6ibiYl5-( z#@&$GERW<(R)^}#dmK=J`9mpbr#}?)t#k&*9-NNeyvFF?gy$H6+f*QaVys${nj*DHoUS9935?fYe=Pw!c)1`RS63iRQ}yO8$LeuMa)6%K-R0BJoA;2`I}$Cj`Knh; z&+K#ZMwxT)g|KUDtucX7;IgjIz4Pwe=Nq1NcHn06Zi^#%myw_Yxu=8u6G1E)FeyA1 z6DTC`)t7Z6>kzZSIEhzzX{*!g*<$xRL%9oF=M(N^4%TdMc+3|T_TFFQF|K6V68@F6 zKv&z1;v3HT6$G9P?k{QyWDEjSeyA_AHhl9j z)?>?SRlx4@abc9s>&6S@bfp>K-l(@X)q?ni{2sIa6cLy++E1=^94Ru}c3Pw=t;yA1Sv>Ol8( zB+RUxTdO{G9a#i7zk`cB+??KCmURWT`O-fa^);0A0>Y~#S_0h&?u`d9A0}q1llbqw=gQBfWZ^A$i^JAyvpcJRmOv6wr>6z~fSrxa ziXueD->1;1r6<|h4NOO4JE0U&=4R2Y&2VkgfGE$O7onjYH6kkywk;Def%UiB9#qzy!8|9_S9F7<#iD5ZfQWsBqurRn22=*vY0I!RbIwj0RVRF>G)Ciz!;~N4e!F9Wyqhb}(76igtlsLE>R?hwWBK2N{Da-6 zPnZ_Id*&|zCl&>XCPGbXpDG1bl3&ix6gskUiEfz>lDjA2I=#xHSXOJU_d~Y=-b9KV z4XXz4OOGBhsWI$XFyhrV3ydN0{OnG5X23IUJF9OHb?FQs&yX+h@wsc+@@vwKj%oRE zi)lQvak0_s;Oo@I@i0&w^|hI5^a*7300rM3qr83{o{upYC*Ax@TbLJ{o6b46es&s7 z{xmAh_cGUGFDSSHO@dUdGx7ut8_drE}i00{WwN~T0ci^Kw*BwWbE5JzR0^>`0in&ih3y^b3>g?_kx9$ zJ|7YH!Q@*~oddYM{6F8tur}uLn?qv;xpAnl0fUDDXnto4ZaeQu9Gg+xV7f2 z7q}29kz=a;6;Btb$lRe)D;6!LRf0S7aP9aUykJ~ZUd!3W4al#oozbm#<+4>ot6R$M zFrYb;=JRT7ytnxJ#WcYGD@>n?{bLDSACuO5j4NW&DQIgdO~i;Mdeys;m^2&pjEqDQ z?ODqC1Bs@#cmnMNHMopF2*uiIbXGOR!R0X4>t-LcqgA=;yx6&}k3Cc&C^AjuBiH?N>)w$joHj1E_Ej~Q;n8{P5vkS24;dm2+5R8x5 z0v`rnPOd5N|7H+B&a8YJGA!^+@>yn=U}r9UKeh79^*h%5MFxU@$b4^@Q5h93o*wBobac}&g#6D&z~9q-BcI~ z;E%HVmeGgOaQtw`?NlMa4Mlq*qI%;UT|QPk{qpkL=q4{Q`<5-&blLO~dc;U2h~`F0 z{Mb`y6j08umjf4~*U%1OUnTDO>H08bZ36d=PW$;omrm1wip{N^q6y|a?NP{1!l@yW zebOp#5WwpDWFE@4{A|wWh8WrlHpztf!xjd@1sOE~aU>~bk#eQlS&c58WXlyiQwJ$2 z3CqtI>TYT$Xz! zVO14IcyZC7WI8>Vu|2hwY=RSTk4!*CEWE|_*oY+!`YB8=B z(eIjWn&!jI^Rro!>wj2$MqT7(EJl&w+I%{z_vyh^2S%52z>}mdf7qvQqmwVhHdGF|28z74>=^HcgMW2J%=|oez)`pXUmq{^%7-`jm8#Vpc zrpn=m!tIzS2EEd@CVbQ)A(nuMh)&?7py3 z7+|Owq@}w{Vu%5euAzGfk?u|fRJyxk=%GRBln%*}K|s2@EJ{H*yua@|=Q{tvb6xw{ zd#!t|JF*#+mN}wI#A=LinITEuv8(d4!AY$p@c&pS>@ZO=&BuZ@wf%G zp0hvmE{bA!aw#;8lwWxGvGs*sP#JPDBk>^(ouetxBg}0*oL5W+`7D)q+!c$iRkB_P zxqnT5#1aOI6B!qd_D)JA`~RsK=sD--UD;t6v4sgqT1xXV>W^ZEJYhWLH|p7Ws~agm zbza_~tH+dxP@oJ8cpp$@L7wB$jYlSPBA-+X(d8b%mwzt&-s zw^ZZ1M6-7*|k4`yaE+qZ%0lwuXvdEGT9Ei7iq#Lyd&+HJXo?vC4=83)Bn707ILq(LXc5?IDWJzj!>g-*R!!RWx7gxUxPv%2g@q ztdCgM89>;-P!Yy}q{P>L`D+_z?H74~B>txpP#F&GY~j0;#z}yI=39w-9w`OI9O>ac zROUG8t2qhVzumB^fwk?&CCv(Cc5I+lG5(p@-RnJX37em_%EQq%dd%YuO^@t0EV zqK@0j5vny9niqiCNLg@Az^srfKXeT0B$eeP=B^l{bo*XP$W?(ap80|y%`&Iavea_T zDKpM#+wy4sc;ahod25i%@8oEL0EL#LFC!5q31W7=Q5IavOn;CnD#Ug36*}IqD4QoV z-ofll9psn42RL6j+a$k)bQmb=>XIfBYW<~ii(?bJVo1|uhwU$Xa!Ev74p6k(qHL{Q zJ{xySY#4uFmY|ozfmMLo=3O;-w-akmT`@iy?<986#W!f_?ipxTd>{~Nv|BhXT4vBr zuqSBc>BZP~|IQ#2V)cLJRo|S_HfKTuM(j)J-g3L@EAuTPTc|#tf)N3e+)%crRoV{} zO(TES1wM1u)rW@dI}2JSCE2Jl@12GfnlLM-A|x`2=`=9I(V7C*OTQWN$H@M*&G;ts2{KgEe{$ERy5nxt3`ve-a+Mi7WT|!XT3K>8Z+&iUbx`_i_yjwt+|vTg_9v{%p9Mm(lP-R>q5Y zh>qT&kckNvcMaV0F_+ST5Hu_+@LFbmZU0ev7JU|p3Bsa!=POtodXgcnbTao7D8_Df zf|WJPB>$0sQ)s#N>wH`QQ?=D2N69|J;P2r3rnIKGY={-N+9$~i#qw*N$;N2Gy)?Cg zH-z;UL{RerCJjDcap$UPW7p0sSuVN6w^~A-HA5-bSDKe$1hG`U&PMo(CO9NC(J$hP zT9=hUXsF1VJ$~}Hr{d>eORE1eGI4RkM^4Cl2ubJg0yul^lXz&H4d1Uc9!HuV=Pdgb z@tP_gf%7t%$mL^Pn`vwu%-t;<_1ff<8KyZ}i!B>luPz@FxT{XN>H`9srO~-|EL5Ln zdF|?yURpH{r;U`MIwM&oD8Gs-=)jJCfI5wRFh&a7H5nE=iD@&l;&zRA)FE2(l(dQBzJA95xkER4{@uKb9>-vk@1NMe z$$uYsBTAFORN)sgJiMx>Ek@|ddJ@YoHTJ=D{3y_iLVUQLnk#%OJ_IC}#7^1M8uLh+5%y zw~_ZmbZmNryoiD8mQW*hGeI$f&m@UH#@7Z4>?$6Gz#35=NL;l_2dswFG((cvywz6- zpffI|tf=25WQ@BWYFL5}2KRok*FwmWGRMxdyn*rMlnM0^*X*pXQ^zzX zFP+5JsgoMS+?cGG&CuHP#OfOooT$@&F^(2~ibVvZyNi)v>T0pRlO|Mor2eZ)|Dzgd zTToY(;_T5eN4e|e5^BRp#a8!1t1?^5v#DX_jWNfWwO*SBs+NyXENkot^-V}~Vp%Ks%Q)1WW!IDZ@aeF6>Z+XdjP(c0;F2>Dv8qox zD<1>P#6u^co$IrStkf;NxvE_F6jI^}?6Qd3jJViSJGmv4rZ+3I(3i zJzC{IQuC6vfW1KKh&&}_hbp;gG#-B3!L}wp_6{jAsy&~~RyaZ|XFumJ(_8J*sDAd> zg)_#LOXdZ?MIHIiZE1~04-i#F5vIlT(Sf;=dNt6?oeISwrjyV=|n zwai4}B#yBW0;*t6k%ddrDl;ZUz}O-g~C4 zJoZx^Q}C3n9c7^E766N)A*_3jM~}5*M<9t%C}DQOAXgB|s{R8=o!9B|qJLbTigcCL z*rnRV%TdiLqM5xxJEbI`c&5q!eA~i1A8ZZ8@BGxyR1WLY_1C6Z)R;_-Tk=VC`}AMSG+ zX4a4aaX+H+If@I?M6ls10nIXt?>hf|dB0FeKiT*0mgm#Yr2C&8-|0?YNd0{pCObr` zo;&^Q?H%tu>wUQ3&66Hv^Na()cG(87xI|{ zK^}EAtHeC<$8qx%gI+w3Hg8!SyI5|9LQs_~jba*&2>Lc4x=`GTTm@#UVu6qYr=9Lt zVGrh!z*FkRzB9+f(tI7usefjp6m5TuNLT)rPI(Sag zC6#AqLE;})yM89uw!8B?HWUwp0U4jt5L*XAyk-V zyjdcqIAK%;W^=mH>0ob#9u^X`eXCP?VTE{6Lp+z+z%c%l#FzJN7Or+h3e9PnLZSN9 z)Xm@I;$#a#DFYq~_K5SGIQWvVqu4Q^7MrSbtiS+A7WgA=4k-k&t&gmdj85go?S@m(Eaq z+t8YN(MOLhv+6#DKtTw9S6k_bU+#(%@DO`mN^9ECjO~hGnlV0TU3bh8Ekz8L{AqJ?1p%TLgWKD zixeCQ%1tqAUv@PRL=83~UNtM0{W{Ep%tQ8`YY&Q6FU}47WHrh&mt~qRvDgn#<{rf0 zc=pb!h)`VL2uUC=-bG1#_INEw{}8JGtJUV2bjp042y|jh|91Y_sB4exH6H0a)Vxa# zIEjfcS6qQS&mq`j-+Yj*SoWB%ybXGFc)$L0>;9aY+U3IM#%6SxKK@U2x{RpO`^%z- z3%iTg0cv`D@Mg=L^=(~MFU3{U?0jZlvAB3&xD0+aXWZozgCQUbX0$_nwEd-QP{O@Z zWHkr`Rx<6!XG{~p1^`sMv#EJxDWf6WCA8)Y2kEIu;nqLiIJr(v?O(W+K1XT57A{U_ z^n$_sORrnxkaE*Y+B#0A150X7HU{mt@Lwhr-QQalGPC4|C?(n3AIjxeQRinB=Ec<> zsyQZ}Ng1kfvO(DTjICyz!S=@AJuY0_A@wu4%x_Bsnx|! z%W@OyystBl<`%vm7FY_N4KSC)Bzz;fd6Xd@C{DS9a^y;TYhtbOpe0-{)#ITgn=^7& zM7I%1IKfJ<;;h(d*qlZJ3vt@^@1h*9bW@&jHdd8a>Be<2685MWtG&6X{xfW;1(9&e z!&YJLD|M+LykF;R3wpe9^5ieLA&au5Ixzwm;M`-!`CqOtG0+;R)BNs(5?U3P2yJpL zys#3;^`JU|NzJcQzp|>A7pDu6;tJb3rF)dip8iZx<(JB9JwX?1f@zm?q6i`M--3Ny z1c6R^>dk|oP9a%Hhy6ZNvz;~<%>mgDM*^QTr8CDeHr&F&w_jP*h)Ob?rYGLE3*3VL zo#~x-$qsEujC-q{uxDIrr9E}L378PcB%JGYJ`t6Y(b26Wa&mUC@t%YU3buH^OJhc+ zF@r_??o-nGUW=8s3Y&rT`YC>CKq{kaFtKr@2GhU#Z37~AG zc!rpsjRk%*^lg~jRG|D1uv02|bGc#Kp~64iyE%at2p(rf%OG19dM$#Q-hO)DaW-WY zU`3=(ca&~Tlux2!P4G15N%q>odG6W@@VZ5%c!@VfUtj$9ko2$L>Il={8_`ti9PQsl zA~IAG-Iqq>pYS5hZMX``0|=oV{K?WGlq2Q`0Lo}q{qVUj=amxEJRJv&&dN%p`O9_F z)T-7_(k>xdc@pa1$TKpl!8ySbQ_| zi)(ydKS4|(Ml%jJak*AwZF0`OWhELN+e?(C&?u|%=aJyHe6W|EYKt)l9Lr#timSrgW* z8f>l2dk~Il;z-J*ATT{!g=xEguTK98_NuaS*(HpdOWHnF39|taQ8xy-zHRQukYmZt zSs(xgBGr4)0AP^>RsVYis!u4oF&s#S<)}{GlprkNE47(%YfS66{@7ar!NVMt@tD|7 zFLvTF`K+;LzYpy)OD!-PbM7(`^%BrS-6_2W3DNHu+Ky_f4#s>1ZJaUlvq28$rR9`< zS!u#g#*!3>Kr}N)Nj^!?dD-Aspyrve_Hm(w2B%A9Xh&bj2|eXoh3jjrK(h5<+NW@M z(_|4jpR2XM$u1yLMNz{FuQq=bJ5%52(SyiSySIhHW-D+8lE)ga6T2v{$mf#v3DG>lgl`4wkT2US| zF$izDi-Ld9o`KCii?R@>0AF4KX&s0)*MO;##?2DuCxPolIqMJ*0675Pmte)9Fg^8UxRuM>Ga`UnkA6$7V>BgMsFTfMwIzl4-g$(+x6S((R2ih)IM%rIdzdh%1a4WO z{FKM!_|%A;2jr^b0*7DyfSTNiQz^uZ7MOiJ(M@>XhpQok~7h#Wilaa#A_I5Nth72F=<`n<|+vBk*~U2T(g zX`kDE{WsvYdu1gc`&}!$>&19fq8NKwzb~fT`DUB6&$uGgR(<}ZTQN4XFw+9XW`7uK zSodSnLbdldSlHoxmy(z(F$y=DqtQM_OFS$K!dn!TBsqf*D+nzUHm!x$yV@0g-B8xi z-?Zq@+e0h*^dHJJv*F~Ju6BAk;jm4s8crSGVM`e-Lb_5Eere+_&`5Q?+c^dh67mJ> zL9EEEt4;x7l0T$fkk~G)Ut-N}Z-Go2%NB@N{7*q*=a{f_2SQ5KUA3bPl-m7S3<#!$29Q zj~y*hJ#1sA>FmKje41g?4}!8kDk3W^BqQ>Wk8)I(Sp9DvlyudjJW&NDQ?cm$1vH{` zDr&!V0qHK?(W;n235SysGK%xb4$4fhK4e-Eh|=ic;Q$76a+geNShAQ6ePmu;sYXd% zOOoL$!qr`nG#btO$KOXNel0bdglJIzMf5?xQYF5x6_!m!`4O&ir4K@zM7)WT#+)A3z-fo~<-@xsZb2Mfg}O?j24 zh+XGosfkOZ^9{-#SAIknnDFLq<JCC; z>_@KrVh?>{X*>?dgpFdkMqy>bdtj)k0^V9;P%?W)sC&v6hNio&P=1h3z{{b2_mstr zVto`f9W$E_VXU4&lkm4yCEE%{Q{Xmn4q-HRd)_QI8v#>eg(Gyd&^}B4aoF2u?cRA| z;5#(akG7CE|DCgMX8gztY55PO5z++vI`)H}9%`!+;}w{(k=mEr@*8THkMvv-8*wo> zQQKWb8lJJimsnUv7&M$|pdV;jN9j&nBdMf$6H-WEv2uhISmM{vw2V2U(L~tKWVzES>BU;eTszi)jtJ)0?3)LeSbfMJf@!LU(i=PMk1q?(*a`S0y z?bs0fvXC2pb!02{!Ck>c!@0B4V8l9kH4tYS|BIvQi-KmO%0eRFa&GmPnKoLDSxD(! zKy41-;04&AfZv|8rp9U_lSq6cXDlLcAdN2FXaLj4fk5yW6s^iIK|>#Y$7rcUS0(Sv z3d+>OvaS{UESAjRQ;*V|r~uocQbZ;KqlINwd;K4l3%*lA;Qs^rPrx@U;HgzvlC%u_ zuZ2w=P!#^-n8EMx&!zH+qP~X^NVtxp5j;rMd@)%(EvQ$hHkauGR|tL5S$NGZRMCED23AF}D}Zs)bDqkL5FPj539oYgz* z)Mq-m5Vdb%qqY=kOJH6^WrrPq5VO>?k1r8@ERhm{7SZ|TCm5cU>d<|r0a7S|IoM3n z+IsSNDr9?y(CwsD%*H6vA0Y0XaHpEEdTg-v&8K1q1SjpvsR2C$Km&Q;NFim z_l+*EH*!&t9Km{H4a|_xN34%cQz5GnybZfP=V(D~2GgXtY>GHXt%ro_$K=c+6gE+B z!1OA%2~hoQ4jp67O=O-jj2&xP1Na$oz)t>3O9=?6WQ0rBGZe@I>s)m!eP1OHrXp5l zzge{EB~^);ViCCG{44RkUL#WNoMt!!m*JDsmEx?sxIZ*_O735Hyg zM!i?MAX3>DYK=9BId)`y0%0(j0|!gJBAlvFJX&@k6LMTuIYto&Y9{8VM^Mf#IoEg?CgB0D77J34P|YsKW=evi;j%F?WHiG5 zSAW3gX$;pmp;RJ@N)cKh@Lea{Kp?O7_36#hPt-XY@LvV<{PA-@F7|2a=sr(kJzULQ zSl=-!$5d`AHge)mXsM&2ja~hiL0P#mu#1G}zKfpyt67;n4P|C&!SgKVSQ*w- zx~OXQVVPyWI_K%mTw3@Cyn)mKlg|V-{iM_MxBLDKOaQP-Sk7Haj^^+g^zT8NDXLWV z*;*$iK?W(ESwFTSLCfM2pkugwo}XityQu=CmRbeKyWCRwJ2p(8fRLHHiQQ<(Vr~Db zT2E#1h%fPzzTn{|(lSeo-A=|bfm_c|NzE2L1B)5bgF_WKuePH-O0)EmO(xVfym3<& z?WtWFz;l%ccZLcW80kiDLRx{M1Sa5Q8vCToGf(lj?FV6^~ToKOil-v z=~aO^7=$5Q|E733^EUZh`t@rYxo{jXCFJICCPeY^N<{!Veb49n`Ca@yR$jhyYK+X1 z82|H&;?0<<-&~Frwqko9T%mv6Dgo6LMgt!Kj4|ODGb)ku zz9%uvfHUKxw(!KfIlU9ch#;}4XlL8cP|`S3>%=uS*3Y?q>wFNV7+qqge~t|?)c>0( zK7wP!GhtUKbwtIC9o4jqT&J}<=htMjCW@h~&J4Wz>?JeGv`*dj2sAYoRzw5r5F$mF zunBywm29CanJfYE=gG9js_WVJY)iOuQhwtuu1Bn&E5%h-DX8-ALWj_d6E_FMyo0yf z=fFGPA~jp(skhQXxwkDy<<%2fy0T)n5|C9l@ESH-F{^Sj5I3^)GLn-%j45_igtcEhq)Cjm}3*y zED|O`@OgD0(K}YO!>5oSq`+!4V8kL|!;dGWYCruJC9c8`0=-j%=D>X2dhuJL#ne;N^|8s8-WUDRGwdI->bL|(FyW(x#f6?;Y;}K#U zEdTF!fzNF2D1p!kTG9VcTdc*O<{DT78gUCPneg`!2;=(gZtF`1<6&FX&&7 zY`pn$RbsOYM;h}ycRaHgHLUlp7+fpBuCheUW$NNrUG!Uxa~XiEelYM~`?vTe<(HD= zlcuApKep$dB)dA*S(MdW;tUQ)Ukgx%0+gh}kp!C-f%M_~34nG5cGc&+e}4FX@TNAm z1m@d+1Y|D)Bm^U5Bd6jx{Qst+NGET=w5BvZ3#Q(MsiusE9pWcTOM1!-xm`CZAKw{T z=kt>g;e$k-5_m*GODzfBDE&jHo|jG=FT4BR?wl9=|N6l8_fKWREfl^&LrJ5i1e#8@ z+H?J>_)(~k-)2v`h_lY;0It5sxiM*9U^Je z4ss8y&0KAH>U&G~d&YG}FFsA~=yXZUV|H>$9^Cx>9dM561`7oKWYjdFX{hQGd%8`hH;o0{OB2`Z3A2~9nm)2qiCSrY zY6*uwHtnGN0aExc#=evH<#S`JhR+l|0jcnxjn&E8~!mU5KtPB%v_C*8p z)qsao{NHrw{Up9r89BNog^<0c(OVYG)c4(1ZpdQJ>$4h&zi)YQ@^Bda<+0y+{3tA7 z{un9Xb6+wj9fY@x$3JW;_~iOqx2R|SB$cshp=2hW4am-=)4PAkM(lNx=mK=mHFA_5 zvtmC=vVDfultkI3M~E_)m4wf$8huR7uKj&#W=>Hv9GReJLWf7t8WZw-sy+GiuGlvR zKA)=VLkFoTx0TkLq-A?aVy8vPzt-n|DPF`BYm#wK@G)P z9bOHSQNHIjy55Xs9UotXBbo=RV}A#EX>!M`h!G#__jSPlx7=iubg$W7txiGg!v>=EHO{3lt#j+);t}>?d8O$US`gn z1@bSP`~j7?z)EO{-fL$t36K2?Mr`|MrPu2;U=To9AmSUawKssv5{@1omN{D-h`Kqw zlrsw3+8rd!bFjp^h9f9N*l)oDL8U?F6P947118$;Db=7U$3_o$6t$2<(d2jTt#?%9 z&p!q3oL{w`y*Kaia|sQP{60k#Z12?L5qVrE7|4c5#jD z(sf04+E3(Um|EkLm{@Ts?+Ngl=ER!q5rxszP)?h-Qf!fW)y%|p|0|JnfJ3GEZDDLs zsur=63N|rLEM}riN!stK#oB5M&QYS(lCj>xm{mA6Tp9mCKtzP+8qcC(k(OT>dt-y_ z5a%$t7#WQ~3Y<8h@oP@FV-sKmJxZsuu$I?>sqGNqrByO;CfU(06H_4PS=T74f^CFT z;h)7f$_+uv_B$>Awx^NLL_{&hH%eU!iC^I{Up9Hm0?LO^XSNMEt-mBai|uCYo9Z)5 z8D_Nt<&=TMd`EL6kL%WODv3L+xE1mjD2@{qg zChVQa#Ykxn-Ge8gtp3AkGzb|fm9^#oTE;pbqQmvx?s9-Wv>K-p;P7NTcx@o7>PhNM zx2_nhK9U>@^a0Dnxh_v%NTM6}+~6dP&}K%!z{Q^@M8`~>J`f)+_x7am3i`1g^%FmQ zN3ef;*IJR2IwBtk_8BvWeg$T!ol`}Cd?(h}^{AH=!zApVOm^qx2B;nUsV z$&1Kqx`94q%FBTYEyt;H;#RIk_*X5k`slkCG`@5>;iFrSp;x)A!0~w|?1$)QJ}+n# zy-tgj?J}qo@arWdGvFnipBck#Ci&hHIM{Y>FcWmboRoyK>6(h?(}K(4B=}}Rb6oo0 z{0JP267Ch9%BRg=+ii$F?l*}Y{4ri~(%k*$Rxn<#69pZmAPt-0)#?Y2podg|%$qBl9- zCv8n(!(5k}}EaSPq{eFoKfaNKC6`{NouZv?5Lb z>%SNjT)ASZrvN7>_9riI+UTE2 z6R6W{k%fnAYOo@&6$Kw)G$p^OY*77bm~O{%2I}lY<+_1yzSmyTigi#Z^GrKd`JOMm z^xAVZr*p>p0!gX_Jk=D09p;emkFKK|pq3IUGs5%yEQeLE{hdy@#h8@pdSB-J!^3Z8 zRg41&6Zqh>tGP+Qu&M+(4#w>*6OIqEmlp*?3B6w&zKXn#`}#s-b9>-wp@&vI?oA0n z5tW!L&@km%6m$=uYq4E!o~xF{e#s~Hfg+D|G9y{yeOOrQb47jeYjk6%f{MD@qjzrd zzyv{0nP5I?7s{JS{Ja%Y(+NDq2Q5Idhon8+pMJWiUf1=LOc6N2;9rW-0XaVjuqx&;A7eYfgPtptT8l6>+ zS4_2=94%xnWmcw8BmVQ&=)oDj=y&ggoMU6c^+m4H4j+swZgkk9@yI@BQ&6aEekAbu z1yEsA3DbjR=XoMu==_HzoRry{&uGq5Urg+C=$7U~_(g?X$ha|n-XaaDlQ z!u~FV3GJy)SpJc)^MF5Xy|+gFZLBi`V{l~RPX^Pa0Xm}|wPl}IYf3|} z63=g@;SiABBOoQp!i|U}q=?7R0{$g8Ahk*rUJcd6T!F`8+lrD+k<55~&Q;BIYTE@4SfA7q7#|R6sIW_55?fjSjMDe-^vv03WjUm)Fx5V;S!TKa zSj-5K)z{8N9-Y3# zWHeWB4>I7jbv;N{OPO0?Bj8DrCmH#8uG1n5+P3&h?R3<>v}Z2q;%hdorVe`xuXRT{ zXbWXCc$ZbI$y68$m2DcmQsL%K5R3xH=tb*lWdcrAl^Uk}A?lgz&h=<~-Kkcnh38_! z0z}2tfxwBLG@!3!APW^=`tCkHZ3KW{B*u1Eshkw@Oy}M4y0?Ol_;TsItB;r&lQ#+j z3~LuT{Y9%<#h!>no4Us&ah=cdil)6N*Wvv@`cbe9g9uOhx+GB8v>EQnyWU7WT0##? z{6s*?BiY@5j{5}dr2H+%CL+R+j<%yrbe^&s_T3Ls$x=a09|aHcac=Z-!d5v;aRk#l z^~ntu(%Wi-&XqEcZ;yeb@LTikBwoSwCbluXY6dTj;3|63=4JR~Ohy z4-40vV()l!ce{w;{z#Kx;_Wn|W4(|a)#s>+7D81Llw?@pj2^;+@WtW{!3t%%`~8y? zi%jf$btPY_^JvmoBRzVqC{;qBe@y6}@6SYLE^bC8@lu&m5wY#YW0P~|K%mq3n7n=! zb^5E_ZKQ~434jt0Q5dT$l_Aq;4p(1g_>J6UWk8z;AD>ldBo5^ZZ@6l3=rU^=RaelW z71Df0wOi3nsG#1Zx~PQHi^r}c&03IQBy5LZ!8$Q^n~fw7bsYRoin}C%pv3<~$Rz%- z`#_7zv*XO$d~4l?fA92Y+CzVe2$#jmh(h$Z{~Zy}OxW*GI_D;IYn92fUY*@MW#t`# zP|udccqXVF*9dgMlvQ>$Blc#g| z;__k-kMxbANNB`tw2lDxg3!3u)LPwNl&gg04BfO%9dQ8)LQbiIRr5Gr#g{ip{(zP` zidgH)pyh{b>0^b1k@$P^`~*z=D!fiVs+7SJs`+^VehWY0aOSr)nxbS_rUesud1D1$ z?bnS`x)Qt!|HROBhaU-6D}P~UlRKa4!t%mj^GR^Y>cYZmQ$p{4l(o^Z8DjF)UzfNM zg->STiqlI|s-<;ak-XvY*Pw7a%m|9>vXWFGS108(bTMo@)V`2SF60&X?|CjFmvF_? zPl=C6EUji$ONItqH`ia7yoD^{hbzdpiqyQ<`dQp52c!|qJWk-%2G3Q?(04jA@aD)r zGd~G_)<(($w}<#r<-y9)(#zPy^khcDIMkgyzO_2&HOuK9(Gp43j50|wFGT7f9(NKn zn53isTOBKz=AAJ@t-~7aI~5a244d z*}F5fz29XIV(iLsTKli+)`9$*PrkeI1Dhx&D360NqHJ=_F1H$F(Lak@sQ52+{vJZT z)H@`jAeG3_X@6{g#txR+WrN-D{ve@BDW)`x+`5b${dY1hU(m+-f8|564-y}fJu zDJf#`R>O*!fKc}a*zuCGD=Zyhc0xTL(^-p5#&cf%!kCFMzUF@Y zD^xcp=*YU$K)gNA2h_mQhk!&HG;s0@($K^+KGQ!t{4yA#OUJEjn)|9yPnF7)LW5s! z2Qi7az5hZ1SnI>8D|(~ep)5BUc1+HtEJ}Mn7d0T~KQ--wZo2^!UCjJNqlEZUKWJDn|-6KqEafmgS z#}p2!++o2e*%D&Y4?dNI zcQN>5MZ@`Utl4lQe$$dm?5B)OzdlZf6i4Z8=P(nrAIkD>TB4xwy;j&(b?lcq>h99X z2q)E}-cV~J<2d>e^rRqbJytUX9xKV8eJm4AQK*aRRF3UM^&7}aYp%8D{nyTRB3d9W zlNuXZGVj|v8v?2O^8)tr$1ykn$jD3;5%2rxD+Cf#=7<^MNp*f!O`gFs&woO#B;(PK z!RuIOE($4*V543ew0Yt`C}Ommtx`1fSh8^%X0eh<_;ILatt6i1Fi86x@ka8muBeRH zd-5JW59jik?xFI`C&#M%r0r!HzlrSEO@r}j{;T8>v0R1-#*A^-8J`@E+epI=_$m3| z4V$dP0G7YE@ELX01!X+<<Hc%J2D1=EPjv-*AZiPNf zD488!bPRO{5xtH~_iwNeue+LSWKtMEF>Jl@KWQww{lEOIpP`}VY)&DD`bXs&V14x- z3h^v!OlGkflP?~+v@Dxi&l9OUky(Ny7zNGEXhPBxL*~&H(5h;1tl&=`|^Yjz^L67ml7sTY@TRQ zG^LiuX{i@E*Rk83i8>V;*4k{w35dEx@#88^-3wIPZ6Y}`YxO}i&~}~ZUrS^N?QYTv z9zEDg_!zTY5{j?jE0SUKZB@CjUJ67pT~&L;v=$3y8O>Hno{vM6lZ)W|WSnqSvf0o- zC{Ol+8@>q2lVy22##k(;=h~B4*!_&O!B#6){lqQ=vGI{EzE8OhEIWY{sxMx=xSueo z=a!kKWYeU~+%JetE#E6YF;Fe9p_xL^$$yYJZfoT$mWn8ZYxRhgLEo877gKOzvl@)o zyBKZg68Oal44fLwXdRwf4OuH|kWS=3Ev;L;oqdhtbnOR{>g>(bf=60v5}ENdHY>@dbz+`{8v4eiEifPo) zdSTc-GgFTu@jwJP!=|ffhkX)dr$~bj?Py#`V=qX?pQjFM+)Z;-Rkwr7x;3+776)*V zd_|mpYVugB0pZqnqH}dp*U8c!F342*hw7ZCm7+7pxNqf1q4arJhf$2BD^cu}4Qxcr z6|MHUMGQT`UGe|p7;=XXyx**vCL*JtzuVi zyF*nNV(mvHs;|d1_Mp4 zpTrn~cWtpTm)-ko1?9Chis<;{@HzWK&-2C0uab8MG6AkmFD?45Fr$+x=>&8FGRnJ? zzY7G|*?31w?rq2KJ(n7XAY>FBhN0JRqMUQ@x4aFV7N_;amD+f3fAh1c50)a z!!%f?%LE1$ew% z>GHPQA|w>%8~fqKs*vg(`dooVm)BeM{7yX`5#)1%U*I#V{PRwS^!6to2VKbsF*E_% zT~0JwMTuMRQt?9axA|)=$(+%ND5@l?IgD5?QJj3{aw2Hqw8Z)^oXV%H(NI~p3e`oy z(TWjFRz(LsIRJ{YAZbpF%Z7$r@IvhFG|Vg^Q631&#j5a2*t|;=UhQr5J2=6i%4+#< z14FBqMl*ynOHfZ)#UI}!bv*!MI*s*hh}2yjE{exVWRAH#l9VK#9*~^gDDdR)haSao zv~h9s#`IU|V;GvUA!|g*Cxx;JoL>$>9lcG{@`O671HLo1Lb7@C<{)Z%O%xS zgKJ;5#(699kx5KlzKKk3fjh7w{?@aZhuGcuVi{3u<`}b)- zy~yo7Nngk_y}Lm$iG38;R=l~j*A;O1)eUZj53D@n8PliRvp*|OefA=QukAixdRnC7 zjRYW8z1|&U|1PY1+k5#S!}#`J97C_<;s&&{fnDfNcG`Nnl??RXP4DyDnw_Fgn@a<< zSh%tGr7}Od>6J)O%zt{`FC@+0?uD#gR&Umat)P$d7o^ zSs7Nxcw-&%q@!dovN!NOaOAyr?BN}F^JX>d-mPTcg9gWoPDp(Ha!pipgu^q$;?K{ zSoC(vNzD!wgo}Ur+gyL2{ySY=`|Rj`t5&`5`HIX3mF&^`t@6O5P0v@HnQxP+R@T|v zf0HJ61=)W8ckf%a;%}o}Fip4A?z0wD*=??dd~h9VKY1j=s$ZIHK-d2t0Bk^$zu9|y zsqXblM@}Z^g}QWR!2hprcl*sc>)Gjp`m#0$>bfizLKii#Fz%AhZ{N}F1SY7u2?!8%WO`pH7du-FBy6Ku% z~j0bv*SzM&Hg)I_YeA}`+9S^_3ZAdwP*9&a*`?epo@00(wZQBzweA!{<$7LozeeG zw)cGJaDVTq`+Aq2*WG6y_UXPhmDhe-b4_2FR|oaz=+g0Fov>(^+!pE2Gw-!xtH+1W zR(`WjVB*gHv0sh#Z1Su9Lx=98H@DEGak&4~t(uKC*mvR%$R&59fBvA` z_O+k;-O0*p!p*1dAsy}Sy@n#)-I?F7I_5=wo2viQ%ljv{@H@v?D|9~I-EVdGuXxOV z_8mJ}@9UH0*6fbFWlWb|>>s|qUF~=s4$1egm)o{AnXY%-(dj{6`gMQrgFKR1An>EF zvDQ!g=+)DQ8s}c8(la+$lLb2l{Mp=a*;puh&V>cQq2<{^s?s-pwn%o6~#u z=~YL4d(6|9{r1^?_V{rB+3yQkPA}ipPk_P+qb_C z*nIZT`>a0xV|Mz@UX*8dX9-`V4B?B?LB43!>K8IbvWyZl zzB`ZP?HQi#>)qd-S4r^O^))^E{U+XdOZ-PX^DWQc+j-T~FVQp{e=y~rxpBA0`+nQ~ z*V{sicYochW0oDCcMh+w3e<`tjPgaKCSR0D`h|3&FH8(;j$>$ew@m#rw_7LgtGjmi zyXBtlJ99htf}LK>x2N&m*`KheKb-uuz1H9PsDGDOKUwSiU!PI3^Sh${3>~BN(MO(> zTS%>SzX?L9Ef@TKW<X&QuD@t=2j#c7odrzn@@EDnqfH{xD(eZJAD@(Nfd_y$Sdg zonwnrE_IA5(oD64l$-ST-hEogZ#^n!ob{LU4a5anuZ@smw~;ggVabHCK}rxU6qAN# zF^K(6N%UQt^Yk<@de+4|iE_PXMt)Ztab{7=gKM|%{qvv4PY-6FRVbiu{}KIy|Gu>1 zq*Nv{mxyQH4XcWV{N6v9bs7C%Z|<8vV8&-VNB{b*c;mEWduWD53iP^>-_!Y z%P(&p?;q^k+JE>c96Z`jKR#$TZ-!UjMtWL4e|q%go%_Gqci$g8d-(m8Tjl8c-D{7Y zfAuYYbMsrgbLGLcEw=ktb2z%OpYQeU!<}0@#F_5>yq|Bi>p!O3@bvQ^?zZbszwBTB z@TLFZe3yQ@^5gSgt6sl+aPMIEPWLPFbN^|(zdC+}RK7X}>31kzJ!tYA?3*Wi>-M$l zKOJ59D!sTdzT3V3&C&JycOTvR`sZ)&!sm}4rRSHoA3f^d|M=CTuU}ld`=#D^aQ*f# zKfk>9Xz%jnN1q3Je0=5ExAJ)V>z&6pVfWs>M_>P-*FOJgtNfa`di?Qt>*3DThp!H= zUAy<_*I(}M-T3D5%l_s4$4ZZ1J-)XOFZKlP?LWIickSAvoAKcfJ2xNSfBO7LA0K>q z>+Y{RH-7$k`_+TbU*7+2?}sNpmaE@9fyet-e*Sf*-@pFz*LTaa=T~06kXu);9$%@i z_{Oa(TfZLOynC~L{p`mV51&2Xwii3kZ+>^<-qFvyzwpg=tNe2N=Fx-gD|+qa;nzo; zzyIc868Q0}eb_xr59*IiAHIOE?B**ze*D009b9>pcD}Z4q^-+8{JQ(NUE7HdufSI? z^~b%f^6JYgx4z-@?aLi5^wrm2|9Jnwt)nXsU*3=P`)e=m9>lGucYnHi|IwpgAHR5Z zbW6S;zg*t=`sT~M@3!jR&dUeC(A9&KZe8xX-`~FZ>+ao`{Nm~5Z_@pj@cq-vw{G6J z|KfOS=cl{hUi*B1|4K`j;pOGKkM1Aujyv|l!L5TW+JEJT{N?(uU)$c#PjB43xA)}o zjoXL6K6tUch1VrtxeL3WYrL~1GVsQWTi1C!{HFi(;OW(a2kr4TgwG#;{oUTRD|^?< zy#qf)di?C(e!aJKb^H3E?&+QDPp@4+-umk2U-IYIZhXF#>DQyf!w1i==kFi<{QTyv z{ksodT|S86#mjV4AAfh{etoi4?i}8@a;?*^h_UUVbB2p2vreZ(RLmiywcfFX`K@Yxh3ida^a{*W2G*PS@|=eDe9_?XR|f z`R1qXSI2k0zPzh=@9^N|^KWSP%6C^^w(YO;kF@pV%Iy~~@v-f`czkPX_xOj)Kiquy z;ChmuK7SZ*UGF;wFTecu;Bb61zTW#P99=%>mmj@+dhOd+4}SeVJi#x+&rkH(lP5PH z-G4S-J^g&AeD}@meZ6`6=*w@OkNxlSmk)2Ya?gIeyZz&LckeuS^5d_i|8(z5f(Kha z)Gx1mH|{?FA>V)e-K`r}5AD{yJMrLN+&}uBcE5gl{pi}0pDy41b$ov9-sNlM`Cfni z{jcz{eW_QTy!=|9$6H(N^W)uTTerU1`?1i@(TKNyc>Zhr@%HY~l^@y<$M@8Jd2*-+ z@!$v_U4QYdUH;+LwR=|v{gU$Wt~|T>Y$vp@FF$^Ce@pc_J^SJC%g3-aK2Q619vmJ# zeo()BzIX7`<>v>twqD#WkMCc;_x#3>^Y!QbcW3La%D#WtW&Z1{%X;VX-isIT?XOpV zczpl(|FQR_T~Q@Xx8L7iQOmC`+l-8ijLiOY0TEDCWN^BDSFaoaWs+i0)_VW@jVwSJ ziV~roezfb@tFffc$;y*;&fc*jV^~@VA82d+@#aB)IrO*IKVaN>J3ISkXX8aVSeQQE z+i3e*kcjkKgX}%hR{;`jy3F*zDup!4uef%cJs;6 zT0AJ>$(M~ct83-6ruunf4&K?RO}BmU)J{KnccPD%ALf&l^>y>Q|7ELBs{rp_%sq7a ztDE~Y_b~FOMeDz;^Y&9%d3vfPXB$F(v$Pv@r>7W!Y89?sX}r}@R+$>G(w z(_Ahv#MF9U{ z?PZhdEO7YJd>)KE<%*?`INX>T7Fl}9{w>>k15@Si5oVJKK10#@Q87k8GRlC66Hkv) zAzezzDyxg3+yf2wcial;(^;hSnSFE3n%dayUlA7VH|K(S{>h_lJb7f2QzC=hC5^B* z&@1W>VEc#G^7o~r)d#m2V)v?v^zEc!!OIW!w!%Sk!LIdm7S1>oJy`DsJ_4&V?D01c95ji0jpJBnS?XZ*EQG-$b8df9j!U9i;I6rDTp|Sk+ z*VOjPQEH*`e_eX+zekU}TC$&O$K0cW7fG3MxV$VkONBE+l{MMt&OR>$l{78&tSP$n&B1GFAZIailkCG(su zdjhLv_O(`FODHC>63#&`&Turs6%&+L4&Jk{FOQ`)>id<|*%vLVeCHr3Qd z{WeW3D%S|DQ(a^XWSkQ-9}-$pRjlcHBNyb>Bu{&Yrc^CN;1l`A4pyMO7#iN6=#&er zx5XtZSY2D2J7c^rS`|*24CzQt40jVpR-4igf?@VmkiD?gz>a{mOv#h6tWjQQ`BliR z?p~}_t|$uG_*McJxlRrjoMvY$k&OOY<(7NV!SmVWm$Qr0Q!BF_A#{Y$5yG!W2wwZ2 zCj^}$DL|%@LANq>u~Mp~aD_s;-s~l+g=IkzJp^kM`qr2z3-VT=eDWWP8gNE8LU28pJzaRpZ>a}6zYH*Ik!bd1sScL_` zi&r71TxmF$e9Ln2tXm6iQ#q+zTx;5jCSpXT*#`xQL@NM-^3`+nMWheckUUz$G+{-_ znT@iJ$I%Y^I~mM9S?IW+>sIlbalvGEtGGjjau2OwwYKW3xVkfc?iIr z`;4>_{6}(3tuY;D*4a8QXK}&GBlZtG*np`xa#gWN0dBtkkQBs1DI05im3iFI3F&+z zImgy;$yu8f$(&=9S;#m(I!=_MyoSQbD;D)b(K{as7hj_l5{2R>j2Ij$!(hi+p?q=a zc}EMKMEI>}VL~K=$2%|qaID6Pu^p>Tt~vgl0CHrqKmiGFWCC3U{|lCaWGG{#;4(FJ z5sKnyNAx+hRt$IRl$`3su#ei2X5dgk%zIY|qbV-Bku9)wz#`mHF@{<=bGBriYpL&* zYb_}iW0YtItY|Dd!9nXoG_gXh9K4@6CIHZUt#0x?{Y6WlIxgrW!f(X|6DSc#;Z+mx zHmy#RnAMO}ZRFshnAB*+=ohk374opX-SZG51Ro=)=4f)Z7+)}_YFbepn>>wZbt+^M zs0j9l8g=xZDQdKd5?P{LZ8dFMAq@9Eo%@bdE(Sx?mTNYUykI|CAE}k~@dAseN;XA% zN=8#PrY2>CCX!68rb)^|I3osc+iv?fiDA2cl=crgX6TrqV}@Uw8I1LJ*k3MCip~t4 zT-H!jfmog6EQYuZVt(Jq3^ogUWF0db;e-IuP*tvi%D|d&cnj-OW-wmJr24>^l!`#E zfjo&`kdrM3Qx$m^MoWgA+HQ!gI+@@!I3E+|plgbevk$6>hH}9xPw2JK7EzSrjCDmN z%RFJsAdBl+UkgaP7c)#fc{8=tZ7g)$&~d{r&kY3r4yqxF@h+CWbw1_fvWsG4i-GNx zu3~-bArODw1{;mo;VC8&44R@16qpkMtHv45-pmbJ2qoB-ohG(?m9wMC8ALIdsFHv> zm%)y~$c=?;t+!yZipE$`H``6I& ztu@cRlGOSowNgYKHFTQ`zZEr1$mRmyg$=}ZW?KYGqZrh|g3UR`2;S>#K@W2Uow0$k z07h*67X2?;!+S{C8>f|Z2Dpy!A&G5|Rg8F@OKurt#<&8FhcdLz)$%%3T}L;<_A1nr zbg&9ZmDDJNNyLpI52#C3HE36=c5Oe~)l{;HP6P}Ek0s?WX>8yC!H;*Fd3kAOdDTof z8FbZ!ZgXw7nd@!A{P(IZWUl#bZ-L1zgtJCx3MyqQS~QuW@9Rf5&99#RfTB zrD|KBNK3Va?o>J7IbYle3*E8{n3SM!b((zJ?~PfEbFragi~n&fCN+<=(8by=AV91g zxKaep3mSo2>vIYzXp{xPsvH$URyKL-$?M>~;#_r@w9p?bYpjU%-7)#L1%{Sxi_!na z>OvhrcL061cEbUh?oj@%f;Lue9TTGfJZ1J)5Sy&8tho-W)pHKePBmjpv^ga)pN+QB zVq&ITVgfxPeM<#2E(LTkrrLU0I3#wFIXG>REye7kSiqwI8e-V?O?it>Cx9V zi7;)@DM2THhrmfRkO921s?|(j;{q#Z5HpEon{bH3`rNk>bSzjhHC%MMQ1#l|${sZa z!N(DSlOaW*mJS7x+UBR{B=gUxtc@VURnv<53@_YpUZv9+fnh8Pa4dJMJ$+y=PuR6!|?RaS{&T;ivndThFp4NXFe-G#QPddh15ZWH-VFAix;0>V- zZ?v=g^SqH&ATJhv2~ik?j04?DXeKR4BZl$K=Dmmls}fRejSd~eBnCMmhf6t-AnN&e>jRG*xMq}b=|fUS-;krCb?&yg8ML|$5* zq9u_C?28uU&p{=QEv6@d6Z4pC%eT$L6MxURBjnaPS;Yvs5GAZF(mpwMzLLWrv_xEm zNsI@R2wl>FgM1Vk8%1WNN#hP6avsLc9m^eeT)wm}#O4+k*@f8rV<9$phwa=NLe`mD zi8qykx9E%FV0>|j^3aI*^I-gBm00sBura#S$lfR?QY0h+cLi6S3>PRNgaUH3VW(So zr-8~51Cu4xQS5%KA_jzJMo~gdkOkt5BO6%Ns#{!5^E+j0V<}Fd_9Pr>wMj)|G+Aj7 z@q(d}zSPKi*!uq4D4}`a!0>qMz3pz5E)u`vf{qJ*O)j`YC1_?-OLpGcT&&81l{#my zSha#{#onBlWpq_ky;BveDax5FHhy4)adv8LwWEWM4mvvc73sj=rCS)3goZO9=BjHkIbfmc#Bxuegkb@j=X4Nb z4aAkasa#xeB_~&r8JtO2M-+L>ZI8H}Yok$t3OXxH=nN##z!(onoYccPe9c79!K6H3o3TYNf78$9vJi+ZFwHM+RM% z?r%m06PcxJ?$ADr*jLh(xK&XIwpNvu0KE&9vZ}+%^q7FAgx%OJ{n zF94XdB~r=;&LW+X=h~{hDVxZ*4G-g^t!mPWzOioY*2cRB{c>fYqlHc*{8qFuAsWHa z9cuZQC@>nURV}wFC~EnxsEUe^NHF>4Y_Jk4>J(iPBOFvQ3=~bSv9P1Wb%e_ZqiU7q z{2&IYrGQzFt@CddQ(yV2OlCmmhVMzA*K3HvKZe!%TgN_Y;JvI=N_YS(CW*2PG%Gm6Q zwV3s;8Z9<=(F8-=#GiYNJjYtHz%B|7w@nA11m_bGIl=5DINW&$|x$DUaK@rN_OWPmUGGWR}<>*t*UIa&zAk85cF&Q!u1jTe9DL&12i>;BZ$k%@xGkaYvSD4gRddd$szzg5H+c`q z`65!nDxHU9MxV=%f*B3>)+F(j_V(T}yNZO2uWzD7rQX#ii$s#?h%-&_OOl@ub zQkrha5R4hwuCjGRVDDATAxF;SOv$303D4co9@c-eL)I%epYp<{+$nHe)ZO@d%KQgqHVfc7O+ViTJ+Pd9H^ zXU7E!`P7PuR_`(i)Kf%9Dj9V{L;3+TfPth+EGcKrh_SUGhT585Iwu++T4Ts+JG|`O z88Z-C13uO&QiD0m04BEbAWFo855jOQkjW2NA(?MZLAr%Wq$_f+IB6w=UO;zsB zGYwKpUKX|BwE#0F?WhLP8m$dYbe>%M!yvfMTtvkzePa57!-%l*M4r9K~pMA^8w$0nR!l&Ls+o=$joo zA?T!B>l$wfBLtOoF)gx_YXG(*96JiYCym)T?S(Fisiie~q*PsN2CpcXRsdu82$G|faJ7j z0WIVdbO{lS3&Yb02Eg0;G%gHho zD_}1=C}5UhxL9YZg35^A%G9)OweX@hq>zj+X42Y)wN`Qneet9)1GBsme?h|Xa#)mDNPEBlCI>uZ;U9UvtTQpnzVF3$4kCJ0nS+C(B@6s0qm zP?#ckt!*wEQr4|nMh-UFQpvHdStJpIt48>uWSB5w0IOQ^$5?v|*Wvy{vlH9FbkZ!v z4qC3Mz`>cATEec4#c25*DvZKG*?WNGiwlF*2}<}xSdrM)No%#!YUMTxzcWwVGD>Os z$=Y5H2j%P`oY~#JEsb17w0{MObdsWz6u+dTAb$r*A@;qit;2E{goP>$Nvv@bogzRL zZ;nu6YrJx`Kn>njk1g>FqUF#U9Oq%7P2bXzT~->(-#$QVlG$XLl5O>mpcVZiuP3c^HfTWBFxLHs zm$R!=OA8aPDLOjn=-}6+gKH^8xA8F9s$1tubjdS^8Y@;&1QmT!*^ZzCn@S2M+0gR3 zeALA%#!zzzMMhAgwx6|pqSeY-qf_*)$3evhZHZZoLg%p6q!simJ%Qsg^#fGynR0}GqoQ=0E0I!US7_AN? z)By@Mg~Zi)8pO0(joEuc#$|2=SINYJg-32b7gxOK5ThfM!XgbyZe=Kx3qffkOdz%} zWo1)9;DP>#xuq-zQ3#kdP8b~+(S5Q=V+~X7?;oYD&5jH@GU&+Qmm>o&*Z1>eV1od0 zFp6U9W}IyRmy2M0#!7|K&9x{nc|b2TVqDvK&ZH?gicF-ki=)UuxT1v0SW>c1tg;l#7K?oY_XJe zXpym#mdpm=L`#jgg5B|_en$pf(!%dX1{0XHKmw~j&kTSi)qp`e0x|Vn+rMq4KuvPd zRk}H8!JDGAb*utW2mu{q;~rFhela8EPdrl&9{A*hi%XioxFiLS!8NJDJ>~z@_VeTwkT#l!N}q+Zx<8fsZD@mi>pfuZ5Y9><&}uC*7o}X* zA{BJeTE-Lj#$hej&K*yNRQBGLToR=mv%;uy6;mE$C_Sn>4>`}iMi9ENE(|uL6BNu4 zY8KVPXt4Sl(&)};BZ&Yp#$z!x>EILP_NmNWBqWNu@!-=7f+VIaaN4A-A(q7ln zx@#)0NX+vV=4utHVY=bmF;yX^>P!g%O{g)~R-dVAT@z$Gs(f|<5^BM0ETa>Z(e^w{ zE98eE2OCg1tS<7%raKuDD&Zck$zn2Qc`nDYi6OtzaMLE|H5<1Qz zJ#Lm9j!kafvFdzFpVLGlU4(E-HW6YS5eMr$3G9d1vfF}62wE!}z33eg56nKf;N-{1 z!mUbCJ55IxiephZfjKHqHAT@v4jm|wJuz#Qi_4{`8xYCa;>Z7;C1)%+t9SbITK- zIQL|8eRD?6=}pP+_e<_AJh`kUW~Lrp59}LiM%P}Xo=H7#do}m0s0M5=IQF7%f+9mN zt6rua*w(J6lM??uDd8W(VE4lH6#S{x?;<6{thO5Yns@p(%{_G%qaJH8t&f|gj5UU0 zMHh%UPkKn~>Um0?=FAmDZ(y-eLAIJ8nqylR&=(dwb|JWVKF;Hl?H;Uc% zBbBsgUu+>(=mkg}MI)xT168${=p#0}g&Me-?lEZ!>pqZ09<0#y*6$T>J!gG^pmnH6G!R9loV z$$vVlSSYYVEgA!I<_RmvVXbup&iuzcPciSLiKdOt>C`!$eo3d3_x|U}K^bc8Lvt&~ z8g;AB6iX3vJgTUj9ut+0ivW(TU3^3&dtWv85N>326WLlKow3`Q~W zZHR5#B4MGZBY14Rd`2(>VNpU1S()rTBBX)PvQC|gnfZv_Dj z2^WakQMRzMD1^bncg{y@flyGtG!#DfxRO7u1qVE4yI;ld6HD#Y`@v54C1hZNgYY zxqy!xb#;RsSPZdd_Q8XmFfM3LG%({W+qAN>v@o-{@W+$IQJVoB8+2^YvB7_V4UE5o z&&XI?fhx*RWr-rH;>C_GoNE$hxLFxMCU6u?;$p2Q0dWxut>ZI_k&ZZm4O+?_qZdU{ zv?2pljQn7YhMZb+AFwI7s_n=kejuvAE1Sqv$ShpcYBbfBm1l~>!pT=d#t22QtC^TX z)|r*oMYZ&}NmB;c_}Sc}HP8??*zOfwV%g{DZ@2qaP0J18QJzkc0FI&`|y)S z4QSAsakK(j`Ci|(zj2WEHupMi=(wTdhF_Z-jQ4lQhh_n1RlQ5WwE9a<3t*evQUb)1 zkM-ujqpXsyG1}}|Kqsme*CHl*;!w0;@^EB74LlN+6LZxFIwVxVw8|a9+sw%s1|z8M zjPoNKL9hV7O~_RViBYkJO=6bG_onsSR0?6`5puT`u>#FEhiDJ~&LV)B77F&ujIYsN3T4RaU zR;ZeiQgJ?^bHhWJ&g;1rABX`tR;zWW4rN4>Tgg8tIw|?mMbM-an2bmvo2s@Lg2GaK z;yh_v3&_eS=f}Fuyx$LHXQLy9jubk*@XPCkJ8Uylm2Az{A^2A3n_N^R*0E3y<+s<}ij(jc_J*eEAyt5_8!bFrqH>H>RAwIHD`q9c@I?^L>48S7uq3`Xb| z^mv(Jpckg6R%SbL=(e4ID{`2OZD;oC4n;6p`G|tGHnl*hMS?~)EQ$e@s0MTMwsRmI zZA#iGizzv5Ib^D}eKWa~q(1Ho6n-l*m_(%@5b*QNKt5t)fa+=}9LXskTj6W@hZxtxGRe-E!K9cHvJ+?yTA@#x zgRaq9$T_GoqC8ScR*q6GqiMyXwF;2In^v;|it@rxCb5{cBbh-yzJ@4ZuY(t96LYR9 zI+G)oR^VGS1Q@Tqh=^Rlu@Fy2B+86gi$a+&W&mu4!*~Od@9!pY!z7P)*Hv#R+Ds4kuEVsgB5vHy^BiLRZcC3%w_9a3!dz(oEal1E+dMpCZPbT zsuk|G73CZNWlRJpx}~Urwru!%-Q9a}!_&pp#f9mP89HX@nBmuE1_Hc83DsIztCl>_isf034oq}pwYH?l z4Qt+Zp7GoY5;0<+L2pq*N^9^{v_|3*(+FnJ7K^K~vS^M}O2{htm;$v*d$A5`e_Mm2 zbKKBGt)h)8vu4wh>moZ8w$NxOCYg|T&UtGxwVu<$47w@DvL zanB$^y-t& z1vJ`7+1S?c0J2C8R^^836{iKYQ}H<`P!6FeCPsqdG#Q&(LmpX`9OO`!odOM5A{hB>ne3#ofg6;F8Y>&TU3j^=ytcCX zk1K_5um9io0Un$`ZByGTN9m99CAZs))7$gG(1qv^ zhJCQ|2p-WRa1SoMb^TkpP#S$7?*l?flzM3cb6}q>R$nzN52AU8~ah&rHf%=PX`xpr204 z_`Baw=wG=)!+Zjq|{yUp!Kl50io!c z=K6^lxx3%xZKp_G|Bg;4rLN(`l}VWqlQMUL&C}lFmb|^59CL={?gw}+CQWy;4P=#^@eW`pGzpCU;DO^&%XD!<-(Fu7qX)a6%2mI~ zgS@*PHV6KUm#e+0wbl7o%L}XTFN4*OqM+h|MYvmEK;`O$P+-qxETc5t3+ylF^y7F_d2XL?Zpnb-x^N$$U za~1MnRS$Qz+RI#;udbJ6aQT>fJz*g~8H_TXi7xH$wgo-AH+yog%Hy>ic82Vj+lIvZ|G-@c4{FrZa|t+pulnC0nMOJ@U(b$;^n z$fF}RRjsm5sxLUKV<~08ID7BpI&_H=fx&DeUh`>feupz+LUuIw~(sC+j! z21dwzW;fLa7?WMJn>XuMx##w`+pbx!>>O?G@9u23NBSsi9ge>9ZpS#Y-^U!&)BJU> z^>IJg56!kHJ;C(!sf9D#19b*$`*Qa{R%2X`^Y-qM%x!mj@9;p(wz|K&El7*P1Fb2OV`+wgnXTvSZXgAIC z!oU9C#Q{eIKQw35*S*0f9h|vZn%m{_5r_TLpoo?CE330Fh6i&T?reTOEPwBv`Ok*D zbNbfBSdWV#2p4}n^y9vL*je<%CHL8|4WaX>k#7#L;n5-&PZ;5KJbm+OqQUi@pv%Yo zy}c`%<69uj(y4vYd)f@Wzy7)qWHOZwMCi)q9?d$uy zhnIsE&+mlOzuV9M{h$B+pZ{qGeEYhELk8FWU_kcYPCs4A2S+Ds?_bPMO;0`hq~7h! zXScui`E02kZ0_Xpr5#j`1abS1%1$1*RUe%PDnID&9`2`$QKb+5&&BA z*9MiMU8!5-;(;5 zCvo8v_ydNXa&&FxA)UiPL%=JP^gu|etT*@LpWlM&F20?EaBn%d=^9Q~1*;9LLU~jZ z%c_`aWI@o$k7%`yP`F%*auNU@oENsr{-xo*-=f1l?0hl5UBks)zPVyWKG^L4 z{XN9)Jh1NiVYiA!9Yh505AMak&rhgjFm(UH)v(pUWA}GFto3vO%hdeA&Km5_&#$c= z&-Y*Ri@7J83kz#cOn=_Jou2;k>|l3qb8&a=z1n-ft3Ry9XV28v*TCE6;dcM=%jJ{u z=I!3$+S{2$(|@})`~K+ZYxn%wYx{C$b#}^?Guy6uOG}DzV_=YbDQhWXlrTd{oK1W`|#tGIl-w69}cG0HlM70 z-JhLZdVlhHd1vAI`j`B8c|Gy^*Y%}cI^OZLw0roHUzOSS&tUD{=Ck$X?W2BL-+R3H z>SS}_)2EfMs}H{{zu9@W@xeTKzCr7|GoMa2^YZ+sxmV`!Xy)s&Uwrc9U?zNZ3yU*T zC;QJ{JqvS(ACA`!k5_UHNz-#%Z{o<2O? zrLBFv8a~9dc1%ypv#;)8eYGs^&3x6Hb7jTy)bzWPt@Sv&32QU-^h^4%GiAO$o>_eE z^y@F1&hXQ@xev>$i~X6kFUt_#&K|$ogQ@LTAD=A0e}A%meAr*~Z|mpj&ADe^cHT^d zoy{++pZUq2){E15>+OqYCtI(+xZ~~V=X&`Iz1^N(e73NBd@!~7@zv|uhr7EoQBTvC z=~wTU54P&d@@{W&Z;E%n%13^lKbb2#pSBmCE$wVfFTB`4Sv_8vvhyBiUeVUW1TUw& zuXf>hao*Ln=lSF6_LIHUxV}Q_;riU0o!Ob4S+le!AEDO|mv+O_)RUF@{j`%_&Tr4o zA51;{^cf${E`LI9x<+-0Zg0=O9C(ozc`s4J4Ur)_0J)GK@ zs>|WU^JzW*>e6ND|pPzqR`FikjZh9-(rTx7xN6&d{=FO8Yab*ra@YKf4i{meL zy=)z?FHUV8yqkXaY;AR3`;QOTU~xWg?tOXudT+lzuX8(3ReyRfPrv`NJ^T9W>d9NR zVIQkc8|iRk{Kam1_M-pz`BB|{i;vfyMYB{syjuD2=GDv9jSnX# ze_VRZv^w=JJf3+|Umd-}<@Gm<3s3gT;?hglTY}yGTi%-6p6}0Ye4KuDQXkGPP0yO6 zoqY86gucYbX=dZgTsnfqsrc|<>u_rE`OXK!oBawe-W{F5hZkG@nRoHs!BQ%pH}=yW z?Dg&Y`Qz7R`rYE}(oE&g8V|Po;j_a{73Zec-!D(4bi{}6_8+g)RDGy-U#{-&t*?g1 zM>~5Tr;qj)r;cBk_2ucMqlFLc_?!NJIrS>}UHP(2``6Ry^m2OV_?TXwJbAaie6X|< zKG4?se~Lv{?P_H_4)YS+o?A*$999=+(Mn>4_hblWggm% z{P3&Sb#`X?>Bh&yd5rJo+)IPI?`G#kwC9tfwRlj%lP?=@R@cgBP4)A}9K5qrn{NBy zshxiE?nECiKg=g9>+9xq|I1dNRsr6CLCjY5nm4;>!m=45Rz9^>Y#!_|jtPq&xB#rgd`T>rZ9 zWQOe4Oop$Hj`hR&m6vPBZ#Fja+ziL9#riP5d0amq9BsTgA8nEUTb*&csbd)cHqOA@&>p9dpPxnk)f4mYNT1^-{N zf6MmXz_i^}P$W^;F6eID-JzjzZQR}6-QC^YT^hH>-QBgIakqxT-QA&ZhX4EH%$%8+ zm@^kM5qXi3S9MXbbLZOYS+DA|Pw0v$Okb5IVMX9gVw66gbB2(935~hA)9vGsP*(UW zkA2wv2_5a>WKV~=oZb0j9Zbw)mWvS2=l&dfE-&*Kb)ryu_4^bcNM9-7+nPXgTJOe? z=)(VGkfLb*+aQs*f`=bK8hja~Tepw@7$hqF|1wB)akA>&nH@|2&@}!p25E;)GNLPO z*n=+&`Txlv{pSCVK|*_}CCgra(tZD_c5^!Pan0?|T32<2EJN1q)w+ZZRmcCCo(xvk z%|r5ewaY@c{Ph9g%wx?sZXmE{`<#aRuvBvN5izGJa$D^G_gnGqBIrsBj5@qB?3Zsrz9^(yO9)4! zB<*!2T{&$@h}P)1iJvuU4o441S*-rS79p7+Bb?5)p@?Gr7XfTqFx6yo?kFKKI!H89 zi!wT{k}+xMJbZ~u;K~95QfDXO_qU$Wt&YxWk-+~lMt#CXHZ}cein7M!*&({wJC>oe z6y+#2>Tth>1>)dH%I1)2T8F2FHdjw8rQAek)skNxBERh*wLtP|MoUx1C#*mb)wHab z6$$Zti{wE24jr><&R`q(-@>TvV{xSc=YRwKJ9&wSnsF87g&%ptQPD)7<-Th2(trD+ z@6FOQ|2Mvi?+a_Cz4W3+9y$kMo6C8O=u1ABqLcuJc%(mdNJ^w#_v|?X< zv?@H+x+b)<=%gk;_p<*yC(I^Q8q001@3XeWp{$CqmRJ*m<&AQ<*MnUQ%|>2gm;!#$ zYxSL4%bm&Y_{~*1j+;tKI2*TT8HpkGx&(!zAT-qlpNG{zxkFT&N$37Hi@g<|s1wF& zRB2G}D5NqaZE}s&rHp`wQX`hUL@^s#>pXrwznxeas-3WsfcZiEmKnO;^64c!OPvH} zN`uRq{LQySRb~gS#_4!UzffBf3u8{CehMD7JDhG*$8Q%Uq9WQwm`XZmW)tU!bQG>h zipFa3Vz;)2QSyTAs}b8wFS_tSwHXRU*!pUrdR8@bNW+kk4!HnoWsEATpi%)^GsQB6 zHgvJuA3BCaVUq5Vk3JN3O`QzA%VMDsh#%;0(Xv0kz23zYj^;q`T=L3bKa=)sRc9|ECXm){Oc3PqV+Wz|h%^#4W^og99Y)QnMK&_RwV4omi# zS)Y$CE0M^gRMnK)(S{~o$kdDv8Ah%;PeCPNpG%yPwlwnV8=|U> zm0Bu{Sym1&6#t$TO{xI0N@>BAt&OW^5MLx-iXp7M7c~dWGKq{J{9t)ooiwcO9r$n# zQg$`J-fs*qgd031uwWXrL%DkuH?P-PCO!MJ0z(NA8D8m(sS36D+Z%!fhrv|Y&rnuc z?B|kTt|-IkSXxL$bT@nvxrn?S$Dn$StRr4_e3Z*9+tBC=w3?_$$95{^y|!fx)wt7e zXJL|AE~B|&f%U$PU-0D+f{MbX>@PQb`2VW5Ur)Gz2ldKi*)VMm{cD>p%hq*QvbANZ z2mDj3cHo29ooVkYK;_{bA`2E$X$}H&Au$AU4yvmfD*de${T@=ap84nvKD z;WRwx-1k}{UJ;u)WUDXYZgclXkhSuuGwr(3cbFyM?!mDVj(__T+%2!BL70)btyWHb zVu+bUQ=tTt9EmSNsF+@9JzJ}KvFhJiRbh&r1A_xcC^K&KZ~1Q&;g`tQ)zr__(q}`L zhthK^SkqmzJM`9J`~+0OXV^>`{;xHWR6(n%&ar&zUC!N5Z2j!7u0SbRIE}Kgw)f3H zL!8+7+^tVUBgV>ilJI)f7hqt6t3kA4+uix7?JfLe3Blr8DAnUi#HMD+ZMoe@o3ztv z5mE5(N?54{HdB@(244XztvK%v)$|MBU7=?!Y&Zz=Pmm6_Xe><*7MYmf{;HTeKsJoM zjAx$JYx5m_yTD-NQHW0ZGb6rc$;6~W&lE(M*i!#`Fng7hFO=brhVqRAMr?yI|mhKxbP=Ol|Y0g_UmbBVuy>lH zpU_=tN+sIQ-#Z2wJCiqhQA&lhh-n8JvO<+qwcOR90pm-g+QD^P%5%@s5{8wnR2qYVBYo zHe`ALwVpt(T89xC6Q{VUk#W`|&d27D`6nOFk$`_po58b>g2?cSgy0S^cAiB!2YE-a z)nHx?*D{g)KTQda)rDECg6x@oJFs7gVQ8i%R6bPy@0v&_peM_VG-ksti5bj7H;fg` zByui&do<7y$LNM1@%1?7JE)-&g#=O-BU%>_wTO}TRcWFtMi({JbT?$iu3Mw0mzdcE z9#V0j9|b5`V#IU&|E-Cdn97QT?;{AZmUJ#5+d@8>S02)SQH;zRwzdz+GQ)xKd) zqscMcbq<<)n>2RRmXpv3WG@BwdI1`1Qw2GXgB`uPS)X)HlSZ|6UyHGW{3LTHP32|< z+#<$-ELN6)^-jV$LM1U}u1iqUa15FhhKV&r&x{zPqMB;RcZsmEQQccfdPSrJGn@gX z4(li#u_lDtA^(qn$IWW4aov?yz*b&iVcj%ruy}K(x#Y0wJJ0D z3@&_k6rp9Sj7+?G8g{NN0w<0mnhT&g98 zOnS4@ZzR)Ye}OaY+YlLcb}YST<7}`|@y)5~5FW2-sh7=H)G;*U5lK*8Xo$tx!sJo${lm-_RB&X@@p6hE|#mIA@YgrMq`63rft=9&Wga5#NV8o0!jO$lbe*PCh~v+gC`X;9TK zgz#elg4~B%S+_>@C>COvSchX?c=pUgJ@;2N5-W}+!D)^P1Y7Ypwk$Gi=)EYUFpJc%QaH$!k4%(pWP>ki`R}^E6Vwop#Cpa(gcJ> zj_DzaRMKtYAXXnCmhpMaB11Bv$T{(xHq-my1rHVsF!K8gE2Nf)YHf2x^JA%!{tAw~ z1(OJ`dC0Uv%5Gu%YYn_(jx(Yq1AsISuxlwG07Jg{HZRM~CgAnyx}{8Vg~zcLZj)opjhY+(7^YMQwR6+NFn;CR`4}E_$!SPx? zh`Z4EUPhs;8cn(Myl>&Cgmb#?-<_(^ORZ(68k+Ic99?1mVed0zNzjLAGi8m^4l294 zO$%$T;I&pxn0Hb;c|1&yTWb(-XsAOGAgrPFA|f3+>GbQH4`{b*xYSx^iXHiIJj0NS zDpBvm)}S*NDI7>dVabBdh7#=fxyFS2-sNMAH&QegPqj&stsPRU{|%g-i>S>kaMas& z+LtF}XRX#YdWdf0$kkSu*J9igNS}+PX6MQ@{ab|)bbPex3?K@Ul$DOZ^duIr$PoXIhWC?X~RT=r}7bRRE65kfbG zCTl7d3~RXqCjh*WN;fFUXtmJHLxsC!p0*Y21HpZG%BdK-Sw(hm?+8EfAopb(>O1+5 z!wR0};5g?!IN&JgDI>P{5s3WH>^s9PR1rG zu?p1&eh)!eOSOnQh$M>?&wetN>ser_F$Mjsn5vtU_q}mxjovF#PI`iS4+*Fj-+0-> z1b|sbTb4(#wqJ$3ew=k)0g#u-lTv_2b=g2Gq>90m%x@Yxa2jK9s6IFS^3tt;X1j)c z`Vq!dX=^3ion`*|kN3xYj~p^a>=PdfYo`Frk)~pKvv`V(J%HgPOK{etmT9Uu=t+z( zl2xoQ2L3A&wrYs1kz2v8v@-NAa?3*$lLW{^4VSZIGw3>k$O$Hm4W1_pKSQ7`kJdwo z{xSycq(g(_w##-gHZE(27)rM;dY#@7qk3~-Dp9`gUfvB6-(%({p`{qA!E1)8G$t{3 zCQy{2{_guZ9S6#hp=BD;ej8NO;ubCUO*iYoXPsS3liBFl8j*F0-oIN|7U{pZ?~ ztovA;Qdgvjk_WWvWlnQdgRSsF*Fa^#0>z?uy1u)63K%jPJLW{Rp=CIFSe0v;dd4!fnqtABRE2LPmi1N9uuLgOk1xwDh zArLqx)zAap3as22@aMv4m7RwYpN!jDpMv9}!Wx{)aaX0%FJ`lJ71a592VzmNm=Nk$ z#9h0TaiMNO1Cd*t!l?63RY|sDdO_>EO1uhGy*Z5|kNq-}nyb@zydQuMh}NaRVC-vP zDUPzijY3031Tq07WoOPJ??ayk$#k}KohfmRyAeM1moETXHx13JE6d6_^bf2Us$L|?Iu zp{eR>c?W8^5^NDg(Ym-KDTCioMR=k@5sr9*M1uIe$Ena)mp3s7S+k;PD)72pm)k}aM zRHz_XlBMW_of?S#YX4hN%}KiYVo2EUMV3cj49RmoMsBLv^>N@JHVoM1Qb+yuj^TV_ zb8@ujdzMiDWIra!$-HBm&yJjf$sh&DT7O3~d5L&H9C*5y}#F8;;4-p`) zlXGD#5^BJJ3v#&f_7o8kLih@nW-0aQx0)T*_Atr4Kk5z*!^fjx z0Jgt|fqmaX43MCdX0DEmwv+L8_Yn>@r`lg23Zhq^qGLnHhgzuW zMgNxLI2By+YBZ*RnNcva?OQP_s!Lky0z<0`pzftgBI8O4PE(Vz%XOtrXXHbI9Dob` znoW#C3d(m#QMwMT>%tSe+OpAsyVW7t6$8^TW!5V3zrQvYv)2+c95p5CR2;s$H zTZm9DU;fBiJs=k$P+7a*6g;T&rQmAoQ8~XhlmJgJyBQ@1=L|ecG$kqXWjLt*TQUo( zDI9K+O`j4&hY<-dKW!}`kR|*_Ot}*>*pxiY=nV&Mqa7Tng9&7okGzYb_=0{4jGx1+ zdeP=fp$25*RTw~PhGFF&C{Imq&?_1C7`B^(C05z*tEJ8#G&jH{F@TcJ8sQ`bWiNQF z93nySG4ZlRNncS0Rvz`?C2u--$Yoy7rY_QwE@EsGjSBz0KlHi~T|O{@tQD~7TChnZ z6j87r8GH5UEu9r6E_IUh!y_-P@UFr^jn+yxfliSY1~gk@l`U8)RhIcn>IAbJ{mkoF zvgc2N)*T?EBRKe}uf2F6$^D2M&Fb$kfpJlt z;!3jP;xz-hKfbNwIRJW{yk84;q1iir(pa`!y(AdZ#*LS4i?iZj^`t*pyAaPLfhJ6P z3TVjQQbCK%oszSd6Cu36%uGe^E4lZT#2t*OTve%!^P|;M2yX+HJsJ+>h$Px(s8qB3 zv51+cFnBCP3XaHn%qzOIBL8PjMKooB>7UV=AMC$l*meC?UZ5Kqo;&P_e$03voC(Hp zkx5_^e26;E_RhYu@52D1IKS*Gx@c(>)9!osy+H++LH2F62 zE$(FLVF_%6eAh6i{NaKJg+5ghBHY@J8WTZR&0Cj_3#@BZV?;ZW&u<#7!c}Qo(hb)+ z3k@2dq+&&(!=jB`1*MveUjoQOL-@x8mipL01-=bWN=?E>%%XHmZTm9K6!v>-!a8Uw8QpXKGyLRQOP7TM}C15A|U!hyQUk!`K4dsG$cDBpQXn9=bx z%2$?^9ji0RXmV&M_i*SqBZ)^ge;c0`rZ9~SCTTCYq;F?RMi-EWh!uX%gQXq~iAA2o>^LPfFu&h`tTL=TQOT#UN6kE=EAB|h$2>|s7xAa9UC}rL>jR*SM^9p z=`$mP;?EW9M#bGg^@8s$x7lWzJ`|?;w1K-3OZn~-`6N)7Oy!XVa$pd7olqC$zLeAEabBl5+ohw3xOeHIMMNJtjAsJ=SeTm`zmslrI*~2 zdGz&B%Eoy?HbA^Q>2ivWpQ)IPl2Cpu)xF3x;>8Pu>X;$Fm@T@HWKZdqJ;N>w(YwHY zRSSfEhY|_jaDWWyn5^f)-hBc$l|-!WBlJ-P|D&HZHY}JM0^4(>qALldl=>5?sq=~d z0O54CNU;@{OY!pxGnarG zUiAWp!IqbXQPT_CxU{DU5%C8!E4xwABB||B^{<4ZO|!Ij*|F>M%K>O)C`N=Yj05%+ zcNl8PfVz-5r_Ick5najzomJrhRcT!cFVx*mnIP8(dXcKcR8|8BuK%$nb})`3D67x6 z&`!ZZBU4Fg($nDF)_)YS^Kg*L#K(Q-PKA`Kyyvxs)|8b=;ZujhG_kJ9JcKt_IsjvG zY5n!PhPZT{W~A8NsYmm#DP4ZvMdsYg%jrf*YL|%4Ne~qH*RT=1cF!m_(jz}`>+%46 zg_Jo-RRQhx)f-aBOKcDJSzALQ4PYVx&HB+II^WJ@XyD)*@}jtKHK-j2lbHRn^-Pkuu7 z;>6;+KTcrVm49W=D>j!^MWXH&kNdd3gaiZf+brg@# zD(!4Dj$LMbhXcoe`VYdHXii}tYC8oNJ`dBOHf8%6x1gVunTm*=Bm|gAt7jklsV-c! zsHN;DGqjRGu_T0-+^5le+M5m|)@z0>i5X(_jyjn6tKj1=ga8+CClmo-EzVq`Hl|b} zbQ;v4wPgx0A{9T{)pPkp7N?o{vj7u_I_!v0>T7ViFOZ9{*I#4bYKn6fHWc=7@EvKO z_09n@;(6Kq(2`#1qma!#xW=tTutXx*+`T&bdu%0?$4#pXNtPe=B5r&M#5tE~Dd@&Y zt@sbM54_Z`9`pk!4a>P;@9Qbf7-czjEa#}cBh51P#WQl#swu27_%kZ4(bj#=wDL;G z_V!JGlJIOjI$+7@RAJO+I9~O3wi#PflVIAE>{7fvzpjBMfakBFB3v&f!l4p%IqZ`c zv*P43?5wa%e@1XrhXyk+pxlLzI{7>lp>|(6AtdwA6mi&?Yb6a3p`Dya&88u9^Fo78 z!+qVSm7Fi^3NVL^jFP6(SQZyZMeu$~YsB-(DwNLBwV~x|&s6drQU{`Je<2SN>yZ3X zR9X@xvubtB@$5vQ7O-!V>_01ds9z7R{cc18PrpTcN9yGzpidlG42->)Lh8N}J9M^# z2&3k)9i2bbW4$eKI^i-ccv<$LS=fHa;R-RzP-d&60xQqZj{ATNEksz1&mSlFFc|YMTHU0uP zM=L6Yja|uMkqfW_$%u{*${h~>MLhW80=GC{hK55`$h0mQB}Ya&pN67~)h$NIIApbJ zjJT4dJ`&&(D!&v)5#9u|MEKXH2(#||Yy%%n+#18rTJEq$3miPta6mR}pe8jowJQc3 z_rFSEPc37Df$3BQt9Ux|$nyfa*Z6G7cAxd>rW8=mqkxjsT9;5Ry0wTJ)?giaz|G(?Aeb8HvfGr)~_+z@_njX{)w+SvYd8H z{!IL-Mq8p0dkl;!7Y`y(R`wYeiuD`AUg}lE1`HWj1&ZZWaMw)-gjOnt#Of*nc4dSv zNE#NwhKp7oQ-G=#4`xiF&9gKd##Ox>^Oh({_*XdP+F!Z6)^94L=`2AIGSlgsMnZMc zaxecBL%zdP;bs%+Q1UpiDuO{DSxARico5lF^J#72r1a;1p)!KW0%ga^bVG1wYcAMm zE6J}cJCuD(K34h?z3yk*5jY{=^VW@rizEGB)+ELhyovqdW(M11L@LOpF}VYwlpeS2 zj!K^^H}<|J)hbIE6%aPBi=1B4alq#NFSE1+MhNt*+kzt|h?L5*B>15}%9Z+=P2)es zhPu@hfdb2SqUK}aC8=V5`9#Q;Uq8qpEc{s!?xmqu)FTXnNwTJtsZ_T^)vYy0^2#i& zAQkpTkdjqVgEs{sO-dR`wdRgm8Z?crLq0yl>WH+AheQJ}ZK=B4Dh|_A_Xak9^Hr0)NdtiDJ)DuO>75l;@$O!%I1^`x zK}N3&W?{me8;u@c94Ohq`Wwl(iOaJ=tXCCF4JGWbMmsmYJO$zC#or(brZVX@;y-(} zAgJD-h}W;FyF@2FP<)Xp*&_7Rs&4l-uBWcxg2d*%eOFG9DAn}36YAi?4Nxv(60L0X^R zu_VLMtXqEab#R7N*SZ_$^{FKG4Eh%^*JA=*Ng?a#{jU1+<^5==F+~|!J6u-Naf)&| zYS`DXB!2lQN$s&XSLz2y$7VK(p3W$k1x1`=;?1quQbUU_dB=nWhmut&Knx$;I7^}c z4DZPcsCHhsH8WYv{^At5UFt%Y(l+E1Nb>O#r1aHSy!p%-B5|aSh0B<-N+?17R2*Lh zcd`C=6&ajFoXC`XerNSpX(vf1hCeHpHyjf%vRPE%nQon-{8~=#J{CfD8`VU(K9tX% zJ=-BXt2~+OnqOgQ$uI3jre%FHAu?S4>7d9U#G+h{CM<;wQ%}wR+jH=E5Z0@_$1$_3 z($m}JcZS$Nj>D;?!^LY5!hfwrZ@Z?7@UM5KRP4BCdJKuyTpR;xy4O;g(QV(*Z{d5` zZ5K(+tIXF|dFdHd?UK(&CKRoY87RO>!pF-Q5PK?^3v8DQ6^05?(blp#Pz=4>l^o_& zI>a^d2Mw1YCUF6S=Wtk#yLyUh%1Ki#CHm>4o<{rHF`Tq_Vo)>7=pgg19LEGC6~89F z@^B->6Oi$MZ#8k)xAb!RIh!hBavK;V;xPDN)aiel(&tf3H`ssxt~r5nZk zvbdFgjG7jI<>k|*W0mSe8Ag5GMxj{FB}FhOjgrOH1{7uLWH_7bJ|UKp<;-A{kRBy; z0~^~)d^GIpaOEacFcY^Vh-z(7gP3#_0x~4K9>>2(qic+7Y{dh&6c?A{k0}1!l3`nL z(05drzk4C@eU;;T@xEJttBVOECXSh~4AT

r4DzI+@Zx$haO>Np^<}Dux)Pv80;4VA4flyvRXoo|0!cqUUOinm)yBl8S4q1oJ#` zr%KNXj?584sc3}LBawl=KKnq*UU)~;CZv>Ouqp+KrDH2`2wvRjL?Tk=))4R{DFKp^ zNf1MsFk}#=F%*n9At{~2PV8MoT_vbi;820XZx0S{sAJ3AFj0mbXkD7jg_MCu13}3w z&8U7C8MMtNmKrjfv@0_elGSu%2%H%l(KRm;1VCn`6RXaWj^Ugs6*xyxO4~{{k_|!+ zDVz8K4j!^E(|?GiSm&JAHb(ka2*50%1}EjnttTo%(Br&gmU~NFf=BPY)bXbp_8$ih z0A|d!3-{nqVMB!t6*m0l*kIo@tRZ=AjMmI#VP)xlWFa`GGoaFm4T&M&V*^>>Ok|K= z2$3Lbr&LnJLyU|i2X+KDlt6+g;~6kY<%w<(g9w_0K$69KPAn!UKQhFCF7s5T$w^6! zKuB38N07M6W@IKHnp6^ag%hX=`^xkNFCAoX+UJauHmrg6*3y=b6&pUS?yN4&RLD>v zLxl{#H!=X1Z)l;Kvk|Nm50qwk+62p0u%Ly|z`zch#&cisTpAJ?DLo8^#VKGyaF>Z| zM1vuYKn9^D+8nYW&JhuPV$p@@p>)~{IeX=`R&sQWn^ZxtpaV-r7*&evg0Yg&LP!N6 zl0eQ{>$FNxhKLd}h|CoCjKxfp2qp^|3^6<@%sa;T3_%9E*|YC7s%S$Mdj6xJVKPF` zjy>zF|C%IKfw6RrDBX!5Ipo25Px{nYQuZ;t-2Z}1^z5QY$c5AalG0@SJD!MY7=|x& zuefcf9nqx}In$DKyAPzQ1KG0DIw^QgCW8_5J{yGw)~i9pfY=Hc8IY8}&!9p8j#hxu zu}oN?T}hrkYLjJLaEXB{`fd_-($K(aVuSCPB0anp($3cI_D;nVDyHy9!NCMF1!c?| zut6H1gGY%hOK?#dZc!R1G|wi+{BrPF3>X4xS9H57;pfu7)BzeElZ(TP(r&SVYsXOr z7BbNThb43zOd|7MlQdQvOoE`nj*Ksms!P6568cZG5>hs3y|k9OO!!dh7_7uZrxGGK z=efDH!>O}Ru?%!zWmqTY%4Q+~4iyS5OlBkyb6zV=Ck6tD%y6H$ z2`MH%Y0+lTreN?`nZi~lZuPhDtlh`TcT^!lg$TbNA~^E~L?G21BnZtosd2_bFvj>8 zxg`5L3iWbJB(Pa2Cqp)h>~e3NE{Q-UkjiBY-H7B=`jiS@6(xClX0%AP5&RA@k8?J7Gm^#0;z^Kp1OW z`4U5;U133m1r-+jc35ECGhe_fumA&(HYLHa6c>9N$kg^m2d*Jvak0P13`CNox+Pdm zCViR0Kt{cf-bX1pgW;3YZgY%E-8AWJ5f$X1ktmH0()gt5r;s5l0YLWrNG#AiXaWtV zMGz3Ilq#1I3sHgu^hFRpXpa}H=T4(^I(mtOaK#ViCyW^w1)Om!#u_<@T}-`R#SE&T z@}GqU6BtwmVc$^E?2s@(a_~Wx#GK^m6@VpV@W9wG{d}Kr6io~{I~`al9+OFGEz69C zsFhD~MDL^Qjq;kK2klLuFIUcz4hN}J1fIPM8fc-B(2%vmqyPh^SlmO5k^Rre5ayX- z6oe3jbQZYG1Ro5wlun^S&^+mA{KR#|vt?-OI1-K?_0ev0bcGBRGE~U$neVzl;o0N}G(N zk6N9HnLuDhNbRGL3;{(}nk-yCI^C#@=t(9y6?t@)8D?b&Qlkqh%E%RJWr>wUFG9+` z^yRP`NjatzQ3;$ZWYETzWFtLR($MuipH7+;Yp8rke-<`Oh7ZYF{)X-rrMD_iAQ zlWeros)Q+}0t?p1IIOY8131VmMv=T#hDn_vqfW_X;tk$JnHcyi9JfX$g&w&mMZ76$ zv{rF%Y@RGQFiIQ6#CUy3y?EkVEo@flrxjKBqu^i?sRGk)zy|Hf;!g~@5>_UHgk+ro zEi+@9@Zn`Al0X}Qcm_seZE`HrLdgMf4J)iMBO0a>F#u)>P(+d*yz?vpVOuFQ1I;z} zK6>Wa$nF*?f%l#>WCpTeE;%bqGz3_}r_upDaZW1|drH9t@R4IS0gwgAoTmw+3WoN( zB!-Q(EnX{juRHyUC{)N$A;a&D49f5~j8<@2dLIl1kuzfOU}7K>a}YJiQor2YA~^4W zK2UmCE5W@WbOFYlgzQU{Vnj|VSZ|8gpDCFw%HUKn|D7)^AOtR5j`F3dx9`XhR19E% zJ}sogOe`Umrc3AoQ=TLbA;zf7#0YMAmO*%fI^=;b$xI$HXd{T1jT0F{g$!eMAxTx! z>fK7SLWb|h|HOsF;NQajGhIkVKkJ+Snz@|PYNZmvCZgvN18B_y_mMd=!_~_rYb9oD zEb~&Aq0Pa^>RK zr`tGd^cxr0t6-suEc~gk@K25`IQi5&@R~{mv|gJ`S~H?dLU@NfaU00WkEPb>MOZMt zWVS$xCXvbSqvevcyArWRA3C|5%4i?RL?R{)79;^Kj29Mm zx97L!*Y_$ksN6???6~GhaUWTDgZqew=uwevtz4EYLUsfagzTNucP3HS?Ym>!9ox2T zJ9%Q;?AW$#=ZVp=ZJQk%9qZ)%#&>hZ`47%`YwYW~s8LmW?X~7_PPX|d($ixG;h-pa zW>h>2k@tSf1k-snQZXX}C*Y4_bKD}3wz z>m%#^_n~YePe1;jc|bt2iS_zQHt<&QZ#Tq+n%51>OA91s$LRCs$Sq3CA4__eg*vd~ zFN_Pi)jQ`4ECJ~snA)Sqa!mRv+vJIeItVx#S%ics)qab+ZLY|J6EO^?$U>2iHO~|a}?cvyM6?|K1DkE)A*sk`CaV%D)F7ZuC3>OeLd~D zPw6Fbi|tsqp1CXlx3-H3LhVHD>n_4B*_kKvK)M%&13JU>I`|oXQJdg@Rw=&y!yF0;yO_ERmLpJ2oz4$c)=7T}%;oosjnCjQm+u6%sKZ>PB^Lqix6pwAYE4|9E{Vn>*M)Eax zWxij%j|9n)$%o%b7}%VjAIj*c$q~$W@8Aia_pPrpqOaJLU%D&PP7+TG4q#|U8BGes z^CZPaH{=5ztlt@lv>R6ul8>ed*}qZAzeEdsiB!-v zBv5{vz+CMd>K#$oUzqWB&)6O&$FE&E zWv9oH1vev0fdY24tv-#X7}SU>7YCfRZmK=o+GQ?SNUIM%jqvNHErzS4_JY~0}^ zAT!glv#lHT7_2I*J?{F<(P_3nzU;nUa_V?94%%s~kO69Xt30<=^N9{1<&u5WfZ zx03CD6)a&H0IPI4w&S^G+D#foUioqzGJXhw9M|oL%p|uPnzU~ z^WrXj58nN058;pd=^Sc0D{8iOQc7JW@3=bCt$QZRS-Oh^UQRdc>t(#S_zjBV4{c(P z-}&j=B}o=t4VxatSpyDw_!mOK4}4zinijmwbOu%FBF_VxSXe~Y2{n2cSV_8z@-g0W zPdZQ4!GhMS?G`Q@z{WdC{pX{X6P|0=aF#FQDJH(D6+8vmqMe7KmR_9Ge;(Q>3fl#) z5EdQVR||I0=Z^f=`tdfGjnkfalP#OJd2>lDNKNX*y@$@%s~cPDS~jwhzSdw%^y^di zEY;``>l9L>3PT(|JH#3|L7_4HZ33etam>5%d(9(7L%`A zJY(?5zh*qo-)D3u!@A9M+hPKp&xWn8F5P+o2pY>Tw`N=1BpeR|BFv%|w*|Yd5n1Xn zdx@q&&06>CSK6km7)_c}i|d~44fhG)ycNUP%dY;{^1R$=$sb*O zzx_`4ti1-GKV2+6?7S?;olDPdvHEr@j$j0L)0cq9Js!k+Dg00GlP90AmJXeCiO)vE zy_q-M{A>KWy*2?vM6A8@9-sTQ_bBs%&XK$~r)Bc_TRyrodhqJU3eDes9}k44<@hp& zL|p_owLP)+PK|!P?4uA~ZjbIA_q&^M9hdhbFXd0I{g*q3GhZKh|5;Q7&%RyIq7cWQ zhW?K`h&@|lh-v!nh$|T9EDwh&ZnN65EpL}+c2)eW+R(M*VK;%QcGn~=^S#Zld>Kz7 zwLaT1h|b~Pz;swdW6(!4S~j^e#U{+-K2*R{mnF z%tzhU{S!-P+WDRrgDJ@8s(y6Wh%hf_@K+Qf-@$&y0sr=SP$5X3!91ie6)*kgh@YQA zviGyw9a#6O-zl!0JNO)>Ahv)N+-ngC6+d^uy#yZHAQ)1SXA`C6Zl7a!#FoUw#~-mU zpS2G&Ux65Na{`eD4t}E38{^ipv}9&SvdXt}a=$2CgdNDR zcf7Q~bZNIjvl@RP+_skiJriv96@?)F39>jtTBDu#S{&4I6B9f`^{!1kPmq`9E zk^EmG`Tut!`TlE-r?s>^v2xggK*!O znvSxXp^&h?%@3x~XWy=_Ntww!JC5P0JFt`;^2M)NR$Uk%e63WkTLbK)InA5ZsjJGS5a^gszm9(&ot2xvHifVY;< z^B7WF+(fYcJv5&$+{VrOQiu%aTl>|&oiHvRkdMxPHX$D!D>PyFV1I6x7T;w(q$C&= zmidKb4j`*r3v|`ZezpH|=*AbA zVtABCs5Of|a0*B^_s84;sdaOq;<4n&GyR=oW2^llz-_aWV zcq)O*6{zmIP|hLy)XAI)N(j@r{f8XJqcWh(MgP)c>Se!M`OepGYeXu7b61qg{V=Mj z7$>^*M0W>D*F?}Z?TV`Hea*a-R)7$k_4MZ;agSo^Q}RlrWO3F8!(WzbdLG>VO_h0vF*u0 z4?QdLZ{8BO4V8bm_*_k_&X$TN)pPR6*dNU@&sPT~xGI8hs8$+YYRilRua;J2RvQ;5 zR)dHU9Bb2a`|#6JgS6X5HjC!RJ4b11;XTOE@1T8zwjF+E1(Mh@Rw&xmg$ga26IN9hkvcUl(yp^s z`~t6rT`NCOtNVd%8&UI$(p({wG45nx(2F}~2ox1EgsLoD7_v6qGs>%q%3DDM^A%2E zt2`lO$|Npot~nQ5A=YNquZX<2@8l;A=%E|hRD0Q`uX6JcznofYB4M^<04l_nM=IfFynx!Sd6oZC+Pd}lxXvWQMS{z6zf_8Zs9 z$-TpqT|O;yAZT>u?_ZT$sXKqOm;Rz?TK_{-K_HOx=7%}6`)|^ZeDD1XxkqU%y{6${ zW*H@FwaVp^)LsLmjE2+OCy+xmwaMMmE(_iyt-)QbqI_dJR%P5vL)lgQK0ZB!g_$s6 z3QY%%oKzWzVM;nm*8CYf7E2g}Wu8CIbadi8Dmf%bJh=Iq&six<8np(`K<6wDfe3Co z@5JiQaL1;R<_Zm8IyHYx`Jing*^=jm?}Sf@I98!l0mN(`iD(eAlEgh;f7 zaC*`y5juLU`lTft#bFQqChBabjn?RcK}QazXrq8oB)G;}qcvCn`~{TeL^5xN&>LAm z&!Dm2IztNAsa5J}qp5OGv=y z6^^8QwLydc7is8bszPd%pwM-j_T>TU*}Nn|kkqqg!$T{F*uen_O0DvKqy8ujqB5PZ zX2qWL!+ou&hS^dl`+2gq!^3?;0;kqh&EZuRWK)=RlD%(Q`W9j9+vDg zwK^k48*)^wrLrD!Y?U7?kjH&1$Fqb$Kp*UK4`IyQFZ{K=aO3?%Xn^dd8eM=folU28 ztbOKns4XKsghlp^2I0_z&elB;Woj0vBlyn$PVZ2s!+zJ~NLo=(6?CONA?>#x3WQFE zD!w^u*`w>-P5iSIuWIYypwz5~zyA-GRyBmz#{P+S5UOtks*;YC3lPnauEGW^kCn>} z<#`5Wgpvh8um30z$LlhW#IY?DO{LTT$Ze=YkpPyoQ=I?oKYlpL4S5Q68p>wB$k;Pk;%crLo1!oRUww6S2i&GD1vT5lFmSl zm8n76%nqdf1>)LprB{x2{94b?lL^gUw+jbyXY8_a5}+RETSq1|B36cnI`z>goHQFm zQI4NSyNZ!b@EV#p^jb_c$4WmD4=g5esK{&$3y*}x!uRn0f_leO6+t`J<@GuD9v?Hs z92&uZ+@R4NdRrEeeaHTVi*q}mB1??LD)x(#kpe_L277vY5`s<;y2Z3CfnR!{~LdU6KxbE*epA=AEnMQ1}pkPDi892*Glm+XJi4%T3*%D3_v#$sV%YKYS;!)cO z&Syh)5)Cm9f_3`<_}60re@$d+pRK-(y~}L`FXio3y+3%a<1|5tir6CfR}rEY?chz- z^)R6VS}!Cs5kZ}(A`wS8U33%c&c9t%Ey%6JYUQy}SB@;}M6_4&MpGF9RUCArTvOLt z--n^3u9LN~(~2AyLf5rmBYVU4=r_Rl(@PDL+1qvN5re%g#9{eFR;a{AEQ(x3l|LN0 zsJM1(amSW<%_s;LsN7tv2&klUDI33?e%TlII7!v*Egpo?C>v}2*iIkf!X)5sc_tXy zSH71-)a|?kF$!-6)rd^L4-}sSK5&Jgv5tfHRpO!xtFwEakDc#!?d!Ov|C!B8r5%2f z!tAgOC@9%jKk+kmEtnQ`hj_hMhv%4e3A?jLZ?as=B^PJ9NnQigubTLnPPu9VbDcpi zlO>&xrIb*f)+~O}k(-T^k7pg|ae2E3*w@XkfN=0nw4C~N|KzWDM=w;9Y#q&DxD)$<0hTkjq8f(3#(bL6UY5hB|i7~QdY`VVB`PlTi@a#n! z`*qOo0V})#%10~c3Ac%4<584zn0G9$9?!BGApYs+sZl2(eXqKQ!OpI z^m`e>2qg?RL4;~ldjy%QP3zq*DH+*$SS=qJOC-7*Oada39E%9E zFt}N4onmBK!wCyQ&ka2h3y~bTy%-eIlZKT&a#9*~1OYtK^5R1CYSr;&u^lN3kZpo# zbWZ?V-^XKfyo}T^y(ryTfz~Oej|TZSN+vl>d@v94FGba0mO?EAY|6%J zY#NXirKV+5GQCEl4&2qQ=b$jd)D~yeQ4X%f$ot7JgaI_Pu1V5@-Z8Hb{FqvLV7^9~!zgaaB0`d^u~lfCpfgcp z31SqHrzS-Jz7%)1X0ht}k8|TrL?(DQ-kBcO_Vl~899>_>^=GJm8uQs-+2TEZwH#1s zgANyy^49nb5Zp*uTfRWnjrR7qySb0=a9fF3pau!zalJpRtnJ|$aSytLzZ@d{-K94z zQIp*-FS?%-Cq7bTD#`CF7Q`8tBC6)*Ho%zC8w)w~*NJKw)4pW=rB0HXtK&p!Px)#8 z?Rq9em4`=N|4E;DK&L$eWBJE~OaFHR2&Hg_m*v}EZE{(y3S6JcwWwVSlw!J3G1*XA zuzDlzc=T{^`@7H7_I)kqC{2>tV)bUCUj&kXnmg^Wwwy?3GfIkVW(JT70O@E*QJdw!u`yh#QU`_6%1|u3ZMQS2pxq*_Zi7c*xEmQ~C zyXy6JosP0HG&y=Fkpx>un|3TK8`=*$rOyfJ1a2N_F(7dCT#NNf&ItIB$ffi`2so_$ zb;~iw8^iH7RZ;n{nHSYGOr`*8xHE-(g6>H_?IHw}rseC=@zeae;`m5#QqvHy=5W5| zy;5rT;|z!jGKMJa8*Mli^2>6FIUFFooJ+3G+W?_kPFr}SBbbnluNnIgIge!(CrkkD zPEq|ko%JHHQ!kl@ur$2=w`9)ScH4DN&D-{?!xoX07VhJuQnuMQ8eM>~TdlIMQz$69 z(^-caC_mm@bDPR2tf+;#K+MwyVZdW#DDm^Tn2D$CyFrt#tz9G`z0_=W6B%d;{_UjU zphu%nN50)2jn)i9CNcCwI1aAjRn?`YpFd3vd`LPrB9b>)odUEg4OW@qZ<2T^|UZ%Xbu4#2kgs;t!LM;C}wRM~%WI-cH1tuvU zg)s|KmM;@-p@Wqx&iF%w>37|Aw$o_KpSc@=5IkPt4YlPVMYjkTjyDEugnVw=o zwHnK7PUvSN+`tgG3c9NJKlRd5*QyZAE}{?5SHLrY{^!P)Wk+>9h}R~IQbsMaz!mPmqC4k`x|$urgdO^& z&ivc@hQuCfE=?*5wK+ey{jV(z?bG@4^5BAjH<3^}pz#O6ZwoIlpuS)U2Fig-Z8`p% z-ZEbpp8{VCU#IR@@aa^Lj@8L^D^(?!)`%76G62W%8gr4E4*WsQF753P%1x zoHyFEu2YIgMpb?;1Esf?Z!gh5`I2a=t*fel=}9na$CJ_tcR~fzhLY}oU~^by>Vs0P zsT~~1$${wWn$f9w5mC4yLTg_B2T`y=RjA1~qUDk{=fTbE_1ipkN12tlUdi+SzB|yk z2;*xaoQ)kmfM}g&i!YdPm&C#(w4UN(LO)0;qR*}>;D8fWa0oS=RHq|q4zVD*eXOF9 zb*EFaApEv^9NjH!;o&R}?y+Hk+|GQWuDY~y0UQh%mc^Q>zL7Q9inL1dSs~Gm=b-0no7clwB$N)Z29oSVkWG6idD%b zk)tU;c%3Rnm#dwOChNmXtfj2fqwzA9ZQ9y#qAK{IKoaL5jEW(60JNrf+9nspWQ~?3 z!ArdASh#*??u;ji7`_}>vIg@Y5n3Of%s*09K_M=o2AAAzzgkj2*v7;(PLGPg?IY)x z>tRHR!YgVoppyO+i+fU%j3UXphH8(&A5!tuZ$`pkm8?2Y8ExtYRED7+Jj;icSM`os zZ>)ElG7$JAR)Ng5^l#H1W4c(q%@oqV7mtB~8gw-q>$3f3>F8{@ zIXet4Du|NF?EOOu9@ocK(Z_fAwfOiHA!AkX!>G_N7Ztf)`w`jYQA^40Fn(}Pw$j33 zH#8VC#}Nc3rMG=4OMdZ{WRwvNuD)SPFV3P-kqSz;&~TZue`IyGr80zdI7O4toN`k* zP23TL3NpiLQB}EW7f*9qM7%-D^nf%_c-NQ~Vm96BR_(_@^!ykj1|{lp`oe4J%p#wa z!45NhMPC23s06X=H*###gih^)F@5%_dezz|e!fDp{esu81wGiL9W^ONmDVf?uG^G) zJLvtlF4g)NRv}dNaXUo^r3AOJzuXC31K)&_uaXCmYo=QqNkphX!L!9w%8suFTy!^T41_(ff_~frz5G^_!U_k_}ld%UWy8Ew&U5 z(ko9`=Vq8C#w~|#Mm3_+Gg;`La^*}b>UkAbx~L{RmELYYQJM_cQ|BVaExhnc(eDQ0 zgW$_?)iFIy7INUAQ|iL38`5RT@KPGq$PKpx- zU}Bc|+P$|Eahd%1;ok5#xgA_~v3zv>=o~hQ5SG#EyKN!+gAXDEm8RK(gJT;dAb`^lWm{de|%}b-+kcwzILlq-9vgdzGsf=R^H0Qwg zr<3qeI)3LWmvK3R&;8Q0QH=+Zhm&jByBhKs!AHWFIGALEgRA$(M?w?&`aw?@grguc zZLcO|ZP$c|A{!J-CrGLWDh;11&P#g~J z99Or-LUl`u8{G2=<0a7!tM4cDR+Frf!s2>M4P zT6I3xr#?ip08`|Q631}}ue@?djG%sv&SLkdeWX}Tbqs~)_Cd(o3+cKE z1A!bhTC~*yjF_KX_rd4~br(5i_FuPH73uEtqQ*r4motnU-!UyMcMA#AHl5oeka z9M`HDG#O-f-pcYE{QA6@B@H~QAH|i`Ll{t1$x~XmZx`?3wh<`RMCYRtriHG+a`$m& zco#a4mJk0ra-0l{f;BTq{(96tlCu!)An3`-NIom$=EmvN*-E@fQafK|zRle@_pl{w&bE5?Mf5GAsYlD-?h z-WEVgi{=Uz2TYrRVn)IsGZ-B=xke^J2V*R7O~4pn(CbvkG5nVs6fe3KdFMw1Wg)3a zXeF`MKVJAFD8zkQ!YV*fajs6J1XR6}j>x#beRNHzEtZKR(xc<%?t4du%aMKt_A(Mt z_nm*+MT(+*DIMlBSB_t}6faBCM`g%^eI8*radU2rUq+}yY5fCnG#!tqzME?#=nG6u zPpgDQQiT!c&^7c2q)^sx0fSttix18vF%}bLXgUmkBvl3!4$0CNAvT+-+4gISw)w~5 zy1J&uK*H6VJ@y6V(qcGlT(myoORWrOj{<4wCTZ#4Z%rH*%>@5bT;sui!m=`cB3O$L zufwP+eX6HY7Er=Cpz_>M)wgj%OOBHh1PcUM9X>eqZ3g_^Gg&$Ifh)KZ5kuvD0>LP6% z#4!i6K-KEk{zk52aq&{2OVXgt2yjNTzwU+l_(MEGj8PKG;;uD=>>x#J82#l_Rw7O9 zGQ$-viBfxCcWX!XP$Sz3QSC%+X)?Xnsrx-6<|3R-a2i=-x>8_t>{27%>hmlm5~M?4 z#~J!sL$h<<@yLNNzkQ_lpScR&FJD`Btn2G|#3`d}wFi3eg);^&I*5&fR20Ho?`jT6 zeA5({m^rhV{Zk2|NVfe#3IwqzIefb{1<*Lr!$BRkKm=$tMxc|#4ZE{bXkWOR`|wLb z4521mkKCvl^kBKBh!Y6;w+EeMA-Ro^x!9a)>Vkn1aVo&tw#sFc{zd8>)&$}~^h!zc zZ)tB)N(lly9**xmsT@N{?!$`w{D;PDuU{rl3H~Ml{8z+Kae&beSoc+J2dS$3{PYXC z8Vu<<#R~9iPW_Lm2OTP&5%ND^@rQBguOR!tszLx+q!Ix3XCjTJ4b|`g8qnA(hD;k! zU?nX*^b$M`k7&qUd#vqE2yvhZTrS{FMwYW9`^rBlJ`lkaN>VmpCJwQwTsMM(k-3ey zFSez_YFGz}ZYCM@6-dsBL4d(S{s0pt**{5C_PPDO>6z^c{wAuEDV=uz^;`5Ze13ov9MhexxLwUz$D*EPU1Ohh4csi0`c?ZahX!4bS9hUNl1jTi+7-S4$$s z1a^Y5KE9@}PC-u<)`1xaNN{Ju$!mbltr9;3x~-iCqNXlQir>iFjI{gyJ2)I)oEL@+ zlBatA{!xjR5R&g6j|uV{kqEl7p~W5~#{qAe-$k;|4Kqy4O&F<4gD(bIA%U|LQ$i~i zbO*Sq*h=_RWY&PN%xH*e7H#m+UAZc)pJJXw$x_u_cv29aZ#acmUU;37S?SJla~Zhx zO&~jkM7u#Fn)|m} z3UNX6qa%KZwJx?`#2gg1h|H6B4-hHB1GTct(T=+imrF>$A`_BPt^Bbh9E6I)^G6_S zalt6GL6o6>+P)Tqe5~~KSwuZihO&Yn;Qiuecd>aMTHC!^+@OG*umSE@;i4{DN+Gy{CefLuw;}A=b_iO=t`L zQD{^MP11@LSsM?yR}xhM!>MP(Nj$~Th}zeLImYO%>Y`U)UCI0a`=>g#p=h1aw9QX@ z3~w= z07RDSEv}TYQX|oJB_8hdJaC^CJ4;&xzm1w}R7TeghSea#;GoO2AxRvRfp|WelzrN1OnY`${E9*I2se{7;j7vTFs`E`c${3@AC@|@ zTW7+Rl0N!|B-~Pxnfgpqp56pgiSO@7OErVpuZmfA80x!KP@n?ypXc;Fl{5AWyMp=I zNyAk&j%9R|JhaAM(zKk0Y=wh@=pqj4IHVhypCKI?+rZBLJl}cWs~zsd0w4WzIWlK7 z@PL|ZxfvzU_M9I^cWWYv}PGrEcx#G6SBFr_vYoQ$}v$;C8J zY6Kw+5zA$o-n3=b&}%acU~~0kpd+A7yW9!_a9Ey)JKXB<5W9#zhgp~1;e`{HauHir z2~PeA#LD0*2BZ9RpFcWElsu?p!Z$W0@%41rZV@DmHF8R!_7!!g>>M97SSLnQY+ioV zAqLF-sxi&Z*jKS`WNg@xms)Hf!GcAhkdG}=hF2Iviz4-h;#d0z;k`7sR+!{HrXwmu zn%QLKscr{PSUcSAMoQhrhxWhI4sc6zII4Qq1MoBp|nIP zH7PO+3>ft~B(r={tkDJFjUX#!57(Mm+;=Z#MaygqtyzH#6Fsrku0L@#7$OwQqPj|K zgv{>BjrK%ywthFw_@|d&Snv@FQCl`!a;grl4w?o}U5o)$O3R>c5%VnekP8VH4ZY8K zKcT#jBoV2oB5SAfx@EM|L>5LCFsT5?7Fo)e#A#IBS30F~PIP}J&Lkkj0vu=itelEl zR0R+f5*(~5oCO6p{#OjMcO!7S)L#s0Wx20EmBa4N>#XaG7!3XJX9LJ9&WBGk%fEL577tNDc(=RJbK#U{Gzy8LE7E&f!=Y?{bQ7<6DgKJ*m*ohBGqs%Y~DIECE`Q zDYK1Ka$^3qq0~uS`XO=$+s$T^>K?5<`DfWt(SY(?yAY37B%+`v11|i8xu|n-je0z| z&?ttTG^SEPsI&j&j6()uw5PL9>b@;RRwdPBPqTdNL97>2q$VTsPE z6bYofFuhWp->ZzP{)bH1Cbhr$4{W7Hl~{gdNkwW2YHDThQB-pDbTpiT6U~x-`Sq`M z_gUzWSU5avUt4CZP@X3B8+4Iyox+;tWUO$NCL4$jh;O^QCAvby+yii6X!9IW(`*Qu zM={Pi{ZM$is#Cy5We>BaZr_QV^OuhWHKDkmKI(Sz!($BGT(P9=omGrxWqI&oBt1Go z3N-UQmWp(XYxECD^dbZuo9duu(r?kMzvG#vbt1wL5}Z zSV}AXhIT30pvnOyZyvYIf48~sa+qeq~whQV$lb-=FwBUAy)6q>-B zv`il!tOx{;%ciFuvfH1{*FKJ}$7o9jyxOxGy*++>z47-3-5=Xpc^y)ID0S;}z8tVV z9G~~vtyXtF zUWLAm>ufN79hM4I)K>A?rnLph@qMEj!xI6ns&j~UZ}lgpf~V{hkUxg%D}_CuNsar~j%CM< zuMFFlI5oOS_gWvv*N;bm>!~%41IaZ^Cp`isUiC-#c{wU{KMo);7e7Xs9yw+rFLHix zW{zoh%su^)JsHNv_V??R*(LCH!neys&g_`T$TDYrJ;LWA@Hze!5yNs~@dtb5D3js0 zsq*i^OWWuD;%*;5C1!#PUuumsH~fRG%Q`MiC(pL^ENwuJUvp>O>*pPcf3~33&JOSM z>ytVcHJ}V(`Oxd)%(|NnF1Myo2jAY_@7}_>s#+iL=znnv2R4CvKV1sG88(RurLq16~y?V$ss{r*_PK5DCMn@aAr zVs%1a=Ylca7s|Hv;+-#Tul}`6JYIdSDBbg}3gEI$v2RB85&q5CR{iu?72wR$xW3=J z5;}Q0yZU^B_0EfGuS;4ll=0je##;zp5K<`pLxCu6-};EaE^Y5#dl42imGiJ2pLKam ze{B`{H{&R(jwH{M0rzOCZ?a$h>OqFvMnTg4Wc>NE_H2BO^KZtf)iY_@jYruC>>&U&N~!^G|#`_u1!VP9q} zQ(jcv^&MNxe;3;qV_np7D{iaU4Fk?^Qn~b-x$u0sp3^qLcx}cTpswsoKeex%^AJKU zi=tno$3e;T?F{pL9nfbWbg-jaUAQ+rN`Kou|NdxFy4zD{`6=AB^|-oi+ zu*t(xG9`OB<&#nFZFI<=WcwFCb}W!St$VU8+9%fhZ>)JD&95-XE?SRvgY+xYB$H0| zO%JoD;=zGO1H{MvYv(Bl&=_-hXVbDak;9G-CI{gwmW~Sc-t_mvtxm0;alY1`t;fgT zrpFe}e(vTSeDe~9=MMjJ+buQ}|Z82&MlT2(a!H{u?YUQ=og_$5%HO+(_$y zbYdHR;IzNSAKP?meL_GSw5@vU*tk1@BVV@$WL>&G{b{l0v8`iDcwdA*!@IP@vrE%c z$%kY{y^Z)?D8RpUj?ekfxdSJTk2={3&hz~PQV}c#PPsLPuG8PO-c%9jZ4)GXym>{V zr=J5lm~3L;lbnP0hA&8bR_;bR-pX37xv+xL{yn-hr7)ATIP{6w$=hC=xF7S|DZ{?J z5{g7W++X=^#dZL)DpvZduD(2Lv*BAQi?V834dFd=b~3+FJ3y0753Pj_u9O6WRt ze1{CbSY3<{KgQ24qV|X=!g#Xs5GQuL%+GU0e4Sru+;2HBLCNU9LEbkXuhvGwIGJjd*wX_+UKv87iUDd zmr$#Q*@Qodh5<1*5cFCP4>W`u-{K~0}01>aI!ur!rclv1*T^>4_ctA zWq%&$=L`GZ@qCU8)_u5J>de_iP5nn2;S{+IEKauRAz_A$Z+;w1S*s?a1FQI5koIQx zPo3H$1N_qm2HvCDG3Nb-r4G+qfMl1`9Xv7UU$;*s{olzD##`^_L`21y0}_YXjo^9K zHR&kU@?>Uh(8(~!F@M_F(Hbokh@hv|0qf$X0K-aQ^oo9TO-A_rjpWaS-IroEik_rtTfuIMAs==lIe zS(8z}V|78*W+LPH=6TTvsh>XeVDBdU^0qiMHb)_|K6-j2KypXEbp6(V`=*de6K_FY zx7YWeR@iV`ehzF+Za1qRc!<_~UH+z4p9bdeZpX(f%dy9AwJ$^8Q<;#EW{OgQ>;D70 zKt#XZPEUV%cCfp*xwyObUhTc#)gM;lvuEn-Yv673aJ&Eb>A&5Y zeSh@ywR`^TwS76WIy>dc@)K13gGuy6uOG}DzV_=YbDQhWXlrTd{oK1W`|#tGIl-w69}cG0HlM70-JhLZdVlhHd1vAI z`j`B8c|Gy^*Y%}cI^OZLw0roHUzOSS&tUD{=Ck$X?W2BL-+R3H>SS}_)2EfMs}H{{ zzu9@W@xeTKzCr7|GoMa2^YZ+sxmV`!Xy)s&Uwrc9U?zNZ3yU*TC;QJ{JqvS(ACA`! zk5_UHNz-#%Z{o<2O?rLBFv8a~9dc1%yp zv#;)8eYGs^&3x6Hb7jTy)bzWPt@Sv&32QU-^h^4%GiAO$o>_eE^y@F1&hXQ@xev>$ zi~X6kFUt_#&K|$ogQ@LTAD=A0e}A%meAr*~Z|mpj&ADe^cHT^doy{++pZUq2){E15 z>+OqYCtI(+xZ~~V=X&`Iz1^N(e73NBd@!~7@zv|uhr7EoQBTvC=~wTU54P&d@@{W& zZ;E%n%3FS(Kbb2#pSBmCE$wVfFTB`4Sv_8vvhyBiUeVUW1TUw&uXf>hao*Ln=lSF6 z_LIHUxV}Q_;riU0o!Ob4S+le!Z=u%@mv+O_)RUF@{j`%_&Tr4oA51;{^cf${E^aXTQBwT>dryk-+2FcO|3ts=TrG;ZEyAA zRR84d=jr@;p5H8%@?n4W%X2?-1Z(RHPo7V?^~dQ8zn+?1dN{Q)RhPqy=hJ%r)w7L< z(<@I`K0p7s^7Y{5-1JtmOZ$6Yj-K<@%$p});>sL;;Hiz77sp@hdf7T&U!2-HcsKp- z+1l#7_8%Xv!Qy=0-23wQ_1=DcUgvh6s{Zs|o__yjd-nC$)swes!#-A@Hqzn7#_z|a^P{@^79X!Yi)N{Oc(wB3&8wHI8y`+g{Q?*_&ELQq&}Qonw~XBJNfAC34Mu=)6B+~ zxpV}JQ}N-!*5TCR^PLZdH~SS{ygNF94==X*Gwzh9n8>4*>C?LS_psrpdwzFghkTVD;2k9PJxP9N&w$iM++a?>u>u1<cjd!2?O#u))640d<70Y#^5ot6^1;$d_&{6hk2eqc%b~xu z{sH68+u7MSI~y;`!NTQeO9NP_ca|?BjKWv@Ihk0l>^24uQ*V&onryCy+ z=P|yUb1x0bMVeiZMyA)r*`_uyAyr9{4k%a ztgoBb{V!X6S_OFbV(y{SU)|iNxrdQIEn5F&owuLD%G0Bbb}9Pr=O4e@9j-oHd%C?0F3#`o;riE&Co^QXW-@$rbgUoF zue@A4ezUQW=Vmx=E!Kze&Exv{;ArE;I=p=HaV8zEPs8hX{o~ko<EV1mewttGog7|Gg%HQ_J1NfqFTMp{tvz3M(r!M>cRJbHywsTKl1{#orE>0B z?p?`gkhg5Uvyl7mOBpGDSbN!}I!k1^G@l0}54qy$BMx__hUF++@_);i2a8XBH$|?HaYo~p_qj*_W+?-)KGQ09>s$t8cT#=XdW}55R!`_Sx*lRy&D_({;q%>_HKSxK~WiV2?|$Qd7TdKQc5-{wV3fapX0hd4 z0@q)cp8Fq$V+~2?`q_89INHF(3{$>m0v4(k-731$MpYAucGTzsfm1oMAs8-TQ*fjW zCKaj;)-Z_r3}lscltgZpnnjx?b)$fFAsdyv3Kk%vEzwn9qBb=sQ&|oT445N2td%Gz z3_(?Nx_GTDTqDz)z^MMim_-MpI<8)*rU$T11QHEBX8X#_J;z_}fyjf@`n#C^)5|nw^M5+%tr2kL`d&QypG&j9)w$hf zuD4B!ze^JQkCWWTpU&R;6lr<>?@%ZAr_9yqDc`|loqh`UFOzNvgOk~xQ$*6&4dJ%h-uAOA=N+W(dWGpqX>|NUMWdqrz z>|IqV>bAGzl=9w{RGra$bGL3JvYCR5!h}SsRn3Y7xun)fDcKQZ5aSF~ImR4)4jHK! zNQ9VM#db$kh-5Ll;gxpJ$l#%%Oj?cx@I}7n>aq?2a<=RVtd`l=T7{NSOkyR>K`zd4 zG{TA>n%9318Cq(SBI~au6W*l<-yqsyP(J-2Z*6tuuR0KP#Sniz5FpUcgTRBOsC4oO znQiNZYHfDeXwVJ-Zf>Q)!J@X=3k^^iR|NrNbgo)#IEzz$GYBXt9jX#vJ!$VNi9a7* z4n`xVn!GMpJQ_a|1mH9h5GzMkxKT>ZK35iL6I}~ph}mRU1pu)CgiN`0;WMdbL4t;4 zCkh0{G2^vt)_aw^`c(FJ$`9m$E~ zZeke40u3P;W?#j!7q%MM5wMmic`~h7nS7(=MvDTd*9Qw zVj}m#!SmVWm$Qr0Q!BF_5IP`qK=}25;Po9^a_baH0dng{p_#68*`Q$$rH1`V~@bglJ> z{elQss@H~BNBJHKC$Cu4 z4@K{MC|rDvRv-#x(mG~1W#zLPuS!^4dfs87lL)^R7A8a@c)UyRB#zZsakgW%$+d-l zCk8n(S)hP4q~+-u33L_vU$7J;Lm49lm#L|XP!z{uJ-fg4PO{>-Tc_kuCyss8jx+;@ z`v2KGv*tE#rr+PcpMsTJR~`dsG`f+iBgwmETamn|^BG{~t5RjL64b5DJ5)S8qc z-W$~p(7*@kZAL;&+2>|kqZH?@+o}pNboN|z;%lxKtynX#V2}tu6bt51BA|gQwBhEW zZnZ6@bvSTVml}dP{R#`yY@M{XdlF&mv9{2B-&ROdo9YEq#Y4(2P^;dSsJ0(M%-GBNL^X?Gq>(7f$96FKY8w|%`QJX(L#;Vy8>6eix=Bkx$22g(TCcu-kN#J! z5u+_NdUhtTx74PrA*pW10NU3SgXut%9%(-5f*HQ|rD@&_S{hY&Ks4jGw zbGyu3FKf(ytm;DkTqEC0*yJ9<*)n?pV?u)fzG5~psS(5C&fU~sY%&H3Nsl(A1vKCS zwjR3E^myhdJjFu~#0iQ~E8N&&?EAgBs&lSBbnfv#OzKk8R1aOfZLw;I9a|Prb1vZ3 z+=f_7D#?Lf5NL)`5XgiQL&WG(jD~7+Y3@RQda@W@Ctd8a1%_5GYoq^-)rAHFJs9ZY zYd6_I;}y!kHS2uO_VhWcfyYWQsKv$*8#!u|YW18LXf`U%IikU$?z49x2Pi~XB^T?a zq;Hu7o=XDGxwPKPf&qy$QDPT>0_GaCdce~Rw9S<&Wy&FnMRoZ@E6rqdCaE7IUeZA8kAt0j*akB_nCa=~Hjif71DozE zT_JE1ysg$o6HKp}K%NsB)^Bu0^=u1FSg<}>+ej`8NX2d!*$KAjVrUe>K~noTC2%s8 zY_X?9*^GVjGg8s{S1=)DtHU+li~CG2+;CE*lf9Z@5Myii`fJJ|*;)XY4OG4QDx9YqoenrS5K1J87AV{;?#4p#ZpqRm(4__C|N0AH4Sb6IghFtK_106s&)JV`_(JIZDDw6XK#0B@2gH_6R+o&OYje_fSPfj z4dLuYCHBV+&MuC-%W-KYi{s{|@qKX|M$Y}><24q?zj0x_2chj+Hz`1Q2Ha3=MDIAz zw{xSKgi$?wNm&Vko~`R8G>cQyt&Z`}+kpPNZ3V}gQ7^z3~OUSjQaAF4tpsmKV#)>o3~ z`YtZzNReA#DEITW{$hNLK(mdje6;p_X=U;){THj^r;XUaj)5HmJH9$@5)?ITZBD4UCkD?3BpM;7Sgt5`m&P zwfr@iLb=EE6w5(p5_-OEot*f4%#KvSId5{dRS*lb9%)~gIQE9KY3yY;C5eyL7X&UY zQPQ^pAF|4^8ofr{+5{zGhQE$E^^4>|y6un`UBB`ixH}>oz!zm=BBvxPTiCqux^tMrkL>LfU zR=*#biUGyUG$gcQYqZWYhD4?{*W+sXx>F4yi?9I~Q5hMWFXABkYMfQ^0;!?-(=mLbMz`x|uFR?QF^ri9fJlV8Qpvf-6*lCh|=x#}KLnQ`J_wTB9M;3$97HI5Eq+ zX0Syz%?2-qeN~@7J7q{^4`#xFCoJILqbiYtIhXF@$0{aS(^NHh%P53i=TfJ#z_10< zVLWBSEg+HFyK1?|ewGw!)7h&EaDD$fWfX~`yoF& zz`=ln0SDhB4&qh1g;^=!sak;AToYdt2sU*jeMyog1#F(+Am^5l8b;qpII+~i4TvoJ z0&Pl>x7zoJ`>`Q=lTd(zk|^e^Ot7YhGoiKUr$^s6NUB{VVG2&|JJg&brAD!^Ehc&P zE_x2=Y-2-!DRwaWy-xZ9Q zYzwMQzBn69QUg~q7j?p|5l9Go9`64hFk%kgdS8n2xczSbWB=1zTK>n9%D~^Zcedqs zmz_rlg{(pyDE;=^@!i=fxsSn1)%3##`bM_?$2$2lnWXW=}^b{0O z2s(G^`{Pb0jMbQ$V`(+2;OG*hFUVym9(}fI#azOm5KGb5c?)562-G`K&r)#QU%9uu z(iO+8<>dhf!?wf^gM)e8mT*_uuomjooGX~-z=z&VKH9=jR7wQn>V(Yb6Zui1v!?s? zV$ijayku5iztf^PKP8s6`j*h7+FK>7^awh8uIibFW*wQX8>&s-Pit9q!ZK;a&k6r0wOJX1Za0|8d`$7|1Y? zVIafzOa@1I6*A<$Nst0%%*?(U=qRZn`P{vEym-U9GKm$8r56)zkt?dH#|(_7cymER z`Y{=-x5Y@7QmZ3?+*=Ua)|*~3s|Luydkij2E_-)O287^k%&n=^AgVfm&%Hc|QSlIy z60Qd_#iHVb1GH3|Apv#t-9rw6Zqng`xP-WzOv~bG@up|6z(B zsrey>t85fN;=ietbc(={5)U$REs+ONxc1=JMbP!DNv-v{^o`*q)Y zAFeATDXYU?DXY`Wsl=3;SgNiRs*)8V?&5`=lw2{~yT%8EY?G-@e2?s;mMn!S3p>Wz zSR7Pl7Zn%coAgGHSQ__c@CK9b1u!O$a8fHzv8LX-j+C8AQnMjzl^)fW7K^yrlzm)H zvxbZwlMP0jdYuf2QD-=3xQHf_UWb`_@b@1*0ypO#`~Zdl4BtH%(AcZA3quF3HOr=C z=X}5(9};cvrXuR3U)+7Zc(8p1sH9{IJJkYC;gp=D44%{EG=ec0#Jlmnb7>{!*ehS; zk|`)zY}3*D`ag*J{Zn^?Yi+D&?6{UdC_#YxNwham~%!<7gFt< zfuR%M<7_k3+PmGuvbjApk{qH&e=N~3CsCt(uBG@Ta8QcxHL??!)~i9NkyqR9Be7Gr z-G&Ly`bk@1=_B0&plXThwt1j2reQ;%FyWwUZozAY_Boh*dR0 zX+ba*ph}bNDtn`~)#bkh5NjW{ z(zagov4I|q4Rk;CV;jA%r$736boYD-f=SoF$f&eY^CRF<;Zc)OZX>`^kiOznc@)wp z^2p|qO(VSAHTbLft&KnIKF9MO>=dBH4iu#t{NmgX6Q#t)TlRE?7Hy5A7&Xwbq7#>a zYS#gBDD_I6{enkA|DrmSSiTdg7POfl_a9TqlJ#eNIc}YK$YnYK>K3 zKp?JBa&H9JOH)YW6nvfDl`Zt*7%BH`W&;IzylsOC(AyH)yH| z8pL91n1X{)q$FQL>Umu;I|+s$wVIOX2xi*$v(tAB&T#OqX_Kx*KPHyLtpX^J`kXDAhF9B~}e8XVLZJ$kOx3$CKi0zy-_pM@o=4)HEq5mIR& zwU?nVoRV=~NkGnGHK8Q1G}a%v_LSwM3IVEvIl}>3bbiKRs{4CKWpiVI!2p8+2HzbF zqMqNk!yqIz#K{}Xy_<0j$#NC7@f8{hmWyjqK#3NjV#IUbd9LU%F=j$EmGd+hC@Um2 zs%pa3P+JH|rHGEE9O{*lyr^7BmZmNhg;q^11T~9Vx}KyK)Yzf#&6XRtEWX902q@lp zn=^LU1qoPP>XEVYmdxgiQC8DTVeszl{s4m^Y2inM!5k(npqkZhCxZnkEkSaOR$YDW z`?tLms4twk;l)V{(Tj0`$t1*-)YLa(grY8~H_^%zG9=&FV=uX3B34$*ZBVQ?hCGR= zac8m3O)ALog$&s>g;8*^T9WVw|%QoKl-9q#pcipdy}4Da;5J-tGbBu zCKJ51A%-y1Rio#(SJ&=8c|7N67~+!VFfPg1o0hxOppl8cxbWk|CG}JQHPPSBi44#J z#cW-T)nM)AkksKUMx15K6T*v*Ibj7Qk?ZxQ3-@u0)T*i$0uL=-kVbcmjiLg?`v~G&aVZw8{auMXVu7S4GAVqpB_A-Frh)=> zrYfEUlOe<~Z`e>7=m4%BqjX7Ztbc5q2Z!?=!#a>-4$094M>rN7_zUH`6*%IJF@z8CU?R8vS`7#e4q+H|Md0!G)UrAd@2#q z8mzUYRDh|f^tf1ZF!|b7#~R0;KIafsx+rj@`fPKX5(mo>)$FI-v)hs>V4X7|N7Xwj z9*AP$#QN9N!mY+&Kg?xP#F46;M3ha$RIos>* z{kNSB=5Ghb;;pripM^(3XvQKe>8Z-9hsYz+==_9evNgu;|HC=^>Qqvv3a4GM-L zK3VnqB{74F5*0DghpC(1+(V+!LdJ#}QB{IBo}CCo%+`Cv>|6|@;>6mLW3s8UEQaW< z6Fq~u0)CxRnmlZ@;D8BiGlPey{nr-)w)>Pi?N%!?5x+r?`MmD^Cu7 zvGBv-!k{Cz_6jjl#YKt|1j#^DL&})Fy5ek2bLyf`+3Z#{PNe z|9x_QxKAyEOv(CUAqR*kQI&xT3l?^U$Yl&gT=X;3LVz9!YfPwB9sA00%C>~0;4v`9 zYDx|3rC^a5(}{k9#t>c8%#hYG8Do2;5cxQ)T#QfiOFRj0YgoNZF~EeMD4 z8qKIDHj!Icdu#_73<=pk6b$AvA^Tigm`ji$HR}xYP}R!T^iqBruK0@xy8txvs+v>JZ(;+=6|r|&r^Nla*< z|M!A`9+d^Ec1#d50TdXdyyKX$2SV8fo*0Zv(6IG{j5+Hf7sll}X51*^#r?;R9t~g^ zRKgDhhB;9Q(On@2B7^#mrY2LtG$M8xdn383X1bt_7q^gei9;{9r&=LZ)eA_Cuoes^ z=16f03wmN+X`E~_C3)9%g}2nJM5*oW9jtRhqd%vzph=ZpcEejJrX|b)DsqLGTC1jo z0$EhK*qAB1#({jakaDXOQ?zcrSU`QVKZI)}T~BHux3+SBY5D$tZY@vS3>auI&|sj! ze}M)*USZA1htMoG(7#PU%wQy{A1zc{Q8HYt3~WWqn0z4#A)*>_6$-uMGpLgeFogy^ z<&Ge#q7Y}$+on!_3f|dTdUGEeO5*0i)FOUXRY5eNpl`O4vZ&W+YCS8@3xiOJ&7=2< z2;x?eC{*2+igwB9^{)r)n948z0F&Zd(KyNHUp-kKheB0!s zQ5zot#h_aDR$&5_PFL~k+v#56)r>w$MItL(`m%WyXfcwgPRRNz*MNI=9T;MEwRzX~ z;pYtvdbwR^|II#&4P!gv$d9r2y6nLa6C)eh{x>Ud<-z#^d7%CO^ zWW8E-_8g}+f*>{gp#U|b5JL$b^#pt=CYV%IO#}fBs%RSNX^<+BK)tngFfLUtao+j@ z`EfmwYZTJBDDY@)WkAD#hHp4YKPqVy;<=OL{|XxD7k{PHgX^Q=pIA&9D+U|v6}A?* zIFpPOU!$)rSP^F%_TU|kEnZyD6)Q9O#KqdGf7H~ARd3yh>y;>`Z7n2QSU`e^_qF7} zguSsuZ!6Tilp-7pFi#F)I;rO>F(Fx?+=6qdG3bcC_L6^ATrv8gtDq?*5qgzEp)?mD zB?BU{P@T7}1&_f6YiHUKVsAgm_L~6;0~7|m@ZIZ$D{M0~Q$nkO!Ny+a8#xY(X0yJ((C?lJgd*N&ShdS4jGRcm~;7cwAh}ASFXCM|w z$+aAUtu+~$QXZ+45PFB23f>0x&LmrH^u1<<6%&do z_BDg_0^h13tdB0Lh^WmnDR?RXVkM|f70R5E!8t_ov)x&!Z^~}jKPq7eP#xefz~Q@x z!xi$_nkD0UZ=4W%P112ix*AROPp2|Uw%-I@}DMeO`A^@A| zg}XxoJ;qw&eYRFyPi^bbNSs}mkT&rJ4hdW8&Eiz0aEvhpE=B*9Ga6QEST&B8lS&aq zPfXFUO{`kNmXs2N@|PC&Uk3;80$igQ(iAuhY#7)uu;Dvr17E4PPfdX+1Vha+^y){9 zImXaXe5hu^9`Xqr)B`uJAtdKq#TKIX)vFF6C_M&t3LE-Hg6rLUq<~KiZB^c#tB!IlGU(0T z((cBv+c0cB|4?X{kFDou&egiPOpvOR(yK=F>O@$h*vu)ZJtbCSU-HHEFVsbEvA8PM zLC9Er=p7GiRjENnE~sAdsi1a|SZlE+fZFzy)l_HoWq~6&+bT6Ar_Y;dG~ndNI|iy& zkRXd1|J2auj8@p852ZKc(W_FEzLYB)>nupNUf)|MJ8v|IDkA-CGoD}EfBbay+1k@V zDGW;Chr+=eDg{q;1vUVs-i!ytG{9JZEDKO{!8R(TUA+11#tK0~?6%$a?dM*i$9?s;Ns7{V$llrA)#Jm`!ZT zhn~m4AXQH#7mP8bRD(3%6Mq#I^HxDMT1zmKl;=}ns>_RdL;pIUVNeP`6dLA3Dd^Y! z3f&cwjV!gAMv!u)Qq|e^BE9uxwyjKv2Rf#Ky5d~2b%atfV|6~HDBU|xAjt;_1shhMDgN+qh@qMCZ^zn zo)|h*F=JD;lxwMW-gcWYSRy~$s;R5HW%XcHwssCK-K1?G!s&Azh%gZ0Z?2t6S0I9# z<_ze9v1-PR&6$W&DHhcKF3w+E5(&9^A0apL>X#>up$7s{R+DRt#3{k4`YNB3?~6PZ zTL>Xm$rMy?aL+jN5=(EhFty)~6ztg%1OiBf3R51nP8HSX^>0w9DDF874Jgz#`h)(&3fO9R!@mWW$RlkYLuf0sotmmpRLmHwP;oJ#XNNAUvG47FLN%UhZpC|jBKqj6&XO>@Sfq+cB{rV3BsQgi zN3uB6)uMNjH+BXV3@jK}@ZGV1V!T2^vt(PiR%fLr7bni@Y9|WW1}xH-*e_HDu7a9w zKo08C_bv?T)TfeC0k8%qcTW2hW7PAe)n>cmvE?8tX+j1{HRx}#Ve<}A{rRaZ2sQ_W zMhq@H%naaLZ?RAS0VVZ`*l~Hh%*nG``HRI0Zae<-gn&dEEhEhX+F&O}WD}lb0 zLsMmf*^q;fiUT7kuK8dr+v(v(y+u!@8hx=PvLVBeGX#m*7bwc*&n;LrPFY7s*%L72Tw#BIKC zKwi;~akgtn`{hmA-j>ZF{B)2CKNJq;Q7V{o1u-wdhLF_buSTqQ3{{a(Irfy-W=v2% zT)c?ngbS+9kV4Egnx+HQh^jT%_4Y1P*gy^qD^Og8gefLdQQ6iL%`n(tQ!2?MGqt)! zt(a1>MhszdCf3M~3MsH$s=WeuwN^(|^;C1rn2MD~8O3C5*48o z*gY78!a#R)`H!$|TvfCXy3U$$|WM*PubTxVlA-DWblh*Rb}^CZ}{k zV!B+aS9{5^@`_CDHtm!Fo35A``AfhCGmA#!E=xN5Nm)XbxofeEeT5 zAsPI|@xOBkiGsgMUrylQeN}9VBwHk72sYbPOjI}AU!1bm_iTwxg3y~b=TurEMM%oA z*$gx#B-NsWP{7A57A!?ml_h-0b9}x094Lb@{7J225_%!-TFxCqU;&Wc`I&x1*|x!#9#&S2m@LkzPmb@T}ayL%g38wVRlvOi#9*je~d zvG8x+S%~NA7F;G%K{y3!YBh^MbuLA*)pAxZzkmxD?<`R2ku6vYRdxAOVGC;Ay(_&! zDcUJ@Ef{LACvG8o|_Ui4&&j%U|%SS(KyXJXW zK4QGW@{!FYiC5p+#|EZgiwX&44J21Ve}5qwVu@g=L=2?*A_mu>2I(=biDOQa6Ouo5 z=Csiip{&-wVDur|N(9bhbsUlvjIN>Osc5Ly1E|p}F>@YQSXDBT6w4JrcwFj2jbX+} z=o}}N5T>`_(bENEi02LsrgzSmX_1S&EB}o~Y@Z+hzb^y)bn<;$*jhU(|B?5nZ$3SZ zJ^hT%e0V?Ui^}0a%A0cT6u$)E?td~m`)7tc^^RY?;`0ms_w?Iu^pDjV3a5Yh>9_v0 zGxvEced7O~ey^nuYFZ`j?`$vVz4qwSz4_C`3$Z^Q_NTRdkM3`m z3toNh<2e1YgS6K#twryM*_%`M(D=4a_3Y`Kp}BrEDm-^K#-9*E==GN4xiISEYk1DL znfCtU7aZ=M*hoYEk`BgKQ*u3_H;_B$c7K|ZJ@=jZrYXTyrt~Q-71*fNpFFGcv;C%7 znQP37g43%tc~_C z$*lg1ce}IauF1)){`tGTyP%<`Yg{m;a}4_LPw85$xyEP5rU!sr;DYjBImYqgS*iaq zdnP-_)9-Pe(1kPd&ixHu0{z7&7wyF}0=&AMxp+p?Z|fdm9HI ze&3WM+5F^SeSMFeea+{E!B49wUyrxDw{f(wDeLDJ0M1^)K0nzndmFpQt5QF$%I;<= zGD@S}y`3Wa`=jyOjXs^*xfLbZ?&}`?>bA7GFXx^=ndO=+?DvI$mA#FvwD;j@A8qAi zmQ$CZ{%XR@JLTKHnvAwvbD4KVcI+o_70~_vk&)ZJjiUOw^08$+&?&;*XYl~ zorAPLYCC&hF7S0O>izA~7hUAnKJJVrTnTu~td-+|W;#Fp_UKnf{i@Oao3tnOGREn0vppGjHq%{9 zO8vv#V^iAARNdIqiRvdN*O_b|_i#oQbXE3sJ~Hz;9*;YlNv^#)JQ!8Q(<#GrEt{m{ z_2FhSyIYbE`xkq>7k-^&xz6ks`T#TXi+1Wp`=IyS-d5k+ z8_D+3#@^2MR{u&LrOm_Xci!cQbNkDfGx|1v*iEW54)#*_Eo7uNy}a$=7uT>lW7sx3i_+obg0QeQUG7?i$zj*>)dnMkN2_1dWb%HV?OC z)D`G~&P08pk?N1P*SZHjdEU>*BhLutTZ*0io$XJ6ESnqsW=*YM((e|dTTitJzB zW2V4d-`vS*b7q*lJ05PvKGGL0|88gR)9oq;Gw_mLd>U;{W@H9VdC1+V9ev!{zK)yJ zL%v??5HtO}_djgqoz0Ch>fu$_|Gd|Zhg;UsE}G}5fBnDH15OFi=!;PwcE|5@aJ1R#{N}T@v#P|FBZpT3$ zU#*`_+SfY?Zuz_dHaV!}^a0aCOU6&0tJHmNkLv5!{l2xMqT_SC&&nqUWpt-|dcXa4 zDi&s;^b`nvKFe>vt$%Er2ZvkZK&NwOe|&XLo%{OU&f(WlmM6Eur=R`r|NWo;{h$Bo zFZld)ESE_ZAix9=tU#x9?Tn+dGNE`DtUj%KQEzIZ~6m z|Fdk@@!IOoC*gBH?e84!mD6FcKmDK6;h@K>sGi`|`u9%?i!(7($8#whYcbyZ>;TQr zzGD12CrLlY)|^0y7Q*Sml%LP=WL~GAyC|~l%k`DA|MetJ9b*4uCmwQiZsw_+*n@5Y z&oJpHrIf7q^~Ya62mYOYx+aCM99(n`pRP)4NrGefIXDPEdn}??#rdBJ(27aT+4$9< zYJtbcrA^uUYPc`A=%hD0xtO0%Tcx>Td(s_Huo<1} zpPQGMgNQXz3gKj1yY+diVZy~5FCczeP~O_u-q=@PSw-M!08I|ER7<<^+iwS3yJq}{ z+vk7SuRB!#bR4>AdoiUj@otzF#H#oHZV&RSzaQT>XWYo2Huiu25(#(`|9Adnmx`Ai zM=kG-@5SFw4rnqSdjF@hp|#_$-S7FZwNDpN7VaHvKeIdc?mc_kMjJ*?%}f+OUr)$ z#pdl-M|XbZhYx-YkC&d_USL_h1!n*L4y@F*y^ZA!VxX0`JFuMZy>^dq>&D9``QFye zdh_K?{loBt{#bf_^f8rti@Phkn~$qrk+=1awff=ULm+?W0Aqbj_SSBWM<@^9@baVE z_x{*ly5rv6Z_hVZAMW22!_fM{(&4XheeLeX`UBitS$TE$Wx0Lhj|Kk`7OH)Hu<&f-*0T?Lw{Ne! z`uOMS_Wg(J@9WLg^+M|()>n4$-FC#4ox{iUL~g%&V4uC*c(A^@b+ljBcW*8~`M7ca z?c22vPj9?meZKwj&1-+_;Tv4vS$g|%qpse2d-sVyJX-qjE-v4?b+D8^@crebg^zm= zo;*l*4`07~c6hWV?>3GeJiouP|910Fevp^_pN}5wKV4fYx8LvG-Di05@R^JF`pyn+ z?zyMwbuQ1|;T?JKfe+T7%JS~g2e)xo)&eaozWlhko^Nm1XG?hJeR;jT;6L14T7Jmx z*Y_Lj>CWA|uUDTg?=L-jziQKq+wY$2+J&trf81Jq_3GpLyTkqE_@e!}xN-Nv`|alo zX?x@S(?99fu5-(ab@Roe2Ol?|yytgYix1uEdwj9AxcuP$>brx5jX$3Jdi%!C&Qf-Z z_udkuGv3~dY_U)zZ+kRzN-@>gQuI!|hg(7@~)0+kV zc<=twZJ%zu*?aVIe*yknxOe~Hg&Kj|EPp(qm(M}o+fQUzv~~O4()u!QK6dY( zZXdM0H?M9!GwV0;;X*xnw)^zP!v4n>e=gQP@6q#Rk=J{--#?5?NA}tJ{aX(gc>QL1 zProkQUb(UGW}&U7M-La>y(bUe+*n+@v-ao1Kh{1RJifcQS;ETR?)#&Mw7K;B*89A6 z7hcoCo25tZ-iLMBe7C;5uzB!u@#TYOPw%<-$Bk!p`Ci@FeSh=U-M#j(-QB)p_7`{S z;;Z*tw}1Wc^y3TjCfqb{-;~2QZyvl_J!~JgZfyAH4nCfkpFes0^v&y!zW%Xtlkn-n%XD+;d3$p75?0rrFW~9=-U8@AJ*F^ydBDa%7hm@{NPd!-eIC+pj%s>^J-9<#tT9N;#s# zmwPwYaiQICJCC34?XEvfH;=Y=|5!ZQU0!(i$gi(1t{mNe-Cuvv|Bn}*l(?fGw(tMC zvsfN4Zohknzka;+a((q+Wi7qN&Gnlb2m7mOe{=mcbc`1JDbrJASZ z{Jd0P<*~)%3U=}7{&XiQWKRy0-RIf!ik?Ke?fPZnvSeyoDePp5%j0CMwzgLn&ZBR$DuraN9Y}D!wfxZb z>h}7)D=64_vs2K{wdl9e?f?c2U%B)2-yZ#N8>(L8@&3g$@KWK!WoSR$xx%H9-Myye z8bG-Cba(w&!Uggce=)`SE%qGF<*>}~cs5cd=lQtM)cH!g=0wra%G#m&G9c{EpUD`|Ulhl^i%J2jZ%gq2?DY+OVUjn}w7|@P**mijY0=(!h0` z$0-A-yZhC6XR!0;RMGFpw zmMVMaG%XX>Z|szht~KD5_S23KnePdJh^pR~BPXYr*m>|5T|3d4 zF24br%n3h6@R{#qo?F3pDHzGOQdp82b7aI3Bwo1Rq6=g3W5Ok=1<3Rbz3h9~@WL%G zOc|A2H?eVkYyd|uAg67nSNlCxhF={TFFeu0w$AXXec)pKr31;&B+s= zn7!4-D7aLXwhZ*>F#1`=@4pG%@7}kP#qm@8n_bVjzOZ!!b zX+Tf?-hffg)j4#3U@1?PRjgYl50{DNSppvvKvwobTm1UVJiYGzm3_Ym-EsFJTkz2V z0)yvT^)0GXATU@K;kjEhYt?~OGe_|rf%52Ai6c>i8*AUV!U9BK zILKOpN6@l=`%d5PxGdHD#`ER9bQmcV^y$~r5J-!|*YZy?o;zEWna^Y2+!M0^K-1|J zAsb#dIhTFQU|G8EB;ur${64$L*VdxTMH(6z(ZHIQd!XLHx=dT-0iZQTz?AGcP%l)9B#uTvKe!~%Sf>1K`&RVJ8p`SP-#Yn*nP#@cVaC4VAOX;=PM#; zc0IOw5k_<&AViMfQ6S&ZqK=Of9kT3Oy;7CWZw6gd-{pEV@-+S~RJm~nTrySNx`RARxo}yEg_FW8U z)9ly9R6iC4%TltTDj1rV`|LAE*+)bue? zIAhLw>r%2F89tKPnXM4;}CmXBtj_;RDHs>LVFu0Bny6xMB$=ZM}lGc^ri zzA~;#IU-c;*(jDUF~0wMkm%fKhugT(hpUG4Dp@T9FK#2_KZF)yY(9#&$b!j7C*a(g zTo6MZ`-Cb)CWCuTpfaZ~u2SDsmp$X4y}i;RGu@2T*C%I5qPjkQS>>ZC-6CGx&#R2} zbB~UtFR}o3T8a!Ca9?hp%2Bn$!HX}=kd{!YmBAv73tV@q*Z?9u9n|=Qp-D=c@!(m^ zb298=0GZA96}0`+OD@2OnpT4!CKEk@A|IcW=~KlbzQ9&bhh{{z0h?0ifA?tXTfaZN zg0uk}+-5hBueH-`OV3q zdxiHOcc0~ZYa0X9ZczSgy5J6<`6c#-d4@cj*L|!`h#3ZIvzWM;7MI6*N+0?gN{1># zG{TOoL0Y`+%_0()jdz9}6jun6AT6X0`fecA_FMZmDIlV^WA1iF?=TL#t-v>=#;djX zX}$A^tesRu;I__4nzg*7nwV20*2$WiV_m;?>a5c8YH?z$5wi(;1vbt^C4TKv%|0yJvCxnb+=@LwD04fM_W*Bgo9m+$JWMY?1Tk_kLLhCozIx^Oe0B@T7x5z(t`%ptdd6x8Y zE2f40ED`dH*(=1d0+AbcGwq_QVUH*oV2FP8ytC{n%iGW>j42W>O%{= z1lZj+^^=#`hUbE?R(RB^LK3c=K3C7o%>`Qlj@0J^-?9UEMc=EhdPZYsvj0jj{T%z~ z&)NJDXsPiwh$b*8eK#6b-e@@UDA|jlsJ5;C$HUwd4^rr%XbMmB*!Xq*;L}h!lqcidxv6s#IHXK895ZLr-3Wj6`_lD=I9)IPzDor;f;j}g!?dGEpTDJ z%2Tt5M>7ssm2#%+GG3KXMa{U)4lwE(S?$zwKCEl$Lk?dBeHH9-Pef)n$a~Uln7p-B z%4xv&ti%k`u|=x;R=`=NF%|LEqF}b;)}iPBtKR>wdjH?4UQRPJzr8Q&jWq>fGz z30+us@#+@It)^jhp^m%9T11QI*Ja3LoGIKR5ZVi={C=gg-D^rJuUM4pNJ}vJLLHhA z*9UXdAAXl;^n?-ZeM#}{R#B&TAGrckMk1!Y&JqVtk&KL@?!j8=*xR$DIfj+I@5@I0 z!}GKn617D!Sss>dfm=2UIcrrbvF5LZEbI5$!N@J)qr)4%=x|G#Ta}SG(#UI?F_l#M z>j6X<`4C?<;fiN%sTwN%^c4mgu}cYCFAszLn*)F+RGA{RylEDQkDO-UsL(eMp$*~) zVyOSj>B(|j@$j@nBS!1P`~ijExcAi(ET6H-{oVid5-`fn1FVRN0pKx3dxI2S9U z4E$VTjFC0Yl=Aq2(QjphcbG zZFWm~v?Mg6f79SD&OcJ+OArY<+;7^ozFZ4ey*G92E9G?PbR2Hv`mr2}2#fr^B9L?i z7ecjDGIgWb=V7Ex)+QPWknH-W)1ilL`NGJ}WN|VIbEs6}N%1$^#Sj`CU9N9kxL>vG zK~Dn{8zY82Ov&rS$$f5?7hzm@eyG;_kr?Aw2M6p1sdVr7kB7Z6^+)$Fz41kliM>XQ z4X$fg%VFqYl1=EhvI--l$XsrwuVq&ZvZ3s?e{raeC9RBl!jLrCc7?ByT$=zm?Y%*NM!FM5Fe4WmHkI z4oWf7W;t)nS9Id#&|D#-;v_g&fFiqK(CFA8nPAWBmoFhApPtmTQa{9yx`yZoGM3>f zOk`y1Vj*AqYO({#hYLCHH*IV236?X-D?XqU^TNtj|4`GWcFCwW3m&-|bv7mpnp5rZ z?E&34msU84vQ;zZ4sQz_Bzhyu+?%!}>twox10RKpZ{z#1buT5lai!!3xb}Z5RhcMs z?WXI~sO?6-+kDsI!DdDKgafYuX6V+_!u#h$ZJZRen^#RPqRzMd0*=WPVOwBT%5LSu zDiA~7_Cr9u2_|==&fIbS^6#z#wEE?HdGUNsp9CKM&lK9>fDvJ0Z7le?ck^6|OOnZ- zHpoQnZUQZmsOTh3tboSSVZUOAL(HaplapjCCA9 zu^Tv2(n&r{9F_pr1LAZC+M5IXFk>4p9FELQJuV$zt4f4N_#7E6cbt3>yL%lvi*_7( zXrb5b!L|#{oZ3?RY(b(#JTEVg|AE23(LOmQ*UwU#yUnSHUPOfRaERn*CgH8- zDMO9xfRl}vS5I#(!}+fbBjRR?V|C6-x?Af>7=?y?Uoa&Jsaz>7MMs^vsKr&p({OL# zfxjPzSw>tnr3wTFf7IoIz~C0_k^b2~F!+quhvqQt0rtDX0y0d#i|UH{P3(KZKpBef z{UPEtWMKwsBL|J*y1FBbntx!hvcQ~Lt~BcS1%pgF*L@xU!@IRO&0cFgVAX^2Ukk%e ziM_1)AxLEo7~)eA3E@YuymO1;`iCHX{SIUOU^+E~RtO9hK`1LL2^OW~`v(U1h;VWM z^&-vt=XBA;juezU@X=R!vI=hW zIEL>QE%{nvXS`FsQU)cxZ=ALRd)HxBD zWwC#hEx5$K9YQZo$P-7zHU=CGCfb<}cP&WNV(FtZaqMi`1UWs!BPFKX$63#bgn}N3 zmcnCTZDL!!g;@<`rpuRANtnu=JrX|0`&`tQhZq5wO`Aj+%IEj(tP``8F{mqh zTJ@}}sP-w#uxZUN&e8D$<=C|dB~~`_`{as>pKycnc=)-aAoAAABef*@>!Z zUQN}z0}kySX%wy^tCEh>$~8@Ec&SZhj9a{&CWKAK7QQJ zD?A)_9i4VIaad%dyVxk#0zxUl%ywzKo-wqqSD#rydO0K>M4Ph&KO-%aDUT002}^dR<vQQ*SkJ0{yWiukNHnoQLhFN1|72&R3_t$As&osMqory3X zF@~tGu+Yf<()iO@{UXD}L#=N%U8IG16$hPuf`&1S0@e9O$j*{`7cfdoC5I<8BbT^q zxP`%6NrmQ(tGyL9)o`V08i?7wI0QlcU7I9gs;DH~@`|o}WnPB^I3E=WU24o!bayi| zNv|?y8nUAfQFO(wm4i$R3T|PAyhWw+CUrAQ;R@!m>~xL&47j-VvSAw#;rnJYV1l_t zg+A^-L*ZY?=f+C`qq(CaiQr?QVix1hoS#2nbD_xExwX%A&Kx%E1iVA5$Z6he0{zPc zcVGQ+!6`xyZvVL8+nzZU`+{9b>H_)#4XLu2c*(9CtdftPNTft0<0a9gTw8t4JT7&K zx|jT7_XHMcgyD&(WjUTw8if>+5^&)*?+_T4(}S(&gu_IemV@=$3^pXO=YZol3u1O? ze#yetUkZ57G#K3Il+gh-0j|Bp=PHl)f#}eQYE4xOK+f@{ehgV-)8SyN>F?Zme5+-O z>5UIgV;moNBITBJf&|qquPqNhQkg5mzaQ8fL4lRU zixfcENv^B-Px0P&da8-ePRVM=?wkX=z2vHqh}pm%0!s`^hb>IEFY}J$tK`$%#Xwzp zGHz!;3yvN|3av(V+p}H~ERbxHvNTy8e{Ob688>1^tAyMLQ7w83{p--fr{wHTQUcGBxYPA=AMnPAd%^#M}OSA(ok+*u`&-fM!~?Oa?UQJ0@?db z9n4f{BjsrB1HP#cdWpP#@nY$8SZH&Jn3$+^M~=BKg5{VqGf85dfP@NK&5vhuMIYBA zY0~Nn%fDAsm3eWvZ+kcFP%@}5h^oCavm`1MCvxxdrscdDUi6Z)Xc(lf@G54$yYml# ztV4gCHS`*xBw|ZX9Ns3rt=+|D(IHdTG<O z(tAILM~%R)Yi%$B`8uLcgMs0X);}=#Jp=|5enACIxdQhGPAn^KbNI7e*pZxMTyRs& zHYlZ2@cCJP8_vJ-JIM%O2`gOPx_l(#oFe4Wfv7;fbwfQimP z53^1bq7h5g_yz=okhn|SUK2(Rc9?i>(+1N@<-JOl64d{K!Lk1hgO8oKE1t%Nm4aeG zHJY85bioZ6rJt{f(<)d1m9j9pRlxH(->t&$HdKg8vGVXD)@5nVG6P1eMP+oTyh&N} zSz+XkR`8fO2AiH@ykY}Tby(^Q(I~ntq%o(s1llm+E^jMrmImX?t{PbR@}pH0%+&6b zqb60E6<}Zvwqh~4V>RyWMhzTb>kdB07dB~-32w$`ur@N=0 z*|*W$u4Z^&{GBlkOU(#uD>~bY;X~&Hz{b^}+_XXhMeJhy!qt8t?##e<*`w>U^hO4; z^IxNunxqnl*n4;q>XPf1j-Bx&gBX6|pBBl_=Zg7YSnMJV@B<7W<|SGgMd1A~=?#wj)W>&i2SGv#x?Ct8zH+ zV|i%N-vJ^Ty=`3BV)I91bb1xb#tQ}Qi!H<;u{#bKWnQe$963a|5 zq3*Oz&8AKji*yvM3o@)N3WKL~*DI8CQP}lI2n_BY%yfdl;5uaCJ<*by#=TD0z0-pB zsonpE!8u?cYg@pPE=S#uv!nZ-t_XWq0t)e7i=&*+|ylouU#121_XV)#1dAqQz;?Vb%ns-qCkd$Lv@@|J|%b2<3kzQ zterk&H3y^X5#jUiq^-w6&B@OY7+kI_JRAWBZO|KE5tZv75c9D#UtB`o(f_cx`6`|o zN`8(Sy-e0|FrrGGncOj!TARRE-3cmQH5`p^ED#mN7D$&$r%X+uF5hAkJV7iMp?gmH z^5V1lZ1m{;YbC+|mFU6ad3IMv0Ete?B@&Te$E*I1sV4^(CzKws2g{_e9+|MbrXMy4 zACR{_hlRsDQ642|-k>|vv*xC>lx|i}g?)W3;^(9M*98}R7onu`b25(-R|}$r#=-^+ z;9F!2?^y?Jjr8!)ys?*Kr4^JfgdL3ll7dfsv5b;Q{)ngp#8pO^ z(iTCP5}}8CjT@3Ks?XZPC-|BFt9JiNjFC44t&0R@P{n~n$D3!~%*U@-t)gvfgq+TO z)St6y+SV35KK-27hqRKV3)Ag5Dwt*kgTq_OfSPDeJuW6L=<@h5c8J5v)q(H zP36RM`maBiWeI&mepdt71!}O;sKQq{zna#I=T!18P24&!<2V4KT!hQGkHk0y26W1= zzm&m$V^Q5S@!2T1kwoTB4NmZ*p3Q}msEaz>ON^t)hqz$mq@p@qwli)J;?CN%YzS@%c8)hhb!24bQBq+M&KQ`5Tsxr zHEyI6#XIp~thai{`h~!=3LJ+RiPWyWTymDKyq9%EC|&?|=`u@K{dsAR(N0#dxBwag zJ})*qr%A#52iD!ns7#WSqg1DY@RVB;A#e6C#>((QSh&056z_aO2*ZO9h0-w;M1G|X zP#(r?N2VXhqcPf(Sr*(G*ef-P(wF0e6R3`CD@cF+iHUvH-?2(A%Fe^P;uZpZs-8dR zev2|+_HCvc;(~zypCx3tRE4M{e(Z!mvL+4rOFUKe9VjxVdIkRWH@K>FgeGtJ4Cyzz zmcY*(3Ds1M@w)o0-9LO^n{~&2aYFyOX9w5nj3oCfqNWy`Gl(lU^a1FZA6yK3T|k%e zJsAmy4x0L%=!|(a2oCV6S~SnYSJG`3y<|GKWR(S09TSM}JUurO2hyf)<+^>FQ^0;y z0_g#(!}Q+CSBn^%tJKWQd23`>1SY#!#b&CC*ZvS4uUqUJT(pAVo z=ijXt(}vy<#}HpDjV)W9ctZV}ZgLIeCOQEMVS)QOMihQp%9Za4OkaV&lw>i%a}ra8 z73vxMOZ6l$#ATy0McVHO*wXjCcVcmUf78eImZ{@V8C!pPukjPi=FEn`QV#lN@MUbt z^Fu>9yrD`5ZTg@$UZrfmi$m2(OpJtcYIB#cy`IuGeh51w0>6$v-ZXRWZ$8G^7-zzJ z$J38HLV>U^DeZv6-s_n&@Lv~v<`_~1Gcqp@4NTsUy;QCUkreQh5=F#Wa4GuO$0!R+ z{UD{_2Xj5I(AbGF@S`f^y*90eVTB6?6_flwE?94(Rd$$~l_&V1Fxv+C8*aSn<@_LJ z>8G|?xfhYfrTvWRK+3)0Z8e1Z)yg4U$L%V()X!4kozGg$>&VKuxy<_^ zBnC`+#u<5O$GYc%Xx>>431-rr4vh=N?p8PauEv5pPa;A+f^mo%eK%5uGGw+d@jZW}Y7SEkzn>U4L_y-2l{)NGC|G;4Jf%!I_+|^Ga zANVu|zDTMEwR^7cBq#`49z_pj!uX)19oVhwm2qUy_#mnTyJDJVcW2b zXi4*gE9J>E^e@xJ;$+Y#)KnG}$~H+1k-@1m^ekQ5ONaoR$#8I>xuPgN=_G6ic*d+P zN<67<*z|?ku07q1cOW<4h|oN#F{erub*|KQdUqFZ5`*lD>Iw^ z)toxFxPq&;TnNxn+?4HGM)a)+eS>4GjIM(Jp3W2Dqlz)T#$On$^U)mwgQ>w6_Mq4J zXS7`29^)@RQBlL_w(B^IOnn>(!9IUs@OTm-=k^~M?DaVl@?0=Uyb-fM8pO%NBP~E+ zaKpzh=@1y~Cnf;f!=3V`{G`lI0AD8I`ok>cOQ2XGwoe1MIt_+sxPkM0K#{m)5`OL= zI(a#!%2hCKv@j+RBi=N6|8iYt@s*dG`{*2hAilT2Aut$Lx3j*6>oEeiROPI$ zk=LQqPpgJtdV)Ri_vFR$e_-&_q8VuqU6Qh0Ox3{%=`B!xY;+@9x^>Jr#}18|@@u24T#UNQwvH(aPa$EM=CYif-s1{iX7*MnqjsV% zYY0PowqA;s@@L~2l~LY2(vQ9EqMds`5_&neNbwUh{=(o1Z-@WD;Gdw)f5Tw4zcAS7 zUog0;RWoKE7*QIGAY-EDGnzjrSQ2$)SQ-U_baDeFa!R?^`G|#8%6f|pAR*w=T=(j{ zB&5ND<(t$wH7bb0htjbMA*chcLtt?H{@@=N9L}Q7@ly9qE+RLQV$a24xWJ=G+^-Ci z#ptaKRLS1_$Rq)BV5BtHO7tG&HCH3x)78dF9rEW#&7CS%w2@lV1G~MLac;zcOk-vi zA4ymlHptT)#g%iSMW#m5X6NDe*t)=ktcK?d@8t|wMknZjD@~QRrbQJ!(H)A2YP&VC z{FH4|7YUoz*1eG?cbp*pdRgp1!9h%|Wq(=zFD*(0gTshq`W2kC68w)-=T|gKFG3ZcX}C5EvZzovI(2 z=$Ej*(+E7h>V!E0{QUa}AQ9tKR&^ku1EwZ5p1!0g&Jr|~nlFKHy2aT&)`eg@mF4N!cOPIq~$ zYFgs&m9*hQibloJIylEn!L4q%190{G23P6@7AuSBJVZXgv#btXU7?phX;x*sK7KzK z*`L-3%wnr^N^o)_oyY|L9A?P0%?Cgl)eMW0(>E|?fO$m>jc_dL$ zB3^*df?bclqfA!@fx)`XuZ@~572g#i1+X772S6=T-2vg?j zT^^z~0vUMaip|8Ti_%lY5_#lIRPnh@a?`4@{VG43S{n8C1b&4PB1Gg4rXy!xc&9B} zr|jeGlvG&lZkE%dTJ0C{ENX6qSbiXhQN!p5?#os58)vZ)XJ&78SyqWwnaa?}uxlvz z?yD}d^}(6mAV4-Iy+QnNO3D0d*fth^?}`}>e7;TRfT*Nsd7aX`!JYF2@%LROXFMkA zeb;Wa+?JzXlB33hEpy^*>PW%B`fefWAY;}0z5HH6NCh~V&Wt^*97#AgwH9tSsdODZ zB7rP{CGq%DrblHrQwLEnEt@|SrgI2XK<0xZmkoF@O<7`OGD)}4`05~ z;%An_p_Rk&!#@`lJZ+dMBR)Qx4Rhij=`($^=Hd8R#Cu-nJOP4|hsfaNTJ5RS%5*22 zUoAb#bybu?|B}IIDhLH#YQC!Gq9e5uBn=X(Wy`xAZ@>N{gMVY_g%bAHqRYcWMz`kU z8|5DP<>XQsvQeFkVRxg$McaqASxl^+*g#|Em6PDk7Lu{{2@wt?8)+dNf?Y@jBI%I2sFs~y-V|#6LMf1H! zJGzEZN9EI`M15`xK#qn85yK^0O=}AiC4FS+J9RAbgJjhMKvZ=NjmMM)GXt44dH|@^ zTP|@$@kqN#TL0s|?=7DMU&vB5*~y-VlTW)n%I}gOwYxG$<~TraCSAA}Jm+00aR3We zCKi2U@W;H84o=gKi{G0PN2&znwuVA^&ozUEj=tNjHk9JjrpLSSo$^z zB7@~&v;N9pKCmC?-!fQ;6OZsGmJVHTC<|rvNRx(5WIvCW`<8xC=*#9IHch|qL!iOW zW$kc{15sEgK-51nSY{g{g9$myQUi}p1H$!T3g6-C3_@gZR{|uC9WG;Nhi;>q9Pcwz zgrDG~Vwnw?T)K@#C!JZ~`Iv!MGqEN7b7f5ReU34w+8l9i^7 zm&ln3QG5F}h-NgQOKI&0`^5a#1UhK7YsA?g&L7LWjF7146~r7ZH8sg=p47=2gR z{NAlrNMoycy;qZXYz)p$9y>xa;U`D8ui%=u{yg^`47OGN!`(oo5)#4AXaT>t$qujX z{iuuIBJvs6s}wI-U)lrATyLh3ybhz+J2sF0$lwF6hZTg-=K~Vr=eaER-LBZ8?z`@i zsm3d91)G>hz7@oWN&#MP&VV$}Pd7t9U+N#|o@BfIKMZi4>)+dK&pe;__Et=F5|tRb zquoAK!8m`H$mF?3iXl1DnlzZiOFz;r=yS0&eL_KhFmT|>gcN|g!uEVKK0`8E8;-aA zlI7nz3WLrkl5CasuWhGV*Q8VfU2@96=@fUU~#`{SiJ2n;TKZPJDz*bdwq&H5(* z%!xSrFBqKsy8=YbiPA9ufx)pBZe6>-J;q1RIHHrg+>ER)#CE18ZiZ5U9K_`Tjv5O+ z6GB{nV6eyJVaHpcG$4okzMG{vrdFb*8{@;#IuD&7j)ymE#p}i|``@bqJZZr0*Uw2;-F_a`?Q*#H;RcWIF}(&cNUDN* zJr*K^&y7tRs&jPTaQnbLJ{|glRu%;ek{@=o*Vdw2V>hil9H1^I46Fl zKlyxY9SGcVvVh!pa%bT1d+4NX!R72xz>atSMLOpzQD^&uTZg4n)n0DS*2oBr#dT|> zxvwT_GuLBt(GAvs%1rMy`@%!;fU^z%s4Zn%ee*Ae{qqK%Sxa>x16?PdK9s2)Dz6`o zKKxgsW zz6H-u)!%j#ph`-dyN)vJk5L|Y`n4>LpK2(CJqHPZ5W6P@2vFHGc-E>QHn=>X zmB5s0o3}xCA9{S4X_;?UAI5_8jaPaNT{a+j`PqL^Q zRXdy(w(zjYgNRVMOi2!y7+#$$!TU~Ce{nEzOp}h~e{k^9zi=>UAC1kg`&P)I2!ez8 z|KQ+&Zq2y=3l7fl7_#K$Hn2VzV_fgrbbO_DHNCz*Tt1OX6jMZL=6d0;bnu8<5TJV6 z-Pd$};3|QpP<-{>yzk0B;U3<~xNeZ~QK#xkc8qcgq2Izg~`hOL<(RDA`OI3bi%Yww!HrdNumh z41*R*HdA6w07BO|KvZkz+kmPS^Nyy6BbmEZrboUopLRuDYV#q%-TY%D@PML$)-~km{ zmO4x7#w8>HNCjBzqYZqy69>5=U8*wMFNh6xGvTCK>o|wnoC&;`AG{gtdKjgJQi8T} zjxW+#PF@cXT3qJX5}NILfyVGB_~L=t2H%;QkaNc_k7xq43}}YCPi`KXn!w|_NQrrF z4v>J#?tje+SSs2yA5t0f*|I!88F^b38wPsav9&t6Gri>`YI1jlKI-cXI@zmr0fR30 zNWdpTJ5nAFcB|d5Pv2L%shDtZgao)3m)5jEUN6hXU7$LL`Pm@2-AmOC29Lio*wXMY zDy2Pa%8-$v{W&G0*)X8xQjJ*9^N@t8YRw@2;TSvp`H`;$pmEFjol7fT69nFHZo3^6 z%&#J-$u9KKg6j1qn3NA;WoevIk)HGDL zoyIr!;Vg?@sLc-?_gutxv-Nyqa=G;L*=H)d!=Ux?<={hG-OIASmY&~pTZga9-t}5s zbI$7c?ZTnsYw7)xpsbw zQ%CJwDLKANEw3-nhkK0#IoX|y{@155?bl~SRbcwg*Ls!gHJ^an2)?Jop1P^V0MEe(%fjA)xE|Gx)S? zV=QdSE|<&0-O6REdVq^Y=vCU|FlfrTwo4<$BSml}rriyA*|%B2lyP)$1Iuri&zEzN zaRPIDzdL&&R;MIn7rw>k0q*i@YU=2|diuo{&g2l_TaSpStMt-xeSCeybVP!BoAPt) zW^Z$F4KAn2kiP#UM#(1NqDtQ;1!rt`ObWHjR`8yjq#2uJ*)nH;XMXb{=y6JrnBMm3 zeEZ;pfR9gZgmJcHfT|{k$hK+d8hCnf3I+C^SAFpUkFB*m`yagoC7*ai-IAR6X+O5K zZGd6)yhSs(Sosa!=iNV&q=037UTUA}n%rzW7^-R#{?Ooozckq1TvN$-0aep|bF{At z=;PzXC)CyK_6X|j%eoENj9IyLpeGGjT3^w9bVN0*?tDEn^4c3&-W$+oCJ|bK^Lieb zZMd`V6#%t&yRHlnfNzL9mkoP_a8B2RJj*0En&yPEA^R~}>uV4i{IFFr(c<>FHTzI+ zE7(M`(1f}SYG2^W$+*tAa?JDf>}q#6HGUl%6T%tGf%tjnDu)~U0B_vyf<2elkIlr4 z!w(Zr5jp?RVEa6Qs5oNAWgjKtj`q41Z_Decuz*T`K~G-~C$`7r&NU?x;)~tcDBYad zLw(<$h~V$UeoxyAN4rDUJ0TN6no%{x{D$q8KS{WtMm$U6k9+z-h2GU$8oTxb8N$k6wI_lOpW-3q(Hzo=uo{KaOfW5NS0bMI& z?X~S-!}Z6zABpRlXNY&w<;o@gsypYG&Q$( zVLSLQf}Ty!f1Y0S4N2KQFKv#7JvuxbF+IGV_azd#Uro3_8+u@0ZVjBoksKVP=+?~V z5LH$Va{XxaciA#})m~lK3kdrBxO1|?)U+;84tI+qWN+KC2$GjO*a4lm*Lu7;EAsVi z?KA{;1eYlB`v=GPtqQcjv@`XUwC`>Vrl6kOgHk$C!OG(~B!-=Oo^@9<-KMQR-D^8s z8HQIA{8!-7n5o{T6}hR?j>oMt;$lsYow}b{n$6%cYbHZMekC{#(8JZ~!1CooNzu(- zd;8falkM<)FHyJW5fJC`d@1I_+|6%IVD0rstCG!|8sfE1vYUogzkplG&D$|SU!A=w zzFG)IYxk$SFww~@9+@67-H+2+Cg0FfY&{P&|qE9 z$ImOE03z@*Q>~!}aiz28wd*zW7bq?P?UrUtl3!s%hS}X=Nk_kM470(-aV_WDjwgec ze7aM@vz?v#daqBX^BDINMNl~*g3AQA8YDumcbjYN&K?oJcAeY58LZj{5c_)GS`iVi zKbX>xR5SJc(tITO)irk;u=?}{G9Er%)$Irfc)c8r@j2Pby0E$(`@N(V4p91bfqQnS zyRo!<>Ba|6+VU-7${<*0w|J1G${7LhoYQ+uZL{yww1X?R{T~%1M;$-`M}K87TaMvh z87!6gj|?7stc5`XcVxQYo{=U83&fp_`% z9~f-^7X}Oc1B2T_4E^1-qepWc08E_jL*4^V#g?xVI?uX_nhK9Q7-GknZr{3y*=cfi zQ`ROtrx!3D5h=|p{0a)XMj*vtmlVJHg1f5n7Ve+U*6uq?YCp53TFaS;7~B?|f(s~! z9OnXNH9H2ER`)JYruGN}i5l5Qxi8phxdS0#h0?ZfKhYOd7zzeI?V} z`q{i#aQv%ZD4Q>i2VPFxp4K`hU;ax5U%uKwWbnG;hzkERp2kWeR!^_Hb;J}aCs(Q9Eyda?o%qgkqc%$N<+ki6c|^{ zPx4|A(L6b$5erznq(^N;fopp}mmr6mva2{DmUV$VPm|D7rXrvAlfl z=RquC$$CvU`W>D^omGOcg{!*M(X(-d9Z;Cj^5XWD030W~Q6tBqsos?`hLM#?=I!LZ zpEN%0pg}z-CmWC$dP^c*-W_=$)TY43`6?A;fdAI=6ns$e+Qt4Tr@vyp>%CNXS3%zU z>X-0%{Axd?^wiLL}ZG3*@JsX#P;wd;lmDfip)G=@`sEa9Q z%pZAbV}G=pD)HeM-9`J_Owih>Ri#g*zy(J;bT-@&HBAPYJ|LOXcR=KuULd<^!if)` ziTrzj%LmK*P5U+JZA1x~(O~&v%?a6eMY|vwnn%cf%NEiccTs|91WXz4aIlv5fO}rC z_=T34bJ3b9>$}p@#QB1^qQGvsL=)3s06!d*gWF9NM-hcu$`Z9pdw%Uo#uzKzPE;nB%X^$rW~?3H?6QBwUF2J=*@G2wIOSD<@oVU)QbZb(gdg zcIBI%fhu-x?;l<7ir(T=DWD5aezyRIHt_D?1bwNmtHZknHX!GHd$^R5b;agsCbS*E zpAIP_eN!hbY#ZF+Trdw1{aiUqDT8VvJ%IHY;fvZ^lf2pXv)0wfkG-NdE-+A3uq zVM4_O?R(MU>4S}-9n8uz`Ikl+A^3CS=#_to$I0q&s z#!3+J7v?v{Z~Efkf{kjX6IOP`VdH&-5XK5&yHxBOor6GB2b=2o8rlxjCSUfmdeMSH z^1nBSjI$^Ju&9Jf2ZgD7c?fJ2U{q(pm1EQ_%f;*?Yt0U-10q`{Zt)=DSXGo+-C4^o?VywN7ugP35S|HA~w{WZZ#P=Onz$=LbnawIkg1I_Oz zL@y2v4dtPjH%-VhTqKXm%Ol5)^4v1)tFr11EUCFL=;7ymPnB8b;VHLU#kRT$aS11@0+7KVt;OUI2|ki!y$f$ekYTF?e0f|B7>5NEL11o3@n!r!Bsi2Dgd@hg zm*k4gt0>)R#;)LY^;%h!E4~tiBID6jC0SmmX>Z4(u2Duc>2ML-vphRWpufps#Lmw(H zx7AKu0kye0C5>TLN88-fPPM-6a`LoQzXP*dQWJG|cKJGWMm>J7Z`lu^md11!x5-lX zp3q4b2ewTyc}5Pa7}M0hCV057^{{_Ik4fk4KcQc&KcV0FUP$Qod!;>x*)Lu~@-n#? zgDn4$kk87LCnwp#xMaaeJy#)==fIFi3a*8y!#hH&`XC|LpgwYd`U;6|J)hiV79{xXH@4jQ1b{JaYtinH=coFMt3xnE$q1bbtPHMF@Uq&ZUW6d6&eYU7n% zk2YvzY^5i zR0zppB@=a?i)Lr;oZu|T5{1RVq+}2uw3UZSu-}$}gno~C+AOGt{|Wt$_;K}fW$0rf zVgPnA?8a!E;Ahl;${0cL!M(Ii;R#)nUS*U%yF8zQ-V}KJJM?Qn|L@T6)0r*ldrf6z znJka%tIlA(dQ6!^(~2OXXuCtd6?n>*ROCR#=d;Xu1X#HdsmSrPFAA2ScTr15SkyDO z$L!ZT8N^w7G`W$z6p6r67a}mO{F)Xu2oAM6bvyHaQT30(nYG~>Et-yP+qTV4(y?vZ zwr$(S8{0NJw$ZVTlW(oH>+Jn+{+Lx0RnJ6S*SLq8i&0J#S#avZDyDS}6{#A`5-l5~ zTGWNkQEbdq=!wl}##$iH|1rUA&x*f6utTRq@}S|I8ru8R3Ig6{E~+yB7Q?$d>`F;c z2|>2F*CnL}##-p}`YY-6ihoxvL;iAvlC8eo_uoinBYnKAuRyVrqT>sa=39l%5di~o zTD*2@j43N62Sw4EVfPF&w!DHgP;@w;1X^F0j#9O3{QKNka-^w^jK+uxvaG$IJuip} zm*v#WTw8*g3!vrGa&m$XD(ILqsreso9@BSwB}}_hj5mWpk{6_z)IK%#0gk70NFtO% z45o?KRl(OJc~UL^**rOi95|qCS;Q?9p8>g2`gV>_YRGyKnxh`|&DW0lEV~LJ$MYku zu49pRNkq-AGZ5R5hOoJktM+tp^G)7yYQAtA(ORq=x#5=UMX-Ma5jM^AU^C?F!I-H> zwqw-<1{R>bEcVTzih&LLpr0Riy=4p{qel6?+}DwN`A<<1L@wm#KSG9|wPllh>5PkM z&QLj1icmyj>nCx&OV6lSOzxMJ*kMM>ISfx!wCzp{A*?4ord%fRk|ZO2^e@9Q{}Td3 zAEfd7)pBBU!pE0Xe_f3-eBe&(=({>-2pZT9AI` zRlvA(Qno03B;J*K652PYzl!lEyM^jBq5{Fr`|%NbICOuu8`MQNC2Kc-&>)`g+nuBrbq{a*gB z>6hbwOuylNbkC(dPu!6J23sj?G?oEYXdC5;7qFXYnqIsA$Ml=^WBR>5(QDn^X#@6s z!(U|`U=-$Y{L2xXgm*O8Yv=Cju&Em>#iD`B!4Y#Fb->y1q(`r9cZ)h_yJn--(wG$K=ggk>aIAVvf+!8m{|oa=nH@awrE0~1Ab3bjhNTWWhr-g7Ig za4v$HnHQ#Q;72kMHA!V3zl<@l)dlFq!~njtKAR!n7uMpDrtq~WJGHqBuo>UR!-Xk=u5KK%a2D-Y28LY6sRH1cE)U&(mMwbLS}_`+`_L2p{lNX}O-j0T9Wn;AgGfF7() z+Dam0^!+5n?2d~{1@{srs<}L_n2gdaW}}S(cssi5h=xuPQMkg^Up7SS?mwnqbffwZ z&j^krqZ`c1y;I>7X%oZW9}&3nT>}Vj785V!cu_S@cgT0LxGA|w2e6}jw^DNPV*yEZ1YNb^TA4b;a z?}HhUyvvZBOFTdK!bANce!hp0^n9T_Hj z_pat628Ait25d87X@J{OIL+BTMHMX7{+u-DO)%KLjyzc+RmrB#hkRv(er6>&gmB9E zKkW%+bj)AIcq0zXe|edzx@idco>;{7GG}yE{4d{km=pj7)YpL=E$GZ#(^xQ|WT5Fb zDtQ!>tr)qr|3Mv$ySw5;LT!?*jOAE(7=pCZEoYKLk}`M_X+|+g!0dAN@rqGHF@aXv zWRi3m*vvs~(f_G_|Nf`?<>x}FD0C-N_#=@4^rQMk0&4#^{|r}TnTAc}S@nb`B^h9J z5D(T6A)5^+%d25-h-uNqtx^{rHpFTd^NSNwk&y-P_3F6PgQw4E{O`+{F@2I&$1UW^ zV5lI$W~e(-_yLe%xHu4GwI$kkgzo>cJy? zn=0YH#K%k*lAcBw+Y)p$Ji|x+_dwbH8jnm!$1dpX5IWI8 zd5c_hV?hPhcza@%`bSiiij|DzsleoPF-`Hf$!ymF6oZldv%7EOo5kbv&VlOf)ukRm zh!Nmp!*gwQi+9uSoJ#%XX6ndvyAjMl`k(I+xzY7 z{oKwSTTG$+&9RjGrY}tyCyO$!nySS6H6rKOOP)BPn{?o{jJw|N#^fvKc8 z1^`9F)d_q6YY$#akjht`61#)0$JEYfMO2j<>qdE`1snZL@tUO3aoE(<#kXg)mil1j zOdqv@@=Ad_pXknX5?Y|;-sv2N7(=d#b zi7=t75oNA_P#ve{>zdJx^epWOFj;UB2{_$cbWQ0d0S?puk&rqN&0CEXV5m5H(#3&B5JR7@VS_g+5hMhXb9(0QE)$nhvcwx&ks)G3O8SS}vUukgrco`YCG(#+=NA#gN z&jl%@C7^L0@$I*9lk3=V!Bf28#Y{=K<3f2W%qo@+B8p`GP zN>T@#*cPe>mcDMoE$f4HRaJ;?MpS{0spkJ?Z8e_>$lQ{!3DCNy!~jPpg4vusW*hZ6 z1XmbS&IgL7FLvA-Z(ufDyC7MMstrlWw7;zDYx@BY9-4d=VY|c^wEBwwG|~GrjcV}Y zqt%d`DxVfdqbfIZjIj@MyQ(dT)Y+!e0rF9mY@Rroqyfd>2J)RV+A714Ldscs={k>V zvm4a0LWFx(tf{@mF&#@~lb#TZsPq4i=Xdn~<@x2TSfFJJ!_)$!XE&o~qxyX@48y~; zZe8pa^BfFSNY&DTzcx+>{Ql$l1t&C)5*-2`7IgtUiNz&nJ(%8v7M}lqJ-^iv&7^}s zl5xQ8A=)+;6Z}-A0R}~*q$0lzYm)+qPGL`AG5SH5-x-wKZP_rf4R zu-YU$85>qLL72ES&UqhS;(@xeBvmLr_pk2Ph#t`LaY>BpE5WJ7e zXJVgooF<7vbYF>&hIGe)WyH_Vztxwn^Ez;}B~iK!X~?`v&z4rJ;3L(YF>7?SS>`aA z+;cLUyU}%_V__3vmZo(&84<)7d`7@xQkSn_6NEr8dk%=VC`S_zPUscE89#y|gh%tP%6ocJs-69_g$m>Y5wwTzAIKq{zzo)!wCOT){JFtVp-s6VpO|I!)~UI>@toyMv)00#ynmo2#S53 z?D#35KMogBan~XWg-l*}YrF+5_1(qF>db<%J)TfWv(X>XE7TJbpeHztj*RLUr!pI& zJI`n5`O7QT^Vstf#gqd9T5X%EYe!2*<4p^w{>_BvYD|;>^K`pUuvJN*dESTXbGv)H zsEQ0SR0m|ZTia{tDf-%Er)pEG*{n`{Q#CvCTHyk0CulKfb?{s#h+x{z<;J9yOZQQK z6o`y9A986bPxG5yK~h)aW_c@LRO>My1@ly1zEO50r4&P36mH-0c>YnI=C>h%Jghw` zgl<$x4cMxn7Z0A z>W)*J2~8T22teviL{xUL5E-aH?&@g&QiP;CvGQ+mmkG^Fc}V;*U4;~sIK@n#8^_uq z5Pl!X6W7+-sDw%MOMzbrvWm`$`)SFjn6`veL%q+L=ysdrwTRJ@MI_0% zYx&+LVg44gYoxd_LecbnikMC{E2i2oec^! zu}DgjrmEy5UZV3S)`=o$Hb5pKJpRj+ja1BJKi8O#_Bohe25@ds4L%(FEoCNUYyI3t;* zJ{40#O#YxHuNgO?>CV_iw&0O-tmMp)Go?I9Zf&{vVO7j3fA~TvATc_P0Lof6JJ|&* z+F`dnl=v-(QZyq+E_$rp#oe}F9=NMw@K?BNx;6P7JOM7gwXKV26gRFj=L?tv;=>Wd z8~0g0CPnEg`8;@PCD9m?Jg@g?#T#>nWTnLq7A%>cNIeYhXt}v;F5W z{tJQ$P%H!0U-&rO99U(Jg5zq`nz!bJh-c^mY)bLhUy4Sgz&lz}izr~hUsNF$d{fY` z_^)DW5hbWnQwB3GVSWn=ywc@0!4Y5@9vlyr8}q}F5z^6D00AV@FuX`5U6bQL7|Gk9 z{&rZ|c&f-sAsx35v_JgO=A=D;WG0`M} z25ED(gt$^PVGOX)$I8B-gFW zpn{JQyyL-k6o>4yDU^4&K>K;~g6ak~vrEL`kuS#x=)rjsiEB90vioG`e(*Yg*Ck&Xo zIop&Wni4>nP_X<20DV1c)JFTW2<^qg)m!eg;fL;mR4PNR4g~!hMTgCkt@lSO`LUK< zR1hVVInYK5_T0}=vFAzsQSt9NLdvoN$gt2a7iBzMlAd&q3^k-AMFcsFg=TrT9|4Mi z`v{cL(Ya$OYi9M0WQ-A$j$3qEH_oC_o(Wnf(M1*qi9gkFqZ0kE`fNl2s{V>_+K9t| zegyMUae`CTE?!P{SoCiVw1Wmn=zusf6rn6_PUTZ@a=Wgl&Wb5IBFaR#C&7!z~>~GO@*q z=W`56qJZkzF{6T=^it|#?d~JYm>9N{^)nU~fK{mQ!Gsx0e*Q43)Qr0Vmd)&7i?AYsT^ zyGl7*>xCTPHA;iHstoBCG#AClrdhE*rFkKVa;v2vT?GE4-~{Qa7E%V!`l1Cfcx3D< zE$7!RaxTT6hRidMz4PvAAJbRIm*#Gp5N@?eeUA;qzz8m)kO39jh;9Y(&uGui=Z)#fFW-3GgP>=#5cl z$3RXBY6|W&<-W`$b0Rxl$^^a&bCp#L(-e{NB`z>SA*YOn?yF@#Es%;{JlsYE)z*Wr z{-CuAQ*jKq$4(o6IdRrxCwGteJ?<#Q_F&=IzL38C=p+c%P@2WeL@c$aL2ir?NPNYJ zsbZI!2}9KOeppXw6Y^UJ7F9gUhvf)zDB-gG!UlL&bx8h@_XJRY51G&@Pg58>)q?P?m1lngMbD*LDx(eNa zeEG{YWl6~2xyg{v26bM0UeI5QXKo3|`>ILoGh0BY)lr;}rD7XzZ5FhprDLQWf}0+T zB8FUQHW+6T7z`%!2(}a&RagQEjlVOfTC)IUxtI2(fDPrlZbp58ON;Y!Z zHDAb-kIG>J#x(|3tCQc$Cq|u;!@IP4ik{OUTBheXPZfM~Z!eFo^*;A^qMm%~jD^h9 zVKhl{{haB2tu(aKC`P}Ins@=&Af-@osGfm8dM$PL5Bj*#p9%6iK)D+|Gu(mJjrBWN zW~PI6VTh|TVc10%B)$T}Dz4``+^F#3U94WHV8l_;E`yN`oh_0T z01%~#T*E*Eoa}=#f~OQzW_MkQM%&ZVL91mSPbsxXY_$-whH9}E;jjO;HGRZ#W;{i1 z@T0+$HW7YGX<=L;6sgWnu_&k<4zeuyaK8C7LnBA`X-kTwt_-Y|G&Ee6zTP6r9xXuu zS^9W~B?8V`4GJr|h8)NgzXr6r+VLl94}(FM#zy}mHz@wM0^6M(4Wzk(I-#XF5|5~u z|6Z^gKdE(qynKwyHYJ|S@H#ScX>}AX+=PpCo5Ilfu$f$*tNy9-fE`#Ul>(H|4j}|F z)-8cbL?xz5&ONt*6{*^gv{^7?M@V*Tu4X&mJam1yuAmxTYILzMG<~#zkkr)Vl6W|^ zcvV(S-L7T|W&IW)$fdIU@VcJ1T)Qo0t4_b65(ydcvT7e>4Tbc~r$8D2J8nhV@$ChHVD^WG^Z0O zbL$P)+LUPpsHxm3P2qOcmC$8ju(N@V7NR76NU9xZx#lR$cr7BY&*B(>V@^S7zoB)q8okx;v-&N_+#yS^}14rdYmQ95P>1O0< z@wnQ%+QzPp!S;|k*qGRF9IKW6k7BVmG!T4)1HB+qMC-nIj_fP*=fWq$GzX2bG~ttO{2H_TA?ta7bO5B@GwZ zG%*|zmw@RvmI0J6$<|}TBfFLC)bUTlR_sctOBdCd*e2vpJ zDnKOUZ)O}eNu(go0yf;ZGoZFw+t9TH?B*;#BiIM9Cdc}V20XI)uIcf~!Esyv;x`Kx zK0SbrZpPL4bZjnRZ-pEruw4jhNi#>4&Lbafk|d3H^)R7{T=_JN&5mc%-4-k&n+XY` zTa=C(K%_j6z2-1sG)DME^PX9WGM$iNr0~ND*Q=~cxyZj~Rb%BHm|Fj(R!|(aFw=6> zaomQaYpPFgkfwCM23ZRPoHWG?shJQ_r&RqZq^fGWJY0|L>Qux#QZK^Hywrwrg z@caVIG2&^>3h7k1IOPzkOt-7Vm@h@tfrcI$p?aNlVZLXJt@I*dbca^M1hFUAsaxV9 z$Nz?AW-7nTQM@MIxWvJXGH(Go8 z&?$i~SK!u+^ev{mp(<>c4Gz0&ZTV1@lkKtF-pe2%eW;l>tv2uNOAK&h@IR!myW%<& zjz4gn1hq;6M8L+fgQMuCSK^wxqjpn%f83TH;fbVm;_olfk`%&!$8lB*N90W_ ztYl$AON-*%NNsQKk(R%Ych6VC4yUIi|!EEz5nYSRCFvnJhk)=5iMK{)C z;?beO&TqLokcrh6wRRV10SYBDv7~KhbCw6RviLu${1()o%xtQXBrg^wm2xyJR~UjF z0;W&V)?atcO6cR_S*xs|@$xv*4{3EnsM#t8kPN&!U53vg{pS!qvlRqonjX(52QwT& zD?ZGQ;tE3g=CR`GAVq$&v|+5^rt$T72+$;qmojur*=Ho@RM|FBYO+ays6$qu%*Kt_ zzM(sqavTFp53Ks~WY?RBa}I**xUO{;D-SBQNP~m@u)>ysA@VMFL)?>k!}XNm(F>|{ zafi!5u$F~Z;)RJDBJ=b4MMC01dhy`LcMHBbS5|X@oNJl~jt`=^pUBmmTR?`1LD+_g zau)H9b^J}cDrtaH!xS4CWNP(|NY%Iqu}6KYF~ejKmdN^=rA5>4c9k}xOXWzqN@wdz zVRWV>+MIWNQN;zu+O(v-!;wptzi&@Q8~h0q1WDp$v6DDP zmX{5Qa9GFcWkA7ySm8LtaG88uIGDY4ZuM+tlilE`nSyq2zs*Q6k36Z~9jYK(rPt@e zepFlPq9=|qb9h=|X=DbWh!oz!n!%jrLS5aG-F#UoXj(UMi-H%^e$C58Olyb~6&wuz zF>iI<2p6t$N+bQeRvD~HwRA}^bq7(0b>x7$3(&Q6JuX(0`fuutRWF$NS8g3nd)2vE zg9fWVpIu09uI^PZ0++=Db|FC)$LM*mX#V*p_(EGkJ58BFypkr}7d0Vh)>LBpri3(rSLhU9d8n_TCk zK;vO?Gk>fb*TLJ)X%Z=;7&&G;1E#17CnK`)F0hqFimJ4V5kV$!G~ukOh%5_Ws3F0v ztUFPS3>D|aS5!Y!*p?b4`q`fahDw1Ifl(~ki+*t71fsRb&oIw?3VO!BOPEwPi zDc+K7mgZQb-Ga>GA|pwcy41tcI9UDe4&)4b8s_3ySQK4h<`(g%WwAz*!PePteJF{& zy!CI6w0PXLtcN4k+-kZ&nPbQd6l9^mbQMI7%~)9tE3cNBD)VwS8(F56Q_vq=7<#;o ziKxPVU7nqt6dZ)UoS4?)eGy=b6bZHhr5Lg&VP{5VwSR{ZK2fe5C0PMLTgrrK|D!<0 zuq@{s@V8$XfJ38c!DppRtJ2C}tOO2X)eXhErYKIqxVo{kxy`%EyYCwoVr#bNHay_x zVMe4~p=F;V4fd3vjaHShh6*s!r`%0)e_b=j?rz*QjI!=e)ImxUFhvln?p$hDp%leb zS{catOU)%7lnow}t8$*Nor6vo9(Nr{hJ;6g{gl1LdpFFZz*NWzN-L)Q4GZ9`Y#XGt z43ht+tTNp%Cc0Q0m#s|)Xhu*)LRts~E7CID|6PCM=;CYgwN@1izI8Wmw^8GvnHtBt zt^Xe{Y(3oCQh=RO|1&SQZsfO4^oK7gwlKU{J8pdTX_R0`rtqerb`b&nN6PR1yxARZN9^}I zJe{|@tFf&AfMG4~Bh+4q-6y$d+3&ycC$a8(cstuSy(b90Ydxy+u`}OyhpkUtEYCKy z9(&)p0?V5iOaR>XP97G=T0XvZj%(O%#LxaZH`VKYzAiVSNW0V4JD_ALHiX16u97v6%f)!dnK`zpSZKQmQ3)GIqb0;u_f}3i6OW^d!gP zBTcxx#gxp?@2O(#8#;okyM8%xlL5M#P`r<7gZX4*ekTuXS8G``%`+J%aS^fuO1O(Cv+lM__7vz z-HNQ*97SiyALDjTr}v6evzZLV)cWpge~#$iK8$gq0`r8qXa7dv(QoijOuH5CdB8;* z>G$zThl0&T0&>IQ4t@opr|mz_^7rX@-Q*#9WM|#zq{z;jgzFq|%em=qdT^}oa*Z;QPWZkfrg9wr~^<+7!9sKJ5*X5Z9?}lL5tLFCH ztGBzMy*Qk+xvcggisL4XG6TVvv7gI}(Y>|Kv%0%|g?~L7G@bLf9iMf1LVv9(o0M_9 z&_=T3!GNDS-9NQ2`|pv9poOBC!x`!0P0i)eIeUD{na(#^%Drpwoo=f~XMsBC$;%Pp zVqz&p0Ad5BwF6Zo5od0g7r!>?6m zZo#XO;<=Jncf-MK%yj0F!lOaVi~GU&n5z}Z?J(8`Vey|bgmL?{4>x9M4D%ih6+-fd z3*7rdP?bCP?wNkqQPLhc%M#1r%J~T#eVEoXDCDE-hLKkkR301;%{P4>JV$|@-UuJ2 zzRHwpCuM8;(YX3#8HkLt@7=>#5s9tJPs9JYU3-*M$F9W;M9#FMQK8(g&COCWD}y@g zomt^^5=^u>d&ZZA@& zjje4SUc^?w)wS(p5=**y?WJMM^Va@)&!$C?O!y&%to*gP+qr#~mrMZunDh|ES;W_i zdzIJj(7i7kriU=&4*JD|o0}|9Ml1WqG`3ae*bXtk*WJVK_tW)gIwj*utE}!Sz+%W{o%FFNS}Q8Ht=4ZcYwjUF%0 z);|{Z=E=f%vhv_x2efW8`abci1e65HeJYVXU?crji&W2td<%HCGYXRZl z_c(rm90dLWXV9$}_c5_L+kfY`zK%S8L0@b1-p_l$XBWTze0Ne{U}-1g89R^I>DBwz zY=wt!g&>kqoB4V;%jS80{M;Ihx=l878yMfYoc%w2u!Y^rlM9Rp_=&F>(7PRhKzpJa z|M%nFhy+9s< zd-uKH41{mXxzsjDU;ENE&p@Qqyh53i#rXH#m*=d8)zuobd+lRGM;i5NF-eFwk^RR8 zR_^rqhgFf`YLA}Y%x6>*=QGj^NT~1O>BN)~KMMrKtqw+pvpD7Oxy|S8tR0i@i5|Ve z?j%H!?EYaxFr>DP%d>ol^X#$wQKWLu2~CMH@2UDs+V3_JboZO~w&8b-(0AWBc%Id@ zC0-BCn(TMM0$uJ$vG@QJD?L`v7!3Sk-a0c9J$z(pPk!k|99qzVMMaqz+0Lm7P)5mN zV_F^SKd$J+mPV#YhS81{k()9$YA{%dynWUWC531^=bD|FtcQ_gVAX8mR*CHF%h)1+ zvKv*_i>25iWGEhU0bI96w(Z*nc1NUv0lXo3#$& z??`DCx@igi>jv?NW)Nr39kCIvD*i2Em*9X@eaCvy4B30P_h8y_sJ^{+;rD_M?vDQ9 z_Gq&Lor)JsdyQZ_hhRof&WqXMcjBmhxhwP$4}VmTt*w%)!MW-I)d>T`poh=!px0Iw zhi^?!-{Y7p7clQFum!*}JciVN>SN^qMqd{Nwo+XtvgVXxqutHlB;`T;myW?}u zOJ}{q@Vv8`=GWzM^_A)6_P}-=vetdn4KNd}85_M@v3zyB3CZ?x_b=dey3uC{AvvVL z@%{JjV?gh-J?zcBzGq$24{PV!la0sc)!v5q=>6>V$#8Q> zSsuTyu5Tv`&-+u2<2i&=7{kEZS<&?4Xj^mF$J2ZkuitYm*89>Fs=FKGz2klVB*Xjt z{f~#+S949nyWy*SQ%_^o_H**>jvoISRC}Ax`#AUhV`KE@ZuNT z-XD#t(p$dr)%9q;H$BMlxiJP=aXaDn-J+#|At2yu4HI|Liyi!7uOXanqx7^hvtB#C zUtSMhPH(nuCkKmE__ujIKP(;Eza7q#{IXLo%F5#2t*V?nzV5$@dN^wHx)y)!@NEtQ ze7ezhNfcPN&7YByCNfrdI%uti4bSCpUX1AGT&AzU>074@2h| zc^us?2n2kdtqm=Ud0wv`VEgB<2Pa;Pjh`R25Hxl>zB&T@7YrdQj|&&aMWcCcy3<2B z)3a82n?ZblG}FoD^Rl>JTfXm$xYi$FIA!+5)64bW?f%4V|J1SGtlrel)>e*`C%V8a zzh9?gol4$qR(DA^R_nVc{Hw*@Imz~GTlb5*hnbPR-)!w_<&A8(%@Yu)J?dObXi zOiF6|3lxo>d#?vEosCrpY65((R*#eYjpB`38L7|D`~-XsT28h2-m+D3Q#>4vOwSK5 zEP(sv+qb_{%l%nv9C3Ozbf-5fTWNJo^6bz2JAU=8dU=cDJP0a&uC-G=pIv<4`;+S{ zE0#f5RdG14_C^P{8!3ZtChu3JK97h0Y<)kP{JzH(wtQ@^dcCtRn0vi?-maGJ&8%>) zd)Dt(?tH?mSnT#c+tnUl=W!YvzAPhqrUYvt@PV)bq=vPxc$+Rykcoa)fv2?%T}AIx1pFDImD#IJ6qzdl{EE8HHh8k;}6 zADi93&CRZB*NVDJG7idIeN0?m&2M$QzRUgcrg>)#)OxHpeui0C`?7aBc}Fw!@Vhtm zIM#Jx@9ZtVeNG2`NnY3idRZ(^9zQ;+a$?H7>o~o-+FvcrXr@b6-tQmvzkznGxO}@8 zPQq&UhMky4>()5DAQ1GgRbD>+;B9_-D(hYOZ3(P1>iTf|-g_P|Z8F(#ew)|xJ6^Z+ zOr&}I!8y80%Rm3voEVJhg~yQgydM*2G<}}_2t56H0G^s~@4oEbk5o0MeMlI6Hr+yQ z&rOx?@VozMMA-IT8l`cYTv>W_c`y9-sEV@!vdiV~IDf?WK0PSv9QV;Kyz$tw5eQxi z6UVt!eAuDi^lSWwetb>e`_{%)_08@7aItmL+(|%J_-zF?opy(Iy%T2Re)q9SmhGqD z_GWiL@`?6l@_JhdW2ak<557v(`R{dVK-)W1e72|O1Cr`KN=;Ais>G=A^~3)1YBFqt zW_MxWD_7&Y;Z`5s!m6eZBX0EVYT&FfZM2r1eWHjfttItr>*37z)7if1))_CC@5PFq zpk{U;isSQO@%iJn_FbH@^XqYM;9}qKd-858#Pg$$ckA8LoeQDxhD$LQSXVirUE4j^ z?ar1<#`UeE*`t7F0qn?|O7y7H$$^X1U{@BVT|oPf_-oKyAz(4gBVa?RR3AMK4#<5)8uCSjd9Wf+wDwfl~b0NeH)+bq3EnhHC&tjeD{qj}tGiuTh`^sXeumUl#!;DrP0zao0 zA~Sk^U6V#qD@K#S@*JyGn)`2LU+^B5I43*M!(Bgw-AhXc_*s|4*VDZmXmLyrmY0ap z-=>~)PBvz88zUzlBSR#MUL8;sjLe70Q1VanhF&r2zmKj4ox8 zABrr#@NOkI;9p+njr}6R^9W=bU|!q%yT~!`7etpfU$Y7=@vp96I^N4#=OpB1E-s@Q z-54XhoJ<(!jIZbD5l>|?AX)JO1Xo=Cd|m#@Cy0#q<3K(2gkLx`4Dj`xFM4MAC4zxo zEH(t^qgT?_#LDI9@5hUNe4e77&XE)sGOvUP!vgvFf+K98&Qzv6(2Z@~Z5CDH#c%8u z2hD@l9Qh>t`nnN1;LQ_5WA;60o=!6vj8SAz)3qr&s4akfJTd$}Zk{f+K?SklRrzV8 z^=JE^pTENigB-@oU$7qM00y01ssI1xWUHU=Y+qW$?}AlhIPn3meWgmTWfLC;^4sBd zWaToSKYPLfv-HqMmCB#4Ga^OuBdBNuTJj%`E|;p@-YVPcSysMAvk%9{eY)>5S~pm% zjBSdR@0J-hbKRMqYY&iK!w>$$I9K(T8VgRF2Y;(-PP7r)ANAwibg^y}=P#v7^u_s= z1t=+eUyc3T@dYwhj#k3Z-tqZ1szkdP1U@ieT=hI%q&2SY5pHf?imgoXe5y1)z6M0T z2OEBi0DJmAg`bGytfm<>JS%q#FA-~sDl00o6+w#Ic>lhsBsD_{ty;n5v$Sf6Od-~Q zAm$@UB#zT)B3~gMV693xTN*V~fu}|mWEvV@>66&NP65dBW8un z3iUPcFs#dc2qLWWD~KDCk^UYx$*g-7z<$5v;n7WJzc; z^dM`ZA|_EYY5Xe02@*i)6-8 z&ZlKEn(CJVkI(D)0+kn0gg42M7aZ0nE~@wWj_nKFWB{P%Kno(U=KG`;0vP1wo)A(n zJ$a^I&;eD1xNtwp=AR}|yjWnLD08MHA~vRp4;pCxQi)~#o^1BrJYiq8bG4-RW3@;* z<(KyznWl|)4VO7PZZw>c5RfhosPhB~6i2!gR0($i`*aMQE{T`rYCX&BwtmY^L%wJV#fq7CaQ&=Q{aWYCcMwb2=^yg25Qyui_ z{JV9a!R5`+^I-2~{V$vURRAIk5v+#*DHN6m5jX>-6-?*&>rx)pdssU|2TV*yycSu- z)ln!03UQno#c)o3=x#Y)M|x0s7zOWN(snH5oQ2gTwN4oiNYAJk6IPfAX+|@H-FioLp7or>9f?X$B!`>oI&kG zjoV@_A)lnb_75;qoq!y0uzr0ic44|yWVUbtc7f#|(yvEzWdA#Mu|+PkBD$!`v3)OL z8+IKlKGR;pfIaLcmx+~AzQ`~;|0wz42&j{M!|{@R`&4f!2PoO7hPjrGYG^M=!y9X6 z3F}-K1tGC{XnnyDY;9=TzoSd(K}6@j+oa*3!2k9YsW>;kJ^=J@PWuf<10(7Mg&33= z2GLmlaidEof=z5ekkHB(KL~LvGhKoeWZ(+j%@C)IV zKX@^LK@oA{$b;NZ=blVMSb$-e7N8~&LEKtQi&Iwpg0_j=>mP^z3GexX4b9MA zm7JNCcx4?MlZ9im`k0V)Z$W%Ka5|ysJSBaqOP@8Hs+Eic`l_RX{CmvxhvO@sYr;un6*jLEjPr>fD7n$hLZFZm zhbz6oRQfE;9x$0#2^lMhxRX*O{d=%pTfnFZ$T4hGFuidZuq16eiIiPh=B;jsOwU0 zL#7f5+_OJ|CB?~vpgNEbbtOL`J|%G7{(0-E-#j*vAbr0lU`)&2WePj-<*76hr4}ab zN7$lED{>(Ji;-($w*BmEo;mco+ie31j?ULlzYSdqW(3{9-yYWCIOg1g?(EQ-$l6`3B@K7$+XM~k z6NFvXmkgRcXJDJuc8738LNqqbs;CmiXAJg|h2kW36zA3Rif*Zn*0D9*gJvjB3RjGA z4XBACAb4j4MW}1q4w1pYQPc7)Yp4rU=K~yR1=^R1DKL%8%ZF@CnN~vPG*d26ms~lS z=7{b5Z52*VGQl;!xL0MunpqZdGVuLu7tBimx02OLZ9==n_3}W0#7masoaaTHI?L&S z`)j5w#{F;BHY8_5p)l;7r))-W-108{<|xDbu~6C3T)$eJDgUgp3j|Fve;cAI0Atmx zt8SXP?}au}=WIt}u~+A_*7gqlMv_DayH24hgaGC$l9%qKCY#9enJiZ<4@y*uK6r2Y zgqUs;;@wfW*w3PjRj9+TbAs2_D>CgS&fhcMlpI8g~os65QS0Ex5ZoH16&W(|hl;-}igYNY&K* zG*$g4tX_4m=ee%?(~Y~?!{cLP;A-RI1915ePOHsXym=vigjVmG)Pu-yr?`zc*W6$a zh&$h#@~+&!-fr$7NTR6atURY##Tb;8KM}BQ?%#ftr;lhNs^^>P{S;5xh0rZly%P80 zW`l;#6cRki^hIdOeX1hBWWlhtpNZfM7*0i$fS?8|=OLkqXK$oq%r1ArN*DY9oDR?%9w}be?!^~!fnD?M8a{pPgMIj7YoLf0VH0Ftjd z`gDn%-xG8#nE0@Hhh>WQRui{EJBv<33K|5}sD%vrtSrLoCxK52(`i%-D7xssm%!Nb zt~A9l_Y?+{P*M_#BJqj-sKiwqOfA*S)a-1RI1#fiat_n~O;s~-EKO7}Vdz#TxGHl& zG5<;kXRn^r2omedg4{7x+8#eRET7kjgHv;hC1DD;Q@3@C-NHjLG>&o6K5&A&s8`Ts zo+i$jmVEVA7KMi~0r8q9R5|l4;$d?0rP?~O+AiO|hc46PLBNtqu8yscX~#o-WuQ=- z!)(<+YU}vmB6GbBc$RWn(k3==32&2lje^97^OnU}%SNepXtcljRm@mI(x{Y#60cmb zi8FiHM_X3bgY&0UFfpM?)bl+*khotvFUa#U0y5JInfy0X!J5vif`+Jqs)|2k^y$=y z&WHvE71F>k;{y2}O^$=%HS8nCOA2Yiw>nJp?A3P{nISw4S4;_W`eOT3W{l7Hf+18h6dtdsPf5VkmL4PNx)XFrXS|NPt)Tu{l<8)k&^1} zKNh}^QLP7tLE=63&eNFMR&%S$Tzwwis%L{TyThH5V}E|XD}A5p&K{k48`oz{&}_Yj zI_sSZ3~SA`R7E41FAYNxxiIR_J+5YcGp&(ULksHHf@`qpv{FJb$oXA{p(N9L!jK;S zQNNO09tH`kd2|~1nT`qmyW6gzn~5ofEZrzhU*b2LZso>5%#w2EZI zT4{0hUK>vTHzBc_;k>$TPPIITYt3|91uK00+5!q^g1R!J#0VLs3Q#w_kEj8S5w_6j zoo@TMOd){|G2)^3A%F6ijchC0zQOkE;vyCWY_S%Sq_Rel`%a1{-;eGL@EoZBW9t)NZtl?&ItD4XAQs1w6l zp?J6Ir)pMd30Y<{3|=i188SRzA#tx+?j76Gq#5>0mVzSN`5p^KIi;dI!X1VO?gyiD z7h)1`N+b5e?lYkuA~QfPXi0x{92v%Vqp>K1fzDW!2f7EIt0Y<1YMgR!$$Q1|m%_sD z)bHVLPX>24$1e|dZ82|`Z;G7rBR=Ixp;A>EYWVyWPu8yEVj1xY8AAp1!9L;qgQTqcLNL zEKjliQ1h?uYQ;37qr>4<8)mT$_^6{=R*}Vq<`ut1-DAdpm{&3-e$J&wL7%2qYR~l&#?w+Hr#V1jTl{Tpa%J(82$@esH2V=FS(NRG)%sdwNx!@iRkj2a zk}N-yar`Gts!rkW?25{q-zbSZOfCalHOYn;44Ww2AKy7dIN8RBFdC{zHbdyhPIi|W z$Qx0MG?Ne@g3uTWLp@NcXQ61o=u^<1mA(*#AgjfYoV~1dDKZum!4NK0%@1-Ag{m$| zfL0_S5Ju~15G;%lO?PExODx61twIlzx^9%4W)I|r06t@kvr5v^Ryn)p(0Ds$IDo3+ zRhIKUY1`t4-sv11%T$chrT|9^P(hv1A4WMV=R3kmSM@@q>FPkt@Q`xl^3wLO!$@Fr zPxgDjm)@i&`M<#Fh39q7JtvhQhcOv_h1i(v+9l<%JWxUXC(dr-A6g4yGo~>L%V@T+ z1v3yhjdZvwfY?{bGni}0D~y(=mtH56QfQ4XK7|ud&!N}fVgjnDV@lgY_MsW5Bb7L8 z@I1UNI&EkZR^pOThxh)_K`B}u#4{|1uRKiG-6BJt&ck#zoYQ<-V9T5$3n?{gq^q5s zhlv`XJ*D!EX`lw45H;&-x3IbQ>3CT}RWYupff+r*m^NI0w%o25m5?kOBBzEXx}}P- z94ePazkka8Mpd-SB5B~ebZnfAY#gK<1b30(Y)~6&9xk7S!?#D^2_1w8-^q)m&@HC04WNj2vY2vsz-%&q|2!y@*e(-MSB&)rXsxU+M`=>~(hy3^m_ zTEtT_OJGoviwdAs_*vih4$ydguW9QpP+>4F_Zr@fk&I|R%r*;y-%0*c0b9yK5hEje zq6WtR89tJYU{zG1o6M-8{b!qVtu}jlZKao6+jydWHjZWgq8O6v2#&jK;FryYz7P{`GtVrQFQYTfqZ=+k0^Omt z1&Nu=%vitfoIkuzKDX1}`=?=+?e}S}m=D7=Be`PUyVT|rn{7;;HCt_-VuPwbGATpj zI@Cc!^O>MX`dwXP&*5sN1#)=AaAC5h>TDyjUv_^_@*5NdelzD}-_%0P&||h=-T%9S zIc{)bgFa2L`6|E+-|sXajjr6Df9=9T(~{5CL3_ye^Ou;e)c^NPADIU~|XFZLa zfW|VMpRk7s!ADN=6=fB!?8B<7CF5lLZYMKSt8p&4T{a5cCld}iDJVL)X{3D>of8&} zL=P|T$W!Jfl?j_GO~0=;>}ToBwY)$jNG~?(PM^!e3Qe)8pYeNI+nUeRrbjj?h|N4JbwP;W$= zE$C}7^n}9MAZA;^3aL&woOq?XT#OE4r+>20#KpGf9y+iO=Y+BQ*5=>~TE;JFixmz9S6rN!mZgPZ zB*hfQs%G$rR1Q(>%2sZ=#O4%xGUeoAiC^cM&rIpWtxoX&zt+oLpFX}fWw|_VUg|rP z(P+*6)pchG@we@NX^Y~f8VIFsl&t?%U;$(Q84YQ}uCybf^0!55hHMQ=alTZ|uXHTe zpQ_BEj(B;95o+`%>Rw$!GBoDO!qsM8b`_i7R{Db0m(EP@@xBLZWcV(JvTn|dC{PjR z&8SHbVIYU+;vqEudY33DpcPkyDKumG$!XRw*Pdk+*YN(X2wXYU2MvS%-3-wFL5kO>maC;QJQ3e#c%=0kN7LU-yKe8;(okCj?Xz(iU48(9 zb6 z1g-Cf)$uawk!fjUfh8eWdCAYmys5PGC92@A@YuN(Ktr0AoKF}GiQ2^esw0|%v;*rb zDtzTcgD%Wupi|0x5%3HxxjIa-c(wQ;1xt`S?Rod~QTI@=u87aq1iIm#4zHpWE>Zbb za(%R7!i;5GjSV;-DV^qY4WkTd0DgF@Mj{N@xI~;Ce94bSwVhorYL&~FU0ESFmsW@T zebECfKfts;zcT%aX|P}>e4!uks}ddi$C_7T?c?omgh+2p605(PB$SQ6V`(=js&~Wt z!Lamk0hmQ@Cm|(@;SCX{}tj36}Zt=f)#7}(d#CbB;_ZPX~+oX@3 zkjgHc_b{#;kf@PDw`x!uRKAIf)2hS=@NzMee=aF786ijaAC51qi;<^qBm)~}S;#IzPV5O+B#)om0s~p09jL6=xXDb>8`Yr6dNC;6;oUmf& zLOg04F2Ty*y1`|UwL7NufNy*rmo?6{_i8}JKR&G)hqQG`$y_(9M01rHO+c2v?Gtsq zzFnqb{LkCu;1gkQEa}^?k@|6c%4z3lLuP0m%|D};*=mQe0OWa}g($wiA%; zeV;Z`*Pg*7hnd8&k3l{WeUm2zxz+f6>)@PFn^?X^&#mtoU3Q_o8tU`U_b>tBT*$w2 zl(ZtS1H+Y~Z>WQ-X}ny@B@g!;5+zE)S3u2Azq!KQZYd(R7pA-8Ml)u=hJm7NPw`<0 zhanWvkJN8#7VvkyR)&F`YKf>RabM=_mFUT+^yBaI4P{G|EZDdL7LpODvW9;S7_R?F zM?a{Hd34-@ZA}<&Zu2qqF03&c3VL4tv{@I6&@WR&*ConcVE?7R4jM71l;s=}riR^& zjy%1ve=9kU#82>BjT*q(Wh-c59wsyMWf_|QT!KQ=iZ<`+(gVpj-+y+>s)rzZTvrOHMR7AcdPCHajO#{3GtRnO&OI1w>)^L zCtZJjv!gs8-0+F!32waLcvGCY=oImpleaEh?ybII&9nWL<&sJJ4*NLU6-Ye#$u<

hlcJe<1Q`?uqJYBazY9vi2lFSY8*7!#-nm%mfeNA0LhQK z{(Pxe$O?OSsz6hjm|hlQ2wkC=ogU)w3f|djCM#})G&zCYLS>k(m+o%P<1-QIBqQ0z z6P5z4d3<>a%^KzH|8c{)u0guzr>VZe4xf%wP!T@p$5H_J8F+k3s`;S^JpLr&a z+$f_AM@ReeiOU#+oOk3$F-LM&j}>RO6EF-@&q$r;77xo0ynv>m=!cN{Wip+uK1%<( zC4Iey>%F_1)BZy`6f4%$rucV=?$Mz?D^e6czvZZ&3s6Yk4wuHQdnV2D3rMWzWyXiq z@f*)dmltRuC8r`|vyd}1QdYK_%=!9DQY{CQAh;``wH15wKWUJpmIxiM6v0^ukLOHw zH5f)lYlel&Sr|ZKk@~jI5Lp;35o_N zDKUqdlbvHEWJJnt(N1Kv&^iPT#?|Zx_W0S@wdHa5Y6lz(D)CYoyf>mcK%YSoB6doP znLO36kSSbt4tE$G^s**O-w>VxORIBX~$73B}ztUD9JJ2>(*EFX7AmFeZ>k0)DZ@G)UvOfSXtPfluII!3wy}z+0 zkIUvsBL`yemWhgmsSq)FFmEC}QBA1z-Jq)0tnLIYaPjftq7LJzObWnKWc3*L@&`d7 zA`W8{jiqa}c5!^ij97W}RY55E34o-`o{>cFB#?7~{V{%+mu!jLk6~yj-H-7}eea88 zRxLt-`eKg3SMAg~K12b_DgEJTO{Pa_!QK&duf_JJ7dKNc)2<+JTqvu@ni`N<9qRtC zS-q%-QUP{q%B+TVF*EDLrR5sIUS%8v&dh}njy47AB zzQYz{aC5FS{JBLLSEw4tJ1aI#5jpP||L99|dL#!EP1+@TKTV!_c~SSscixAE72uKv zS+M2_+^!!2!D)kYCIKZu1ufDPqhPN~dOq$R=k?Z@>mWBPCHjf&cc6>ntu)tpoua}-% zC|p zy9Z*-9Wf8_Pxzoa(0#L}uvbq=md>$l0KBd-b?TawtWb&2X=sfkQZ2B$MC@Q&*Fly9 zdaQyZr(%ROVvC3(OKsqJkyHe!7pvumG1g7sQ%gb{1Sh<~K^82Y3F+xrw~p}Vyisu# ztBN|v5k6Gb8l#`^X7OyzH@y?FR@YqGro8*KOO``2fR}x#ie;Hly6+8-mEUY^e__xT zLvKXXX@pA-2#*MP!u~8pwp7PsVAmwm(-fCXunLb7K^)QYtvgu{^UL2Arl*Ip!H~UF z3tI|fQJXJUN1IM&aSvDT@#m3O+fT%VX2eSFk%xq;8$z4=>?y#kAhGl{TQWQWHS|jf zoZgARTuY{kq;mi~eNlR>sH{Uhlt?35_~S-4wGEI-?3wp(C# z&StFSc}V9P=E}!R&ZtuqQf?9mW>E;|@)G0@?`ua7uCgk<>dyX~?1VC2))O*Vg6`hL zZbXSJ0J(j){6i`^KTK)oSS#2654XB6Cc5UoyVchqx7tC!NM8i$U$^=wi1}Z)nkrn9 zUJik2ZYukBKQCkihSm_knm_9_Q@W)>la$$I>0j}q=0DtOqjoRXwF$o}b+{wo%<;_j zt1mnRGyKq{5h(=t4`Ja3w#GtCB*z-wNy#p9Vn79@2y=TV*!>PM*)K<%P+pT?mVHgCwwO68-eT0!q0>Hp?|%YbfrTj{u;1a`g-!acv;-+JV?|r68YwAf zd~%KV#n9Rkv$}I)K|mmDwd)gHhGi-&9S~r*r~ax^v+H?|;O3|*HP04SP(1}r&mtv?vS2`cA|?LC zl%jt(g2VJ#!#fi#<16xOl%(53R_YwO+HqK%5^|>J)8W|9w<_7N>5P_~XbL9^`fI{0 zl_qHT<2#u~g?A|yzfpQ+OtveviM8<^$&wR~K*^lW+kE%{#pFhSi`r#>fveTs^t8{%T4}~7qfY5# ziH>~BiuR>0_W1zg8x>c2NZ<8F*6b2Ed(gOX7tiYV3&N~vY@e-8hxO#*qH&pY75U<} z+x+tWlLxr+r!+Xq{a5T?Uy{*KTT` zX9XCzyZSK@H$T00GY>7FMOWGJo~UuMd$Mx={5~kaer&Lg#iROIVJ`5N$&hB9)kN8W z4={Ckv-QfW%*dH9Qm>kc6?Kucyy<$z0Tz(v#XIdJtZqoc6m;Sl$TuezeF%qRNcqso!+cGA`T z_3PqYd)UuErO?{AZw4h^SvD$;wr|NaUcH7bEChS*6s>>S)-i7nfp|ylrFvE4&1}S4 z_E(bLKtw*AXZQVcTIkr^VG_Ys5Sx6x(IDA#Y)D}Nh3N! z1jmY-SwOV6(_9hGS$kw#gH9i(F32^5(3^BT$jg22o`|JBGbt^Bv_x0!j8vYwHO_>Ad+r`Iu(8VTK+$+cdk z4Kg$J)+1|B?Gu*A@4zdE8CK{|&hmf`ZE1Xl?4{blCvB_RX>T-MSR9Cj8*Z2)>1}$0 ze+mINgBieHFCPx)johfY!UGq+ge_P#(}+g-#s??1%9E2nEnc7ngTOVeCH1C?GaJA$ zhbiy0A`;g9gRX&f7T~VC^2@LQk5FFkS)~6s_c6Sqo@p-sw8Yb38_haI7T*QtPHfee zF%ER+`^F}I2xFke1&5`TS8QiK^V`|=JASRtTZAR#SVQC)8b>7p#YJ>*n+f3&O_EP% z`^%Ga#mnL*dHXBxic~uVi*1j{x!l>|?EQl3T7D+~omg=5!b$ss9g8)f{6KM1yY>0* z2go&*6z2hCQoi4P$RTo)U*!PAywV!dszk3tC__Ijh3DLa<=(~#Y`+8-)Zq zUB9`Q2@mb9KD^bmRK)K{BaUC2J{UVzy|{rEU6XVg(_hd`&Mzvzy*)I@TN{1oJETjy z@9#!q9Q2h`o!Jn2rd7Fml6t!g1fAOH;8XeiUN5fR9k&e6 zvp)efRVUgvz@|7(JDAr`s7PGAEH}x~6+#@oMaV}%hAO%zZPA_H!$mw#_Mx!tP9J|9 z9{+s9)4mzb*H(xHv{)Rf0h~SHypEkje}$#XDE!H}cLAxH#cd!p^A029)?hIFX^(Xx z{Vh2+lluvgxWTi1ZsRo3hv4KjiPG34b8G}Hf15a74QGEW(Lt6R3GQ_uljUoLN) zulJu9o-^G?q`>8SCU@6F+3H6-KRP;FjGm(_canUFJZD!A1{B4}LO3r`FM`Zla@;R2 zZu?rU2dBLpMneD8bUX|$p0@6!x7GpL6mX6?v0Ys}8Q$$Y1ZN5YuHKJZ zxIa#Lz9P8JuCJ}Nz3na+J#X(#bl)VwN!wiP4XiH)KX}K9Bl+COW2jwBNP0O#gC-kx z`lN*Qy8KrNqTTpEW$a}{Qi`hY z>OMIXc&>dMF0Zc^DP%IPElY029OpIF-aelD3p`+)FF%Bz^$*mEdmv@Zu=Rhu zE(5X_+Nub9Zq!eZ7Bj60&i5KJR;A?yDi}IEny&5c_Jd6s8h^BV?VR(vOIxnOUGP^V znIZw#eU1?kG5I@?q+QNW8Tz7Yo9h`L%pbO9gRkDZ(;g;~5Y>-g?s|h6Fp?gC*01D9 zv%grSp6f)D(=9NG@&J7<9f;)Xsi)$~yppvL0<^6?C?=LTt#sN)Yv8$}$m z=6}1Hs9UTOsAv~h(uQSN)w?-)AoN~xXToe+5Ga4|cX2M)O=ZAkAQX75zq&Fc;pH9vpT#RJEt?})OnS+yk_aR`-iu#bTHQ=d*(xexT3Vbx z`WMy%E81+_#07dP@o52rdo4UPNX|EE-q-p!=l*Xk9vAODX)c(631Xj>)pcDyPhST0 z4Sar`74Oz(J8$T*7o>ZGA09QAv>h1^^3Ir;=dG?cXXiWW9uAL-YySOjNfw{arQU9+ zJKcZXot=rpRc-hzg`~M`FqG2`51_tyJ6U#~b$EHbv}m@;M(>Yo-DJ$CIe29KcxTgl zckg0IwD79)c<9ZmwuPBo#JJ#1jcZtag7{!}ae3NbTWz_g<@aU<5b}CG-tWJTv?p{D zytQ20eGI=nMV=GygUXS0O@(XKf0^tBOffK9dKW&_VxHYJD$6O z1OWWO1<*ge4tmcvoSj$moPAC*m(w<=Z>j{WfNy6z$3;o|6`j_=_Hk&y2EXUqX|~te zpO*6F3+Z->>^oqem5gcMCW8@#%1H`_;X^GyK$mg|FS>%#dK^{e}y@cY|K zbbf7*ntE5$CVD!7Ul~7_fF8rR4;+N2>s~Jhue0)J`|Ep8ZRfUScpb0b31`m!T;BJ- zdBR9L9PPgkbg#|iRd7TtKH%_rJ3W2@@&cE%T@!wsF&sQEuCD)q;PH6B^MB}^e^Qrr zNy5bW_-W@uipR^V!EyBdx^iObz(si7UADez$+~E>azzLXA!o|1#r-zdhhDP=oVfPk~rn8H42+wa9dNybC9t>MeMFbnY&jjbE z8^1Czw|c$md~Rk9JUU*?A1>*_o=)D%k{lQ?fGY&u(C1i3(R^ds-tqU%BLDrVW_iC*H09^j&vMxcp4?d9S1HA6k? z^u?58v?!s0?O_!d9Lc8z>3aA0E?Yym_ulrxf!e8kz152Y0Qx6*1Or}_PTRe0U*@?5 znmrtz`rGc@UJm&4DFOWDK+l7a-AU5`!codrFI@Jm=~+~1*N3LYh6iq7Q@h)h!U{S$)0x^nwZ4NPK}yXHQ%izuk6zvHZ9^+hKFHKbQ>ktY$~*Y+v!ny3g-; zxw$7EtMY1n(D$~pyV$uNhdz62zji&C?LNBpQ<5jz``V=~nm=2r<3s~U${Wd%$3597 zM)U6dEsySl=aI-$bo}|26eMO2y$x~%5_GzLR7g9i5MGaa+Q{(j#ur6%E~li%KYT=f zXmhv>RE`iTuTeyFi=AxKeb3iM6toeI*i#!?;#n%~#g<{9e81UN^IZcfBsWYDcc@K~6~+wGdG*nU=R-%@lU4JNJ3(-MmmA0sVo|1H1a zX40h`=vf3SJMpIUCL0R+Yrd`CJhr)IczY>VmVu6E6gUYJAg30xGc)vQB)OOe0Q#i59Y zqkL&4Vjb0|)< zSSxD?aImY%(5xb-NTIG`d@5W+aLMu5xE+4)Wg#Om-IgWz z6uE}Dwghmy*(s1E5sfDsd+3Wtd=av+*`}43Q0F`kmHo@i4ExeD+L4$HBQ;JHc*7;E zVDvxLOshh&IkZ?_jFfVK_u3%LH|OpcKd_wRi<$f-tQ@q-UtQW6UY4@cD5axTX#c30 zHHJZ-%_^9tUU;UDU~@-V`v^?A9c3dc-fC-@iX{*P8H**}Tm^k?zsPojW&W<=B0{@o zBD?U4-KNz5_jRO4w!Jm;`Vt0Zmd+XoX5aYGK;Ls5pM~@}J~DE=xGK{cCUP!IUMaV3 zsAW%|X2F+U2J#bf_~$;cAsU-GEh(zVgcY(QZ1|GqnKrw$ztBvjcGM(oMTz7Jc61|r z=z5r!w6jjsddkx!(rCD!byOQ zmOCX3E>X)$`6r`KzB*Xkncx8VXJ0(f&3wt(~hkn_H-3?uzCyD?dX= zpYwazyO>+}j;64K2vY{e3qobH>1wn-=pMxt)t&jKVU#lPOX$%tU0 zw9JW1TW)I#6+FYIXvwPH@17*U#B?Hz9VX0lBikXVHO$rvwJnSK;866fySdqCa$!n> zqZPt)Vaw)AyZe4i=M8II7xa-=tC|?-0#eogH=PN^Osx*~8C44;XDa<8XPWQ#4HVD} zWPCMgL%~|>tmD-0o{Br9?DfMwmE85~y?_n^1D_lC+s$;hQnM5579Mx>v-hT)1(r={ zkr{ka)Ye;pBoej$s37Xbk-%_DhM5x}Zj+iroP?^ikJWG?a`JD{Eb2JQZ{r+`y|8u`t4aVVC#<-;wJ?9Yx%)h% zP*2bXNQ~*Z?NWsJjDmfg(E(~1;|#6QI+Z|1C@8>`>zh+XS9Oa^vdaUsF;mroBeBeQhQsd6Jj)=zzN7e@T>^xwaTCmbzkv9b@2mDwI>M}~T=9MSpA4#D zwZBVl9DrH?d^ZuqAXLL>%W_;GqwU7f#tnw{@AG)1XoSyO%x4wzZtC(I(nI9ZvHARs z+)Ld{g}|l^{q5*#p{WwWOl7qmed;HPST5A+qh#WDPpfKJ5R-ZQznIL3|6(%5;R~TK z!XCQ)X(+f364o^i48P=%^RYvvS;7v_3$sZh^vsiZsJHZrne?(?+93|4*%nr;C4|l0 zTby%$hhT;81~Hj9CS=WBNkR~J8O!Qy1(Nv3huulPl|D7#_4{YkqdNvb4M1?--Fe)1 z+D9T2C2jVV${&NAOuql+WVZW_ag|Kj6@%$VJ#%ubx>}eEGuG@`<#9+9?jsV*NZH^) z3TmgBPfJ;=x$|mkRrZrBeiC8QA19x{!eONT+>_!Qu5JiBnTVNL(TWZ2FQj`&a^M`> z@?$RfL+4od9G<7sSN5Dboi4P@ECO&ijV?v;oy{=*EG6x^OqN2=Yc5aLCKFyuzM|-y zmG1|cO3HSQM`8#l7H4Bfee3!!lllJtz+@U$C0PFtCUdOd9c!{Z{^FV|2NNAjtD`vb zoud>E0m_|mO5BoTS#XwocLPV`;BLW=y5v_;Pj^{A1tE^MO8>eJ7wL z<@>^Ss9sgV8t`cF5@FIj8wXVfU0yEJ-)Y3w!IMxc_wFGyLp(ZDf+tmPXO2@)EPTmF zM+fZTCM!8qPHbB!p>c=VV7HM|=Vmo=Lt=P_#CKLKa!b|xz||0T)$+hrT{P_yNL)S~mn^mmPnfJg%Bd}o%gG-Xh#RHK-Cu$|V3F+zswVA{#z#;Y zR#OqeWKPGVdMiuKaY$DcGaLI)D@V+>Exo~wvr_ry=vZ3M54f*jmtTjhX}q{u6ca<^<&x)1^zk@|P?rJZU{CB_C1x zDOK=$^VZS0od=UnYvQ<_!)kK}R}`tGkJA=x+RjD`6EJSdxl2w;p#y47VF_j;?BHTB z(vSr5?$S~w>@OuVX=+SOwuyV-pzT2l_75oV0NwbBx=bW$s7B4$ayCA7E!VqgbfAQ5 zr!f0pB{R$*fGvRcC{LbZt;oa>R1#D!cfY51Qx`dEMG_UF{5E_^y9v9f?}3)2yh&66 zh;K_-^cWd`84eVCuxCUqg26V0!5 z1kR8VKd=1CuN_RZKs*>!7QsN$v{s`2bZUK_lD_?qlzH=)lu0f6zobluf27PGASn~` zoxnhx)~tesTBzhZw)SNGWDg3?7qN!KKH1nlNZS;J+DN>+ys4*rR+>o+=SSr4S1Y=o?zeU2vedzl4$80V|b0L{9w2P(xq~Q4vDj17{&gs3W=D#z_YWhRujO;qn|NI6bGaGDjnmm~PGBbxk zX6C~`W~Qd^+0{jI@qz;S#ebQZSQw1jOwSG(8kdeo=b+0Na6~9hED)L*;BT8^W1zso zP4BCbYCWG2l8{<_kO@LFQP`umhzc-5aUmC*rQ)?}S&gCWuvgJQXr@u$n?_c8sEXnr z5)hin1TG_QN&;p-TX%sgwnxvVbgOp8lNkSLaDW)j7&U_vH{G>%s*iET)umMbD^jTW z0uDqql5v&h%E4aPn-M6CBlSQD;h$1Iz7^ou!KX9{XKH&13N zjpwWQ|FxN^1Tr(78aQ@X0tqf{iJweNWN&5Hg9OjHwIsbogD7djA!y<8wu<}$vA1A^ zlcuiiB*)DxfVV59X2pwbxg``r6b-`%>i+XfQ~P9^8Mch`ObYe|1tY>KtMPQCGKDmw zfp&;W4B$859tRt_l^tKkn|pjo;*4en-4P_O25o5?I8Ttv)ESm;34SjbT6P@j{o`gn z|8F-FTf~okVLh(867`E30d0G_Ho=s>pg%Z*H!rI~(gVt$Z1hlmtsT!VeQN9O<`bop zx^&%r>{o+4V!xEjM91k%@y~^VW9BDJNl+S=_J z_pYKqY^G4VF0LbxEvHI?FExiKrXaXTQ%YX5VJtg2hZ3D-5a_8SpluR~sZo_|UYVDW z&#&=Ref@G_SLX^k9U2``>O-mWx87J)g9=~Qbi9uIG(^Xg;l?8Mbe>u%Zok|N5qhMI4L7u z^>kzleSXHLt94Sg*9^Cem7#4{tyMy*s!PhvECJ{*I|3DmxH%ehe_n)i5htq{O6o}5 z;@KbOO36)U04S8GQ zVo;-w*v1lk63Bd%QY4@?8wy6G&Lti8)+U`vBW-axk937=XM)lxDD9E}fl7sI}x zfcjpN2>~9Lj{+MOxlIl#D4Qjnu37&ovr7*E%DwIknwf&!OkV@AAVLDHp{}1RfwBDy zI3dHsS_dTu!JiM;RZ6*!4qUP(5pyp7DkX)h((9wA=TwY}R;{WTEMeKJ>mrsYb_ z$t*#y!r^h&L$qWm$;nM^R$uK77=p*~-Qtm(iL}RivfU^92^RE0WQ5HB*53*C-{nk9 zK2f$yK9;*zShgLR^ZD-WToom=!x0x*q!|dsLPtdD7N;+2R(L3-RRD4tP$MZj`+v)s zpc4WTQ8XjCW{s5JI07fq4Kd+o7TDy^rrhpfC zyO9++vAZ#B&knIc*uyw{+Ch%sAoJ2G@-(E-i4BJB+G15#zvj5nKWnp`unC>wNO3+J zBjvX=llp9RFtVR;xIgt((6M!KV8Ju27t(OvXVfL9QsnSHIYFtLxK%d1n*e>@0g6Co zqtkEe{m=A{$L=|hdqm_dYf_U$XH%&Cu*@wM=#BQtHLegm*wPU?mN2db`~K@fqlSOK zcO_V}rDObK_@XSq`wn7!9n&Lrg4$!ryFQmW>xgX9_*aoDC`QGkRiV|{{n!F)k5&## zoVSOr>S!g%HqoMUh0?QvM|p|zftTg;++=K-I@7qB3upMA&beP{tf;-8o=$Yzf2cYs z>K0s)<6hidGd=C$PxvM%ku#x{S!@PxLKfsyI zDfU6+G)0xB3XCL1%@xw#4mh`^ zNVqlhx~jok729leqYpJw_fx`-FHZk8cOO)L9{}#Lg-JG9!@)@(r0;9h=n%3{H?$Sk@Ef za%|Xx*vQ!VjSBHTu35Pga!TZ-EiSI-!wRsjEYQ$W^J90g`8M-!{%?1YLiJ|8{@{q- zf}Aj>@z)GJh_JP(hDq_boEuXjQG&-?4zjW%Pv1(G&aA@5{Ut&68gJPzjtMdu-~jb0 zSjARyi)6N)Tv?NbuD#aFuME)gH+l3!tE57bulYhzReOx$)rtE7~F?k0sun zV`rOGqQqhcj=eN~axu3H56r(OB#yw1-k2JeoPt?&hLusFytu|r5w$W1XG*+)ggbRx zL0!sT^+-y^#~BCfQB7tzf?Sp2P}BlQ9D6dJm{zZ%3#Uj-#B)MKeyo2`R%B>28IjRo z$bPw?-24Nw?cw4ciV-Ue!<(HkxjPq9sqXo02u#Bv9IFDkxQv0%d$mANH<2uIf4&EW0s#v3 zcde=Pm8;A#`a@!y40@` zJ|}Y=IiiGOW!XfFb68;P9j_xK&JKtbsG^Fxe#LSZ$G3_4;%RiTUgC?UaEX1u$E}xKVKqBB!O)TLl z_VZ-L!01eQbx9!k3~vFP%LJK=d+QSPvEwcJFiR9%oEN=t^*}0q6)4r%-MG?bnW`_b zev`R>jF2k%XUo;7RA@w2C-SSVRY`s2?GjlRSYy^OzO~x_gSfwZisOqPecu4VHMj+L3+@u!-Cct_0m2OK?gV!o2<|et zySux)>*4#``<&VjPu;q=Zq@t&{bYJ|_gb&dd*Bp*$t80#{8A%#)m{fm`z$%L)7OnQq8`=uxh}6mQW#HEJt=e}7-J`0a^zQ5%9F7YK zq-bFsE*k<)A<=;R5A*RvcaFC#09dLa!o}}`T zM7dEh4O^~LY8o7%vF%we802O0zQ?h6za<+gEm1>K6gGnqe3+ z3`c>;5Qv`1C`}#5OQuRU*RS;?tA=C9Op)REB;ffz=lx~(-7#AiCN<-BEQt|a8F?5f zQA-NTCdG~{y9_y+8fv`yOB3S|%@cDRKFw$8QhXstsY>1=D222!i&E}6n~_RAoWU~5 z^d~L9sXt*Byj&;!WjJC8L&LRPPk+@ByDjfqY`Owz-~}w~PDvvwqkc5UQpH4~wYoch zs#%{xn1w0gSU)xd2=Ahrx*LB-R(YUZDh2&-$rRtU2q|82+i&Fyz2EpW0v-)Q!i%i( zjCEC~h`X`ciI+6M+;t;J#M&+2c@KI6&nW$~Nf#qHn8b?NEC95AmuW+%AAC}wzKc+E zkVR#(riJ%})&D%1iLs-#MUIx0o2e9`>+HRo)`(^Q<6Rna=Q+CxnB68+q8opXmH$!w z*IK~H`tyr-V%uU?({Y+I6be@I|Wj<5*NxkMKQG@#ci{z$R}`-pGtVQr8x7O_}X`V7aRj@0S^(f?EVW$qB=B zTVvveJ5hcW8^nUpJJm0kyDY^7oC#L`*!xHC+0m)@jto2OkC? z!({)>U}@VcQI2Cas)fT-c_?){T7Mmw9HB@gMC5C11TTceJPIcZcaP42247C8nn!Di zU%Myo1^lM^Cl3NeNFj-XYi>x6pwS<1a9? zN*|nAG`S$*x5MGWU*f?xsSucuxreNp7f_&Ynt79ag895hp{kQP$*U|dzcARrP;?2_ZOVpVk_qt%Pu{ma$> zkJU&PC^s+{dY1Al7#-jC9K5kIegR|8Y>WSEp6zD}PxWsuNt5{)o^+!^*RIGjr_>s}a!d-v7O(AFZwafa^ zOTu14II`5efO^xJbG`SUZMP<7Wn@ux77RVc)vB%y(9|@J(7gOdtP2?T8o;L*gAt?g z7uFIt=l^(R+#~jbd7slSz1=*Xyxdrn!)JrJbPSvHH?zs}JC<=Q^eCAV$T)`D%HY{V0Nvbu>v44viMSzJxdA{0 zhnUGvg)v=%^qlBJndH|1>)NS^ia@G^!QDiJr`7*c6gk2##C^^i&-JLkz zf7%_xm_^x^8n>ZIe`h+xn@NEk>KBSA`hs2J{QBxc3rS__)$!}2Df=2zGLyKqIUORA z+H=%D1YifBV>4q}VS1i11LGNT1&whIaK1{^?Mq7+wYx!K@nYiDuxVMg5z^xPDblG zy#9uzdt&S7Jn>cjR0|IZ{}=N4Yn2N{y65;jUJMW7JwEHqN_(@)XPVLkQJJy5ESx|u z2O9CXi))8r-Xy*{))_5ZB$cjr8EYUy9(zc0URi~5C2uD-pay&M6wsJQLfaf05#g6E zj?tDx*03F^&hcQ4{KXyYRd~U#ovLd<*lA>hAcTcGN~WrWarrj*lhjz!mx@tCfr6@l z$+P@J=>VWYx%7>-DZ^O;n`3MWA8EsDT2(w3tJY$BxDkrG*xTU|Kz_WR8UA z8-Qw|8*2!UH@wo8@PJZFjIIXjh}*dp0vjSbTg_o)3Una6Xr(&RieqA<3_@zny@YE(52dY5=ZN|#Vc;cLH|{WO}}Y{Yu5Bjqzin`vcd9vZ5)NRP_FGZJ-3*3 zHCKe{Uoz6bLrRM`O&K#WWvE$hf3ZR`1OZYQay0Sgue-S9&##Tx_lB@2{$vYf?oLQ3 zP@Mm32QwPk8MH1)2{6Av<|?}vo%%@=v`L_T5Rr-|D%cNm*BNCOIHjXo^0cYvG%Ski zGU1o$k1P;t1mwz2n-VW17$%8dkXFl-jm%-7YUbwXRT2%DPLCm@Bd-btPsJ)MNS6hx zmg@l^E0G%jeeLU~)FwF+T?6yS|L*RuY`>?Ttf{m#*0t1e3z78vCyHJd2Y>n|$12%H zl^^v%=`&2YpgT$r>YmKS4;Jj(4PZ^^$A*3*j{-)NZdGH8s8+-^7x6>m{TrZT6)F1_r#OyQ)o`hb(&dRO}zT1SbINLN&KbCSBUFUx7b07giF8 zAZ?-s9M0<(EWWw>F)Qvsx}wsRr>K^3@!7VdnKM$IP_BGrD9N;;p$xbxX%L{|n3ML> z{{M^=T?aaXA%Z^_IgM-qL+$S=-JO`;blfSzMVl!<{E!E$xpum&@l^%~Nw9ddP@`Pz z4`8#;KFhmQ0l>YB3XKg_jm-Iuq7#S)-tFfXSqAQ;%mUl+HVu}T0tI2q z^1x%Acp}vU58}$nhMz1q39C35WDslEhi!H9nI0mMzutUUT#TiyG^M_u-5jV^IUmbJ zO5{)dj5d(|+xSPB;=&_Z`U}T}*T49svUNCtol$89 z@L2b(H=jz0g(NcqIu4UxEz5VszA#CZp9Na@60p@vgl&s?KI+)&XhHksItCYx$XVvC z2|0AlLYPpDZYM5@Ud^}ncF06*TJd6CE(#WJ#ZK#&QN|3QN9*HJX-U4z-1ZX!<9N*a+zH z1%lsjlLtmfg>U_*k^}PsPfm=i#uFj<0w5U26QO@89>2fmAO*|s5l6tdG=bze^~m#i z!dbugsq_=2s3yFNVJ0GlMW&i%D?&vhB(Lh+F5T|3{5ur5CqnLPRU1iMmSn7!C8CH^ z%(&~(N3!mL&!qp#*mo)on-(6tpRXnvU--A3-z@U^1Lh8$oxG2ZH1W6Jm-OwPP{~Mz zjM$K4#Ny`9Hs%+|iM*|G3)ea>x3{0cen?Pw?Jle(L2m$7%=E~+ie#z!jPxx!rbh~f zK3!k0B`+<1pBHJWN&;M#W9@)CLIT)R}iLQP=Iw)2qRZVLL2_=oT zZyuA7;5I7lQO8tuEw;&sBQY)Nn84IE{_8&-F~f*tG(rj#yLQLU@iZcLz8Q{!w`*QI zhmn0WZ(gY&Vg%S58mw7@R{-Z@0g(>}<4+ed1ULgHN zq_46q1w;OUp8TdRFh|H%2szE2kRpeiGoGqubl?}6Uaq#t1iWxZg~?@(H74nKkcSy? zjG9NNHkL`HT$vYhCSzKvCAh$6zfIO`9Apunh5{hlSdeNLBI1QgG2ZEi)3Sgt(I3=( zcB-Je2=(1ROpobEEIJnv4F2BT$002x$;kog#1o6?a&Plw(QBQgEwWr_`yQgdq2(Ai zg!LV88pv)plS3W3_c;}nlA@ZWT%n`4#JO+Svcs}^dVb*ijYWF1?&)xW4|@xOOwl|V z3G3q1#7ZZF4>!9eVl6PqYuP%#PONXY80n(81zWi&6jEvg77!Ac?b4EheqwtGgjxdxnRGyacqN8k62v87ACB&31MU0GE{CD>6 zu<(vBO)dS*OX>g_Mj{DgoKtuU7p4_Rpi2NtAXy`1WW1>Ts9b&Nu&y17qY+&h4PK-6@7Qib=mUdvgmM z%<$20H6VPwyK3gG%-e|%7AWS35kdY(Cy^BF4?TDHV=^KiGNEMo(#+PrDCsc%s$G^? zpjiWGLu^e)wvU8Z;y*gDWU_U%e0_YKZvllIyq8bhpnucGKn&f|fBkJYz8Blsd&* z%K7+JWQxBOB)N=+;AbQHPJNW3aWBD?6({%UklGy)FAT$N5j}b&(!- zpCUxl%YcN%$7-yF;8iWTAkn3dg^+i?Pr1>!^HcHK&PGe^`07CQZP3TZsRbigbB4~! z#{wvPQ6Dn!u&;>mqxb#KV)nduaQ4x`MBXEnH+{AQ(b@`AGNISj2{&+2c=;q5dl2M+ z@Z0Ug+Ecd}@cw&zO0NSSD~pd{$00N}^k0kXL9(Dig)z5Jgu=_~6+}oYHD}GjWJg}W z)lwoRDcVxMY|&R7bkPoLnSEYWUIkf7H{8=F>gPWi_vz-)uZ*><0@&Wy|CkbYUQx%w zrLDd_zH;zn&Go$}8nV&fD2^SA#CG~PY9QfU<0@FVzCV0aYioN=Gx5I{*YbYYzTed1 zrw3Vr)Aoc2t3~65l**No-{lLBVan>m9XG)z*_Du1))zSa0sbFzu z*tQbJRI7@uMY;TD7GL`ol0=G zKlASr@b#4}u~>0vdr$S)g7<}XDWcljkcATEia2Gu&#P~uxPWWBP!)k_eEe`{!h-U+ zYn*_mBk@{wZApe;hca4HQ36phd)2EAa4|G8LD@KNF#o`bvi{LbD_OHdy?f=->iV7% zgXlC+hMgVL*xKk+S513@^Y!6rWZ{7N<(Gr$gBjLgvHH@QoVbKlU29{0xR($6d(+fa zv%XeW+*$khWTm}q`it|{-Hdj{P`5YEI{oKA)#~CcL2!Bah@qJ2#nY-A)onO^%6{7A zyPa)JgPD!R#ao?6>1;OVnBZ#96-jOCy2#s!J&9T;NZO=>P+P3J4+3v($P$tCH zl)tu4U-Cr-dy%4CC8i3EE~6hg@Dh)`?gdqHH|Yr4u`-z(?E6N z%JMn^W68ba1bzb=0XzWbjUFJg;V$|Nb#_Q?!1C$u%Wr-;;$QumD#SEzCkXdPh?<|1 zhU%~W&=2&CnU@v^JJcB($O^2UQvC!q&JeFSq+A|VMLcf|4>__u-@C2`uxQNL-bGv^ z*YXqD?3v}Hjw@ypdHtRR;h%LV097hYGAcM%X8bJJ*7^!&g;oVv3fJW3q69KayiKne z6V7&s#y``T!|mge=7IwWP8MuK^$Q`e`K5n&O;g(c7YAM%J^#Dg^KKOLkkzsG$$`_x zGR_Cx+(UK~T3Osg+JCtrdiml#iSxPUgBO7I4rLdGQD!ferVbXsGNEZns~gAXOm~iL z1|NJE3B{Y2wE~$PqaUstA`;d!Bk4rvm&h6xax0R2umBG8J8p+F!lPBU1~*jHSvwmb zpYHQ+BsK4`ZrEFsLsNrdnoh?caZhIZACC^Pd&O)PRig@r^W8cgHo0{?R4!BEqlCXc zt+no+t;1bNZ3PDd`Q<~Aqn&uD&3yIwsC+9M+f;X-2ZVTgW?@h&4N@VB<2pdJ@HV zc5o(eI9r;iZ=ale?9WOLA8`$9olkQj?cEhGVuZ$fvltSyoO3EIo_1pHQ0;Cu9c>7; zes7&BR*a{*7;dcayz-Y`-D+^Zw|+eNeB6Bggx-8(HX21fDX>tK)yLJ@faZ1agJM>@ z=O1@tr>%E_8qo8a!d3V2MDdoj(Afpyc~Y%$vCx~3 zMbD3yyE$14{xrqpN9v%yX57MrW=v4^BZ<(#;q*iW;KOCS;`Oxa59ESejRm5xH-FEJ zN2b?VjZ}@|g68o1K?3&;_9HY^``%&qs47oA?Aq5X(`iK+TJ4LIPYjGy$M@nak6_5t zbV?Xq8J;gjlI6p6x;DW?{+2Oyh+zJO?Vj+k|!-XJBr8`XSU%4a7!xlX1Iw9P_ZK$4mh{a4P(5X6Wx#Nod#OPha_%>~1U zJog&}x1WffswGdtJRbXVUQ%l^i=OBT)Xv^$o6BCxqhdqJGm5Qm&DfVxh-tyF?^5qK z9%u;wDM3D`r5`zLZqh6yF0rXuHT+a`7QK(1`+B@!mC<`D-YG^amtc1`Z#8;S5W!{e zlixsTeR*+xQPWUcd{g_2Jc_lNZ0q#^F|2N@`73H#%!g^F01gfpO42Do(I)je@{GU4 z-TKCvu&-2)e81?$$8W(z*Vo0j zR8(EQcHbJu2cs9-(^c@x?a^m(xZ|dWuaWb74G0)H+d!Vc!P!_7`W#sOr|RamaLKFL z)KFmP;UfK^1InS9K=*}63q>-ZXu;IhvaaQ9EZkuQ;_A zECq@&p)p)=__X@x{a6WK9F_N1*G=y2HuE@z~s5_sPYArrY6} zTR~E0kL0V?U&Hl~;vbynm6i72&yw%f(ou8^uU!&^oo6Yalz@ov*Lc(qblvm3((?v;#q|CY+N1L;AgC(Yzb9=@O0j;LDeTTMc&x?M@>yS4v zm|D{P>M4Os@`3o2-}~Cz31@W0{oY&Kr=c#*;75}r@Zs-Y{gx}h#a6@{^eb@d92{_9 z{q=9a>s^W=zo>`TO=DrQ!-ic^`Cg;!32OH~`OE##z3>X~?RsCe);qylw?tCWJ$qGa z@#FIKqVM@Z-~0aO?%euy>&EuAaSYp6bl%_I5>O|`YVHbC3`@zRc@qCv;==CL6?S85hR zn-o`aR{enF4@cLxr2t^#{chJV`J%pG=G#G=hsDYH`}Ii&HnEQ-*I!NST8 z`d<34qq|nVJvbtUT>{wj5JXh(74N-2rX5{9IFiJhb3J2kg*Z=rT{3YUh8Icti)#-* zF52Jpu?_jP479CZ8~Xyr{`~oH?AhguIQgN8Bg3=Zg9GGRu*zMl;)}^TJ*yCs=89=> z|Ip!I_NuzP&($^5*W+n3crz8rA`b zr=M_fq1U~kyl@~^Cq%04} z7$Dl{?iV~KTJ>MExLW(};<(eJ3~YHYzBuH7xp`RmTyL`&!FQEBtIZ4Nmt|w9&%cPC z7w-CBELX0lkIxo-zOVPaZY%uRbv)?1)>l8f>zSJIBsrRIU6{z?PwHT-?=al(!|^sI zxBb3)AJJ&wS>>SVFpFw)a&rFg=AQm?`*pLghmXtOi}<{)*@gTj9agYi_n>^G%I)RQ zwFC3z$Hls5b(edx!#{ptZMEfRA@lWcuJ!YF!<~1zK)3bs>}S6o$oP16l`7lx3S3$D zNj+agtuoX+uXOUfdRaF93T^M*=IBloYn59xBFm0oOQn6*mU)HITTgg*r-9e z+X`%LdVjwwr@T&=#af32*f^}}18;B7KBonoWKxc&qb^6io|?bBY#!eAxc>QlQd#KS z)DG9tvFMZj5YYB?vj2^t!l%j0{c0Wj_CU{d-q6^1(zAvda-fP@nnAh;ziD^z&Fh?d ziHYxyqT{er7F)mX-=yh97HTNcH?Nx^>7GmIqVDH%Ga4Po>Fr~fC$wbw@OYx?Qv(G#F?2oCf zXP6CVJaXZ*ffo2)4g$qL#y~k>;EBZ@iO+c7=O(IG(KojNK;J7Qp-=Vr8>9u>SD%({ zIKBgcf*6Oi#P;i%b93H9*|0ZX=^iw*>Vhi%=N$mL%pY5^7Bq@@q0v<7Djw=`#r`)G$$u>!taYP&meR4`qI+d zr8<#PLPnUmkFMF#XrmfWM%q|cEjepJkAa+Jwm2?9CFN?`@={jDeU&GC71yE?{mU;{ z;N8hQvlAWGit2WbPb`I7#gS*mOgY1>6*m8ed*x&M^x+QL!*Eh9f(n)YoT#P-2C+mV$fw%oX*0z#Qi7NkN7WPod}i&<>bBcJANSnqrGHHX~% zRpTY2`N{9V)btAJg@=tqeucw3UDiLOef!6$I~$vLgxOCEyNuaB1A6ngsWIWI_K8lj zhuzZixoBGhjiUwIi5&_vd`WK{iTI*yR4eVZ7?EKtQdV8s?QL9~=ua}VX1_#8XKd7d z`O_6XFw%J=;LUPFaf~+*RK^gM(8lr4GdEY9!Q+v^9T(iKeNn!>SQe?L z6)w*r_iN;RnNOQW@5SOACNzVOu2t3WoN9Xq4o=FRq&S)8+azqMc11b;>uJ(@5?)&+ z43|eH!IeSv_96M1Qwb~lRdHz%{TtoOy2tDberc@{)EB+=CjSJ<6B{-B9H(4b$ib!M zf6I1oP_;6+>lkWjX${5V;%MjlpTD3by-#Me(0OckqXA3BR~W17Ut zbBh+Nr;jWT5erSCUJ5?Qq%WsKXTTKFB-u22>s$V^p{92nb!s<4wd@BCg$RS~8N$p-y zeKB=4ElXKr@sI(EY(h7H&w&Tfq6BrC?s&O!eVo4s8-Zvu0)+-cg7ow8-52eQ1K(&R ze&~zy#k*1JY$q3;d?3MXg;;VWI)^bhC-hsqJB(>KcB#%fHexZqw0Z`lFyJl$KeJYv zLUUBxr>4S4YKXH?`l#J2oel+G^M6RNo{44qBOHoLO!vLShnOX4g4P!n&>i=i0v@sN zKlz(_ncdW+WVRnNf>Hr~U!maJCI6$q0CY4l$?}GjS-ocp9*qrjcEqJJA1@yG&acVn zODYID)TML?0YUS2E%k20-oh}6*yW}eItYHVJ(97dYQ~>jPQJ9TLAqRhV!uhOp_JjD zjh@Ta<*|Um`Q@GDq)A;%N$b)&?5^W@}V-EcJlZd&yg8$ zpH*Lt7*Yn_F{kv4!L(vZ1d3We1%4j_YlrcIf|lcuT>bs{^x0}8&V*Eq{Bs)GYs%ZX z!Uh=@sW=)Qo8piJjpKgi;%U%!dYuVjL1~xVwu@IE@psSmFMzW-L{Zi?MR^Z0T(Nl- zM*R@y{s{#*h6Ss?DMgb~8HX_fgJ=fZ?uv~ZCW8=d!Z*GK!_s#kYvEFKqXq@>#KC>5 zoB@TpxEN%ft$M^ovO2~TMXZIFz&fB51H| z=R9Dl5ythj7TFAsn?vbA5YE^Q-8p6T zg|tjI3LF*H-OXN-aPOul8i{5yZuBgodbyov#5e{4_)a%%2J3?a6C_QhyppZcz#~z# z+G?aqs;HLkP~kvd0Af6C18p<^I%!sir7S&@BxZYp!<}Qui#zU>pc_|lsleL7#1Jo@ zALjo>N*Iu9Na&s$_`&68Z1Yia`W?v(caS-D>xC-d<_iC11d^@uFuUpkp9&XLDu%Nb*;*;mIpiVb@gUfk>;oh zwe9Blcd6Wmp}UY8&7^*N-TUOhiMv3zwC8L2jZFRWobaw+skFFZV3(NW#O2X+J6SWwlOef{0V6!l)TV6-XU zU}Kl95Rsk?Tc%4_%ba#Lpt5S(T&@CI8%|%KpQfCXBp|5=-y^QzZSoDDC14B#SI_o&MtdTJ%Sj$cxP1%L%aCY@sOO|dZ z&j8JqDi*Egit<@%st6Z(!F*;l=>3aCKHX{d59T^&b%HmCe@yJShh0Cj^;(Bu(khhS z%l%vp9KI*l;{Pcn1nbydS6A_gX0+878CR0*BNLg-^yXIaKr^DFkl^&-bh_=kMC=D^pwi%ojcxZe1dbe6arL2Z( zsG{K?i)aLdA2XTzU1sXp46MxSO!uGlG$SCvaUJ}|6Goz&Uc9@jao4KPqA-TX6(nII z$0I52M@Z8HN7>JMls^HpdSI?HoLHrVE&xmhCujlJLP{s4=~($;9g`@R+apGJoRP|Z zB({3!`mp@dr0JGh=q0WY(A7!xl=U%IfBpB3V3DQD)M&usBE2^n?34EW9RCy!*9{K0eA!c|E-nF8~tTq|spXkuP#Q zXAyw*Ur2q-a?quwe$T2RSFf8-&B#Si?wTh*lifrkfR?jn3`>vi6?Y6`#!TlvlFYr) z{-u%1<#r2>U|ty<8IPXx`<|V>jAr5zlt3tA`)*3gi6cT&QKK8HQlA6}<5E?*#qD!+ zRu;+SWg&ZK$L;u&zwRl0nvwJusAQ-_8y}AE4t0QoH%>xIc|__P9dNdliGpt-Czmy8 ze)m!h!c`iT6BVIXniFvI^E8=BJ9FajS_1*H#r=f|r16f*=}lN#tr<%Gbj=QW&-VkS z@7@+ja~BlISlP!BDc(E%{h&9f5i6A8)0Q|uqyWRd^wNgud3@w_14sGSy)f-@ zPEkgfnNB{IF7>;oTJ_-B%}KdGJG6-Y?#fA0xf!zVSEg7Ao$Tt!D3I_8GtILg24;v@ zSKNW6!2K|7d4r%yQz6;w`lC~I=*9MrOx7?l<#lm~e`>6h6L^Si!xim}Y_iz=-c@av zw?m&U1QrUVU6Qc(z9!Z5LQ+Opg z=dPtge`-WVTnYS21@(VjqULF>{a*mec9CrQjDU9Y4?-hmv!iz-=9CczefOw)t8x6ZM)U{GZ$AlF;oZo_WuCvF%OHX3-{7jMrS(?%aOQtG0oR11Z{>dZ{!_iwatJ>+=yZ+QzTIOKF zR>5inm+(-ac*0?}7qg{N$a{j;_aasF5Zn7#=SKa5IXb1(VX`MXEywJKcz9XTaYh^o z?PbHVs6Idid|HT_TcVdX7%xjgj*(hUE(;9`1- zIG2P{bDTMp2l)`K*>M~)1nXMr)SelQ_#~PI$lW;gIA61>b|)rI5FrFAe8G}znsQbJ zE;Kp3NF?WR8)ZIDL3?^G8cnW)qDzT@*&J&CtEeI!mIC;xz}qc_OW#+(a%9XO=DBCN zwO;65N_VbRNJs#ITIVVGuBE!wqv@l#SZaySu>y0?=>@~v&rmBf+R#@W=DitQ%jmOg z`#tvu5`ydQ?Eo>g12GyKiTTe__mGq?3$*lh&dQSCMMniOSdxPaDG)0tYEqa`jq7=C zqp3LXz*QNs=~{+uja@aSY`>0Z6MsJYt*0^w+;ydBLE+cGleVG@JP*QH)tK?<1FjKh zL^4}%-<--Yimc_vs_XF89e=}5&)sERPAzd*gFqM zs^GR4_`|$uwD|YY*pWGA1^u3vV7?oi<|01Bz`M~pBuPUWE(7min;DDeMJMzrUt0`+ z1l!&WNuwN}xa(k^6!q3hso^{f%6*(tA3vjW0hg+O7>5&sp0<1! zmS{RL!)0DqPk4$$ci?7}a;Oloja5$iXY5RAhd+*=KTYcYmoPPL&$>n?ThU=;Sx%4w0%He*EWx zvDI1pNI+wiqY9o^Hx1#Y{J7B@v94}?_@Cnzg(m59ib8_{QUNc5O&1%LFY7Ok`sC8u z-|vUDGxuX<KUuI{3oqTUZ*7LULg9glF1yLs0FTlx4jWGET|xE2Y*Pq{dd_zk*1nf+4G~rp%$WJ6;FVGNaX+?SquHnl(RX zrkVq^_bc^r zAMAzLpnH1i3#l7J26G^g44vZb)usmr5UKHCm|p|JNiUzE1L=Lcbfw^y;#+2rOLcUl z!H2Y^eiw0|WQ!6!3z-QN6eeg=anVZ8go+O5p%d;1xmcquYwXv{x zD%TVEIfJvOrdfbJp71kO$S}oBt(@PbFG`)b4Tw4oEV{Esv)-L+XdUb-T|x2ouk~Km zNDGYHqaWCH2zN{k0=z830ytQq@}g@Y1XId+?GC1+pkbGT3Ve=L}rXX>nOAB5VD?zFE4phUC^l68Z~ zHH^%<(^GfX3fmUWJ7Mfrak$JUhT|D->kD(vv0NNagaLHe+m8z%o8zDd)^j0-S_OKG zO};?0ACn4KNA^Z?N0)wwCKSi7Cf=HRre?4wRI{ql|K(8E@VrS3(YyRoURRFeWt$^u z3bm&A)+=#waunlDwAQTUZ#2!p%t~iBy9(D+5~!S@Y)@Vgm)yRs)-zbFj6^<2E=At5 z%<(w=I^S>j$%4f{S+J4`>}Nl~r~W=9Oa-A|*wHfz+m2C;#X857lAe=%p#rBV4q8N& zsWN1l!~-6T;3IR~saro=?oYC7@NpO~zq50HgM}ecL}II3S!S0KAx7e-atVRg?4 zZgO{oJaMtrQUt@2_8GgH+FSTO0}!yoOIvqjs^eTDVVA4~4TXSoOK6?AO3-yTjX5oH zU7>I>ybpAWOayY)c8Q=zD&}I2%274L_4mC)%*T%x9VdEL`g{c+5{vUW6xQBN`+GOD zC1J+=&1U$wnhum_*);0xdI$?Z46?7N>HZ%R%#t{mp<}eYa_ZY?-I+yM_F*~v`ck~WX05lq&f1Q^4rwL9NXEGy=pXTF#FrtB8Z`GgrGx;5OvLM!Bx_3d*;}|+6UlMbw;Tc`J$B^!Pwmq3 zP$uyef^&jo;z#_uRU5M@rPRJ}+roU)up+5X&SfU0G|i0nWmMv5 zr}#>A5~BzF6e~f#bK4nPzRX_*UQ#8<&n~_vjYUDgKq0~4db%@*0=x}Tp|WSus4Zw7 z?EB}a;nZjQHEk)>m@38nC?O^KLJ_?Vn&S&GY1raGq>2H4H6kiIrBr&z965?~;=kGV zlqwT<#r9+%vs%=4bvf#p=NaTC6K2~)$rjQX8z*a;uoK8~xKBtdj;n6ALCdq`rU#6v zV>-7~dWVD0^2~o;%zb>jV(TvRyNpm0u%*V17ZMWK}e| z$O#fem1*5TQK)&T@b4g({KTwBLeFzhNvw>JE29Ux}ZM6N`m&>!pnA;Cx8 zpCs56Nu^K;)Qmq`#Ob;x98xpWa-$SVs0-5yI-jpgLK$LP zPt2)&5fgFl9W)wUVsvd3-yNdkoRw!qM~8y zJL3X(-jC2lDw>J=HEc7-aO#bD7w-5l-}Uuwx1?Q#k#m^}+*%YCJS%WvT(O>$T0o$H zQ-r@e?Vpf1S@_rHzWnFi%&-MwY#wykO{B28#Kw&yIF`65%~J%UDroexFsMdj znJBOtKCq3yWEnO?1pmw|2(>l{8sy$h5Md;72g-pZ38mQ!`ea6SnaT!sd6c zlzSLQq)psTmbVsf!_6iM!haa`?Ow3mh3H|OO681&{(faLF(Z_cMw&0jT8P!f6WU}+ zObCP^CQ*CUZ6i?%ju+a8(*`qy&&J?E2|%(B%H{JV?akDBOT)CJW6WHmx4JZ=H$%Qa zqsBVd7b0>vORpS+UK&cv($IelEX$&G9lvk!(kvK`Iw=;{;|Z|0qd)t;s*_>khBT!l zv<&m;DJIHd7#i3yoTmn=2HbbDg!AqQej$|!n0M~Cs^=zjQ6c*}$PxC(MwTr)ei->% zl`7pQ3Jyw(VuT(jmCl<6IkSR?X}Cj42i7A^j`I>(hI-@E(!exb9C4VUvM>>1Pz}A< z0%jYAUsR5az2JC7XZ+<0H`~i43SRL4<#Cvi{j|>4<*Z^+e%?hg&c|Y7NNz1lsWQc$S=YA6C{-HKozp} zz^ij{T)fA*G-7d?65TV#>mZeJO{;UOGTytz_!F;H26bDWFiriObN(gx>at>5*T3{! zN?^b3TEAmPVYfa>hoHAa4jS|lv_WW8m^T01P}v;sjsi3#@v!ie!`E5)i%HQuIA+y+ zxdOT_Je+@0ohe;BdA4)R;o%s*@uvQ>+Sp#djl?I6Dt>O|o2>CY?yp7Y9+A9VNiGZ2 zc!7^5(mozdUsZ+bn>A2-hG#OiA!9VN{>u#ZY<9=)eQ}J!1(|#rb`e|VQqs?1kuBR0 z|C1S9I@S0uGw4!HmA)Mo@cf+9E>Q9z*etZ_G(w_UL&RVk7fMmes$~3)LaWD??TL(& zHw!6a26D5(Jb$}~!a66TgaaB3lay!2OWzAwct>MA?PJHGP2;3=(ZXGlbnuTP1a;g6 z2j}DUZ=YMLjV)4yLQmd|d-EZt=_`O6R}$=HdA6rjdv=#v6WX$~WJYkB_-1Qcy)=tX z!m%T8|5MAyRvRQT7~Xalj$gEdtREH0-^RXx&B*B^4uYdAptm>w}HL z|GW{$u*3byWUbT~+!f*A+Bc5poN;EOTua{ zqi2h%=Q>sb0h_>rNo->|8IZR~d3Q5ZoHi-9R`6F)-w30y_Y($}atJ0V$vX?v(f@B4 zj7ld_FiR9f5WvW4kv==n%z24CRzl4gr>h103WXFLcuI@_F=i+OciUpFvZL0hq zp+Nyy%Kt)x9R_HVaWy~ZqOZC?LxTZH?xg0&eoB**PCrdCIIRUpO~+dXpL~@&W*i1n zQho*>JP-+Za{C%9!3$yH?u${10iy^*Bj@8mLu%|%Jd1LlFj$>UA8vc^69yx+C$FfM z$Ce&LZ}+PqdSJNhatA>RuvFH?lBC9GoV9dAJkTjyw*p~LRtFC^RJA7yWPY@6eTlFN zs~M2VHIXY}*J>{tab z+p!ttbo~tQgZY5QZ~7C(L4M!`WnJQfub4O`r6#1))R1S^q=)yD5-w5C_)0Jy-w6u= zyu((S5X~I~Bd{0EQksWmGbpS|E#zU5`*(d6)>ArHTc|#ixP&gWAIBM)jy?hqRWx6F zo$(cushY=G;agX(x{1P^2IqYE(UFr&5mL$*#@h6ww5j>`mAA}m^GTSVyeU-ifG0in zmS82zsahSg0x>MsT572ESj8@F;WmCHtX2Vg%gmWZ98+}dfa9lP-=zcYrK*^z&M8tJ zsK6k$5Q9yE_y`F*Pcd{+>m}Vjcr{+#zDkE;z93DuXH`1%fSRDiWx7QZGv&{Z8d>1zJTjyeWE{V^3{wMhU8_%*-13(pa=u*F!gl)rJDbXxOWVW><`~J zCpIRwZFMjcXJXs7Z95Zll8J3w9oy{Kb|!W<-`{^Yy$Dk27{!eFH@ap_)Q z_n??%z28QwAmJc1$iXOXb?FBo-IY@vd-{dUWHM4LL_;Uoc}qU^-@kFGR+kST3qhdKee0#Bx=G1AWbD->A{tFs?ZoFo&}m?u=9UT2ko}?{ckxs4vx^(a6f6l5XTWL;A68X)pYg zrW$)kX(X)2jdL>-uDKM+~TiWMh|(+!>}QmMEA(SiYRbibCY)dJE=L!3fw z@03Ts;9w;+V|!&4%Oq=CN*Hx^Ddy`cJaC;xMbTfjs2aV>-+$8?H~w_p18$bJuBbrXdmdZXiLvx^n8oK`vcU@X*^%&}qNDiG zQ{yi)a+N{Mn>U3+aRoDQA9pZq|PRBTuxhP4KnY{a;uXc&8ZLB4{~1n|CJaz;g;r z2e~k8ASsXK%QR>?I-)sTV-PYX`OoqNdtHUWZDS0hM=w(G%VT_PcHkA-8ET{T- zz9YEKYBMy$Yux=UzXk4rsiff9>?fXL4ns9X4QL^xZSoJ5R2F}6Dp92Qp=xd4wS6~a z3WOx=I*oI!LU9^O|H;SbOhKYC@aTJq5CQ1`CwY0$5LgOq-0wD7=b5uT_>={I8IiKL zyzflzPgawJXc48phV{J!bWp=SNSq`ko6#_X9EBS3q_9qnM0lxlOj|z9#-SwqZfln> z56(6Ec#=3$F{VyRtE~7v;;0i;Tq!V$FCfRm@=M^-3gom>RX(ZA#upk4aFxve^qOx; z))emhicgG%XAh$)lxIsZVHiu5eYJ7Y1!G{|stEzSDA1Yj>|UB#7Ga{k;9&k-Lj`yN z47@`T+GAt?m`iv#qj)V@W0^LmvQwL%37ao$BJ}9HfpQkP)gCms3`xlZrCLYI$(LEw z%~HwZmfJe=Z3nsaxglbqw$>rQM)TkRN=fCq?|lp4ILAoMD$@4H2442&->}mo18C$b zTh^oHMh5n?me9$i>C8w}w{@gLJ=biDVL?e*C$W8}cA9(|DOFj)^jwzs5e^SJFl30o zp2Dq5ZU7Ov#XRJi_JWhGfti1KQou_;-0LoH5+yLwoxeL9m-u5qLQ`g;bA}GIJNaSS`_R>h`v_eh|x$t6AZ(u`9)Nw>3 z8#VBF)QNtUGnA9SPwF;(^~_M4?lb)UKSI4Z5C6YGz0u8F`WYv3ecGe=KmW@rAu>E& zYV@$`k>_c$B`Z`wos16Bnz%NhzanNw{(sE$ZYBpBWBO;CwL2?36kcATj2SGeeAHR6 zglCMI^aI$^3uquvX-v-pc>pK!zl28PN75u8o;kKl9Y(7}b7bX-`Xe`uuVxiGrJH2R zd>7C<_Jz=%2Q&rN2Xi?yXWE2klqRxWa?30%_@!JaG_6j?MTSa0?G>1X*p({Lge9?I zYiaqTJqC^k;5#p_OX3zmMe48s?r^;Z(Dm5nq=#VnMKO-x-$_YaOet%@O+d_g;#1t zb0S5_DVllgNqX)a3O4ggoMNg4zXAbpUq;;DE0_%110A{Lg%laq(m*Eh>%NgDEJwA& zD8y_C=K=0DuQE%4#ifj3D;!X~9|PyaFrE^uL(C}_u$fM!J}ei~h`9;7Us=6f!AX*; z5GE6AFc_yI-nmWjpF(a)?8=;Se&zpTrWX~bB8kGl8_k|kPuL-zyYabC$XAwkEWX*SBoNGdC|Is0w4 z-zy?T`PZaat)(Fy)9Yji^+&cz{aekqANl1a>Bhw>f_Ua=No8OQJW23g z?sxEk4Kci{|5sRVMe2$>65h*rh4y|L*$Eghtf?WsaxN6{SK*%*rorSi75Lw;<=u|U zK?bqa9iObOnDOieHc-stUat~i=TgO+>D*esEX&cPnA<5>@l#z6c~X^pu5iG>@XRta zeC+m`D6$3E(njdA%p}8j;|+(TC62{5eH&&8My~(8>uU(J-BlocPEk@kT>kLoVf8pF zH&Uh$N|4zU2~&|#;ab8+|%rbnrhf{i3y0~L>2HP1VfTvrSO2p3J&|EE}Q%qt(L zPgVCr1w2>F-CVjxL5iiQTC@kgGAD$>bNzVb4tXr53S7w7r+TX5xV6x5+H$f$0iq&O zu_(_#$n_oJZnyLlIjTG+yD%lglCs^T^J-39zIt(UH9S2s!m~d}{r%7^DzwCUyka~q z8;JwwPn_Sf+vIJ%55_UxxOluYT1i(^6q0Nn7AD#lo=kK8$A=Qf0NSj>?|sdC(Yt2D z`#EDB

^GISKyD3AkB|ok~!*f(yThsn}WR>NYy8=o)OF3WthAgw>tY1Zf<7B^|CN zd`KjXq0VVvz$C`sN+e0jkP}9aJC|7;Wasd(u3E1D7qQ;HPvftqi(RQdjT^5YnDy;H zaz<4md;2#EYWM{ugoV+-IkJDKVFkk|7@;LWH+)r;9ux00x2O)Oe$0U zemvpx_qJ^DzBBopP55d``(J}7|wjbEBSlI$yF8|z_ za@X`R)=#OS-_lxTOnrL@P2F45|0xCY4qE+&EM9cG^;X+mrTqTS$uGC9i-V5$v01D} z;d;5L3m8x6=!fF(10nDAG0RMFm9rvo1m{23`(!6gND9hF5oq5i(XPjiHTj(V)2G_( zqV;Et|A+XV`@wpg{7anML3Kj~k#MVxx&SoiXR6zvAZP^vbmgLsU9~H)g4x$?p^j~` zDX^N-4XOt3iHpj)I{k6=aCQ;tRcrND&2;{t-(8WQd2Mk$+;VsFr$_JXpg|p|Xd)qt zrZd9Avw^8wYV%Ir;iT?Yk(3^?OwAs)H>uOBbX6nOL9rT8fdw$%vb{@{C%lq@ao>KZ!zn0S9F{oEy87+){ zJUrWNzCmDk?JEm2<=;(z|Brb?^-29n`7%G|(@-3{AWz?IpU}6XfLY(aA|vi$b2RSg zXZIp7s}bsr}Jm5cSKLCwg{PSx!E#t&dK!mw2R z8j8TnhOOnR0@K@@PpUPW`?5TQ8gjGSTyM#J*Xr0a6Lq%e`$kym<5-9Gt7OeR2y|IR zxU%x|iNi8S9A-GFc_r1GP0QrRT1JIGAu&z4_M&hQE@@&1MFRDE0gXh%IO@r z?Yd0n)QPn_!Fs$8cC@b9__3O-?fvQ$AKUE4Wf$Yw@ZPZ2p?Blb{7i9iYVAi)xKuSe zHD?EOr8#Kz7q*MxBl>*qXm@L#a&c?_auhJW)zZTNQM8S?^q81MJ6Y#^8YiMOhN z-cL(+nY5!LE>ABn@8&y~hql_kZuf*=nk{o}!n}hYSM#%MzA3a@lJ9~i`T~YIABRlY zjIEqn_6II)CpP!%*Bd_=GL*h}Z8(L~i~G0>0H;samq^(Dz^08B_|kqk3tM5;{36>n z#3a9@#iMOe{yHxo=l2B;x4X9fMK1h^c6@2u$Y$ovx|pIa`uO+p_dk)a9pSOqJ!aCG zO>>PmFC{`dU3fe3-xs23P}UUH5@%eTo$BB`SJ;Ubr{vPvxU9MRBL(pz$^#9FUzRtg zmc_=koeM`&Z)yT&^Pci&N%mpyBxg=~9rr@CX zoZNTuhWG(lZmZgf1B&@%@x;oSjgtL3CVV|>RXgX=t!9tvzO$yKeLzM3s$c%6r@eQ@ z`YDgvl0R^1>W?;Pk!kIS>+|KaOV>d-TTCGOrBT4ExS*lnkKg&tyFCGM_Mv?xP*Ypq zhmHSg-^2avz3#2e-z|Xikh(-**WUlewt1jsi}=+?Kl_4Vey`)U!7(rI7BQ8X+F*}e zyUVwy@GWr`g&EPl6}9x`4`lx3`!7+do<137r#H)R$WZ|F^n- zZMMXOSJCxZOgir?8_$QUUtm>iw|o*dJV)=QoQuShM-7+@(A?=h;^>`h74w~&f&AKIM-$H(j zMHy>ptL=W@vUe7KeMg!Y3bl_t@K@G&i|2N2PTj=rA#M2yxqT(rup>w8>`KjrutFJq z(2;q=tnSfOWAE>#>9w}>N`G+uD(iEL`r1MG8lQylQ~qF`=={Rpo#-V4O^WknWzXM# z^zHRw7(nZBa9sbMv-v$rF2K2?zj&0;LGMap$|2nQOty4I<)az=l&h($V7SPZ=gF?E z#(vsS-p!9(8TaHBVTmuXL_o*wb235mr*Fc>BXK?(n_23IhYa*d%Syqyh$y~w#bl~* zY~(&(6&IB$%F}yh{q2OJgs6`kEfji1S5hSy`hZ!KV&`jK>fn;w(n5VhQE0lcMHR*M zil;3%vJ=?dK>=9=~8!sQA||8R_2P}MEnW#3GsO}TsYj2EVq`yu(RAUTDY(FE^+?2-~Dp5 zf`1A#ebZ9aFVJlUn|%bO_tum%RzPUVP8_{i=$|c+J8E&#zvU;>M(B(8IWb(7wW;~n z+oSQd?~>2wX?HL@Jbe0OG}v2PTAKN?cSpnzAn^GwVY8#^0Elfr@sYvz=*B1DWx~(b z(g@Mwj(o+h`_90ue?i>s(Nv2VUek=dmAURe6-UfBT!L^G<;+m=*vX@_dQ0zmVPNeU zwLCx8!PlKh+~q3YH+8Wzebb-SaesN%@k0D+ez(-OmNZ?)o4KXGvgN0BAKO{sU$rPN zo)f?Qp%`&byW;)IlMU(FIXW(nMf~B~>agXjVa42{PE5?NL2Vweqvs)2O2(Zzt^3?f1~L>%RfBtoGcHnHy*tGl2doylf=W% ztPxEA?ezwWJk$_%JFs>FyRn;5*1fzk#-mVnx_Dm(#PQGSJG-w(ZTaE>g>BDEIW1f_ zyMKJ^P%r%%?A?#H=QlzRhR?J8*Nt*tQHVNqb@lmmJpMT@7OO`QXJ4$fWtsbLX#3tC z-VTND^&dQy19P^#u8;RF$J-h^?j7IxHrCwjJiF|=y>5`E+&`- zw6xwGI9l$P8jhk`9v`skGf;?+a)6qAt9J4S_a8SO1Q)(@B|e|mw-777Ee&@TrtZ#u zC!@Vb_bArRPb@E?=7z6sb@E3>Hm!Gt&!jEbacBBxKOY~KZilp3TDXWh*L8K4bwj?S z{PpH&levLhg!dznQkqA_Zth(tCg3M zXJ2c6AD6hXq4ChgMjjY>ls{Nlq#K*O9yc!zQ{6e*4uEl<7lHoL&KU^u{5}InD<8FWye+rZZ)Xn(9dEUE7ye&4 zw$EHXI~2F8jQ;2MAD@SNQ@gkFXF2+RWjn1mZU5{|PKw*B#jTZOt!ZE1{7VAf9FOj7 z3{%@)rF6gUDu!WtzS>+|jGN0Zac%j9y)2FC!k;N)+xJ}nG3em)9v=QlcCzKy=TEpHx= zj58p^26??4pJw|U8)Bx;shT*o$j&WZnpd|^)Ggk(Yn?-VX69-J4zH`XSvOukmrm20 z;O7{w3g@ECnSW=&9r%XT8*bk6ODGRqp!toYjrZC#pjfyqL%kZ=GU$H zHgn3$lPIKj{l}y6`R5IkFXQ+0qn>>wQB`G8i=p@4C1`fX>c{$~$Jy;=#o%CSM~3{q zh2=*-2sFNN(HXB#$k1F6Ol~c(gi_;%a_~X_`iA?0;&wK3MZD4Q%DguWb9nr4l=QH> zecL-)Y<~OEf2QutjLpAI^)I;SAz;(aAB262!*zBcKif1Iy7-#mPbhHrd=KFV?{{^- zD}s6RwdsuVp*Yzbo=iPs-UlLFydOfm;vF@693AsdZF#@1_-X)k1!`<y|($+`rB_TVs_7ttcPbWLBKbg%N=1a zJh^N?zkgGQWYhY7eswtlPgUl_XD#~cTbfOo#6;(>G4dIYSm1WdDzCr$VccJLR_C#a z_3Z3GFbK&lscRQ+H+J6sF7?kQ{rtekC1IY;w3caoV!x4ahO3WuZ=)kF{w;s*?naj< z%Y%phhaErP`p@U5VYJ_LTk$uD*K)t_jOD#n7`^W+kxy?QK<*G3T}e{{Dm6Z?EPfM` zIBoM%mjc}tD$j)fy!Fp2dk#9@-#Gt~3UGgJ#y!?=5|;!Q{#;HGaIM?BKv*Oo=X#O% z{eV~@ddBh3Za+wtO-P#{RAK&yY*OpqTIfJczHDOcygB$!o~K%8_ouv$+k^P#Y>x4` zi3(NC+dmkN(q2gKaI~eLJ(5sDlP7ZLo5d%5Y2z&y^I}pr)67#F>Zzwe5G`%)Pr;oh zTRf?YC|4Uiyz6boDz4lzTbMRB7jX-b&snNGq;DU(=HXj@Mu9+L-+E>gJoV+l`B=H$OT0Yqxk`zwVw5ULoUl z?WY5^yu6Oux*u%@yA%Y@*2qRA0oG1kmnc6RO!9_)Kiz_Qi{v2_&Pj@J*c8dpm6}Pl zVs{NXvBGCE6q;eTU-A;bZp0QncT8pixL~2lOuWmmsJ-B;F-2QntFLZnG;yk1U za<}5H-rmO;oB;;$;9Mw>n_>RQLHy-3`0DvBs=E7W-xuSmXV(5`STpDR5QYP`uPa*S ziiKs6k(91&WUe~ETgs@{IirRc=~$J6)wbejk5HOcOU7WPsO8h4|0de~g{tAkznn{C zCwLIn{sI7r?P?unUnkJ3*ue)dfQnZK!C8{kFyZ7}r)2_r*xG61IQ*$$I_fVr{^MjI z5bVehsvW*+A)40lCmtToX}>kJKR0_I7MF4Hnc+QqT0e(WHm{q-A7Vh(g)E;06ig)< z_SGr$MD#%|s&PSDe>1``50OGb)j_U|{khNa8)~X}AwwL{bS`|%;8?rwBF*|O@%ANj z-oB{3mc0!C%&)AS)vbTzu%3q#Aqk7xz^V=n9*(Mz$4)&u0%5_BAliob(~?XW;TuM) z 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%0Dva%kPSZkEfq4L<%hkU&d?tyvRjvJtu0+A889G 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

prs*3!ZC26vk6gnq9a^$ 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 diff --git a/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json index fa5d6447762be..47bb1868e7065 100644 --- a/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json @@ -9,7 +9,7 @@ "version": "1.5.0-dev" }, "date_detection": false, - "dynamic": "strict", + "dynamic": "false", "dynamic_templates": [ { "strings_as_keyword": { @@ -49,36 +49,14 @@ } } }, - "as": { + "dll": { "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "authenticode": { - "properties": { - "cert_signer": { + "code_signature": { "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" + "exists": { + "type": "boolean" }, - "serial_number": { + "status": { "ignore_above": 1024, "type": "keyword" }, @@ -86,234 +64,111 @@ "ignore_above": 1024, "type": "keyword" }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" } } }, - "cert_timestamp": { + "compile_time": { + "type": "date" + }, + "hash": { "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { + "md5": { "ignore_above": 1024, "type": "keyword" }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { + "sha1": { "ignore_above": 1024, "type": "keyword" }, - "memory_size": { + "sha256": { "ignore_above": 1024, "type": "keyword" }, - "protection": { + "sha512": { "ignore_above": 1024, "type": "keyword" } } }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { + "malware_classifier": { "properties": { - "number": { - "type": "long" - }, - "organization": { + "features": { "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" } - }, - "ignore_above": 1024, - "type": "keyword" + } } } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" }, - "country_name": { + "identifier": { "ignore_above": 1024, "type": "keyword" }, - "location": { - "type": "geo_point" + "score": { + "type": "double" }, - "name": { - "ignore_above": 1024, - "type": "keyword" + "threshold": { + "type": "double" }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "upx_packed": { + "type": "boolean" }, - "region_name": { + "version": { "ignore_above": 1024, "type": "keyword" } } }, - "ip": { - "type": "ip" - }, - "mac": { + "mapped_address": { "ignore_above": 1024, "type": "keyword" }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { + "mapped_size": { "type": "long" }, - "registered_domain": { + "name": { "ignore_above": 1024, "type": "keyword" }, - "top_level_domain": { + "path": { "ignore_above": 1024, "type": "keyword" }, - "user": { + "pe": { "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { + "company": { "ignore_above": 1024, "type": "keyword" }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, + "description": { "ignore_above": 1024, "type": "keyword" }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { + "file_version": { "ignore_above": 1024, "type": "keyword" }, - "id": { + "original_file_name": { "ignore_above": 1024, "type": "keyword" }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, + "product": { "ignore_above": 1024, "type": "keyword" } @@ -321,2674 +176,341 @@ } } }, - "cloud": { + "ecs": { "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { + "version": { "ignore_above": 1024, "type": "keyword" - }, - "instance": { + } + } + }, + "endpoint": { + "properties": { + "artifact": { "properties": { - "id": { + "hash": { "ignore_above": 1024, "type": "keyword" }, "name": { "ignore_above": 1024, "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" } } }, - "machine": { + "policy": { "properties": { - "type": { + "id": { "ignore_above": 1024, "type": "keyword" } } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" } } }, - "container": { + "event": { "properties": { - "id": { + "action": { "ignore_above": 1024, "type": "keyword" }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } + "category": { + "ignore_above": 1024, + "type": "keyword" }, - "labels": { - "type": "object" + "created": { + "type": "date" }, - "name": { + "dataset": { "ignore_above": 1024, "type": "keyword" }, - "runtime": { + "hash": { "ignore_above": 1024, "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { + }, + "id": { "ignore_above": 1024, "type": "keyword" }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" + "ingested": { + "type": "date" }, - "domain": { + "kind": { "ignore_above": 1024, "type": "keyword" }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { + "module": { "ignore_above": 1024, "type": "keyword" }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { + "outcome": { "ignore_above": 1024, "type": "keyword" }, - "top_level_domain": { + "sequence": { + "type": "long" + }, + "type": { "ignore_above": 1024, "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } } } }, - "dns": { + "file": { "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" + "accessed": { + "type": "date" }, - "op_code": { + "attributes": { "ignore_above": 1024, "type": "keyword" }, - "question": { + "code_signature": { "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" + "exists": { + "type": "boolean" }, - "registered_domain": { + "status": { "ignore_above": 1024, "type": "keyword" }, - "subdomain": { + "subject_name": { "ignore_above": 1024, "type": "keyword" }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" + "trusted": { + "type": "boolean" }, - "type": { - "ignore_above": 1024, - "type": "keyword" + "valid": { + "type": "boolean" } } }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "doc_values": false, - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "index": false, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, "created": { "type": "date" }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { + "ctime": { "type": "date" }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { + "device": { "ignore_above": 1024, "type": "keyword" }, - "ingested": { - "type": "date" - }, - "kind": { + "directory": { "ignore_above": 1024, "type": "keyword" }, - "module": { - "ignore_above": 1024, + "drive_letter": { + "ignore_above": 1, "type": "keyword" }, - "original": { - "doc_values": false, - "ignore_above": 1024, - "index": false, - "type": "keyword" + "entry_modified": { + "type": "double" }, - "outcome": { + "extension": { "ignore_above": 1024, "type": "keyword" }, - "provider": { + "gid": { "ignore_above": 1024, "type": "keyword" }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "file_classification": { - "properties": { - "captured_file": { - "type": "boolean" - }, - "entry_modified": { - "type": "double" - }, - "is_signature_trusted": { - "type": "boolean" - }, - "macro_details": { - "properties": { - "code_page": { - "type": "long" - }, - "errors": { - "properties": { - "count": { - "type": "long" - }, - "error_type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "file_extension": { - "type": "long" - }, - "macro_collection_hashes": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project_file_hashes": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "stream_data": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_code_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - } - } - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "quarantine_result": { - "properties": { - "alert_correlation_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "quarantine_path": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "signature_signer": { - "ignore_above": 1024, - "type": "keyword" - }, - "temp_file_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_blacklisted": { - "type": "boolean" - }, - "yara_hits": { - "properties": { - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "matched_data": { - "ignore_above": 1024, - "type": "keyword" - }, - "rule_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "doc_values": false, - "ignore_above": 1024, - "index": false, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "modules": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "authenticode": { - "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cert_timestamp": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mapped_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapped_size": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe_exports": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ordinal": { - "type": "long" - } - }, - "type": "nested" - }, - "pe_imports": { - "properties": { - "dll_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "import_names": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "signature_signer": { - "ignore_above": 1024, - "type": "keyword" - }, - "signature_status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "argv_list": { - "ignore_above": 1024, - "type": "keyword" - }, - "authenticode": { - "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cert_timestamp": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "cpu_percent": { - "type": "double" - }, - "cwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "defense_evasions": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "delta_count": { - "ignore_above": 1024, - "type": "keyword" - }, - "evasion_subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "evasion_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_sections": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "thread": { - "properties": { - "thread_id": { - "type": "long" - }, - "thread_start_address": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "total_memory_size": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "env_variables": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "file_hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "gid": { - "type": "long" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "handle": { - "properties": { - "handle_id": { - "type": "long" - }, - "handle_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "handle_type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "has_unbacked_execute_memory": { - "type": "boolean" - }, - "hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash_matched_module": { - "type": "boolean" - }, - "is_endpoint": { - "type": "boolean" - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "memory_percent": { - "type": "double" - }, - "memory_region": { - "properties": { - "allocation_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "allocation_protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram": { - "properties": { - "histogram_array": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_flavor": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_resolution": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "length": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "permission": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_base": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_tag": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_on_disk": { - "type": "boolean" - } - }, - "type": "nested" - }, - "modules": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "authenticode": { - "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cert_timestamp": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "mapped_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "mapped_size": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe_exports": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ordinal": { - "type": "long" - } - }, - "type": "nested" - }, - "pe_imports": { - "properties": { - "dll_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "import_names": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "signature_signer": { - "ignore_above": 1024, - "type": "keyword" - }, - "signature_status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "num_threads": { - "type": "long" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe_info": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "authenticode": { - "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cert_timestamp": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "compile_time": { - "type": "long" - }, - "entry_point_address": { - "type": "long" - }, - "is_dll": { - "type": "boolean" - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe_exports": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ordinal": { - "type": "long" - } - }, - "type": "nested" - }, - "pe_imports": { - "properties": { - "dll_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "import_names": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "resources": { - "properties": { - "resource_data": { - "properties": { - "entropy": { - "type": "double" - }, - "size": { - "type": "long" - } - } - }, - "resource_id": { - "type": "long" - }, - "resource_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource_type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sections": { - "properties": { - "entropy": { - "type": "double" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_offset": { - "ignore_above": 1024, - "type": "keyword" - }, - "raw_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "virtual_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "virtual_size": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "signature_signer": { - "ignore_above": 1024, - "type": "keyword" - }, - "signature_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_info": { - "properties": { - "code_page": { - "type": "long" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "type": "long" - }, - "value_string": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - } - } - }, - "pgid": { - "type": "long" - }, - "phys_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "services": { - "ignore_above": 1024, - "type": "keyword" - }, - "session_id": { - "type": "long" - }, - "short_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "signature_signer": { - "ignore_above": 1024, - "type": "keyword" - }, - "signature_status": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "threads": { + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { "properties": { - "entrypoint": { + "md5": { "ignore_above": 1024, "type": "keyword" }, - "id": { - "type": "long" + "sha1": { + "ignore_above": 1024, + "type": "keyword" }, - "start": { - "type": "date" + "sha256": { + "ignore_above": 1024, + "type": "keyword" }, - "uptime": { - "type": "long" + "sha512": { + "ignore_above": 1024, + "type": "keyword" } - }, - "type": "nested" + } }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, + "inode": { "ignore_above": 1024, "type": "keyword" }, - "token": { + "macro": { "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" + "code_page": { + "type": "long" }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" + "collection": { + "properties": { + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } }, - "integrity_level": { - "type": "long" + "errors": { + "properties": { + "count": { + "type": "long" + }, + "error_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" + "file_extension": { + "type": "long" }, - "is_appcontainer": { - "type": "boolean" + "project_file": { + "properties": { + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } }, - "privileges": { + "stream": { "properties": { - "description": { + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { "ignore_above": 1024, "type": "keyword" }, - "enabled": { - "type": "boolean" + "raw_code": { + "ignore_above": 1024, + "type": "keyword" }, - "name": { + "raw_code_size": { "ignore_above": 1024, "type": "keyword" } }, "type": "nested" + } + } + }, + "malware_classifier": { + "properties": { + "features": { + "properties": { + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } }, - "sid": { + "identifier": { "ignore_above": 1024, "type": "keyword" }, - "type": { - "ignore_above": 1024, - "type": "keyword" + "score": { + "type": "double" }, - "user": { + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { "ignore_above": 1024, "type": "keyword" } } }, - "tty_device_major_number": { - "type": "integer" - }, - "tty_device_minor_number": { - "type": "integer" - }, - "tty_device_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "type": "long" - }, - "unbacked_execute_byte_count": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_execute_region_count": { - "ignore_above": 1024, - "type": "keyword" - }, - "unique_pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "unique_ppid": { + "mode": { "ignore_above": 1024, "type": "keyword" }, - "uptime": { - "type": "long" + "mtime": { + "type": "date" }, - "user": { + "name": { "ignore_above": 1024, "type": "keyword" }, - "virt_memory_bytes": { + "owner": { "ignore_above": 1024, "type": "keyword" }, - "working_directory": { + "path": { "fields": { "text": { "norms": false, @@ -2997,126 +519,64 @@ }, "ignore_above": 1024, "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { + }, + "pe": { "properties": { - "bytes": { + "company": { "ignore_above": 1024, "type": "keyword" }, - "strings": { + "description": { "ignore_above": 1024, "type": "keyword" }, - "type": { + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { "ignore_above": 1024, "type": "keyword" } } }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" + "size": { + "type": "long" }, - "reference": { + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" }, - "ruleset": { + "temp_file_path": { "ignore_above": 1024, "type": "keyword" }, - "uuid": { + "type": { "ignore_above": 1024, "type": "keyword" }, - "version": { + "uid": { "ignore_above": 1024, "type": "keyword" } } }, - "server": { + "host": { "properties": { - "address": { + "architecture": { "ignore_above": 1024, "type": "keyword" }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, "domain": { "ignore_above": 1024, "type": "keyword" @@ -3156,6 +616,14 @@ } } }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, "ip": { "type": "ip" }, @@ -3163,29 +631,56 @@ "ignore_above": 1024, "type": "keyword" }, - "nat": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { "properties": { - "ip": { - "type": "ip" + "family": { + "ignore_above": 1024, + "type": "keyword" }, - "port": { - "type": "long" + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" } } }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { + "type": { "ignore_above": 1024, "type": "keyword" }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" + "uptime": { + "type": "long" }, "user": { "properties": { @@ -3245,153 +740,296 @@ } } }, - "service": { + "process": { "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { + "args": { "ignore_above": 1024, "type": "keyword" }, - "name": { - "ignore_above": 1024, - "type": "keyword" + "args_count": { + "type": "long" }, - "node": { + "code_signature": { "properties": { - "name": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { "ignore_above": 1024, "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" } } }, - "state": { + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" }, - "type": { + "cpu_percent": { + "type": "double" + }, + "cwd": { "ignore_above": 1024, "type": "keyword" }, - "version": { + "domain": { "ignore_above": 1024, "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "env_variables": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group": { "ignore_above": 1024, "type": "keyword" }, - "as": { + "handles": { "properties": { - "number": { + "id": { "type": "long" }, - "organization": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classifier": { + "properties": { + "features": { "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" } - }, - "ignore_above": 1024, - "type": "keyword" + } } } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" } } }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" + "memory_percent": { + "type": "double" }, - "geo": { + "memory_region": { "properties": { - "city_name": { + "allocation_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocation_protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram": { + "properties": { + "histogram_array": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_flavor": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_resolution": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "length": { "ignore_above": 1024, "type": "keyword" }, - "continent_name": { + "memory": { "ignore_above": 1024, "type": "keyword" }, - "country_iso_code": { + "memory_address": { "ignore_above": 1024, "type": "keyword" }, - "country_name": { + "module_path": { "ignore_above": 1024, "type": "keyword" }, - "location": { - "type": "geo_point" + "permission": { + "ignore_above": 1024, + "type": "keyword" }, - "name": { + "protection": { "ignore_above": 1024, "type": "keyword" }, - "region_iso_code": { + "region_base": { "ignore_above": 1024, "type": "keyword" }, - "region_name": { + "region_size": { "ignore_above": 1024, "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" }, - "port": { - "type": "long" + "region_tag": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_on_disk": { + "type": "boolean" } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" + }, + "type": "nested" }, - "registered_domain": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" + "num_threads": { + "type": "long" }, - "user": { + "parent": { "properties": { - "domain": { + "args": { "ignore_above": 1024, "type": "keyword" }, - "email": { + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" }, - "full_name": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { "fields": { "text": { "norms": false, @@ -3401,31 +1039,98 @@ "ignore_above": 1024, "type": "keyword" }, - "group": { + "exit_code": { + "type": "long" + }, + "hash": { "properties": { - "domain": { + "md5": { "ignore_above": 1024, "type": "keyword" }, - "id": { + "sha1": { "ignore_above": 1024, "type": "keyword" }, - "name": { + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { "ignore_above": 1024, "type": "keyword" } } }, - "hash": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" }, - "id": { + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" }, - "name": { + "uptime": { + "type": "long" + }, + "working_directory": { "fields": { "text": { "norms": false, @@ -3436,272 +1141,344 @@ "type": "keyword" } } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "target": { - "properties": { - "process": { + }, + "pe": { "properties": { - "args": { + "company": { "ignore_above": 1024, "type": "keyword" }, - "args_count": { - "type": "long" + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" }, - "argv_list": { + "original_file_name": { "ignore_above": 1024, "type": "keyword" }, - "authenticode": { + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "phys_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "services": { + "ignore_above": 1024, + "type": "keyword" + }, + "session_id": { + "type": "long" + }, + "short_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "call_stack": { "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" }, - "cert_timestamp": { + "memory_section": { "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { + "memory_address": { "ignore_above": 1024, "type": "keyword" }, - "subject_name": { + "memory_size": { "ignore_above": 1024, "type": "keyword" }, - "timestamp_string": { + "protection": { "ignore_above": 1024, "type": "keyword" } } }, - "more_info_link": { + "module_path": { "ignore_above": 1024, "type": "keyword" }, - "program_name": { + "rva": { "ignore_above": 1024, "type": "keyword" }, - "publisher_link": { + "symbol_info": { "ignore_above": 1024, "type": "keyword" } } }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, + "entrypoint": { "ignore_above": 1024, "type": "keyword" }, - "cpu_percent": { - "type": "double" + "id": { + "type": "long" }, - "cwd": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { "ignore_above": 1024, "type": "keyword" }, - "defense_evasions": { + "token": { "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" - }, - "protection": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "delta_count": { + "domain": { "ignore_above": 1024, "type": "keyword" }, - "evasion_subtype": { + "impersonation_level": { "ignore_above": 1024, "type": "keyword" }, - "evasion_type": { - "ignore_above": 1024, - "type": "keyword" + "integrity_level": { + "type": "long" }, - "instruction_pointer": { + "integrity_level_name": { "ignore_above": 1024, "type": "keyword" }, - "memory_sections": { + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { "properties": { - "memory_address": { + "description": { "ignore_above": 1024, "type": "keyword" }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" + "enabled": { + "type": "boolean" }, - "protection": { + "name": { "ignore_above": 1024, "type": "keyword" } }, "type": "nested" }, - "module_path": { + "sid": { "ignore_above": 1024, "type": "keyword" }, - "thread": { - "properties": { - "thread_id": { - "type": "long" - }, - "thread_start_address": { - "ignore_above": 1024, - "type": "keyword" - } - } + "type": { + "ignore_above": 1024, + "type": "keyword" }, - "total_memory_size": { + "user": { "ignore_above": 1024, "type": "keyword" } - }, - "type": "nested" + } }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { "domain": { "ignore_above": 1024, "type": "keyword" }, - "env_variables": { + "impersonation_level": { "ignore_above": 1024, "type": "keyword" }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { "ignore_above": 1024, "type": "keyword" }, - "exit_code": { - "type": "long" + "is_appcontainer": { + "type": "boolean" }, - "file_hash": { + "privileges": { "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { + "description": { "ignore_above": 1024, "type": "keyword" }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" + "enabled": { + "type": "boolean" }, - "sha512": { + "name": { "ignore_above": 1024, "type": "keyword" } - } + }, + "type": "nested" }, - "gid": { - "type": "long" + "sid": { + "ignore_above": 1024, + "type": "keyword" }, - "group": { + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { "ignore_above": 1024, "type": "keyword" + } + } + }, + "tty_device": { + "properties": { + "major_number": { + "type": "integer" + }, + "minor_number": { + "type": "integer" }, - "handle": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uptime": { + "type": "long" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "target": { + "properties": { + "dll": { + "properties": { + "code_signature": { "properties": { - "handle_id": { - "type": "long" + "exists": { + "type": "boolean" }, - "handle_name": { + "status": { "ignore_above": 1024, "type": "keyword" }, - "handle_type": { + "subject_name": { "ignore_above": 1024, "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" } - }, - "type": "nested" + } }, - "has_unbacked_execute_memory": { - "type": "boolean" + "compile_time": { + "type": "date" }, "hash": { "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, "md5": { "ignore_above": 1024, "type": "keyword" @@ -3720,26 +1497,24 @@ } } }, - "hash_matched_module": { - "type": "boolean" - }, - "is_endpoint": { - "type": "boolean" - }, - "malware_classification": { + "malware_classifier": { "properties": { - "compressed_malware_features": { + "features": { "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" + "data": { + "properties": { + "buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } } } }, @@ -3747,9 +1522,6 @@ "ignore_above": 1024, "type": "keyword" }, - "prevention_threshold": { - "type": "double" - }, "score": { "type": "double" }, @@ -3765,176 +1537,166 @@ } } }, - "memory_percent": { - "type": "double" + "mapped_address": { + "ignore_above": 1024, + "type": "keyword" }, - "memory_region": { + "mapped_size": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { "properties": { - "allocation_base": { + "company": { "ignore_above": 1024, "type": "keyword" }, - "allocation_protection": { + "description": { "ignore_above": 1024, "type": "keyword" }, - "bytes": { + "file_version": { "ignore_above": 1024, "type": "keyword" }, - "histogram": { - "properties": { - "histogram_array": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_flavor": { - "ignore_above": 1024, - "type": "keyword" - }, - "histogram_resolution": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "length": { + "original_file_name": { "ignore_above": 1024, "type": "keyword" }, - "memory": { + "product": { "ignore_above": 1024, "type": "keyword" + } + } + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" }, - "memory_address": { + "status": { "ignore_above": 1024, "type": "keyword" }, - "module_path": { + "subject_name": { "ignore_above": 1024, "type": "keyword" }, - "permission": { + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_percent": { + "type": "double" + }, + "cwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "env_variables": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "handles": { + "properties": { + "id": { + "type": "long" + }, + "name": { "ignore_above": 1024, "type": "keyword" }, - "protection": { + "type": { "ignore_above": 1024, "type": "keyword" - }, - "region_base": { + } + }, + "type": "nested" + }, + "hash": { + "properties": { + "md5": { "ignore_above": 1024, "type": "keyword" }, - "region_size": { + "sha1": { "ignore_above": 1024, "type": "keyword" }, - "region_tag": { + "sha256": { "ignore_above": 1024, "type": "keyword" }, - "type": { + "sha512": { "ignore_above": 1024, "type": "keyword" - }, - "unbacked_on_disk": { - "type": "boolean" } - }, - "type": "nested" + } }, - "modules": { + "malware_classifier": { "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "authenticode": { - "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cert_timestamp": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "compile_time": { - "type": "date" - }, - "hash": { - "properties": { - "imphash": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "malware_classification": { + "features": { "properties": { - "compressed_malware_features": { + "data": { "properties": { - "data_buffer": { + "buffer": { "ignore_above": 1024, "type": "keyword" }, @@ -3946,72 +1708,104 @@ "type": "keyword" } } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" } } }, - "mapped_address": { + "identifier": { "ignore_above": 1024, "type": "keyword" }, - "mapped_size": { - "type": "long" + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory_percent": { + "type": "double" + }, + "memory_region": { + "properties": { + "allocation_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocation_protection": { + "ignore_above": 1024, + "type": "keyword" }, - "path": { + "bytes": { "ignore_above": 1024, "type": "keyword" }, - "pe_exports": { + "histogram": { "properties": { - "name": { + "histogram_array": { "ignore_above": 1024, "type": "keyword" }, - "ordinal": { - "type": "long" - } - }, - "type": "nested" - }, - "pe_imports": { - "properties": { - "dll_name": { + "histogram_flavor": { "ignore_above": 1024, "type": "keyword" }, - "import_names": { + "histogram_resolution": { "ignore_above": 1024, "type": "keyword" } }, "type": "nested" }, - "signature_signer": { + "length": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "permission": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_tag": { "ignore_above": 1024, "type": "keyword" }, - "signature_status": { + "type": { "ignore_above": 1024, "type": "keyword" + }, + "unbacked_on_disk": { + "type": "boolean" } }, "type": "nested" @@ -4038,6 +1832,27 @@ "args_count": { "type": "long" }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, "command_line": { "fields": { "text": { @@ -4048,20 +1863,11 @@ "ignore_above": 1024, "type": "keyword" }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, + "entity_id": { "ignore_above": 1024, "type": "keyword" }, - "exit_code": { - "type": "long" - }, - "name": { + "executable": { "fields": { "text": { "norms": false, @@ -4071,30 +1877,30 @@ "ignore_above": 1024, "type": "keyword" }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { + "exit_code": { "type": "long" }, - "start": { - "type": "date" - }, - "thread": { + "hash": { "properties": { - "id": { - "type": "long" + "md5": { + "ignore_above": 1024, + "type": "keyword" }, - "name": { + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { "ignore_above": 1024, "type": "keyword" } } }, - "title": { + "name": { "fields": { "text": { "norms": false, @@ -4104,236 +1910,97 @@ "ignore_above": 1024, "type": "keyword" }, - "uptime": { + "pgid": { "type": "long" }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe_info": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "authenticode": { - "properties": { - "cert_signer": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "cert_timestamp": { - "properties": { - "issuer_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "timestamp_string": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "more_info_link": { - "ignore_above": 1024, - "type": "keyword" - }, - "program_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "publisher_link": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "compile_time": { + "pid": { "type": "long" }, - "entry_point_address": { + "ppid": { "type": "long" }, - "is_dll": { - "type": "boolean" - }, - "malware_classification": { - "properties": { - "compressed_malware_features": { - "properties": { - "data_buffer": { - "ignore_above": 1024, - "type": "keyword" - }, - "decompressed_size": { - "type": "integer" - }, - "encoding": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "identifier": { - "ignore_above": 1024, - "type": "keyword" - }, - "prevention_threshold": { - "type": "double" - }, - "score": { - "type": "double" - }, - "threshold": { - "type": "double" - }, - "upx_packed": { - "type": "boolean" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe_exports": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ordinal": { - "type": "long" - } - }, - "type": "nested" - }, - "pe_imports": { - "properties": { - "dll_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "import_names": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" + "start": { + "type": "date" }, - "resources": { - "properties": { - "resource_data": { - "properties": { - "entropy": { - "type": "double" - }, - "size": { - "type": "long" - } - } - }, - "resource_id": { - "type": "long" - }, - "resource_name": { + "thread": { + "properties": { + "entrypoint": { "ignore_above": 1024, "type": "keyword" }, - "resource_type": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sections": { - "properties": { - "entropy": { - "type": "double" + "id": { + "type": "long" }, "name": { "ignore_above": 1024, "type": "keyword" }, - "raw_offset": { + "service": { "ignore_above": 1024, "type": "keyword" }, - "raw_size": { - "ignore_above": 1024, - "type": "keyword" + "start": { + "type": "date" }, - "virtual_address": { + "start_address": { "ignore_above": 1024, "type": "keyword" }, - "virtual_size": { + "start_address_module": { "ignore_above": 1024, "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" } }, - "type": "nested" + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" }, - "signature_signer": { + "description": { "ignore_above": 1024, "type": "keyword" }, - "signature_status": { + "file_version": { "ignore_above": 1024, "type": "keyword" }, - "version_info": { - "properties": { - "code_page": { - "type": "long" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "type": "long" - }, - "value_string": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" } } }, @@ -4365,30 +2032,47 @@ "ignore_above": 1024, "type": "keyword" }, - "signature_signer": { - "ignore_above": 1024, - "type": "keyword" - }, - "signature_status": { - "ignore_above": 1024, - "type": "keyword" - }, "start": { "type": "date" }, "thread": { "properties": { - "id": { - "type": "long" + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + } }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "threads": { - "properties": { "entrypoint": { "ignore_above": 1024, "type": "keyword" @@ -4396,185 +2080,87 @@ "id": { "type": "long" }, - "start": { - "type": "date" - }, - "uptime": { - "type": "long" - } - }, - "type": "nested" - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { + "name": { "ignore_above": 1024, "type": "keyword" }, - "impersonation_level": { + "service": { "ignore_above": 1024, "type": "keyword" }, - "integrity_level": { - "type": "long" + "start": { + "type": "date" }, - "integrity_level_name": { + "start_address": { "ignore_above": 1024, "type": "keyword" }, - "is_appcontainer": { - "type": "boolean" + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" }, - "privileges": { + "token": { "properties": { - "description": { + "domain": { "ignore_above": 1024, "type": "keyword" }, - "enabled": { - "type": "boolean" + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" }, - "name": { + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { "ignore_above": 1024, "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tty_device_major_number": { - "type": "integer" - }, - "tty_device_minor_number": { - "type": "integer" - }, - "tty_device_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "type": "long" - }, - "unbacked_execute_byte_count": { - "ignore_above": 1024, - "type": "keyword" - }, - "unbacked_execute_region_count": { - "ignore_above": 1024, - "type": "keyword" - }, - "unique_pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "unique_ppid": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt_memory_bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "thread": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" - }, - "memory_section": { - "properties": { - "memory_address": { + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { "ignore_above": 1024, "type": "keyword" }, - "memory_size": { + "type": { "ignore_above": 1024, "type": "keyword" }, - "protection": { + "user": { "ignore_above": 1024, "type": "keyword" } } }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" + "uptime": { + "type": "long" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" } }, - "type": "nested" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { "ignore_above": 1024, "type": "keyword" }, @@ -4627,430 +2213,89 @@ "type": "keyword" } } - } - } - } - } - }, - "thread": { - "properties": { - "call_stack": { - "properties": { - "instruction_pointer": { - "ignore_above": 1024, - "type": "keyword" }, - "memory_section": { + "tty_device": { "properties": { - "memory_address": { - "ignore_above": 1024, - "type": "keyword" + "major_number": { + "type": "integer" }, - "memory_size": { - "ignore_above": 1024, - "type": "keyword" + "minor_number": { + "type": "integer" }, - "protection": { + "name": { "ignore_above": 1024, "type": "keyword" } } }, - "module_path": { - "ignore_above": 1024, - "type": "keyword" - }, - "rva": { - "ignore_above": 1024, - "type": "keyword" - }, - "symbol_info": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "service_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "start": { - "type": "date" - }, - "start_address": { - "ignore_above": 1024, - "type": "keyword" - }, - "start_address_module": { - "ignore_above": 1024, - "type": "keyword" - }, - "token": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { + "uptime": { "type": "long" }, - "integrity_level_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { - "properties": { - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, "user": { "ignore_above": 1024, "type": "keyword" - } - } - } - } - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { + "virt_memory_bytes": { "ignore_above": 1024, "type": "keyword" }, - "name": { + "working_directory": { "fields": { "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { + "norms": false, + "type": "text" + } + }, "ignore_above": 1024, "type": "keyword" } } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" } } }, - "token": { + "threat": { "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "impersonation_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "integrity_level": { - "type": "long" - }, - "integrity_level_name": { + "framework": { "ignore_above": 1024, "type": "keyword" }, - "is_appcontainer": { - "type": "boolean" - }, - "privileges": { + "tactic": { "properties": { - "description": { + "id": { "ignore_above": 1024, "type": "keyword" }, - "enabled": { - "type": "boolean" - }, "name": { "ignore_above": 1024, "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" } - }, - "type": "nested" - }, - "sid": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" + } }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" + } } } }, @@ -5109,143 +2354,6 @@ "type": "keyword" } } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } } } }, @@ -5258,8 +2366,8 @@ }, "number_of_replicas": "1", "number_of_shards": "1", - "refresh_interval": "1s" + "refresh_interval": "5s" } } } -} +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json deleted file mode 100644 index 6a7911b5be61f..0000000000000 --- a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "type": "doc", - "value": { - "id": "3KVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579881969541, - "agent": { - "id": "963b081e-60d1-482c-befd-a5815fa8290f", - "version": "6.6.1", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" - } - }, - "event": { - "created": "2020-01-24T16:06:09.541Z" - }, - "host": { - "architecture": "x86", - "hostname": "cadmann-4.example.com", - "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", - "ip": [ - "10.192.213.130", - "10.70.28.129" - ], - "mac": [ - "a9-71-6a-cc-93-85", - "f7-31-84-d3-21-68", - "2-95-12-39-ca-71" - ], - "os": { - "full": "Windows 10", - "name": "windows 10.0", - "version": "10.0", - "variant" : "Windows Pro" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "3aVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579881969541, - "agent": { - "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", - "version": "6.0.0", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" - } - }, - "event": { - "created": "2020-01-24T16:06:09.541Z" - }, - "host": { - "architecture": "x86_64", - "hostname": "thurlow-9.example.com", - "id": "2f735e3d-be14-483b-9822-bad06e9045ca", - "ip": [ - "10.46.229.234" - ], - "mac": [ - "30-8c-45-55-69-b8", - "e5-36-7e-8f-a3-84", - "39-a1-37-20-18-74" - ], - "os": { - "full": "Windows Server 2016", - "name": "windows 10.0", - "version": "10.0", - "variant" : "Windows Server" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "3qVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579881969541, - "agent": { - "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", - "version": "6.8.0", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "00000000-0000-0000-0000-000000000000" - } - }, - "event": { - "created": "2020-01-24T16:06:09.541Z" - }, - "host": { - "hostname": "rezzani-7.example.com", - "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", - "ip": [ - "10.101.149.26", - "2606:a000:ffc0:39:11ef:37b9:3371:578c" - ], - "mac": [ - "e2-6d-f9-0-46-2e" - ], - "os": { - "full": "Windows 10", - "name": "windows 10.0", - "version": "10.0", - "variant" : "Windows Pro" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "36VN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579878369541, - "agent": { - "id": "963b081e-60d1-482c-befd-a5815fa8290f", - "version": "6.6.1", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" - } - }, - "event": { - "created": "2020-01-24T15:06:09.541Z" - }, - "host": { - "architecture": "x86", - "hostname": "cadmann-4.example.com", - "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", - "ip": [ - "10.192.213.130", - "10.70.28.129" - ], - "mac": [ - "a9-71-6a-cc-93-85", - "f7-31-84-d3-21-68", - "2-95-12-39-ca-71" - ], - "os": { - "full": "Windows Server 2016", - "name": "windows 10.0", - "version": "10.0", - "variant" : "Windows Server 2016" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "4KVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579878369541, - "agent": { - "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", - "version": "6.0.0", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" - } - }, - "event": { - "created": "2020-01-24T15:06:09.541Z" - }, - "host": { - "hostname": "thurlow-9.example.com", - "id": "2f735e3d-be14-483b-9822-bad06e9045ca", - "ip": [ - "10.46.229.234" - ], - "mac": [ - "30-8c-45-55-69-b8", - "e5-36-7e-8f-a3-84", - "39-a1-37-20-18-74" - ], - "os": { - "full": "Windows Server 2012", - "name": "windows 6.2", - "version": "6.2", - "variant" : "Windows Server 2012" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "4aVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579878369541, - "agent": { - "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", - "version": "6.8.0", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "00000000-0000-0000-0000-000000000000" - } - }, - "event": { - "created": "2020-01-24T15:06:09.541Z" - }, - "host": { - "architecture": "x86", - "hostname": "rezzani-7.example.com", - "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", - "ip": [ - "10.101.149.26", - "2606:a000:ffc0:39:11ef:37b9:3371:578c" - ], - "mac": [ - "e2-6d-f9-0-46-2e" - ], - "os": { - "full": "Windows Server 2012", - "name": "windows 6.2", - "version": "6.2", - "variant" : "Windows Server 2012" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "4qVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579874769541, - "agent": { - "id": "963b081e-60d1-482c-befd-a5815fa8290f", - "version": "6.6.1", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "00000000-0000-0000-0000-000000000000" - } - }, - "event": { - "created": "2020-01-24T14:06:09.541Z" - }, - "host": { - "hostname": "cadmann-4.example.com", - "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", - "ip": [ - "10.192.213.130", - "10.70.28.129" - ], - "mac": [ - "a9-71-6a-cc-93-85", - "f7-31-84-d3-21-68", - "2-95-12-39-ca-71" - ], - "os": { - "full": "Windows Server 2012R2", - "name": "windows 6.3", - "version": "6.3", - "variant" : "Windows Server 2012 R2" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "46VN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579874769541, - "agent": { - "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", - "version": "6.0.0", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" - } - }, - "event": { - "created": "2020-01-24T14:06:09.541Z" - }, - "host": { - "hostname": "thurlow-9.example.com", - "id": "2f735e3d-be14-483b-9822-bad06e9045ca", - "ip": [ - "10.46.229.234" - ], - "mac": [ - "30-8c-45-55-69-b8", - "e5-36-7e-8f-a3-84", - "39-a1-37-20-18-74" - ], - "os": { - "full": "Windows Server 2012R2", - "name": "windows 6.3", - "version": "6.3", - "variant" : "Windows Server 2012 R2" - } - } - } - } -} - -{ - "type": "doc", - "value": { - "id": "5KVN2G8BYQH1gtPUuYk7", - "index": "endpoint-agent", - "source": { - "@timestamp": 1579874769541, - "agent": { - "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", - "version": "6.8.0", - "name" : "Elastic Endpoint" - }, - "endpoint": { - "policy": { - "id": "00000000-0000-0000-0000-000000000000" - } - }, - "event": { - "created": "2020-01-24T14:06:09.541Z" - }, - "host": { - "architecture": "x86", - "hostname": "rezzani-7.example.com", - "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", - "ip": [ - "10.101.149.26", - "2606:a000:ffc0:39:11ef:37b9:3371:578c" - ], - "mac": [ - "e2-6d-f9-0-46-2e" - ], - "os": { - "full": "Windows Server 2012", - "name": "windows 6.2", - "version": "6.2", - "variant" : "Windows Server 2012" - } - } - } - } -} diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json.gz b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..94a96c54ee9cb66afbd74d89be6a774720c8c94e GIT binary patch literal 732 zcmV<20wet&iwFP!000021KpKNPuoBcfbaPgQO=CDJNxi%Pc@W^1FBjf6;)La`%K&> zag-NO#DDKPkdW8`B1A7pu@%qG?9S`kx$Shjz4^@~^geWZzH+^zTCg<3MJ>5aAL+C7 z_3F*}mk~KDj{T=xT%P|v|M4w;U#Zs`V;`<-r7-rBsvOV3^h0{nwZg1gOx@#_N%C=C zj>2qiMw29^LUF=5V}c8Pv{qT|v1PMyC757&upXju0l32)EGH6_betMPaz$+K=+SB! zrn9md*QPRKneII--EEFq`@MVGq#Bm)W=FR1;RFj!G&_Y;R2ujRDWE7g2X(@*;FNzZ z@?y(}WH&aWRhlo$`Jj8cOtF>Yes803*LG^zir-CxnFrrYS<7@#%^H@qWbMIO`VYpA z;$Y;&pT%~n?t^pOdg^Qhn(G|64-_;y1mr4bRx_a#*|DmN_;Wj0N-|2)lama2zMF`8 zjj{|$dUR_^u}sm7a`~8=Ut33wjBB1HQV(dL9D;^a3rqG}RtZlOBZ+qMjZzp^3*)kBjhFol8_Nv=_l!wnR;Jzrj)&g&Nv-nH8-&`Z8`%r5rt+D zgP_1oonWLXOqDJ^W*kLw0G(Q8HJr2sAPP+EW>U7PMU^2F8B+(d$yss`tb>Rk4VVV+ zf`bHM(`5wSL*TC^my+j!P0o`SiTygQp2T&v!5i}GTD%S}{sXUCz07OwqK>&?fXc{p z+0y{lash-0V^Bq^Jb8xKcQ#E^%3i~(yZ$Ts!&I2bo@r`>B7`uITGkgLiK*WWg{Zk> zXgLpByvQq88BKE$5%&z!R0mAI_6oszo-%SUuPW3yS`BDG3Gk}HD*;VXj{v4oTSNUm OM(sbt^Zzdg3jhEPfL<^F literal 0 HcmV?d00001 diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json index d6647e62b0191..61ddf3c4e65db 100644 --- a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/mappings.json @@ -3,7 +3,7 @@ "value": { "aliases": { }, - "index": "endpoint-agent", + "index": "endpoint-agent-1", "mappings": { "properties": { "@timestamp": { @@ -28,15 +28,6 @@ } }, "type": "text" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" } } }, @@ -52,6 +43,15 @@ } }, "type": "text" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" } } } @@ -60,21 +60,12 @@ "event": { "properties": { "created": { - "type": "date" + "type": "long" } } }, "host": { "properties": { - "architecture": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, "hostname": { "fields": { "keyword": { @@ -162,4 +153,4 @@ } } } -} +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/endpoint_alerts_page.ts b/x-pack/test/functional/page_objects/endpoint_alerts_page.ts index d04a2d5ac2f27..a5ad45536de89 100644 --- a/x-pack/test/functional/page_objects/endpoint_alerts_page.ts +++ b/x-pack/test/functional/page_objects/endpoint_alerts_page.ts @@ -10,11 +10,18 @@ export function EndpointAlertsPageProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); return { - async enterSearchBarQuery() { - return await testSubjects.setValue('alertsSearchBar', 'test query'); + async enterSearchBarQuery(query: string) { + return await testSubjects.setValue('alertsSearchBar', query, { clearWithKeyboard: true }); }, async submitSearchBarFilter() { return await testSubjects.click('querySubmitButton'); }, + async setSearchBarDate(timestamp: string) { + await testSubjects.click('superDatePickerShowDatesButton'); + await testSubjects.click('superDatePickerstartDatePopoverButton'); + await testSubjects.click('superDatePickerAbsoluteTab'); + await testSubjects.setValue('superDatePickerAbsoluteDateInput', timestamp); + await this.submitSearchBarFilter(); + }, }; } From b9cc3e940cb8b21ca32783e249d47da2eeb37afa Mon Sep 17 00:00:00 2001 From: spalger Date: Tue, 17 Mar 2020 07:38:39 -0700 Subject: [PATCH 44/76] skip flaky test (#60369) --- .../siem/cypress/integration/timeline_flyout_button.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts index 736eee421a305..02da7cbc28462 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/timeline_flyout_button.spec.ts @@ -29,7 +29,8 @@ describe('timeline flyout button', () => { cy.get(TIMELINE_FLYOUT_HEADER).should('have.css', 'visibility', 'visible'); }); - it('sets the flyout button background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the flyout button', () => { + // FLAKY: https://github.com/elastic/kibana/issues/60369 + it.skip('sets the flyout button background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the flyout button', () => { dragFirstHostToTimeline(); cy.get(TIMELINE_NOT_READY_TO_DROP_BUTTON).should( From 0f9f81c30affd6e79f00f9edca03a8abf0842ce5 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 17 Mar 2020 10:46:54 -0400 Subject: [PATCH 45/76] [SIEM] Fix link on overview page (#60348) * Fix link on overview page * no needs of useMemo * clean up * review I * review II * review III --- .../public/components/link_to/helpers.test.ts | 19 ++++++++++ .../siem/public/components/link_to/helpers.ts | 7 ++++ .../link_to/redirect_to_detection_engine.tsx | 8 ++-- .../components/link_to/redirect_to_hosts.tsx | 9 +++-- .../link_to/redirect_to_network.tsx | 6 ++- .../link_to/redirect_to_timelines.tsx | 8 +++- .../public/components/navigation/helpers.ts | 7 ++-- .../navigation/tab_navigation/index.tsx | 4 +- .../navigation/use_get_url_search.tsx | 20 ++++++++++ .../page/overview/overview_host/index.tsx | 21 ++++++---- .../page/overview/overview_network/index.tsx | 21 ++++++---- .../components/recent_timelines/index.tsx | 14 +++++-- .../public/components/url_state/helpers.ts | 38 +------------------ .../signals_histogram_panel/index.tsx | 33 ++++++++++------ .../overview/alerts_by_category/index.tsx | 7 +++- .../overview/events_by_dataset/index.tsx | 11 +++++- 16 files changed, 146 insertions(+), 87 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/link_to/helpers.test.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/link_to/helpers.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/navigation/use_get_url_search.tsx diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/link_to/helpers.test.ts new file mode 100644 index 0000000000000..14b367de674a2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/link_to/helpers.test.ts @@ -0,0 +1,19 @@ +/* + * 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 { appendSearch } from './helpers'; + +describe('appendSearch', () => { + test('should return empty string if no parameter', () => { + expect(appendSearch()).toEqual(''); + }); + test('should return empty string if parameter is undefined', () => { + expect(appendSearch(undefined)).toEqual(''); + }); + test('should return parameter if parameter is defined', () => { + expect(appendSearch('helloWorld')).toEqual('helloWorld'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/helpers.ts b/x-pack/legacy/plugins/siem/public/components/link_to/helpers.ts new file mode 100644 index 0000000000000..9d818ab3b6479 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/link_to/helpers.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export const appendSearch = (search?: string) => (search != null ? `${search}` : ''); diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx index 3701069389b72..18111aa93a27a 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx +++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { DetectionEngineTab } from '../../pages/detection_engine/types'; +import { appendSearch } from './helpers'; import { RedirectWrapper } from './redirect_wrapper'; export type DetectionEngineComponentProps = RouteComponentProps<{ @@ -63,9 +64,10 @@ export const RedirectToEditRulePage = ({ const baseDetectionEngineUrl = `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`; -export const getDetectionEngineUrl = () => `${baseDetectionEngineUrl}`; -export const getDetectionEngineAlertUrl = () => - `${baseDetectionEngineUrl}/${DetectionEngineTab.alerts}`; +export const getDetectionEngineUrl = (search?: string) => + `${baseDetectionEngineUrl}${appendSearch(search)}`; +export const getDetectionEngineAlertUrl = (search?: string) => + `${baseDetectionEngineUrl}/${DetectionEngineTab.alerts}${appendSearch(search)}`; export const getDetectionEngineTabUrl = (tabPath: string) => `${baseDetectionEngineUrl}/${tabPath}`; export const getRulesUrl = () => `${baseDetectionEngineUrl}/rules`; export const getCreateRuleUrl = () => `${baseDetectionEngineUrl}/rules/create`; diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_hosts.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_hosts.tsx index 05139320b171d..746a959cc996a 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_hosts.tsx @@ -7,10 +7,12 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { RedirectWrapper } from './redirect_wrapper'; import { HostsTableType } from '../../store/hosts/model'; import { SiemPageName } from '../../pages/home/types'; +import { appendSearch } from './helpers'; +import { RedirectWrapper } from './redirect_wrapper'; + export type HostComponentProps = RouteComponentProps<{ detailName: string; tabName: HostsTableType; @@ -44,9 +46,10 @@ export const RedirectToHostDetailsPage = ({ const baseHostsUrl = `#/link-to/${SiemPageName.hosts}`; -export const getHostsUrl = () => baseHostsUrl; +export const getHostsUrl = (search?: string) => `${baseHostsUrl}${appendSearch(search)}`; -export const getTabsOnHostsUrl = (tabName: HostsTableType) => `${baseHostsUrl}/${tabName}`; +export const getTabsOnHostsUrl = (tabName: HostsTableType, search?: string) => + `${baseHostsUrl}/${tabName}${appendSearch(search)}`; export const getHostDetailsUrl = (detailName: string) => `${baseHostsUrl}/${detailName}`; diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_network.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_network.tsx index f206e2f323a74..71925edd5c086 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_network.tsx +++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_network.tsx @@ -7,10 +7,12 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { RedirectWrapper } from './redirect_wrapper'; import { SiemPageName } from '../../pages/home/types'; import { FlowTarget, FlowTargetSourceDest } from '../../graphql/types'; +import { appendSearch } from './helpers'; +import { RedirectWrapper } from './redirect_wrapper'; + export type NetworkComponentProps = RouteComponentProps<{ detailName?: string; flowTarget?: string; @@ -33,7 +35,7 @@ export const RedirectToNetworkPage = ({ ); const baseNetworkUrl = `#/link-to/${SiemPageName.network}`; -export const getNetworkUrl = () => baseNetworkUrl; +export const getNetworkUrl = (search?: string) => `${baseNetworkUrl}${appendSearch(search)}`; export const getIPDetailsUrl = ( detailName: string, flowTarget?: FlowTarget | FlowTargetSourceDest diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_timelines.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_timelines.tsx index 1b71432b3f729..27765a4125afc 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_timelines.tsx +++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_timelines.tsx @@ -6,9 +6,12 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { RedirectWrapper } from './redirect_wrapper'; + import { SiemPageName } from '../../pages/home/types'; +import { appendSearch } from './helpers'; +import { RedirectWrapper } from './redirect_wrapper'; + export type TimelineComponentProps = RouteComponentProps<{ search: string; }>; @@ -17,4 +20,5 @@ export const RedirectToTimelinesPage = ({ location: { search } }: TimelineCompon ); -export const getTimelinesUrl = () => `#/link-to/${SiemPageName.timelines}`; +export const getTimelinesUrl = (search?: string) => + `#/link-to/${SiemPageName.timelines}${appendSearch(search)}`; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts index 9a95d93a2df70..899d108fe246d 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts @@ -10,7 +10,7 @@ import { Location } from 'history'; import { UrlInputsModel } from '../../store/inputs/model'; import { TimelineUrl } from '../../store/timeline/model'; import { CONSTANTS } from '../url_state/constants'; -import { URL_STATE_KEYS, KeyUrlState } from '../url_state/types'; +import { URL_STATE_KEYS, KeyUrlState, UrlState } from '../url_state/types'; import { replaceQueryStringInLocation, replaceStateKeyInQueryString, @@ -18,10 +18,9 @@ import { } from '../url_state/helpers'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; -import { TabNavigationProps } from './tab_navigation/types'; import { SearchNavTab } from './types'; -export const getSearch = (tab: SearchNavTab, urlState: TabNavigationProps): string => { +export const getSearch = (tab: SearchNavTab, urlState: UrlState): string => { if (tab && tab.urlKey != null && URL_STATE_KEYS[tab.urlKey] != null) { return URL_STATE_KEYS[tab.urlKey].reduce( (myLocation: Location, urlKey: KeyUrlState) => { @@ -58,7 +57,7 @@ export const getSearch = (tab: SearchNavTab, urlState: TabNavigationProps): stri ); }, { - pathname: urlState.pathName, + pathname: '', hash: '', search: '', state: '', diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx index cebf9b90656ca..ab4d75a2b1168 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx @@ -66,7 +66,9 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { () => Object.values(navTabs).map(tab => { const isSelected = selectedTabId === tab.id; - const hrefWithSearch = tab.href + getSearch(tab, props); + const { query, filters, savedQuery, timerange, timeline } = props; + const hrefWithSearch = + tab.href + getSearch(tab, { query, filters, savedQuery, timerange, timeline }); return ( { + const mapState = makeMapStateToProps(); + const { urlState } = useSelector(mapState, isEqual); + const urlSearch = useMemo(() => getSearch(tab, urlState), [tab, urlState]); + return urlSearch; +}; diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx index 3868885fa29ee..52c142ceff480 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx @@ -8,7 +8,7 @@ import { isEmpty } from 'lodash/fp'; import { EuiButton, EuiFlexItem, EuiPanel } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { useMemo } from 'react'; import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; import { ESQuery } from '../../../../../common/typed_json'; @@ -23,6 +23,8 @@ import { getOverviewHostStats, OverviewHostStats } from '../overview_host_stats' import { manageQuery } from '../../../page/manage_query'; import { inputsModel } from '../../../../store/inputs'; import { InspectButtonContainer } from '../../../inspect'; +import { useGetUrlSearch } from '../../../navigation/use_get_url_search'; +import { navTabs } from '../../../../pages/home/home_navigations'; export interface OwnProps { startDate: number; @@ -51,7 +53,15 @@ const OverviewHostComponent: React.FC = ({ setQuery, }) => { const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - + const urlSearch = useGetUrlSearch(navTabs.hosts); + const hostPageButton = useMemo( + () => ( + + + + ), + [urlSearch] + ); return ( @@ -95,12 +105,7 @@ const OverviewHostComponent: React.FC = ({ /> } > - - - + {hostPageButton} = ({ setQuery, }) => { const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - + const urlSearch = useGetUrlSearch(navTabs.network); + const networkPageButton = useMemo( + () => ( + + + + ), + [urlSearch] + ); return ( @@ -96,12 +106,7 @@ const OverviewNetworkComponent: React.FC = ({ /> } > - - - + {networkPageButton} ; @@ -45,6 +48,11 @@ const StatefulRecentTimelinesComponent = React.memo( const noTimelinesMessage = filterBy === 'favorites' ? i18n.NO_FAVORITE_TIMELINES : i18n.NO_TIMELINES; + const urlSearch = useGetUrlSearch(navTabs.timelines); + const linkAllTimelines = useMemo( + () => {i18n.VIEW_ALL_TIMELINES}, + [urlSearch] + ); return ( ( /> )} - - {i18n.VIEW_ALL_TIMELINES} - + {linkAllTimelines} )} diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index d085af91da1f0..b30244e57d0f1 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -19,12 +19,7 @@ import { TimelineUrl } from '../../store/timeline/model'; import { formatDate } from '../super_date_picker'; import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; -import { - LocationTypes, - UrlStateContainerPropTypes, - ReplaceStateInLocation, - UpdateUrlStateString, -} from './types'; +import { ReplaceStateInLocation, UpdateUrlStateString } from './types'; export const decodeRisonUrlState = (value: string | undefined): T | null => { try { @@ -113,42 +108,13 @@ export const getTitle = ( return navTabs[pageName] != null ? navTabs[pageName].name : ''; }; -export const getCurrentLocation = ( - pageName: string, - detailName: string | undefined -): LocationTypes => { - if (pageName === SiemPageName.overview) { - return CONSTANTS.overviewPage; - } else if (pageName === SiemPageName.hosts) { - if (detailName != null) { - return CONSTANTS.hostsDetails; - } - return CONSTANTS.hostsPage; - } else if (pageName === SiemPageName.network) { - if (detailName != null) { - return CONSTANTS.networkDetails; - } - return CONSTANTS.networkPage; - } else if (pageName === SiemPageName.detections) { - return CONSTANTS.detectionsPage; - } else if (pageName === SiemPageName.timelines) { - return CONSTANTS.timelinePage; - } else if (pageName === SiemPageName.case) { - if (detailName != null) { - return CONSTANTS.caseDetails; - } - return CONSTANTS.casePage; - } - return CONSTANTS.unknown; -}; - export const makeMapStateToProps = () => { const getInputsSelector = inputsSelectors.inputsSelector(); const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector(); const getTimelines = timelineSelectors.getTimelines(); - const mapStateToProps = (state: State, { pageName, detailName }: UrlStateContainerPropTypes) => { + const mapStateToProps = (state: State) => { const inputState = getInputsSelector(state); const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; const { linkTo: timelineLinkTo, timerange: timelineTimerange } = inputState.timeline; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx index 079293bd45231..e25442b31da4e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/index.tsx @@ -11,19 +11,21 @@ import styled from 'styled-components'; import { isEmpty } from 'lodash/fp'; import { HeaderSection } from '../../../../components/header_section'; -import { SignalsHistogram } from './signals_histogram'; + import { Filter, esQuery, Query } from '../../../../../../../../../src/plugins/data/public'; -import { RegisterQuery, SignalsHistogramOption, SignalsAggregation, SignalsTotal } from './types'; -import { signalsHistogramOptions } from './config'; -import { getDetectionEngineUrl } from '../../../../components/link_to'; import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; -import { useKibana, useUiSetting$ } from '../../../../lib/kibana'; -import { InspectButtonContainer } from '../../../../components/inspect'; import { useQuerySignals } from '../../../../containers/detection_engine/signals/use_query'; +import { getDetectionEngineUrl } from '../../../../components/link_to'; +import { InspectButtonContainer } from '../../../../components/inspect'; +import { useGetUrlSearch } from '../../../../components/navigation/use_get_url_search'; import { MatrixLoader } from '../../../../components/matrix_histogram/matrix_loader'; - +import { useKibana, useUiSetting$ } from '../../../../lib/kibana'; +import { navTabs } from '../../../home/home_navigations'; +import { signalsHistogramOptions } from './config'; import { formatSignalsData, getSignalsHistogramQuery, showInitialLoadingSpinner } from './helpers'; +import { SignalsHistogram } from './signals_histogram'; import * as i18n from './translations'; +import { RegisterQuery, SignalsHistogramOption, SignalsAggregation, SignalsTotal } from './types'; const DEFAULT_PANEL_HEIGHT = 300; @@ -101,6 +103,7 @@ export const SignalsHistogramPanel = memo( signalIndexName ); const kibana = useKibana(); + const urlSearch = useGetUrlSearch(navTabs.detections); const totalSignals = useMemo( () => @@ -184,6 +187,16 @@ export const SignalsHistogramPanel = memo( ); }, [selectedStackByOption.value, from, to, query, filters]); + const linkButton = useMemo(() => { + if (showLinkToSignals) { + return ( + + {i18n.VIEW_SIGNALS} + + ); + } + }, [showLinkToSignals, urlSearch]); + return ( @@ -210,11 +223,7 @@ export const SignalsHistogramPanel = memo( /> )} - {showLinkToSignals && ( - - {i18n.VIEW_SIGNALS} - - )} + {linkButton} diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx index f71d83558ae9d..e0d383c59e2ee 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx @@ -30,6 +30,8 @@ import { histogramConfigs, } from '../../../components/alerts_viewer/histogram_configs'; import { MatrixHisrogramConfigs } from '../../../components/matrix_histogram/types'; +import { useGetUrlSearch } from '../../../components/navigation/use_get_url_search'; +import { navTabs } from '../../home/home_navigations'; const ID = 'alertsByCategoryOverview'; @@ -73,10 +75,11 @@ const AlertsByCategoryComponent: React.FC = ({ const kibana = useKibana(); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); + const urlSearch = useGetUrlSearch(navTabs.detections); const alertsCountViewAlertsButton = useMemo( - () => {i18n.VIEW_ALERTS}, - [] + () => {i18n.VIEW_ALERTS}, + [urlSearch] ); const alertsByCategoryHistogramConfigs: MatrixHisrogramConfigs = useMemo( diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx index 315aac5fcae9e..cc1f9b1cc5681 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx @@ -28,6 +28,8 @@ import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; import * as i18n from '../translations'; import { MatrixHisrogramConfigs } from '../../../components/matrix_histogram/types'; +import { useGetUrlSearch } from '../../../components/navigation/use_get_url_search'; +import { navTabs } from '../../home/home_navigations'; const NO_FILTERS: Filter[] = []; const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; @@ -69,10 +71,15 @@ const EventsByDatasetComponent: React.FC = ({ const kibana = useKibana(); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); + const urlSearch = useGetUrlSearch(navTabs.hosts); const eventsCountViewEventsButton = useMemo( - () => {i18n.VIEW_EVENTS}, - [] + () => ( + + {i18n.VIEW_EVENTS} + + ), + [urlSearch] ); const filterQuery = useMemo( From ce64895b2e83a18555027ad8c451eb1135c66c10 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 17 Mar 2020 10:53:59 -0400 Subject: [PATCH 46/76] [FTR] Add support for --include and --exclude files via tags (#60123) --- .../src/functional_test_runner/cli.ts | 18 +++++-- .../lib/config/schema.ts | 13 +++-- .../lib/mocha/decorate_mocha_ui.js | 7 ++- .../lib/mocha/load_test_files.js | 27 +--------- .../lib/mocha/setup_mocha.js | 14 +++++- .../run_tests/__snapshots__/args.test.js.snap | 50 +++++++++++++++++++ .../run_tests/__snapshots__/cli.test.js.snap | 2 + .../functional_tests/cli/run_tests/args.js | 15 ++++++ .../src/functional_tests/lib/run_ftr.js | 6 ++- 9 files changed, 117 insertions(+), 35 deletions(-) diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index 11b9450f2af6e..3aaaa47ead5b6 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -48,12 +48,15 @@ export function runFtrCli() { kbnTestServer: { installDir: parseInstallDir(flags), }, + suiteFiles: { + include: toArray(flags.include as string | string[]).map(makeAbsolutePath), + exclude: toArray(flags.exclude as string | string[]).map(makeAbsolutePath), + }, suiteTags: { include: toArray(flags['include-tag'] as string | string[]), exclude: toArray(flags['exclude-tag'] as string | string[]), }, updateBaselines: flags.updateBaselines, - excludeTestFiles: flags.exclude || undefined, } ); @@ -104,7 +107,15 @@ export function runFtrCli() { }, { flags: { - string: ['config', 'grep', 'exclude', 'include-tag', 'exclude-tag', 'kibana-install-dir'], + string: [ + 'config', + 'grep', + 'include', + 'exclude', + 'include-tag', + 'exclude-tag', + 'kibana-install-dir', + ], boolean: ['bail', 'invert', 'test-stats', 'updateBaselines', 'throttle', 'headless'], default: { config: 'test/functional/config.js', @@ -115,7 +126,8 @@ export function runFtrCli() { --bail stop tests after the first failure --grep pattern used to select which tests to run --invert invert grep to exclude tests - --exclude=file path to a test file that should not be loaded + --include=file a test file to be included, pass multiple times for multiple files + --exclude=file a test file to be excluded, pass multiple times for multiple files --include-tag=tag a tag to be included, pass multiple times for multiple tags --exclude-tag=tag a tag to be excluded, pass multiple times for multiple tags --test-stats print the number of tests (included and excluded) to STDERR diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 75623d6c08890..28e8396d0beba 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -64,9 +64,16 @@ export const schema = Joi.object() testFiles: Joi.array().items(Joi.string()), testRunner: Joi.func(), - excludeTestFiles: Joi.array() - .items(Joi.string()) - .default([]), + suiteFiles: Joi.object() + .keys({ + include: Joi.array() + .items(Joi.string()) + .default([]), + exclude: Joi.array() + .items(Joi.string()) + .default([]), + }) + .default(), suiteTags: Joi.object() .keys({ diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js index 64fc51a04aac9..1cac852a7e713 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js @@ -16,7 +16,8 @@ * specific language governing permissions and limitations * under the License. */ - +import { relative } from 'path'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { createAssignmentProxy } from './assignment_proxy'; import { wrapFunction } from './wrap_function'; import { wrapRunnableArgs } from './wrap_runnable_args'; @@ -65,6 +66,10 @@ export function decorateMochaUi(lifecycle, context) { this._tags = [].concat(this._tags || [], tags); }; + const relativeFilePath = relative(REPO_ROOT, this.file); + this.tags(relativeFilePath); + this.suiteTag = relativeFilePath; // The tag that uniquely targets this suite/file + provider.call(this); after(async () => { diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js index 70b0c0874e5e9..6ee65b1b7e394 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/load_test_files.js @@ -31,28 +31,12 @@ import { decorateMochaUi } from './decorate_mocha_ui'; * @param {String} path * @return {undefined} - mutates mocha, no return value */ -export const loadTestFiles = ({ - mocha, - log, - lifecycle, - providers, - paths, - excludePaths, - updateBaselines, -}) => { - const pendingExcludes = new Set(excludePaths.slice(0)); - +export const loadTestFiles = ({ mocha, log, lifecycle, providers, paths, updateBaselines }) => { const innerLoadTestFile = path => { if (typeof path !== 'string' || !isAbsolute(path)) { throw new TypeError('loadTestFile() only accepts absolute paths'); } - if (pendingExcludes.has(path)) { - pendingExcludes.delete(path); - log.warning('Skipping test file %s', path); - return; - } - loadTracer(path, `testFile[${path}]`, () => { log.verbose('Loading test file %s', path); @@ -94,13 +78,4 @@ export const loadTestFiles = ({ }; paths.forEach(innerLoadTestFile); - - if (pendingExcludes.size) { - throw new Error( - `After loading all test files some exclude paths were not consumed:${[ - '', - ...pendingExcludes, - ].join('\n -')}` - ); - } }; diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js index 326877919d985..61851cece0e8f 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js @@ -18,6 +18,8 @@ */ import Mocha from 'mocha'; +import { relative } from 'path'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { loadTestFiles } from './load_test_files'; import { filterSuitesByTags } from './filter_suites_by_tags'; @@ -50,10 +52,20 @@ export async function setupMocha(lifecycle, log, config, providers) { lifecycle, providers, paths: config.get('testFiles'), - excludePaths: config.get('excludeTestFiles'), updateBaselines: config.get('updateBaselines'), }); + // Each suite has a tag that is the path relative to the root of the repo + // So we just need to take input paths, make them relative to the root, and use them as tags + // Also, this is a separate filterSuitesByTags() call so that the test suites will be filtered first by + // files, then by tags. This way, you can target tags (like smoke) in a specific file. + filterSuitesByTags({ + log, + mocha, + include: config.get('suiteFiles.include').map(file => relative(REPO_ROOT, file)), + exclude: config.get('suiteFiles.exclude').map(file => relative(REPO_ROOT, file)), + }); + filterSuitesByTags({ log, mocha, diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap index bbf8b38712ac1..434c374d5d23d 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/args.test.js.snap @@ -16,6 +16,8 @@ Options: --bail Stop the test run at the first failure. --grep Pattern to select which tests to run. --updateBaselines Replace baseline screenshots with whatever is generated from the test. + --include Files that must included to be run, can be included multiple times. + --exclude Files that must NOT be included to be run, can be included multiple times. --include-tag Tags that suites must include to be run, can be included multiple times. --exclude-tag Tags that suites must NOT include to be run, can be included multiple times. --assert-none-excluded Exit with 1/0 based on if any test is excluded with the current set of tags. @@ -34,6 +36,10 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -52,6 +58,10 @@ Object { "debug": true, "esFrom": "snapshot", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -69,6 +79,10 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -90,6 +104,10 @@ Object { "extraKbnOpts": Object { "server.foo": "bar", }, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -107,6 +125,10 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "quiet": true, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -124,6 +146,10 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "silent": true, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -140,6 +166,10 @@ Object { "createLogger": [Function], "esFrom": "source", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -156,6 +186,10 @@ Object { "createLogger": [Function], "esFrom": "source", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -173,6 +207,10 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "installDir": "foo", + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -190,6 +228,10 @@ Object { "esFrom": "snapshot", "extraKbnOpts": undefined, "grep": "management", + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -206,6 +248,10 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], @@ -223,6 +269,10 @@ Object { "createLogger": [Function], "esFrom": "snapshot", "extraKbnOpts": undefined, + "suiteFiles": Object { + "exclude": Array [], + "include": Array [], + }, "suiteTags": Object { "exclude": Array [], "include": Array [], diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap index b12739b3b5df5..6ede71a6c3940 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/__snapshots__/cli.test.js.snap @@ -16,6 +16,8 @@ Options: --bail Stop the test run at the first failure. --grep Pattern to select which tests to run. --updateBaselines Replace baseline screenshots with whatever is generated from the test. + --include Files that must included to be run, can be included multiple times. + --exclude Files that must NOT be included to be run, can be included multiple times. --include-tag Tags that suites must include to be run, can be included multiple times. --exclude-tag Tags that suites must NOT include to be run, can be included multiple times. --assert-none-excluded Exit with 1/0 based on if any test is excluded with the current set of tags. diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js index b34006a38a45d..7d2414305de8e 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js @@ -46,6 +46,14 @@ const options = { updateBaselines: { desc: 'Replace baseline screenshots with whatever is generated from the test.', }, + include: { + arg: '', + desc: 'Files that must included to be run, can be included multiple times.', + }, + exclude: { + arg: '', + desc: 'Files that must NOT be included to be run, can be included multiple times.', + }, 'include-tag': { arg: '', desc: 'Tags that suites must include to be run, can be included multiple times.', @@ -115,6 +123,13 @@ export function processOptions(userOptions, defaultConfigPaths) { delete userOptions['kibana-install-dir']; } + userOptions.suiteFiles = { + include: [].concat(userOptions.include || []), + exclude: [].concat(userOptions.exclude || []), + }; + delete userOptions.include; + delete userOptions.exclude; + userOptions.suiteTags = { include: [].concat(userOptions['include-tag'] || []), exclude: [].concat(userOptions['exclude-tag'] || []), diff --git a/packages/kbn-test/src/functional_tests/lib/run_ftr.js b/packages/kbn-test/src/functional_tests/lib/run_ftr.js index 9b631e33f3b24..14883ac977c43 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_ftr.js +++ b/packages/kbn-test/src/functional_tests/lib/run_ftr.js @@ -22,7 +22,7 @@ import { CliError } from './run_cli'; async function createFtr({ configPath, - options: { installDir, log, bail, grep, updateBaselines, suiteTags }, + options: { installDir, log, bail, grep, updateBaselines, suiteFiles, suiteTags }, }) { const config = await readConfigFile(log, configPath); @@ -37,6 +37,10 @@ async function createFtr({ installDir, }, updateBaselines, + suiteFiles: { + include: [...suiteFiles.include, ...config.get('suiteFiles.include')], + exclude: [...suiteFiles.exclude, ...config.get('suiteFiles.exclude')], + }, suiteTags: { include: [...suiteTags.include, ...config.get('suiteTags.include')], exclude: [...suiteTags.exclude, ...config.get('suiteTags.exclude')], From b71099d620012e5df2850a69faac7efaf0f26d29 Mon Sep 17 00:00:00 2001 From: spalger Date: Tue, 17 Mar 2020 08:13:32 -0700 Subject: [PATCH 47/76] skip flaky suite (#58643) (#58991) --- .../security_and_spaces/tests/alerting/alerts.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 70c885bb0a692..6766705f688a6 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -26,7 +26,9 @@ export default function alertTests({ getService }: FtrProviderContext) { const esTestIndexTool = new ESTestIndexTool(es, retry); const taskManagerUtils = new TaskManagerUtils(es, retry); - describe('alerts', () => { + // FLAKY: https://github.com/elastic/kibana/issues/58643 + // FLAKY: https://github.com/elastic/kibana/issues/58991 + describe.skip('alerts', () => { const authorizationIndex = '.kibana-test-authorization'; const objectRemover = new ObjectRemover(supertest); From 6a70d21ef31bcc93a9a0d512c753a78fb2b54515 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Tue, 17 Mar 2020 16:29:01 +0100 Subject: [PATCH 48/76] [ML] Functional tests - disable df analytics clone tests --- .../apps/machine_learning/data_frame_analytics/cloning.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts index 512de861e673a..51155fccc358d 100644 --- a/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts +++ b/x-pack/test/functional/apps/machine_learning/data_frame_analytics/cloning.ts @@ -12,7 +12,9 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const ml = getService('ml'); - describe('jobs cloning supported by UI form', function() { + + // failing test, see https://github.com/elastic/kibana/issues/60389 + describe.skip('jobs cloning supported by UI form', function() { this.tags(['smoke']); const testDataList: Array<{ From 9318862f1967d6fefdfca5e94417ff1617e5ba06 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 17 Mar 2020 12:30:17 -0400 Subject: [PATCH 49/76] Allow kbn-config-schema to ignore unknown keys (#59560) * allow kbn-config-schema to ignore unknown keys * Consolidate unknown key configuration * updates following merge Co-authored-by: Elastic Machine --- ...plugin-core-server.routeconfig.validate.md | 4 +- packages/kbn-config-schema/README.md | 4 +- .../src/types/object_type.test.ts | 43 ++++++++++++++++--- .../src/types/object_type.ts | 21 ++++++--- src/core/server/http/router/route.ts | 4 +- src/core/server/http/router/router.test.ts | 2 +- .../server/ui_settings/routes/set_many.ts | 2 +- .../server/ui_settings/ui_settings_config.ts | 2 +- .../autocomplete/value_suggestions_route.ts | 4 +- src/plugins/data/server/search/routes.ts | 6 +-- src/plugins/timelion/config.ts | 2 +- src/plugins/timelion/server/routes/run.ts | 12 ++---- .../vis_type_timeseries/server/routes/vis.ts | 2 +- .../plugins/rendering_plugin/server/plugin.ts | 2 +- .../api/license/register_license_route.ts | 2 +- .../plugins/rollup/server/routes/api/jobs.ts | 2 +- .../signals/signal_params_schema.ts | 6 +-- .../lib/framework/kibana_framework_adapter.ts | 2 +- .../apm/server/routes/create_api/index.ts | 2 +- .../canvas/server/routes/workpad/update.ts | 2 +- .../plugins/case/server/routes/api/utils.ts | 2 +- .../file_upload/server/routes/file_upload.js | 12 +++--- x-pack/plugins/graph/server/routes/explore.ts | 2 +- x-pack/plugins/graph/server/routes/search.ts | 2 +- .../routes/api/templates/validate_schemas.ts | 6 +-- .../framework/kibana_framework_adapter.ts | 2 +- .../server/routes/inventory_metadata/index.ts | 2 +- .../results/log_entry_categories.ts | 2 +- .../results/log_entry_category_datasets.ts | 2 +- .../results/log_entry_category_examples.ts | 2 +- .../log_analysis/results/log_entry_rate.ts | 2 +- .../routes/log_analysis/validation/indices.ts | 2 +- .../server/routes/log_entries/entries.ts | 2 +- .../server/routes/log_entries/highlights.ts | 2 +- .../infra/server/routes/log_entries/item.ts | 2 +- .../server/routes/log_entries/summary.ts | 2 +- .../routes/log_entries/summary_highlights.ts | 2 +- .../infra/server/routes/metadata/index.ts | 2 +- .../server/routes/metrics_explorer/index.ts | 2 +- .../infra/server/routes/node_details/index.ts | 2 +- .../infra/server/routes/snapshot/index.ts | 2 +- .../lens/server/routes/existing_fields.ts | 2 +- .../plugins/lens/server/routes/field_stats.ts | 6 +-- .../searchprofiler/server/routes/profile.ts | 2 +- .../server/routes/authentication/common.ts | 2 +- .../server/routes/authentication/oidc.ts | 6 +-- .../server/routes/role_mapping/post.ts | 4 +- .../security/server/routes/views/login.ts | 2 +- .../server/routes/api/validate_schemas.ts | 10 ++--- .../routes/api/indices/register_get_route.ts | 2 +- .../api/watch/register_execute_route.ts | 4 +- .../routes/api/watch/register_save_route.ts | 2 +- .../api/watch/register_visualize_route.ts | 4 +- 53 files changed, 132 insertions(+), 96 deletions(-) diff --git a/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md b/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md index 204d8a786fede..3bbabc04f2500 100644 --- a/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md +++ b/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md @@ -14,7 +14,7 @@ validate: RouteValidatorFullConfig | false; ## Remarks -You \*must\* specify a validation schema to be able to read: - url path segments - request query - request body To opt out of validating the request, specify `validate: false`. In this case request params, query, and body will be \*\*empty\*\* objects and have no access to raw values. In some cases you may want to use another validation library. To do this, you need to instruct the `@kbn/config-schema` library to output \*\*non-validated values\*\* with setting schema as `schema.object({}, { allowUnknowns: true })`; +You \*must\* specify a validation schema to be able to read: - url path segments - request query - request body To opt out of validating the request, specify `validate: false`. In this case request params, query, and body will be \*\*empty\*\* objects and have no access to raw values. In some cases you may want to use another validation library. To do this, you need to instruct the `@kbn/config-schema` library to output \*\*non-validated values\*\* with setting schema as `schema.object({}, { unknowns: 'allow' })`; ## Example @@ -49,7 +49,7 @@ router.get({ path: 'path/{id}', validate: { // handler has access to raw non-validated params in runtime - params: schema.object({}, { allowUnknowns: true }) + params: schema.object({}, { unknowns: 'allow' }) }, }, (context, req, res,) { diff --git a/packages/kbn-config-schema/README.md b/packages/kbn-config-schema/README.md index 8719a2ae558ab..a4f2c1f6458cf 100644 --- a/packages/kbn-config-schema/README.md +++ b/packages/kbn-config-schema/README.md @@ -239,7 +239,7 @@ __Output type:__ `{ [K in keyof TProps]: TypeOf } as TObject` __Options:__ * `defaultValue: TObject | Reference | (() => TObject)` - defines a default value, see [Default values](#default-values) section for more details. * `validate: (value: TObject) => string | void` - defines a custom validator function, see [Custom validation](#custom-validation) section for more details. - * `allowUnknowns: boolean` - indicates whether unknown object properties should be allowed. It's `false` by default. + * `unknowns: 'allow' | 'ignore' | 'forbid'` - indicates whether unknown object properties should be allowed, ignored, or forbidden. It's `forbid` by default. __Usage:__ ```typescript @@ -250,7 +250,7 @@ const valueSchema = schema.object({ ``` __Notes:__ -* Using `allowUnknowns` is discouraged and should only be used in exceptional circumstances. Consider using `schema.recordOf()` instead. +* Using `unknowns: 'allow'` is discouraged and should only be used in exceptional circumstances. Consider using `schema.recordOf()` instead. * Currently `schema.object()` always has a default value of `{}`, but this may change in the near future. Try to not rely on this behaviour and specify default value explicitly or use `schema.maybe()` if the value is optional. * `schema.object()` also supports a json string as input if it can be safely parsed using `JSON.parse` and if the resulting value is a plain object. diff --git a/packages/kbn-config-schema/src/types/object_type.test.ts b/packages/kbn-config-schema/src/types/object_type.test.ts index 29e341983fde9..47a0f5f7a5491 100644 --- a/packages/kbn-config-schema/src/types/object_type.test.ts +++ b/packages/kbn-config-schema/src/types/object_type.test.ts @@ -276,10 +276,10 @@ test('individual keys can validated', () => { ); }); -test('allow unknown keys when allowUnknowns = true', () => { +test('allow unknown keys when unknowns = `allow`', () => { const type = schema.object( { foo: schema.string({ defaultValue: 'test' }) }, - { allowUnknowns: true } + { unknowns: 'allow' } ); expect( @@ -292,10 +292,10 @@ test('allow unknown keys when allowUnknowns = true', () => { }); }); -test('allowUnknowns = true affects only own keys', () => { +test('unknowns = `allow` affects only own keys', () => { const type = schema.object( { foo: schema.object({ bar: schema.string() }) }, - { allowUnknowns: true } + { unknowns: 'allow' } ); expect(() => @@ -308,10 +308,10 @@ test('allowUnknowns = true affects only own keys', () => { ).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`); }); -test('does not allow unknown keys when allowUnknowns = false', () => { +test('does not allow unknown keys when unknowns = `forbid`', () => { const type = schema.object( { foo: schema.string({ defaultValue: 'test' }) }, - { allowUnknowns: false } + { unknowns: 'forbid' } ); expect(() => type.validate({ @@ -319,3 +319,34 @@ test('does not allow unknown keys when allowUnknowns = false', () => { }) ).toThrowErrorMatchingInlineSnapshot(`"[bar]: definition for this key is missing"`); }); + +test('allow and remove unknown keys when unknowns = `ignore`', () => { + const type = schema.object( + { foo: schema.string({ defaultValue: 'test' }) }, + { unknowns: 'ignore' } + ); + + expect( + type.validate({ + bar: 'baz', + }) + ).toEqual({ + foo: 'test', + }); +}); + +test('unknowns = `ignore` affects only own keys', () => { + const type = schema.object( + { foo: schema.object({ bar: schema.string() }) }, + { unknowns: 'ignore' } + ); + + expect(() => + type.validate({ + foo: { + bar: 'bar', + baz: 'baz', + }, + }) + ).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`); +}); diff --git a/packages/kbn-config-schema/src/types/object_type.ts b/packages/kbn-config-schema/src/types/object_type.ts index f34acd0d2ce65..5a50e714a5931 100644 --- a/packages/kbn-config-schema/src/types/object_type.ts +++ b/packages/kbn-config-schema/src/types/object_type.ts @@ -30,17 +30,25 @@ export type TypeOf> = RT['type']; // this might not have perfect _rendering_ output, but it will be typed. export type ObjectResultType

= Readonly<{ [K in keyof P]: TypeOf }>; +interface UnknownOptions { + /** + * Options for dealing with unknown keys: + * - allow: unknown keys will be permitted + * - ignore: unknown keys will not fail validation, but will be stripped out + * - forbid (default): unknown keys will fail validation + */ + unknowns?: 'allow' | 'ignore' | 'forbid'; +} + export type ObjectTypeOptions

= TypeOptions< { [K in keyof P]: TypeOf } -> & { - /** Should uknown keys not be defined in the schema be allowed. Defaults to `false` */ - allowUnknowns?: boolean; -}; +> & + UnknownOptions; export class ObjectType

extends Type> { private props: Record; - constructor(props: P, { allowUnknowns = false, ...typeOptions }: ObjectTypeOptions

= {}) { + constructor(props: P, { unknowns = 'forbid', ...typeOptions }: ObjectTypeOptions

= {}) { const schemaKeys = {} as Record; for (const [key, value] of Object.entries(props)) { schemaKeys[key] = value.getSchema(); @@ -50,7 +58,8 @@ export class ObjectType

extends Type> .keys(schemaKeys) .default() .optional() - .unknown(Boolean(allowUnknowns)); + .unknown(unknowns === 'allow') + .options({ stripUnknown: { objects: unknowns === 'ignore' } }); super(schema, typeOptions); this.props = schemaKeys; diff --git a/src/core/server/http/router/route.ts b/src/core/server/http/router/route.ts index bb0a8616e7222..9789d266587af 100644 --- a/src/core/server/http/router/route.ts +++ b/src/core/server/http/router/route.ts @@ -179,7 +179,7 @@ export interface RouteConfig { * access to raw values. * In some cases you may want to use another validation library. To do this, you need to * instruct the `@kbn/config-schema` library to output **non-validated values** with - * setting schema as `schema.object({}, { allowUnknowns: true })`; + * setting schema as `schema.object({}, { unknowns: 'allow' })`; * * @example * ```ts @@ -212,7 +212,7 @@ export interface RouteConfig { * path: 'path/{id}', * validate: { * // handler has access to raw non-validated params in runtime - * params: schema.object({}, { allowUnknowns: true }) + * params: schema.object({}, { unknowns: 'allow' }) * }, * }, * (context, req, res,) { diff --git a/src/core/server/http/router/router.test.ts b/src/core/server/http/router/router.test.ts index a936da6a40a9f..9655e2153b863 100644 --- a/src/core/server/http/router/router.test.ts +++ b/src/core/server/http/router/router.test.ts @@ -59,7 +59,7 @@ describe('Router', () => { { path: '/', options: { body: { output: 'file' } } as any, // We explicitly don't support 'file' - validate: { body: schema.object({}, { allowUnknowns: true }) }, + validate: { body: schema.object({}, { unknowns: 'allow' }) }, }, (context, req, res) => res.ok({}) ) diff --git a/src/core/server/ui_settings/routes/set_many.ts b/src/core/server/ui_settings/routes/set_many.ts index 5623c3fe11b80..d19a36a7ce768 100644 --- a/src/core/server/ui_settings/routes/set_many.ts +++ b/src/core/server/ui_settings/routes/set_many.ts @@ -24,7 +24,7 @@ import { CannotOverrideError } from '../ui_settings_errors'; const validate = { body: schema.object({ - changes: schema.object({}, { allowUnknowns: true }), + changes: schema.object({}, { unknowns: 'allow' }), }), }; diff --git a/src/core/server/ui_settings/ui_settings_config.ts b/src/core/server/ui_settings/ui_settings_config.ts index a54d482a0296a..a0ac48e2dd089 100644 --- a/src/core/server/ui_settings/ui_settings_config.ts +++ b/src/core/server/ui_settings/ui_settings_config.ts @@ -39,7 +39,7 @@ const configSchema = schema.object({ }) ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }); diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/data/server/autocomplete/value_suggestions_route.ts index 03dbd40984412..b7569a22e9fc9 100644 --- a/src/plugins/data/server/autocomplete/value_suggestions_route.ts +++ b/src/plugins/data/server/autocomplete/value_suggestions_route.ts @@ -39,7 +39,7 @@ export function registerValueSuggestionsRoute( { index: schema.string(), }, - { allowUnknowns: false } + { unknowns: 'allow' } ), body: schema.object( { @@ -47,7 +47,7 @@ export function registerValueSuggestionsRoute( query: schema.string(), boolFilter: schema.maybe(schema.any()), }, - { allowUnknowns: false } + { unknowns: 'allow' } ), }, }, diff --git a/src/plugins/data/server/search/routes.ts b/src/plugins/data/server/search/routes.ts index e618f99084aed..b90d7d4ff80ce 100644 --- a/src/plugins/data/server/search/routes.ts +++ b/src/plugins/data/server/search/routes.ts @@ -28,9 +28,9 @@ export function registerSearchRoute(router: IRouter): void { validate: { params: schema.object({ strategy: schema.string() }), - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), - body: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { unknowns: 'allow' }), }, }, async (context, request, res) => { @@ -64,7 +64,7 @@ export function registerSearchRoute(router: IRouter): void { id: schema.string(), }), - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), }, }, async (context, request, res) => { diff --git a/src/plugins/timelion/config.ts b/src/plugins/timelion/config.ts index 561fb4de9f58d..eaea1aaca1b7b 100644 --- a/src/plugins/timelion/config.ts +++ b/src/plugins/timelion/config.ts @@ -25,7 +25,7 @@ export const configSchema = schema.object( graphiteUrls: schema.maybe(schema.arrayOf(schema.string())), }, // This option should be removed as soon as we entirely migrate config from legacy Timelion plugin. - { allowUnknowns: true } + { unknowns: 'allow' } ); export type ConfigSchema = TypeOf; diff --git a/src/plugins/timelion/server/routes/run.ts b/src/plugins/timelion/server/routes/run.ts index b7a4179da768e..b773bba68ea81 100644 --- a/src/plugins/timelion/server/routes/run.ts +++ b/src/plugins/timelion/server/routes/run.ts @@ -78,15 +78,11 @@ export function runRoute( es: schema.object({ filter: schema.object({ bool: schema.object({ - filter: schema.maybe( - schema.arrayOf(schema.object({}, { allowUnknowns: true })) - ), - must: schema.maybe(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), - should: schema.maybe( - schema.arrayOf(schema.object({}, { allowUnknowns: true })) - ), + filter: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + must: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + should: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), must_not: schema.maybe( - schema.arrayOf(schema.object({}, { allowUnknowns: true })) + schema.arrayOf(schema.object({}, { unknowns: 'allow' })) ), }), }), diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_type_timeseries/server/routes/vis.ts index e2d1e4d114ad5..9abbc4ad617dc 100644 --- a/src/plugins/vis_type_timeseries/server/routes/vis.ts +++ b/src/plugins/vis_type_timeseries/server/routes/vis.ts @@ -23,7 +23,7 @@ import { getVisData, GetVisDataOptions } from '../lib/get_vis_data'; import { visPayloadSchema } from './post_vis_schema'; import { Framework, ValidationTelemetryServiceSetup } from '../index'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const visDataRoutes = ( router: IRouter, diff --git a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts index fad19728b7514..3f6a8e8773e04 100644 --- a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts @@ -33,7 +33,7 @@ export class RenderingPlugin implements Plugin { { includeUserSettings: schema.boolean({ defaultValue: true }), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), params: schema.object({ id: schema.maybe(schema.string()), diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts index cdc929a2f3bb3..03ec583a34166 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts +++ b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts @@ -15,7 +15,7 @@ export function registerLicenseRoute(server: Server, legacy: Legacy, xpackInfo: validate: { query: schema.object({ acknowledge: schema.string() }), body: schema.object({ - license: schema.object({}, { allowUnknowns: true }), + license: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts index e58bc95b9a375..e45713e2b807c 100644 --- a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts +++ b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts @@ -127,7 +127,7 @@ export function registerJobsRoute(deps: RouteDependencies, legacy: ServerShim) { { id: schema.string(), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }), }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts index d1726f93108c7..adbb5fa618957 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts @@ -26,13 +26,13 @@ export const signalParamsSchema = () => savedId: schema.nullable(schema.string()), timelineId: schema.nullable(schema.string()), timelineTitle: schema.nullable(schema.string()), - meta: schema.nullable(schema.object({}, { allowUnknowns: true })), + meta: schema.nullable(schema.object({}, { unknowns: 'allow' })), query: schema.nullable(schema.string()), - filters: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + filters: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), maxSignals: schema.number({ defaultValue: DEFAULT_MAX_SIGNALS }), riskScore: schema.number(), severity: schema.string(), - threat: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + threat: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), to: schema.string(), type: schema.string(), references: schema.arrayOf(schema.string(), { defaultValue: [] }), diff --git a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts index 7d42149223b32..004ac36bad5b4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts @@ -61,7 +61,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { this.router.post( { path: routePath, - validate: { body: configSchema.object({}, { allowUnknowns: true }) }, + validate: { body: configSchema.object({}, { unknowns: 'allow' }) }, options: { tags: ['access:siem'], }, diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index a84a24cea17d2..e216574f8a02e 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -71,7 +71,7 @@ export function createApi() { body: bodyRt || t.null }; - const anyObject = schema.object({}, { allowUnknowns: true }); + const anyObject = schema.object({}, { unknowns: 'allow' }); (router[routerMethod] as RouteRegistrar)( { diff --git a/x-pack/plugins/canvas/server/routes/workpad/update.ts b/x-pack/plugins/canvas/server/routes/workpad/update.ts index 83b8fef48e9be..64736bcd57fd5 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/update.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/update.ts @@ -120,7 +120,7 @@ export function initializeUpdateWorkpadAssetsRoute(deps: RouteInitializerDeps) { // ToDo: Currently the validation must be a schema.object // Because we don't know what keys the assets will have, we have to allow // unknowns and then validate in the handler - body: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { unknowns: 'allow' }), }, options: { body: { diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts index 04fe426bb2ecc..27ee6fc58e20a 100644 --- a/x-pack/plugins/case/server/routes/api/utils.ts +++ b/x-pack/plugins/case/server/routes/api/utils.ts @@ -141,4 +141,4 @@ export const sortToSnake = (sortField: string): SortFieldCase => { } }; -export const escapeHatch = schema.object({}, { allowUnknowns: true }); +export const escapeHatch = schema.object({}, { unknowns: 'allow' }); diff --git a/x-pack/plugins/file_upload/server/routes/file_upload.js b/x-pack/plugins/file_upload/server/routes/file_upload.js index acbc907729d95..d75f03132b404 100644 --- a/x-pack/plugins/file_upload/server/routes/file_upload.js +++ b/x-pack/plugins/file_upload/server/routes/file_upload.js @@ -28,12 +28,12 @@ export const bodySchema = schema.object( {}, { defaultValue: {}, - allowUnknowns: true, + unknowns: 'allow', } ) ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ); const options = { @@ -48,7 +48,7 @@ export const idConditionalValidation = (body, boolHasId) => .object( { data: boolHasId - ? schema.arrayOf(schema.object({}, { allowUnknowns: true }), { minSize: 1 }) + ? schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { minSize: 1 }) : schema.any(), settings: boolHasId ? schema.any() @@ -58,7 +58,7 @@ export const idConditionalValidation = (body, boolHasId) => defaultValue: { number_of_shards: 1, }, - allowUnknowns: true, + unknowns: 'allow', } ), mappings: boolHasId @@ -67,11 +67,11 @@ export const idConditionalValidation = (body, boolHasId) => {}, { defaultValue: {}, - allowUnknowns: true, + unknowns: 'allow', } ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ) .validate(body); diff --git a/x-pack/plugins/graph/server/routes/explore.ts b/x-pack/plugins/graph/server/routes/explore.ts index 125378891151b..ceced840bdbc6 100644 --- a/x-pack/plugins/graph/server/routes/explore.ts +++ b/x-pack/plugins/graph/server/routes/explore.ts @@ -23,7 +23,7 @@ export function registerExploreRoute({ validate: { body: schema.object({ index: schema.string(), - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/plugins/graph/server/routes/search.ts b/x-pack/plugins/graph/server/routes/search.ts index 91b404dc7cb91..6e9fe508af3d3 100644 --- a/x-pack/plugins/graph/server/routes/search.ts +++ b/x-pack/plugins/graph/server/routes/search.ts @@ -21,7 +21,7 @@ export function registerSearchRoute({ validate: { body: schema.object({ index: schema.string(), - body: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts index fb5d41870eece..8bf2774ac38b3 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts @@ -11,9 +11,9 @@ export const templateSchema = schema.object({ indexPatterns: schema.arrayOf(schema.string()), version: schema.maybe(schema.number()), order: schema.maybe(schema.number()), - settings: schema.maybe(schema.object({}, { allowUnknowns: true })), - aliases: schema.maybe(schema.object({}, { allowUnknowns: true })), - mappings: schema.maybe(schema.object({}, { allowUnknowns: true })), + settings: schema.maybe(schema.object({}, { unknowns: 'allow' })), + aliases: schema.maybe(schema.object({}, { unknowns: 'allow' })), + mappings: schema.maybe(schema.object({}, { unknowns: 'allow' })), ilmPolicy: schema.maybe( schema.object({ name: schema.maybe(schema.string()), diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 6ff749c040220..e2ff93ce356e6 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -76,7 +76,7 @@ export class KibanaFramework { public registerGraphQLEndpoint(routePath: string, gqlSchema: GraphQLSchema) { // These endpoints are validated by GraphQL at runtime and with GraphQL generated types - const body = schema.object({}, { allowUnknowns: true }); + const body = schema.object({}, { unknowns: 'allow' }); type Body = TypeOf; const routeOptions = { diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts index 33328bdfebaf4..7e9b7ada28c8e 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts @@ -18,7 +18,7 @@ import { } from '../../../common/http_api/inventory_meta_api'; import { getCloudMetadata } from './lib/get_cloud_metadata'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initInventoryMetaRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts index 7eb7de57b2f92..6852a102afc86 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts @@ -19,7 +19,7 @@ import { import { throwErrors } from '../../../../common/runtime_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryCategoriesRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts index 8132633028277..730e32dee2fbe 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts @@ -19,7 +19,7 @@ import { throwErrors } from '../../../../common/runtime_types'; import { InfraBackendLibs } from '../../../lib/infra_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryCategoryDatasetsRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts index 67c6c9f5b9924..44f466cc77c89 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts @@ -19,7 +19,7 @@ import { throwErrors } from '../../../../common/runtime_types'; import { InfraBackendLibs } from '../../../lib/infra_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryCategoryExamplesRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index 6551316fd0c64..38dc0a790a7a3 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -20,7 +20,7 @@ import { import { throwErrors } from '../../../../common/runtime_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryRateRoute = ({ framework, logEntryRateAnalysis }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts index fe579124cfe10..54ae0b4529daa 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts @@ -19,7 +19,7 @@ import { import { throwErrors } from '../../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initValidateLogAnalysisIndicesRoute = ({ framework }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/entries.ts b/x-pack/plugins/infra/server/routes/log_entries/entries.ts index 361535886ab22..93802468dd267 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/entries.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/entries.ts @@ -22,7 +22,7 @@ import { import { parseFilterQuery } from '../../utils/serialized_query'; import { LogEntriesParams } from '../../lib/domains/log_entries_domain'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/highlights.ts b/x-pack/plugins/infra/server/routes/log_entries/highlights.ts index 8af81a6ee313d..8ee412d5acdd5 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/highlights.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/highlights.ts @@ -22,7 +22,7 @@ import { import { parseFilterQuery } from '../../utils/serialized_query'; import { LogEntriesParams } from '../../lib/domains/log_entries_domain'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/item.ts b/x-pack/plugins/infra/server/routes/log_entries/item.ts index 22663cb2001f0..3a6bdaf3804e3 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/item.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/item.ts @@ -20,7 +20,7 @@ import { logEntriesItemResponseRT, } from '../../../common/http_api'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesItemRoute = ({ framework, sources, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/summary.ts b/x-pack/plugins/infra/server/routes/log_entries/summary.ts index 05643adbe781f..3f5bc8e364a58 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/summary.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/summary.ts @@ -21,7 +21,7 @@ import { } from '../../../common/http_api/log_entries'; import { parseFilterQuery } from '../../utils/serialized_query'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts b/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts index ecccd931bb371..6c6f7a5a3dcd3 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts @@ -21,7 +21,7 @@ import { } from '../../../common/http_api/log_entries'; import { parseFilterQuery } from '../../utils/serialized_query'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesSummaryHighlightsRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/metadata/index.ts b/x-pack/plugins/infra/server/routes/metadata/index.ts index a1f6311a103eb..03d28110d612a 100644 --- a/x-pack/plugins/infra/server/routes/metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/metadata/index.ts @@ -23,7 +23,7 @@ import { getCloudMetricsMetadata } from './lib/get_cloud_metric_metadata'; import { getNodeInfo } from './lib/get_node_info'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initMetadataRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts index 64cdb9318b6e1..c22095a31195a 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts @@ -15,7 +15,7 @@ import { populateSeriesWithTSVBData } from './lib/populate_series_with_tsvb_data import { metricsExplorerRequestBodyRT, metricsExplorerResponseRT } from '../../../common/http_api'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initMetricExplorerRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/node_details/index.ts b/x-pack/plugins/infra/server/routes/node_details/index.ts index 4a09615f0a17c..36906f6f4125b 100644 --- a/x-pack/plugins/infra/server/routes/node_details/index.ts +++ b/x-pack/plugins/infra/server/routes/node_details/index.ts @@ -18,7 +18,7 @@ import { } from '../../../common/http_api/node_details_api'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initNodeDetailsRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts index 5f28e41d80c25..e45b9884967d0 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/index.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/index.ts @@ -14,7 +14,7 @@ import { parseFilterQuery } from '../../utils/serialized_query'; import { SnapshotRequestRT, SnapshotNodeResponseRT } from '../../../common/http_api/snapshot_api'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initSnapshotRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index 57c1680413537..b1964a9150982 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -55,7 +55,7 @@ export async function existingFieldsRoute(setup: CoreSetup) { indexPatternId: schema.string(), }), body: schema.object({ - dslQuery: schema.object({}, { allowUnknowns: true }), + dslQuery: schema.object({}, { unknowns: 'allow' }), fromDate: schema.maybe(schema.string()), toDate: schema.maybe(schema.string()), timeFieldName: schema.maybe(schema.string()), diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 786aba5efe3fb..5c91be9dfbd78 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -24,7 +24,7 @@ export async function initFieldsRoute(setup: CoreSetup) { }), body: schema.object( { - dslQuery: schema.object({}, { allowUnknowns: true }), + dslQuery: schema.object({}, { unknowns: 'allow' }), fromDate: schema.string(), toDate: schema.string(), timeFieldName: schema.maybe(schema.string()), @@ -34,10 +34,10 @@ export async function initFieldsRoute(setup: CoreSetup) { type: schema.string(), esTypes: schema.maybe(schema.arrayOf(schema.string())), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }, }, diff --git a/x-pack/plugins/searchprofiler/server/routes/profile.ts b/x-pack/plugins/searchprofiler/server/routes/profile.ts index c47ab81b2ab7e..4af3f0519cbc0 100644 --- a/x-pack/plugins/searchprofiler/server/routes/profile.ts +++ b/x-pack/plugins/searchprofiler/server/routes/profile.ts @@ -12,7 +12,7 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) = path: '/api/searchprofiler/profile', validate: { body: schema.object({ - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), index: schema.string(), }), }, diff --git a/x-pack/plugins/security/server/routes/authentication/common.ts b/x-pack/plugins/security/server/routes/authentication/common.ts index c9856e9dff7f1..19d197b63f540 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.ts @@ -21,7 +21,7 @@ export function defineCommonRoutes({ router, authc, basePath, logger }: RouteDef path, // Allow unknown query parameters as this endpoint can be hit by the 3rd-party with any // set of query string parameters (e.g. SAML/OIDC logout request parameters). - validate: { query: schema.object({}, { allowUnknowns: true }) }, + validate: { query: schema.object({}, { unknowns: 'allow' }) }, options: { authRequired: false }, }, async (context, request, response) => { diff --git a/x-pack/plugins/security/server/routes/authentication/oidc.ts b/x-pack/plugins/security/server/routes/authentication/oidc.ts index 232fdd26f7838..96c36af20e982 100644 --- a/x-pack/plugins/security/server/routes/authentication/oidc.ts +++ b/x-pack/plugins/security/server/routes/authentication/oidc.ts @@ -103,7 +103,7 @@ export function defineOIDCRoutes({ router, logger, authc, csp, basePath }: Route // The client MUST ignore unrecognized response parameters according to // https://openid.net/specs/openid-connect-core-1_0.html#AuthResponseValidation and // https://tools.ietf.org/html/rfc6749#section-4.1.2. - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, @@ -178,7 +178,7 @@ export function defineOIDCRoutes({ router, logger, authc, csp, basePath }: Route }, // Other parameters MAY be sent, if defined by extensions. Any parameters used that are not understood MUST // be ignored by the Client according to https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin. - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, @@ -217,7 +217,7 @@ export function defineOIDCRoutes({ router, logger, authc, csp, basePath }: Route }, // Other parameters MAY be sent, if defined by extensions. Any parameters used that are not understood MUST // be ignored by the Client according to https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin. - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, diff --git a/x-pack/plugins/security/server/routes/role_mapping/post.ts b/x-pack/plugins/security/server/routes/role_mapping/post.ts index bf9112be4ad3f..11149f38069a7 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/post.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/post.ts @@ -36,8 +36,8 @@ export function defineRoleMappingPostRoutes(params: RouteDefinitionParams) { // and keeping this in sync (and testable!) with ES could prove problematic. // We do not interpret any of these rules within this route handler; // they are simply passed to ES for processing. - rules: schema.object({}, { allowUnknowns: true }), - metadata: schema.object({}, { allowUnknowns: true }), + rules: schema.object({}, { unknowns: 'allow' }), + metadata: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/plugins/security/server/routes/views/login.ts b/x-pack/plugins/security/server/routes/views/login.ts index e2e162d298e45..ee1fe01ab1b22 100644 --- a/x-pack/plugins/security/server/routes/views/login.ts +++ b/x-pack/plugins/security/server/routes/views/login.ts @@ -28,7 +28,7 @@ export function defineLoginRoutes({ next: schema.maybe(schema.string()), msg: schema.maybe(schema.string()), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts index f6f8bb4de4d83..e5df0ec33db0b 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts @@ -37,9 +37,9 @@ export const policySchema = schema.object({ config: schema.maybe(snapshotConfigSchema), retention: schema.maybe(snapshotRetentionSchema), isManagedPolicy: schema.boolean(), - stats: schema.maybe(schema.object({}, { allowUnknowns: true })), - lastFailure: schema.maybe(schema.object({}, { allowUnknowns: true })), - lastSuccess: schema.maybe(schema.object({}, { allowUnknowns: true })), + stats: schema.maybe(schema.object({}, { unknowns: 'allow' })), + lastFailure: schema.maybe(schema.object({}, { unknowns: 'allow' })), + lastSuccess: schema.maybe(schema.object({}, { unknowns: 'allow' })), }); const fsRepositorySettings = schema.object({ @@ -100,7 +100,7 @@ const hdsRepositorySettings = schema.object( readonly: schema.maybe(schema.boolean()), ['security.principal']: schema.maybe(schema.string()), }, - { allowUnknowns: true } + { unknowns: 'allow' } ); const hdsfRepository = schema.object({ @@ -158,7 +158,7 @@ const sourceRepository = schema.object({ { delegateType: schema.string(), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), ]), }); diff --git a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts index df6f62135baeb..a1184cbebd139 100644 --- a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts @@ -11,7 +11,7 @@ import { isEsError } from '../../../lib/is_es_error'; import { RouteDependencies } from '../../../types'; import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; -const bodySchema = schema.object({ pattern: schema.string() }, { allowUnknowns: true }); +const bodySchema = schema.object({ pattern: schema.string() }, { unknowns: 'allow' }); function getIndexNamesFromAliasesResponse(json: Record) { return reduce( diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts index 7aaa77c05a5f0..14a14a6f64d7b 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts @@ -19,8 +19,8 @@ import { Watch } from '../../../models/watch/index'; import { WatchHistoryItem } from '../../../models/watch_history_item/index'; const bodySchema = schema.object({ - executeDetails: schema.object({}, { allowUnknowns: true }), - watch: schema.object({}, { allowUnknowns: true }), + executeDetails: schema.object({}, { unknowns: 'allow' }), + watch: schema.object({}, { unknowns: 'allow' }), }); function executeWatch(dataClient: IScopedClusterClient, executeDetails: any, watchJson: any) { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts index 572790f12a5f8..61d167bb9bbcd 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts @@ -22,7 +22,7 @@ const bodySchema = schema.object( type: schema.string(), isNew: schema.boolean(), }, - { allowUnknowns: true } + { unknowns: 'allow' } ); function fetchWatch(dataClient: IScopedClusterClient, watchId: string) { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts index 200b35953b6f2..90550731bf23a 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts @@ -16,8 +16,8 @@ import { Watch } from '../../../models/watch/index'; import { VisualizeOptions } from '../../../models/visualize_options/index'; const bodySchema = schema.object({ - watch: schema.object({}, { allowUnknowns: true }), - options: schema.object({}, { allowUnknowns: true }), + watch: schema.object({}, { unknowns: 'allow' }), + options: schema.object({}, { unknowns: 'allow' }), }); function fetchVisualizeData(dataClient: IScopedClusterClient, index: any, body: any) { From 156066dc6fa512169f1af4244f5c4cdefb61be14 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 17 Mar 2020 12:34:03 -0400 Subject: [PATCH 50/76] [Fleet] Add config revision to fleet agents (#60292) --- .../common/types/models/agent.ts | 3 +- .../sections/fleet/agent_list_page/index.tsx | 45 +++++-- .../ingest_manager/server/saved_objects.ts | 3 +- .../server/services/agents/acks.ts | 14 +++ .../server/services/agents/checkin.test.ts | 117 ++++++++++++++++++ .../server/services/agents/checkin.ts | 35 +++++- .../server/services/agents/enroll.ts | 1 - .../server/services/agents/update.ts | 8 +- 8 files changed, 205 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index ad06e8d3c9c11..179cc3fc9eb55 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -56,8 +56,9 @@ interface AgentBase { access_api_key_id?: string; default_api_key?: string; config_id?: string; + config_revision?: number; + config_newest_revision?: number; last_checkin?: string; - config_updated_at?: string; actions: AgentAction[]; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index acf09dedc25f7..14a579eb72598 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -26,6 +26,7 @@ import { EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem, + EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react'; @@ -289,6 +290,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { }, { field: 'active', + width: '100px', name: i18n.translate('xpack.ingestManager.agentList.statusColumnTitle', { defaultMessage: 'Status', }), @@ -299,10 +301,10 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { name: i18n.translate('xpack.ingestManager.agentList.configColumnTitle', { defaultMessage: 'Configuration', }), - render: (configId: string) => { + render: (configId: string, agent: Agent) => { const configName = agentConfigs.find(p => p.id === configId)?.name; return ( - + = () => { {configName || configId} - - - - - + {agent.config_revision && ( + + + + + + )} + {agent.config_revision && + agent.config_newest_revision && + agent.config_newest_revision > agent.config_revision && ( + + + +   + {true && ( + <> + + + )} + + + )} ); }, }, { field: 'local_metadata.agent_version', + width: '100px', name: i18n.translate('xpack.ingestManager.agentList.versionTitle', { defaultMessage: 'Version', }), diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts index 860b95b58c7f7..31cf173c3e4f9 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts @@ -32,7 +32,8 @@ export const savedObjectMappings = { config_id: { type: 'keyword' }, last_updated: { type: 'date' }, last_checkin: { type: 'date' }, - config_updated_at: { type: 'date' }, + config_revision: { type: 'integer' }, + config_newest_revision: { type: 'integer' }, // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 default_api_key: { type: 'keyword' }, updated_at: { type: 'date' }, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index 98a5f69f9d2b0..cf9a47979ae8b 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -51,8 +51,22 @@ export async function acknowledgeAgentActions( }); if (matchedUpdatedActions.length > 0) { + const configRevision = matchedUpdatedActions.reduce((acc, action) => { + if (action.type !== 'CONFIG_CHANGE') { + return acc; + } + const data = action.data ? JSON.parse(action.data as string) : {}; + + if (data?.config?.id !== agent.config_id) { + return acc; + } + + return data?.config?.revision > acc ? data?.config?.revision : acc; + }, agent.config_revision || 0); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { actions: matchedUpdatedActions, + config_revision: configRevision, }); } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts new file mode 100644 index 0000000000000..d3e10fcb6b63f --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shouldCreateConfigAction } from './checkin'; +import { Agent } from '../../types'; + +function getAgent(data: Partial) { + return { actions: [], ...data } as Agent; +} + +describe('Agent checkin service', () => { + describe('shouldCreateConfigAction', () => { + it('should return false if the agent do not have an assigned config', () => { + const res = shouldCreateConfigAction(getAgent({})); + + expect(res).toBeFalsy(); + }); + + it('should return true if this is agent first checkin', () => { + const res = shouldCreateConfigAction(getAgent({ config_id: 'config1' })); + + expect(res).toBeTruthy(); + }); + + it('should return false agent is already running latest revision', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 1, + }) + ); + + expect(res).toBeFalsy(); + }); + + it('should return false agent has already latest revision config change action', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 2, + actions: [ + { + id: 'action1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config1', + revision: 2, + }, + }), + }, + ], + }) + ); + + expect(res).toBeFalsy(); + }); + + it('should return true agent has unrelated config change actions', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 2, + actions: [ + { + id: 'action1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config2', + revision: 2, + }, + }), + }, + { + id: 'action1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config1', + revision: 1, + }, + }), + }, + ], + }) + ); + + expect(res).toBeTruthy(); + }); + + it('should return true if this agent has a new revision', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 2, + }) + ); + + expect(res).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index 0ff4af4ffe351..d80fff5d8eceb 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -37,7 +37,7 @@ export async function agentCheckin( const actions = filterActionsForCheckin(agent); // Generate new agent config if config is updated - if (isNewAgentConfig(agent) && agent.config_id) { + if (agent.config_id && shouldCreateConfigAction(agent)) { const config = await agentConfigService.getFullConfig(soClient, agent.config_id); if (config) { // Assign output API keys @@ -149,12 +149,37 @@ function isActionEvent(event: AgentEvent) { ); } -function isNewAgentConfig(agent: Agent) { +export function shouldCreateConfigAction(agent: Agent): boolean { + if (!agent.config_id) { + return false; + } + const isFirstCheckin = !agent.last_checkin; - const isConfigUpdatedSinceLastCheckin = - agent.last_checkin && agent.config_updated_at && agent.last_checkin <= agent.config_updated_at; + if (isFirstCheckin) { + return true; + } + + const isAgentConfigOutdated = + agent.config_revision && + agent.config_newest_revision && + agent.config_revision < agent.config_newest_revision; + if (!isAgentConfigOutdated) { + return false; + } + + const isActionAlreadyGenerated = !!agent.actions.find(action => { + if (!action.data || action.type !== 'CONFIG_CHANGE') { + return false; + } + + const data = JSON.parse(action.data); + + return ( + data.config.id === agent.config_id && data.config.revision === agent.config_newest_revision + ); + }); - return isFirstCheckin || isConfigUpdatedSinceLastCheckin; + return !isActionAlreadyGenerated; } function filterActionsForCheckin(agent: Agent): AgentAction[] { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts index 0f73f71817eb0..52547e9bcb0fb 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts @@ -37,7 +37,6 @@ export async function enroll( current_error_events: undefined, actions: [], access_api_key_id: undefined, - config_updated_at: undefined, last_checkin: undefined, default_api_key: undefined, }; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/update.ts b/x-pack/plugins/ingest_manager/server/services/agents/update.ts index 9eabf0944bdc4..59d0ad31d1a64 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/update.ts @@ -8,14 +8,18 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { listAgents } from './crud'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { unenrollAgents } from './unenroll'; +import { agentConfigService } from '../agent_config'; export async function updateAgentsForConfigId( soClient: SavedObjectsClientContract, configId: string ) { + const config = await agentConfigService.get(soClient, configId); + if (!config) { + throw new Error('Config not found'); + } let hasMore = true; let page = 1; - const now = new Date().toISOString(); while (hasMore) { const { agents } = await listAgents(soClient, { kuery: `agents.config_id:"${configId}"`, @@ -30,7 +34,7 @@ export async function updateAgentsForConfigId( const agentUpdate = agents.map(agent => ({ id: agent.id, type: AGENT_SAVED_OBJECT_TYPE, - attributes: { config_updated_at: now }, + attributes: { config_newest_revision: config.revision }, })); await soClient.bulkUpdate(agentUpdate); From cea277e7c288cb2d4bf90acca2d57b80da7438d4 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 17 Mar 2020 13:06:12 -0400 Subject: [PATCH 51/76] [SIEM][Detections Engine] - Add rule markdown field to rule create, detail, and edit flows (#60108) * add rule note markdown field to rule creation, rule details, and rule edit flows Co-authored-by: Gloria Hornero Co-authored-by: Elastic Machine --- .../signal_detection_rules.spec.ts | 108 ++-- .../siem/cypress/screens/rule_details.ts | 28 +- .../run_check_circular_deps_cli.js | 10 + .../detection_engine/rules/types.ts | 2 + .../rules/all/__mocks__/mock.ts | 153 +++++ .../__snapshots__/index.test.tsx.snap | 453 ++++++++++++++ .../description_step/helpers.test.tsx | 403 ++++++++++++ .../components/description_step/helpers.tsx | 91 ++- .../description_step/index.test.tsx | 297 ++++++++- .../components/description_step/index.tsx | 58 +- .../step_about_rule/default_value.ts | 1 + .../components/step_about_rule/index.test.tsx | 155 +++++ .../components/step_about_rule/index.tsx | 140 +++-- .../components/step_about_rule/schema.tsx | 20 +- .../step_about_rule/translations.ts | 7 + .../step_about_rule_details/index.test.tsx | 175 ++++++ .../step_about_rule_details/index.tsx | 147 +++++ .../step_about_rule_details/translations.ts | 27 + .../components/step_define_rule/index.tsx | 6 +- .../components/step_schedule_rule/index.tsx | 51 +- .../rules/create/helpers.test.ts | 589 ++++++++++++++++++ .../detection_engine/rules/create/helpers.ts | 20 +- .../detection_engine/rules/create/index.tsx | 6 +- .../detection_engine/rules/details/index.tsx | 58 +- .../detection_engine/rules/helpers.test.tsx | 291 +++++++++ .../pages/detection_engine/rules/helpers.tsx | 156 +++-- .../pages/detection_engine/rules/types.ts | 9 +- .../pages/detection_engine/translations.ts | 2 +- 28 files changed, 3166 insertions(+), 297 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts index 8c384c9010665..ce73fe1b7c2a5 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -7,30 +7,30 @@ import { newRule } from '../objects/rule'; import { - ABOUT_DESCRIPTION, - ABOUT_EXPECTED_URLS, ABOUT_FALSE_POSITIVES, ABOUT_MITRE, ABOUT_RISK, - ABOUT_RULE_DESCRIPTION, ABOUT_SEVERITY, + ABOUT_STEP, ABOUT_TAGS, ABOUT_TIMELINE, + ABOUT_URLS, DEFINITION_CUSTOM_QUERY, - DEFINITION_DESCRIPTION, DEFINITION_INDEX_PATTERNS, + DEFINITION_STEP, RULE_NAME_HEADER, - SCHEDULE_DESCRIPTION, SCHEDULE_LOOPBACK, SCHEDULE_RUNS, + SCHEDULE_STEP, + ABOUT_RULE_DESCRIPTION, } from '../screens/rule_details'; import { CUSTOM_RULES_BTN, ELASTIC_RULES_BTN, RISK_SCORE, RULE_NAME, - RULES_TABLE, RULES_ROW, + RULES_TABLE, SEVERITY, } from '../screens/signal_detection_rules'; @@ -127,10 +127,25 @@ describe('Signal detection rules', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER) - .invoke('text') - .should('eql', `${newRule.name} Beta`); - + let expectedUrls = ''; + newRule.referenceUrls.forEach(url => { + expectedUrls = expectedUrls + url; + }); + let expectedFalsePositives = ''; + newRule.falsePositivesExamples.forEach(falsePositive => { + expectedFalsePositives = expectedFalsePositives + falsePositive; + }); + let expectedTags = ''; + newRule.tags.forEach(tag => { + expectedTags = expectedTags + tag; + }); + let expectedMitre = ''; + newRule.mitre.forEach(mitre => { + expectedMitre = expectedMitre + mitre.tactic; + mitre.techniques.forEach(technique => { + expectedMitre = expectedMitre + technique; + }); + }); const expectedIndexPatterns = [ 'apm-*-transaction*', 'auditbeat-*', @@ -139,77 +154,60 @@ describe('Signal detection rules', () => { 'packetbeat-*', 'winlogbeat-*', ]; - cy.get(DEFINITION_INDEX_PATTERNS).then(patterns => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern) - .invoke('text') - .should('eql', expectedIndexPatterns[index]); - }); - }); - cy.get(DEFINITION_DESCRIPTION) - .eq(DEFINITION_CUSTOM_QUERY) + + cy.get(RULE_NAME_HEADER) .invoke('text') - .should('eql', `${newRule.customQuery} `); - cy.get(ABOUT_DESCRIPTION) - .eq(ABOUT_RULE_DESCRIPTION) + .should('eql', `${newRule.name} Beta`); + + cy.get(ABOUT_RULE_DESCRIPTION) .invoke('text') .should('eql', newRule.description); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_SEVERITY) .invoke('text') .should('eql', newRule.severity); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_RISK) .invoke('text') .should('eql', newRule.riskScore); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_TIMELINE) .invoke('text') .should('eql', 'Default blank timeline'); - - let expectedUrls = ''; - newRule.referenceUrls.forEach(url => { - expectedUrls = expectedUrls + url; - }); - cy.get(ABOUT_DESCRIPTION) - .eq(ABOUT_EXPECTED_URLS) + cy.get(ABOUT_STEP) + .eq(ABOUT_URLS) .invoke('text') .should('eql', expectedUrls); - - let expectedFalsePositives = ''; - newRule.falsePositivesExamples.forEach(falsePositive => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_FALSE_POSITIVES) .invoke('text') .should('eql', expectedFalsePositives); - - let expectedMitre = ''; - newRule.mitre.forEach(mitre => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach(technique => { - expectedMitre = expectedMitre + technique; - }); - }); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_MITRE) .invoke('text') .should('eql', expectedMitre); - - let expectedTags = ''; - newRule.tags.forEach(tag => { - expectedTags = expectedTags + tag; - }); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_TAGS) .invoke('text') .should('eql', expectedTags); - cy.get(SCHEDULE_DESCRIPTION) + + cy.get(DEFINITION_INDEX_PATTERNS).then(patterns => { + cy.wrap(patterns).each((pattern, index) => { + cy.wrap(pattern) + .invoke('text') + .should('eql', expectedIndexPatterns[index]); + }); + }); + cy.get(DEFINITION_STEP) + .eq(DEFINITION_CUSTOM_QUERY) + .invoke('text') + .should('eql', `${newRule.customQuery} `); + + cy.get(SCHEDULE_STEP) .eq(SCHEDULE_RUNS) .invoke('text') .should('eql', '5m'); - cy.get(SCHEDULE_DESCRIPTION) + cy.get(SCHEDULE_STEP) .eq(SCHEDULE_LOOPBACK) .invoke('text') .should('eql', '1m'); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts index 46da52cd0ddd8..6c16735ba5f24 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts @@ -4,35 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ABOUT_DESCRIPTION = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; +export const ABOUT_FALSE_POSITIVES = 4; -export const ABOUT_EXPECTED_URLS = 4; +export const ABOUT_MITRE = 5; -export const ABOUT_FALSE_POSITIVES = 5; +export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; -export const ABOUT_MITRE = 6; +export const ABOUT_RISK = 1; -export const ABOUT_RULE_DESCRIPTION = 0; +export const ABOUT_SEVERITY = 0; -export const ABOUT_RISK = 2; +export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; -export const ABOUT_SEVERITY = 1; +export const ABOUT_TAGS = 6; -export const ABOUT_TAGS = 7; +export const ABOUT_TIMELINE = 2; -export const ABOUT_TIMELINE = 3; +export const ABOUT_URLS = 3; export const DEFINITION_CUSTOM_QUERY = 1; -export const DEFINITION_DESCRIPTION = - '[data-test-subj="definition"] .euiDescriptionList__description'; - export const DEFINITION_INDEX_PATTERNS = - '[data-test-subj="definition"] .euiDescriptionList__description .euiBadge__text'; + '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text'; + +export const DEFINITION_STEP = + '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description'; export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; -export const SCHEDULE_DESCRIPTION = '[data-test-subj="schedule"] .euiDescriptionList__description'; +export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description'; export const SCHEDULE_RUNS = 0; diff --git a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js index 8ca61b2397d8b..f3a97f5b9c9b6 100644 --- a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js +++ b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js @@ -17,6 +17,16 @@ run( [resolve(__dirname, '../../public'), resolve(__dirname, '../../common')], { fileExtensions: ['ts', 'js', 'tsx'], + excludeRegExp: [ + 'test.ts$', + 'test.tsx$', + 'containers/detection_engine/rules/types.ts$', + 'core/public/chrome/chrome_service.tsx$', + 'src/core/server/types.ts$', + 'src/core/server/saved_objects/types.ts$', + 'src/core/public/overlays/banners/banners_service.tsx$', + 'src/core/public/saved_objects/saved_objects_client.ts$', + ], } ); diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts index 4d2aec4ee8740..f962204c6b1b4 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts @@ -33,6 +33,7 @@ export const NewRuleSchema = t.intersection([ threat: t.array(t.unknown), to: t.string, updated_by: t.string, + note: t.string, }), ]); @@ -86,6 +87,7 @@ export const RuleSchema = t.intersection([ status_date: t.string, timeline_id: t.string, timeline_title: t.string, + note: t.string, version: t.number, }), ]); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts index e2287e5eeeb3f..5627d33818500 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts @@ -4,7 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ +import { esFilters } from '../../../../../../../../../../src/plugins/data/public'; import { Rule, RuleError } from '../../../../../containers/detection_engine/rules'; +import { AboutStepRule, DefineStepRule, ScheduleStepRule } from '../../types'; +import { FieldValueQueryBar } from '../../components/query_bar'; + +export const mockQueryBar: FieldValueQueryBar = { + query: { + query: 'test query', + language: 'kuery', + }, + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + saved_id: 'test123', +}; export const mockRule = (id: string): Rule => ({ created_at: '2020-01-10T21:11:45.839Z', @@ -37,9 +70,129 @@ export const mockRule = (id: string): Rule => ({ to: 'now', type: 'saved_query', threat: [], + note: '# this is some markdown documentation', version: 1, }); +export const mockRuleWithEverything = (id: string): Rule => ({ + created_at: '2020-01-10T21:11:45.839Z', + updated_at: '2020-01-10T21:11:45.839Z', + created_by: 'elastic', + description: '24/7', + enabled: true, + false_positives: ['test'], + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + from: 'now-300s', + id, + immutable: false, + index: ['auditbeat-*'], + interval: '5m', + rule_id: 'b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea', + language: 'kuery', + output_index: '.siem-signals-default', + max_signals: 100, + risk_score: 21, + name: 'Query with rule-id', + query: 'user.name: root or user.name: admin', + references: ['www.test.co'], + saved_id: 'test123', + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + meta: { from: '0m' }, + severity: 'low', + updated_by: 'elastic', + tags: ['tag1', 'tag2'], + to: 'now', + type: 'saved_query', + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + note: '# this is some markdown documentation', + version: 1, +}); + +export const mockAboutStepRule = (isNew = false): AboutStepRule => ({ + isNew, + name: 'Query with rule-id', + description: '24/7', + severity: 'low', + riskScore: 21, + references: ['www.test.co'], + falsePositives: ['test'], + tags: ['tag1', 'tag2'], + timeline: { + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + title: 'Titled timeline', + }, + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + note: '# this is some markdown documentation', +}); + +export const mockDefineStepRule = (isNew = false): DefineStepRule => ({ + isNew, + index: ['filebeat-'], + queryBar: mockQueryBar, +}); + +export const mockScheduleStepRule = (isNew = false, enabled = false): ScheduleStepRule => ({ + isNew, + enabled, + interval: '5m', + from: '6m', + to: 'now', +}); + export const mockRuleError = (id: string): RuleError => ({ rule_id: id, error: { status_code: 404, message: `id: "${id}" not found` }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..4d416e70a096c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap @@ -0,0 +1,453 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`description_step StepRuleDescriptionComponent renders correctly against snapshot when columns is "multi" 1`] = ` + + + , + "title": "Severity", + }, + Object { + "description": 21, + "title": "Risk score", + }, + Object { + "description": "Titled timeline", + "title": "Timeline template", + }, + ] + } + /> + + + +

+ , + "title": "Reference URLs", + }, + Object { + "description": +
    +
  • + test +
  • +
+
, + "title": "False positive examples", + }, + Object { + "description": + + + + + + + + + + + + + + , + "title": "MITRE ATT&CK™", + }, + Object { + "description": + + + tag1 + + + + + tag2 + + + , + "title": "Tags", + }, + Object { + "description": +
+ # this is some markdown documentation +
+
, + "title": "Investigation notes", + }, + ] + } + /> + + +`; + +exports[`description_step StepRuleDescriptionComponent renders correctly against snapshot when columns is "single" 1`] = ` + + + , + "title": "Severity", + }, + Object { + "description": 21, + "title": "Risk score", + }, + Object { + "description": "Titled timeline", + "title": "Timeline template", + }, + Object { + "description": +
    +
  • + + www.test.co + +
  • +
+
, + "title": "Reference URLs", + }, + Object { + "description": +
    +
  • + test +
  • +
+
, + "title": "False positive examples", + }, + Object { + "description": + + + + + + + + + + + + + + , + "title": "MITRE ATT&CK™", + }, + Object { + "description": + + + tag1 + + + + + tag2 + + + , + "title": "Tags", + }, + Object { + "description": +
+ # this is some markdown documentation +
+
, + "title": "Investigation notes", + }, + ] + } + /> +
+
+`; + +exports[`description_step StepRuleDescriptionComponent renders correctly against snapshot when columns is "singleSplit 1`] = ` + + + , + "title": "Severity", + }, + Object { + "description": 21, + "title": "Risk score", + }, + Object { + "description": "Titled timeline", + "title": "Timeline template", + }, + Object { + "description": +
    +
  • + + www.test.co + +
  • +
+
, + "title": "Reference URLs", + }, + Object { + "description": +
    +
  • + test +
  • +
+
, + "title": "False positive examples", + }, + Object { + "description": + + + + + + + + + + + + + + , + "title": "MITRE ATT&CK™", + }, + Object { + "description": + + + tag1 + + + + + tag2 + + + , + "title": "Tags", + }, + Object { + "description": +
+ # this is some markdown documentation +
+
, + "title": "Investigation notes", + }, + ] + } + type="column" + /> +
+
+`; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx new file mode 100644 index 0000000000000..56c9d6da15607 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx @@ -0,0 +1,403 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiLoadingSpinner } from '@elastic/eui'; + +import { coreMock } from '../../../../../../../../../../src/core/public/mocks'; +import { esFilters, FilterManager } from '../../../../../../../../../../src/plugins/data/public'; +import { SeverityBadge } from '../severity_badge'; + +import * as i18n from './translations'; +import { + isNotEmptyArray, + buildQueryBarDescription, + buildThreatDescription, + buildUnorderedListArrayDescription, + buildStringArrayDescription, + buildSeverityDescription, + buildUrlsDescription, + buildNoteDescription, +} from './helpers'; +import { ListItems } from './types'; + +const setupMock = coreMock.createSetup(); +const uiSettingsMock = (pinnedByDefault: boolean) => (key: string) => { + switch (key) { + case 'filters:pinnedByDefault': + return pinnedByDefault; + default: + throw new Error(`Unexpected uiSettings key in FilterManager mock: ${key}`); + } +}; +setupMock.uiSettings.get.mockImplementation(uiSettingsMock(true)); +const mockFilterManager = new FilterManager(setupMock.uiSettings); + +const mockQueryBar = { + query: { + query: 'test query', + language: 'kuery', + }, + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + saved_id: 'test123', +}; + +describe('helpers', () => { + describe('isNotEmptyArray', () => { + test('returns false if empty array', () => { + const result = isNotEmptyArray([]); + expect(result).toBeFalsy(); + }); + + test('returns false if array of empty strings', () => { + const result = isNotEmptyArray(['', '']); + expect(result).toBeFalsy(); + }); + + test('returns true if array of string with space', () => { + const result = isNotEmptyArray([' ']); + expect(result).toBeTruthy(); + }); + + test('returns true if array with at least one non-empty string', () => { + const result = isNotEmptyArray(['', 'abc']); + expect(result).toBeTruthy(); + }); + }); + + describe('buildQueryBarDescription', () => { + test('returns empty array if no filters, query or savedId exist', () => { + const emptyMockQueryBar = { + query: { + query: '', + language: 'kuery', + }, + filters: [], + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: emptyMockQueryBar.filters, + filterManager: mockFilterManager, + query: emptyMockQueryBar.query, + savedId: emptyMockQueryBar.saved_id, + }); + expect(result).toEqual([]); + }); + + test('returns expected array of ListItems when filters exists, but no indexPatterns passed in', () => { + const mockQueryBarWithFilters = { + ...mockQueryBar, + query: { + query: '', + language: 'kuery', + }, + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithFilters.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithFilters.query, + savedId: mockQueryBarWithFilters.saved_id, + }); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual(<>{i18n.FILTERS_LABEL} ); + expect(wrapper.find(EuiLoadingSpinner).exists()).toBeTruthy(); + }); + + test('returns expected array of ListItems when filters AND indexPatterns exist', () => { + const mockQueryBarWithFilters = { + ...mockQueryBar, + query: { + query: '', + language: 'kuery', + }, + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithFilters.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithFilters.query, + savedId: mockQueryBarWithFilters.saved_id, + indexPatterns: { fields: [{ name: 'test name', type: 'test type' }], title: 'test title' }, + }); + const wrapper = shallow(result[0].description as React.ReactElement); + const filterLabelComponent = wrapper.find(esFilters.FilterLabel).at(0); + + expect(result[0].title).toEqual(<>{i18n.FILTERS_LABEL} ); + expect(filterLabelComponent.prop('valueLabel')).toEqual('file'); + expect(filterLabelComponent.prop('filter')).toEqual(mockQueryBar.filters[0]); + }); + + test('returns expected array of ListItems when "query.query" exists', () => { + const mockQueryBarWithQuery = { + ...mockQueryBar, + filters: [], + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithQuery.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithQuery.query, + savedId: mockQueryBarWithQuery.saved_id, + }); + expect(result[0].title).toEqual(<>{i18n.QUERY_LABEL} ); + expect(result[0].description).toEqual(<>{mockQueryBarWithQuery.query.query} ); + }); + + test('returns expected array of ListItems when "savedId" exists', () => { + const mockQueryBarWithSavedId = { + ...mockQueryBar, + query: { + query: '', + language: 'kuery', + }, + filters: [], + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithSavedId.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithSavedId.query, + savedId: mockQueryBarWithSavedId.saved_id, + }); + expect(result[0].title).toEqual(<>{i18n.SAVED_ID_LABEL} ); + expect(result[0].description).toEqual(<>{mockQueryBarWithSavedId.saved_id} ); + }); + }); + + describe('buildThreatDescription', () => { + test('returns empty array if no threats', () => { + const result: ListItems[] = buildThreatDescription({ label: 'Mitre Attack', threat: [] }); + expect(result).toHaveLength(0); + }); + + test('returns empty tactic link if no corresponding tactic id found', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + expect(result[0].title).toEqual('Mitre Attack'); + expect(wrapper.find('[data-test-subj="threatTacticLink"]').text()).toEqual(''); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]').text()).toEqual( + 'Audio Capture (T1123)' + ); + }); + + test('returns empty technique link if no corresponding technique id found', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123456' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA0009' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + expect(result[0].title).toEqual('Mitre Attack'); + expect(wrapper.find('[data-test-subj="threatTacticLink"]').text()).toEqual( + 'Collection (TA0009)' + ); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]').text()).toEqual(''); + }); + + test('returns with corresponding tactic and technique link text', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA0009' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + expect(result[0].title).toEqual('Mitre Attack'); + expect(wrapper.find('[data-test-subj="threatTacticLink"]').text()).toEqual( + 'Collection (TA0009)' + ); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]').text()).toEqual( + 'Audio Capture (T1123)' + ); + }); + + test('returns corresponding number of tactic and technique links', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [ + { reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }, + { reference: 'https://test.com', name: 'Clipboard Data', id: 'T1115' }, + ], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA0009' }, + }, + { + framework: 'MITRE ATTACK', + technique: [ + { reference: 'https://test.com', name: 'Automated Collection', id: 'T1119' }, + ], + tactic: { reference: 'https://test.com', name: 'Discovery', id: 'TA0007' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(wrapper.find('[data-test-subj="threatTacticLink"]')).toHaveLength(2); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]')).toHaveLength(3); + }); + }); + + describe('buildUnorderedListArrayDescription', () => { + test('returns empty array if "values" is empty array', () => { + const result: ListItems[] = buildUnorderedListArrayDescription( + 'Test label', + 'falsePositives', + [] + ); + expect(result).toHaveLength(0); + }); + + test('returns ListItem with corresponding number of valid values items', () => { + const result: ListItems[] = buildUnorderedListArrayDescription( + 'Test label', + 'falsePositives', + ['', 'falsePositive1', 'falsePositive2'] + ); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual('Test label'); + expect(wrapper.find('[data-test-subj="unorderedListArrayDescriptionItem"]')).toHaveLength(2); + }); + }); + + describe('buildStringArrayDescription', () => { + test('returns empty array if "values" is empty array', () => { + const result: ListItems[] = buildStringArrayDescription('Test label', 'tags', []); + expect(result).toHaveLength(0); + }); + + test('returns ListItem with corresponding number of valid values items', () => { + const result: ListItems[] = buildStringArrayDescription('Test label', 'tags', [ + '', + 'tag1', + 'tag2', + ]); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual('Test label'); + expect(wrapper.find('[data-test-subj="stringArrayDescriptionBadgeItem"]')).toHaveLength(2); + expect( + wrapper + .find('[data-test-subj="stringArrayDescriptionBadgeItem"]') + .first() + .text() + ).toEqual('tag1'); + expect( + wrapper + .find('[data-test-subj="stringArrayDescriptionBadgeItem"]') + .at(1) + .text() + ).toEqual('tag2'); + }); + }); + + describe('buildSeverityDescription', () => { + test('returns ListItem with passed in label and SeverityBadge component', () => { + const result: ListItems[] = buildSeverityDescription('Test label', 'Test description value'); + + expect(result[0].title).toEqual('Test label'); + expect(result[0].description).toEqual(); + }); + }); + + describe('buildUrlsDescription', () => { + test('returns empty array if "values" is empty array', () => { + const result: ListItems[] = buildUrlsDescription('Test label', []); + expect(result).toHaveLength(0); + }); + + test('returns ListItem with corresponding number of valid values items', () => { + const result: ListItems[] = buildUrlsDescription('Test label', [ + 'www.test.com', + 'www.test2.com', + ]); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual('Test label'); + expect(wrapper.find('[data-test-subj="urlsDescriptionReferenceLinkItem"]')).toHaveLength(2); + expect( + wrapper + .find('[data-test-subj="urlsDescriptionReferenceLinkItem"]') + .first() + .text() + ).toEqual('www.test.com'); + expect( + wrapper + .find('[data-test-subj="urlsDescriptionReferenceLinkItem"]') + .at(1) + .text() + ).toEqual('www.test2.com'); + }); + }); + + describe('buildNoteDescription', () => { + test('returns ListItem with passed in label and note content', () => { + const noteSample = + 'Cras mattism. [Pellentesque](https://elastic.co). ### Malesuada adipiscing tristique'; + const result: ListItems[] = buildNoteDescription('Test label', noteSample); + const wrapper = shallow(result[0].description as React.ReactElement); + const noteElement = wrapper.find('[data-test-subj="noteDescriptionItem"]').at(0); + + expect(result[0].title).toEqual('Test label'); + expect(noteElement.exists()).toBeTruthy(); + expect(noteElement.text()).toEqual(noteSample); + }); + + test('returns empty array if passed in note is empty string', () => { + const result: ListItems[] = buildNoteDescription('Test label', ''); + + expect(result).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx index df767fbd4ff8c..bc454ecb1134a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx @@ -9,9 +9,10 @@ import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, - EuiLink, EuiButtonEmpty, EuiSpacer, + EuiLink, + EuiText, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; @@ -27,8 +28,12 @@ import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './t import { SeverityBadge } from '../severity_badge'; import ListTreeIcon from './assets/list_tree_icon.svg'; -const isNotEmptyArray = (values: string[]) => - !isEmpty(values) && values.filter(val => !isEmpty(val)).length > 0; +const NoteDescriptionContainer = styled(EuiFlexItem)` + height: 105px; + overflow-y: hidden; +`; + +export const isNotEmptyArray = (values: string[]) => !isEmpty(values.join('')); const EuiBadgeWrap = styled(EuiBadge)` .euiBadge__text { @@ -106,13 +111,6 @@ const TechniqueLinkItem = styled(EuiButtonEmpty)` } `; -const ReferenceLinkItem = styled(EuiButtonEmpty)` - .euiIcon { - width: 12px; - height: 12px; - } -`; - export const buildThreatDescription = ({ label, threat }: BuildThreatDescription): ListItems[] => { if (threat.length > 0) { return [ @@ -124,7 +122,11 @@ export const buildThreatDescription = ({ label, threat }: BuildThreatDescription const tactic = tacticsOptions.find(t => t.id === singleThreat.tactic.id); return ( - + {tactic != null ? tactic.text : ''} @@ -133,6 +135,7 @@ export const buildThreatDescription = ({ label, threat }: BuildThreatDescription return ( - {values.map((val: string) => - isEmpty(val) ? null :
  • {val}
  • - )} - + +
      + {values.map(val => + isEmpty(val) ? null : ( +
    • + {val} +
    • + ) + )} +
    +
    ), }, ]; @@ -193,7 +202,9 @@ export const buildStringArrayDescription = ( {values.map((val: string) => isEmpty(val) ? null : ( - {val} + + {val} + ) )} @@ -218,21 +229,37 @@ export const buildUrlsDescription = (label: string, values: string[]): ListItems { title: label, description: ( - - {values.map((val: string) => ( - - - {val} - - - ))} - + +
      + {values + .filter(v => !isEmpty(v)) + .map((val, index) => ( +
    • + + {val} + +
    • + ))} +
    +
    + ), + }, + ]; + } + return []; +}; + +export const buildNoteDescription = (label: string, note: string): ListItems[] => { + if (note.trim() !== '') { + return [ + { + title: label, + description: ( + +
    + {note} +
    +
    ), }, ]; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx index 84c662dd00199..2c6f47fd27c44 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx @@ -3,12 +3,88 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; +import { shallow } from 'enzyme'; -import { addFilterStateIfNotThere } from './'; +import { + StepRuleDescriptionComponent, + addFilterStateIfNotThere, + buildListItems, + getDescriptionItem, +} from './'; -import { esFilters, Filter } from '../../../../../../../../../../src/plugins/data/public'; +import { + esFilters, + Filter, + FilterManager, +} from '../../../../../../../../../../src/plugins/data/public'; +import { mockAboutStepRule } from '../../all/__mocks__/mock'; +import { coreMock } from '../../../../../../../../../../src/core/public/mocks'; +import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations'; +import * as i18n from './translations'; + +import { schema } from '../step_about_rule/schema'; +import { ListItems } from './types'; +import { AboutStepRule } from '../../types'; describe('description_step', () => { + const setupMock = coreMock.createSetup(); + const uiSettingsMock = (pinnedByDefault: boolean) => (key: string) => { + switch (key) { + case 'filters:pinnedByDefault': + return pinnedByDefault; + default: + throw new Error(`Unexpected uiSettings key in FilterManager mock: ${key}`); + } + }; + let mockFilterManager: FilterManager; + let mockAboutStep: AboutStepRule; + + beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + setupMock.uiSettings.get.mockImplementation(uiSettingsMock(true)); + mockFilterManager = new FilterManager(setupMock.uiSettings); + mockAboutStep = mockAboutStepRule(); + }); + + describe('StepRuleDescriptionComponent', () => { + test('renders correctly against snapshot when columns is "multi"', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('[data-test-subj="listItemColumnStepRuleDescription"]')).toHaveLength(2); + }); + + test('renders correctly against snapshot when columns is "single"', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('[data-test-subj="listItemColumnStepRuleDescription"]')).toHaveLength(1); + }); + + test('renders correctly against snapshot when columns is "singleSplit', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('[data-test-subj="listItemColumnStepRuleDescription"]')).toHaveLength(1); + expect( + wrapper + .find('[data-test-subj="singleSplitStepRuleDescriptionList"]') + .at(0) + .prop('type') + ).toEqual('column'); + }); + }); + describe('addFilterStateIfNotThere', () => { test('it does not change the state if it is global', () => { const filters: Filter[] = [ @@ -182,4 +258,221 @@ describe('description_step', () => { expect(output).toEqual(expected); }); }); + + describe('buildListItems', () => { + test('returns expected ListItems array when given valid inputs', () => { + const result: ListItems[] = buildListItems(mockAboutStep, schema, mockFilterManager); + + expect(result.length).toEqual(10); + }); + }); + + describe('getDescriptionItem', () => { + test('returns ListItem with all values enumerated when value[field] is an array', () => { + const result: ListItems[] = getDescriptionItem( + 'tags', + 'Tags label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Tags label'); + expect(typeof result[0].description).toEqual('object'); + }); + + test('returns ListItem with description of value[field] when value[field] is a string', () => { + const result: ListItems[] = getDescriptionItem( + 'description', + 'Description label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Description label'); + expect(result[0].description).toEqual('24/7'); + }); + + test('returns empty array when "value" is a non-existant property in "field"', () => { + const result: ListItems[] = getDescriptionItem( + 'jibberjabber', + 'JibberJabber label', + mockAboutStep, + mockFilterManager + ); + + expect(result.length).toEqual(0); + }); + + describe('queryBar', () => { + test('returns array of ListItems when queryBar exist', () => { + const mockQueryBar = { + isNew: false, + queryBar: { + query: { + query: 'user.name: root or user.name: admin', + language: 'kuery', + }, + filters: null, + saved_id: null, + }, + }; + const result: ListItems[] = getDescriptionItem( + 'queryBar', + 'Query bar label', + mockQueryBar, + mockFilterManager + ); + + expect(result[0].title).toEqual(<>{i18n.QUERY_LABEL} ); + expect(result[0].description).toEqual(<>{mockQueryBar.queryBar.query.query} ); + }); + }); + + describe('threat', () => { + test('returns array of ListItems when threat exist', () => { + const result: ListItems[] = getDescriptionItem( + 'threat', + 'Threat label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Threat label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + + test('filters out threats with tactic.name of "none"', () => { + const mockStep = { + ...mockAboutStep, + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'none', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + const result: ListItems[] = getDescriptionItem( + 'threat', + 'Threat label', + mockStep, + mockFilterManager + ); + + expect(result.length).toEqual(0); + }); + }); + + describe('references', () => { + test('returns array of ListItems when references exist', () => { + const result: ListItems[] = getDescriptionItem( + 'references', + 'Reference label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Reference label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + + describe('falsePositives', () => { + test('returns array of ListItems when falsePositives exist', () => { + const result: ListItems[] = getDescriptionItem( + 'falsePositives', + 'False positives label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('False positives label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + + describe('severity', () => { + test('returns array of ListItems when severity exist', () => { + const result: ListItems[] = getDescriptionItem( + 'severity', + 'Severity label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Severity label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + + describe('riskScore', () => { + test('returns array of ListItems when riskScore exist', () => { + const result: ListItems[] = getDescriptionItem( + 'riskScore', + 'Risk score label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Risk score label'); + expect(result[0].description).toEqual(21); + }); + }); + + describe('timeline', () => { + test('returns timeline title if one exists', () => { + const result: ListItems[] = getDescriptionItem( + 'timeline', + 'Timeline label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Timeline label'); + expect(result[0].description).toEqual('Titled timeline'); + }); + + test('returns default timeline title if none exists', () => { + const mockStep = { + ...mockAboutStep, + timeline: { + id: '12345', + }, + }; + const result: ListItems[] = getDescriptionItem( + 'timeline', + 'Timeline label', + mockStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Timeline label'); + expect(result[0].description).toEqual(DEFAULT_TIMELINE_TITLE); + }); + }); + + describe('note', () => { + test('returns default "note" description', () => { + const result: ListItems[] = getDescriptionItem( + 'note', + 'Investigation notes', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Investigation notes'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx index cb5c98bb23f07..1d58ef8014899 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx @@ -7,6 +7,7 @@ import { EuiDescriptionList, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isEmpty, chunk, get, pick } from 'lodash/fp'; import React, { memo, useState } from 'react'; +import styled from 'styled-components'; import { IIndexPattern, @@ -28,18 +29,28 @@ import { buildThreatDescription, buildUnorderedListArrayDescription, buildUrlsDescription, + buildNoteDescription, } from './helpers'; +const DescriptionListContainer = styled(EuiDescriptionList)` + &.euiDescriptionList--column .euiDescriptionList__title { + width: 30%; + } + &.euiDescriptionList--column .euiDescriptionList__description { + width: 70%; + } +`; + interface StepRuleDescriptionProps { - direction?: 'row' | 'column'; + columns?: 'multi' | 'single' | 'singleSplit'; data: unknown; indexPatterns?: IIndexPattern; schema: FormSchema; } -const StepRuleDescriptionComponent: React.FC = ({ +export const StepRuleDescriptionComponent: React.FC = ({ data, - direction = 'row', + columns = 'multi', indexPatterns, schema, }) => { @@ -55,11 +66,14 @@ const StepRuleDescriptionComponent: React.FC = ({ [] ); - if (direction === 'row') { + if (columns === 'multi') { return ( {chunk(Math.ceil(listItems.length / 2), listItems).map((chunkListItems, index) => ( - + ))} @@ -69,8 +83,16 @@ const StepRuleDescriptionComponent: React.FC = ({ return ( - - + + {columns === 'single' ? ( + + ) : ( + + )} ); @@ -78,7 +100,7 @@ const StepRuleDescriptionComponent: React.FC = ({ export const StepRuleDescription = memo(StepRuleDescriptionComponent); -const buildListItems = ( +export const buildListItems = ( data: unknown, schema: FormSchema, filterManager: FilterManager, @@ -108,7 +130,7 @@ export const addFilterStateIfNotThere = (filters: Filter[]): Filter[] => { }); }; -const getDescriptionItem = ( +export const getDescriptionItem = ( field: string, label: string, value: unknown, @@ -132,13 +154,6 @@ const getDescriptionItem = ( (singleThreat: IMitreEnterpriseAttack) => singleThreat.tactic.name !== 'none' ); return buildThreatDescription({ label, threat }); - } else if (field === 'description') { - return [ - { - title: label, - description: get(field, value), - }, - ]; } else if (field === 'references') { const urls: string[] = get(field, value); return buildUrlsDescription(label, urls); @@ -166,14 +181,9 @@ const getDescriptionItem = ( description: timeline.title ?? DEFAULT_TIMELINE_TITLE, }, ]; - } else if (field === 'riskScore') { - const description: string = get(field, value); - return [ - { - title: label, - description, - }, - ]; + } else if (field === 'note') { + const val: string = get(field, value); + return buildNoteDescription(label, val); } const description: string = get(field, value); if (!isEmpty(description)) { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts index d15cce15877b4..417133f230610 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts @@ -29,4 +29,5 @@ export const stepAboutDefaultValue: AboutStepRule = { title: DEFAULT_TIMELINE_TITLE, }, threat: threatDefault, + note: '', }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx new file mode 100644 index 0000000000000..0ed479e235151 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import { ThemeProvider } from 'styled-components'; +import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { StepAboutRule } from './'; +import { mockAboutStepRule } from '../../all/__mocks__/mock'; +import { StepRuleDescription } from '../description_step'; +import { stepAboutDefaultValue } from './default_value'; + +const theme = () => ({ eui: euiDarkVars, darkMode: true }); + +describe('StepAboutRuleComponent', () => { + test('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(StepRuleDescription).exists()).toBeTruthy(); + }); + + test('it prevents user from clicking continue if no "description" defined', () => { + const wrapper = mount( + + + + ); + + const nameInput = wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0); + nameInput.simulate('change', { target: { value: 'Test name text' } }); + + const descriptionInput = wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0); + const nextButton = wrapper.find('button[data-test-subj="about-continue"]').at(0); + nextButton.simulate('click'); + + expect( + wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0) + .props().value + ).toEqual('Test name text'); + expect(descriptionInput.props().value).toEqual(''); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleDescription"] label') + .at(0) + .hasClass('euiFormLabel-isInvalid') + ).toBeTruthy(); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleDescription"] EuiTextArea') + .at(0) + .prop('isInvalid') + ).toBeTruthy(); + }); + + test('it prevents user from clicking continue if no "name" defined', () => { + const wrapper = mount( + + + + ); + + const descriptionInput = wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0); + descriptionInput.simulate('change', { target: { value: 'Test description text' } }); + + const nameInput = wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0); + const nextButton = wrapper.find('button[data-test-subj="about-continue"]').at(0); + nextButton.simulate('click'); + + expect( + wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0) + .props().value + ).toEqual('Test description text'); + expect(nameInput.props().value).toEqual(''); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleName"] label') + .at(0) + .hasClass('euiFormLabel-isInvalid') + ).toBeTruthy(); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleName"] EuiFieldText') + .at(0) + .prop('isInvalid') + ).toBeTruthy(); + }); + + test('it allows user to click continue if "name" and "description" are defined', () => { + const wrapper = mount( + + + + ); + + const descriptionInput = wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0); + descriptionInput.simulate('change', { target: { value: 'Test description text' } }); + + const nameInput = wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0); + nameInput.simulate('change', { target: { value: 'Test name text' } }); + + const nextButton = wrapper.find('button[data-test-subj="about-continue"]').at(0); + nextButton.simulate('click'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx index 4f06d4314c1f3..bfb123f3f3204 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx @@ -39,6 +39,7 @@ import { schema } from './schema'; import * as I18n from './translations'; import { PickTimeline } from '../pick_timeline'; import { StepContentWrapper } from '../step_content_wrapper'; +import { MarkdownEditorForm } from '../../../../../components/markdown_editor/form'; const CommonUseField = getUseField({ component: Field }); @@ -46,6 +47,12 @@ interface StepAboutRuleProps extends RuleStepProps { defaultValues?: AboutStepRule | null; } +const ThreeQuartersContainer = styled.div` + max-width: 740px; +`; + +ThreeQuartersContainer.displayName = 'ThreeQuartersContainer'; + const TagContainer = styled.div` margin-top: 16px; `; @@ -75,7 +82,7 @@ const AdvancedSettingsAccordionButton = ( const StepAboutRuleComponent: FC = ({ addPadding = false, defaultValues, - descriptionDirection = 'row', + descriptionColumns = 'singleSplit', isReadOnlyView, isUpdateView = false, isLoading, @@ -120,68 +127,74 @@ const StepAboutRuleComponent: FC = ({ }, [form]); return isReadOnlyView && myStepData.name != null ? ( - - + + ) : ( <>
    - - + + + - - - - - - - - + + + + + + + + + + + = ({ dataTestSubj: 'detectionEngineStepAboutRuleMitreThreat', }} /> + + + + {({ severity }) => { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx index 42cf1e0d95649..7c1ab09b7309c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx @@ -95,7 +95,14 @@ export const schema: FormSchema = { label: i18n.translate( 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateLabel', { - defaultMessage: 'Investigate detections using this timeline template', + defaultMessage: 'Timeline template', + } + ), + helpText: i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateHelpText', + { + defaultMessage: + 'Select an existing timeline to use as a template when investigating generated signals.', } ), }, @@ -184,4 +191,15 @@ export const schema: FormSchema = { ), labelAppend: OptionalFieldLabel, }, + note: { + type: FIELD_TYPES.TEXTAREA, + label: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.noteLabel', { + defaultMessage: 'Investigation notes', + }), + helpText: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.noteHelpText', { + defaultMessage: + 'Provide helpful information for analysts that are performing a signal investigation. These notes will appear on both the rule details page and in timelines created from signals generated by this rule.', + }), + labelAppend: OptionalFieldLabel, + }, }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts index 3b6680fd4e687..dfa60268e903a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts @@ -68,3 +68,10 @@ export const URL_FORMAT_INVALID = i18n.translate( defaultMessage: 'Url is invalid format', } ); + +export const ADD_RULE_NOTE_HELP_TEXT = i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepAboutrule.noteHelpText', + { + defaultMessage: 'Add rule investigation notes...', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx new file mode 100644 index 0000000000000..4a4e96ec74902 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import { EuiProgress, EuiButtonGroup } from '@elastic/eui'; +import { ThemeProvider } from 'styled-components'; +import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { StepAboutRuleToggleDetails } from './'; +import { mockAboutStepRule } from '../../all/__mocks__/mock'; +import { HeaderSection } from '../../../../../components/header_section'; +import { StepAboutRule } from '../step_about_rule/'; +import { AboutStepRule } from '../../types'; + +const theme = () => ({ eui: euiDarkVars, darkMode: true }); + +describe('StepAboutRuleToggleDetails', () => { + let mockRule: AboutStepRule; + + beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + mockRule = mockAboutStepRule(); + }); + + test('it renders loading component when "loading" is true', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(EuiProgress).exists()).toBeTruthy(); + expect(wrapper.find(HeaderSection).exists()).toBeTruthy(); + }); + + test('it does not render details if stepDataDetails is null', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(StepAboutRule).exists()).toBeFalsy(); + }); + + test('it does not render details if stepData is null', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(StepAboutRule).exists()).toBeFalsy(); + }); + + describe('note value is empty string', () => { + test('it does not render toggle buttons', () => { + const mockAboutStepWithoutNote = { + ...mockRule, + note: '', + }; + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="stepAboutDetailsToggle"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="stepAboutDetailsNoteContent"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="stepAboutDetailsContent"]').exists()).toBeTruthy(); + }); + }); + + describe('note value does exist', () => { + test('it renders toggle buttons, defaulted to "details"', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(EuiButtonGroup).exists()).toBeTruthy(); + expect( + wrapper + .find('EuiButtonToggle[id="details"]') + .at(0) + .prop('isSelected') + ).toBeTruthy(); + expect( + wrapper + .find('EuiButtonToggle[id="notes"]') + .at(0) + .prop('isSelected') + ).toBeFalsy(); + }); + + test('it allows users to toggle between "details" and "note"', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('EuiButtonGroup[idSelected="details"]').exists()).toBeTruthy(); + expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeFalsy(); + + wrapper + .find('input[title="Investigation notes"]') + .at(0) + .simulate('change', { target: { value: 'notes' } }); + + expect(wrapper.find('EuiButtonGroup[idSelected="details"]').exists()).toBeFalsy(); + expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeTruthy(); + }); + + test('it displays notes markdown when user toggles to "notes"', () => { + const wrapper = mount( + + + + ); + + wrapper + .find('input[title="Investigation notes"]') + .at(0) + .simulate('change', { target: { value: 'notes' } }); + + expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeTruthy(); + expect(wrapper.find('Markdown h1').text()).toEqual('this is some markdown documentation'); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx new file mode 100644 index 0000000000000..c61566cb841e8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx @@ -0,0 +1,147 @@ +/* + * 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 { + EuiPanel, + EuiProgress, + EuiButtonGroup, + EuiButtonGroupOption, + EuiSpacer, + EuiFlexItem, + EuiText, + EuiFlexGroup, + EuiResizeObserver, +} from '@elastic/eui'; +import React, { memo, useState } from 'react'; +import styled from 'styled-components'; +import { isEmpty } from 'lodash/fp'; + +import { HeaderSection } from '../../../../../components/header_section'; +import { Markdown } from '../../../../../components/markdown'; +import { AboutStepRule, AboutStepRuleDetails } from '../../types'; +import * as i18n from './translations'; +import { StepAboutRule } from '../step_about_rule/'; + +const MyPanel = styled(EuiPanel)` + position: relative; +`; + +const FlexGroupFullHeight = styled(EuiFlexGroup)` + height: 100%; +`; + +const VerticalOverflowContainer = styled.div((props: { maxHeight: number }) => ({ + 'max-height': `${props.maxHeight}px`, + 'overflow-y': 'hidden', +})); + +const VerticalOverflowContent = styled.div((props: { maxHeight: number }) => ({ + 'max-height': `${props.maxHeight}px`, +})); + +const AboutContent = styled.div` + height: 100%; +`; + +const toggleOptions: EuiButtonGroupOption[] = [ + { + id: 'details', + label: i18n.ABOUT_PANEL_DETAILS_TAB, + }, + { + id: 'notes', + label: i18n.ABOUT_PANEL_NOTES_TAB, + }, +]; + +interface StepPanelProps { + stepData: AboutStepRule | null; + stepDataDetails: AboutStepRuleDetails | null; + loading: boolean; +} + +const StepAboutRuleToggleDetailsComponent: React.FC = ({ + stepData, + stepDataDetails, + loading, +}) => { + const [selectedToggleOption, setToggleOption] = useState('details'); + const [aboutPanelHeight, setAboutPanelHeight] = useState(0); + + const onResize = (e: { height: number; width: number }) => { + setAboutPanelHeight(e.height); + }; + + return ( + + {loading && ( + <> + + + + )} + {stepData != null && stepDataDetails != null && ( + + + + {!isEmpty(stepDataDetails.note) && stepDataDetails.note.trim() !== '' && ( + { + setToggleOption(val); + }} + data-test-subj="stepAboutDetailsToggle" + /> + )} + + + + {selectedToggleOption === 'details' ? ( + + {resizeRef => ( + + + + + {stepDataDetails.description} + + + + + + + )} + + ) : ( + + + + + + )} + + + )} + + ); +}; + +export const StepAboutRuleToggleDetails = memo(StepAboutRuleToggleDetailsComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts new file mode 100644 index 0000000000000..fa725366210de --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.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. + */ +import { i18n } from '@kbn/i18n'; + +export const ABOUT_PANEL_DETAILS_TAB = i18n.translate( + 'xpack.siem.detectionEngine.details.stepAboutRule.detailsLabel', + { + defaultMessage: 'Details', + } +); + +export const ABOUT_TEXT = i18n.translate( + 'xpack.siem.detectionEngine.details.stepAboutRule.aboutText', + { + defaultMessage: 'About', + } +); + +export const ABOUT_PANEL_NOTES_TAB = i18n.translate( + 'xpack.siem.detectionEngine.details.stepAboutRule.investigationNotesLabel', + { + defaultMessage: 'Investigation notes', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx index 490a8d9d194cb..2327ac36a5906 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx @@ -87,7 +87,7 @@ const getStepDefaultValue = ( const StepDefineRuleComponent: FC = ({ addPadding = false, defaultValues, - descriptionDirection = 'row', + descriptionColumns = 'singleSplit', isReadOnlyView, isLoading, isUpdateView = false, @@ -155,9 +155,9 @@ const StepDefineRuleComponent: FC = ({ }, []); return isReadOnlyView && myStepData?.queryBar != null ? ( - + = ({ addPadding = false, defaultValues, - descriptionDirection = 'row', + descriptionColumns = 'singleSplit', isReadOnlyView, isLoading, isUpdateView = false, @@ -80,31 +85,35 @@ const StepScheduleRuleComponent: FC = ({ return isReadOnlyView && myStepData != null ? ( - + ) : ( <> - - + + + + + + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts new file mode 100644 index 0000000000000..dbc5dd9bbe29a --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts @@ -0,0 +1,589 @@ +/* + * 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 { NewRule } from '../../../../containers/detection_engine/rules'; +import { + DefineStepRuleJson, + ScheduleStepRuleJson, + AboutStepRuleJson, + AboutStepRule, + ScheduleStepRule, + DefineStepRule, +} from '../types'; +import { + getTimeTypeValue, + formatDefineStepData, + formatScheduleStepData, + formatAboutStepData, + formatRule, +} from './helpers'; +import { + mockDefineStepRule, + mockQueryBar, + mockScheduleStepRule, + mockAboutStepRule, +} from '../all/__mocks__/mock'; + +describe('helpers', () => { + describe('getTimeTypeValue', () => { + test('returns timeObj with value 0 if no time value found', () => { + const result = getTimeTypeValue('m'); + + expect(result).toEqual({ unit: 'm', value: 0 }); + }); + + test('returns timeObj with unit set to empty string if no expected time type found', () => { + const result = getTimeTypeValue('5l'); + + expect(result).toEqual({ unit: '', value: 5 }); + }); + + test('returns timeObj with unit of s and value 5 when time is 5s ', () => { + const result = getTimeTypeValue('5s'); + + expect(result).toEqual({ unit: 's', value: 5 }); + }); + + test('returns timeObj with unit of m and value 5 when time is 5m ', () => { + const result = getTimeTypeValue('5m'); + + expect(result).toEqual({ unit: 'm', value: 5 }); + }); + + test('returns timeObj with unit of h and value 5 when time is 5h ', () => { + const result = getTimeTypeValue('5h'); + + expect(result).toEqual({ unit: 'h', value: 5 }); + }); + + test('returns timeObj with value of 5 when time is float like 5.6m ', () => { + const result = getTimeTypeValue('5m'); + + expect(result).toEqual({ unit: 'm', value: 5 }); + }); + + test('returns timeObj with value of 0 and unit of "" if random string passed in', () => { + const result = getTimeTypeValue('random'); + + expect(result).toEqual({ unit: '', value: 0 }); + }); + }); + + describe('formatDefineStepData', () => { + let mockData: DefineStepRule; + + beforeEach(() => { + mockData = mockDefineStepRule(); + }); + + test('returns formatted object as DefineStepRuleJson', () => { + const result: DefineStepRuleJson = formatDefineStepData(mockData); + const expected = { + language: 'kuery', + filters: mockQueryBar.filters, + query: 'test query', + saved_id: 'test123', + index: ['filebeat-'], + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with no saved_id if no savedId provided', () => { + const mockStepData = { + ...mockData, + queryBar: { + ...mockData.queryBar, + saved_id: '', + }, + }; + const result: DefineStepRuleJson = formatDefineStepData(mockStepData); + const expected = { + language: 'kuery', + filters: mockQueryBar.filters, + query: 'test query', + index: ['filebeat-'], + }; + + expect(result).toEqual(expected); + }); + }); + + describe('formatScheduleStepData', () => { + let mockData: ScheduleStepRule; + + beforeEach(() => { + mockData = mockScheduleStepRule(); + }); + + test('returns formatted object as ScheduleStepRuleJson', () => { + const result: ScheduleStepRuleJson = formatScheduleStepData(mockData); + const expected = { + enabled: false, + from: 'now-660s', + to: 'now', + interval: '5m', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with "to" as "now" if "to" not supplied', () => { + const mockStepData = { + ...mockData, + }; + delete mockStepData.to; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-660s', + to: 'now', + interval: '5m', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with "to" as "now" if "to" random string', () => { + const mockStepData = { + ...mockData, + to: 'random', + }; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-660s', + to: 'now', + interval: '5m', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object if "from" random string', () => { + const mockStepData = { + ...mockData, + from: 'random', + }; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-300s', + to: 'now', + interval: '5m', + meta: { + from: 'random', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object if "interval" random string', () => { + const mockStepData = { + ...mockData, + interval: 'random', + }; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-360s', + to: 'now', + interval: 'random', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + }); + + describe('formatAboutStepData', () => { + let mockData: AboutStepRule; + + beforeEach(() => { + mockData = mockAboutStepRule(); + }); + + test('returns formatted object as AboutStepRuleJson', () => { + const result: AboutStepRuleJson = formatAboutStepData(mockData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with empty falsePositive and references filtered out', () => { + const mockStepData = { + ...mockData, + falsePositives: ['', 'test', ''], + references: ['www.test.co', ''], + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object without note if note is empty string', () => { + const mockStepData = { + ...mockData, + note: '', + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object without timeline_id and timeline_title if timeline.id is null', () => { + const mockStepData = { + ...mockData, + }; + delete mockStepData.timeline.id; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with timeline_id and timeline_title if timeline.id is "', () => { + const mockStepData = { + ...mockData, + timeline: { + ...mockData.timeline, + id: '', + }, + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object without timeline_id and timeline_title if timeline.title is null', () => { + const mockStepData = { + ...mockData, + timeline: { + ...mockData.timeline, + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + }, + }; + delete mockStepData.timeline.title; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with timeline_id and timeline_title if timeline.title is "', () => { + const mockStepData = { + ...mockData, + timeline: { + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + title: '', + }, + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { id: '1234', name: 'tactic1', reference: 'reference1' }, + technique: [{ id: '456', name: 'technique1', reference: 'technique reference' }], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: '', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with threats filtered out where tactic.name is "none"', () => { + const mockStepData = { + ...mockData, + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'none', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { id: '1234', name: 'tactic1', reference: 'reference1' }, + technique: [{ id: '456', name: 'technique1', reference: 'technique reference' }], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + }); + + describe('formatRule', () => { + let mockAbout: AboutStepRule; + let mockDefine: DefineStepRule; + let mockSchedule: ScheduleStepRule; + + beforeEach(() => { + mockAbout = mockAboutStepRule(); + mockDefine = mockDefineStepRule(); + mockSchedule = mockScheduleStepRule(); + }); + + test('returns NewRule with type of saved_query when saved_id exists', () => { + const result: NewRule = formatRule(mockDefine, mockAbout, mockSchedule); + + expect(result.type).toEqual('saved_query'); + }); + + test('returns NewRule with type of query when saved_id does not exist', () => { + const mockDefineStepRuleWithoutSavedId = { + ...mockDefine, + queryBar: { + ...mockDefine.queryBar, + saved_id: '', + }, + }; + const result: NewRule = formatRule(mockDefineStepRuleWithoutSavedId, mockAbout, mockSchedule); + + expect(result.type).toEqual('query'); + }); + + test('returns NewRule with id set to ruleId if ruleId exists', () => { + const result: NewRule = formatRule(mockDefine, mockAbout, mockSchedule, 'query-with-rule-id'); + + expect(result.id).toEqual('query-with-rule-id'); + }); + + test('returns NewRule without id if ruleId does not exist', () => { + const result: NewRule = formatRule(mockDefine, mockAbout, mockSchedule); + + expect(result.id).toBeUndefined(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts index de6678b42df6f..07578e870bf2b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts @@ -19,7 +19,7 @@ import { FormatRuleType, } from '../types'; -const getTimeTypeValue = (time: string): { unit: string; value: number } => { +export const getTimeTypeValue = (time: string): { unit: string; value: number } => { const timeObj = { unit: '', value: 0, @@ -39,7 +39,7 @@ const getTimeTypeValue = (time: string): { unit: string; value: number } => { return timeObj; }; -const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { +export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { const { queryBar, isNew, ...rest } = defineStepData; const { filters, query, saved_id: savedId } = queryBar; return { @@ -51,7 +51,7 @@ const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJso }; }; -const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRuleJson => { +export const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRuleJson => { const { isNew, ...formatScheduleData } = scheduleData; if (!isEmpty(formatScheduleData.interval) && !isEmpty(formatScheduleData.from)) { const { unit: intervalUnit, value: intervalValue } = getTimeTypeValue( @@ -71,8 +71,17 @@ const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRul }; }; -const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => { - const { falsePositives, references, riskScore, threat, timeline, isNew, ...rest } = aboutStepData; +export const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => { + const { + falsePositives, + references, + riskScore, + threat, + timeline, + isNew, + note, + ...rest + } = aboutStepData; return { false_positives: falsePositives.filter(item => !isEmpty(item)), references: references.filter(item => !isEmpty(item)), @@ -93,6 +102,7 @@ const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => return { id, name, reference }; }), })), + ...(!isEmpty(note) ? { note } : {}), ...rest, }; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx index d816c7e867057..c9f44ab0048f9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx @@ -286,7 +286,7 @@ const CreateRulePageComponent: React.FC = () => { isLoading={isLoading || loading} setForm={setStepsForm} setStepData={setStepData} - descriptionDirection="row" + descriptionColumns="singleSplit" /> @@ -315,7 +315,7 @@ const CreateRulePageComponent: React.FC = () => { { defaultValues={ (stepsData.current[RuleStep.scheduleRule].data as ScheduleStepRule) ?? null } - descriptionDirection="row" + descriptionColumns="singleSplit" isReadOnlyView={isStepRuleInReadOnlyView[RuleStep.scheduleRule]} isLoading={isLoading || loading} setForm={setStepsForm} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index e73852ec91287..a35caf4acf67b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -38,13 +38,13 @@ import { } from '../../../../containers/source'; import { SpyRoute } from '../../../../utils/route/spy_routes'; +import { StepAboutRuleToggleDetails } from '../components/step_about_rule_details/'; import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page'; import { SignalsHistogramPanel } from '../../components/signals_histogram_panel'; import { SignalsTable } from '../../components/signals'; import { useUserInfo } from '../../components/user_info'; import { DetectionEngineEmptyPage } from '../../detection_engine_empty_page'; import { useSignalInfo } from '../../components/signals_info'; -import { StepAboutRule } from '../components/step_about_rule'; import { StepDefineRule } from '../components/step_define_rule'; import { StepScheduleRule } from '../components/step_schedule_rule'; import { buildSignalsRuleIdFilter } from '../../components/signals/default_config'; @@ -105,13 +105,15 @@ const RuleDetailsPageComponent: FC = ({ // This is used to re-trigger api rule status when user de/activate rule const [ruleEnabled, setRuleEnabled] = useState(null); const [ruleDetailTab, setRuleDetailTab] = useState(RuleDetailTabs.signals); - const { aboutRuleData, defineRuleData, scheduleRuleData } = + const { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData } = rule != null - ? getStepsData({ - rule, - detailsView: true, - }) - : { aboutRuleData: null, defineRuleData: null, scheduleRuleData: null }; + ? getStepsData({ rule, detailsView: true }) + : { + aboutRuleData: null, + modifiedAboutRuleDetailsData: null, + defineRuleData: null, + scheduleRuleData: null, + }; const [lastSignals] = useSignalInfo({ ruleId }); const userHasNoPermissions = canUserCRUD != null && hasManageApiKey != null ? !canUserCRUD || !hasManageApiKey : false; @@ -291,16 +293,23 @@ const RuleDetailsPageComponent: FC = ({
    {ruleError} - {tabs} - {ruleDetailTab === RuleDetailTabs.signals && ( - <> - + + + + + + + {defineRuleData != null && ( = ({ )} - - - - {aboutRuleData != null && ( - - )} - - - + {scheduleRuleData != null && ( = ({ - + + + + {tabs} + + {ruleDetailTab === RuleDetailTabs.signals && ( + <> { + describe('getStepsData', () => { + test('returns object with about, define, and schedule step properties formatted', () => { + const { + defineRuleData, + modifiedAboutRuleDetailsData, + aboutRuleData, + scheduleRuleData, + }: GetStepsData = getStepsData({ + rule: mockRuleWithEverything('test-id'), + }); + const defineRuleStepData = { + isNew: false, + index: ['auditbeat-*'], + queryBar: { + query: { + query: 'user.name: root or user.name: admin', + language: 'kuery', + }, + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + saved_id: 'test123', + }, + }; + const aboutRuleStepData = { + description: '24/7', + falsePositives: ['test'], + isNew: false, + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + riskScore: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline: { + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + title: 'Titled timeline', + }, + }; + const scheduleRuleStepData = { enabled: true, from: '0s', interval: '5m', isNew: false }; + const aboutRuleDataDetailsData = { + note: '# this is some markdown documentation', + description: '24/7', + }; + + expect(defineRuleData).toEqual(defineRuleStepData); + expect(aboutRuleData).toEqual(aboutRuleStepData); + expect(scheduleRuleData).toEqual(scheduleRuleStepData); + expect(modifiedAboutRuleDetailsData).toEqual(aboutRuleDataDetailsData); + }); + }); + + describe('getAboutStepsData', () => { + test('returns timeline id and title of null if they do not exist on rule', () => { + const mockedRule = mockRuleWithEverything('test-id'); + delete mockedRule.timeline_id; + delete mockedRule.timeline_title; + const result: AboutStepRule = getAboutStepsData(mockedRule, false); + + expect(result.timeline.id).toBeNull(); + expect(result.timeline.title).toBeNull(); + }); + + test('returns name, description, and note as empty string if detailsView is true', () => { + const result: AboutStepRule = getAboutStepsData(mockRuleWithEverything('test-id'), true); + + expect(result.name).toEqual(''); + expect(result.description).toEqual(''); + expect(result.note).toEqual(''); + }); + + test('returns note as empty string if property does not exist on rule', () => { + const mockedRule = mockRuleWithEverything('test-id'); + delete mockedRule.note; + const result: AboutStepRule = getAboutStepsData(mockedRule, false); + + expect(result.note).toEqual(''); + }); + }); + + describe('determineDetailsValue', () => { + test('returns name, description, and note as empty string if detailsView is true', () => { + const result: Pick = determineDetailsValue( + mockRuleWithEverything('test-id'), + true + ); + const expected = { name: '', description: '', note: '' }; + + expect(result).toEqual(expected); + }); + + test('returns name, description, and note values if detailsView is false', () => { + const mockedRule = mockRuleWithEverything('test-id'); + const result: Pick = determineDetailsValue( + mockedRule, + false + ); + const expected = { + name: mockedRule.name, + description: mockedRule.description, + note: mockedRule.note, + }; + + expect(result).toEqual(expected); + }); + + test('returns note as empty string if property does not exist on rule', () => { + const mockedRule = mockRuleWithEverything('test-id'); + delete mockedRule.note; + const result: Pick = determineDetailsValue( + mockedRule, + false + ); + const expected = { name: mockedRule.name, description: mockedRule.description, note: '' }; + + expect(result).toEqual(expected); + }); + }); + + describe('getDefineStepsData', () => { + test('returns with saved_id if value exists on rule', () => { + const result: DefineStepRule = getDefineStepsData(mockRule('test-id')); + const expected = { + isNew: false, + index: ['auditbeat-*'], + queryBar: { + query: { + query: '', + language: 'kuery', + }, + filters: [], + saved_id: "Garrett's IP", + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns with saved_id of null if value does not exist on rule', () => { + const mockedRule = { + ...mockRule('test-id'), + }; + delete mockedRule.saved_id; + const result: DefineStepRule = getDefineStepsData(mockedRule); + const expected = { + isNew: false, + index: ['auditbeat-*'], + queryBar: { + query: { + query: '', + language: 'kuery', + }, + filters: [], + saved_id: null, + }, + }; + + expect(result).toEqual(expected); + }); + }); + + describe('getHumanizedDuration', () => { + test('returns from as seconds if from duration is less than a minute', () => { + const result = getHumanizedDuration('now-62s', '1m'); + + expect(result).toEqual('2s'); + }); + + test('returns from as minutes if from duration is less than an hour', () => { + const result = getHumanizedDuration('now-660s', '5m'); + + expect(result).toEqual('6m'); + }); + + test('returns from as hours if from duration is more than 60 minutes', () => { + const result = getHumanizedDuration('now-7400s', '5m'); + + expect(result).toEqual('1h'); + }); + + test('returns from as if from is not parsable as dateMath', () => { + const result = getHumanizedDuration('randomstring', '5m'); + + expect(result).toEqual('NaNh'); + }); + + test('returns from as 5m if interval is not parsable as dateMath', () => { + const result = getHumanizedDuration('now-300s', 'randomstring'); + + expect(result).toEqual('5m'); + }); + }); + + describe('getScheduleStepsData', () => { + test('returns expected ScheduleStep rule object', () => { + const mockedRule = { + ...mockRule('test-id'), + }; + const result: ScheduleStepRule = getScheduleStepsData(mockedRule); + const expected = { + isNew: false, + enabled: mockedRule.enabled, + interval: mockedRule.interval, + from: '0s', + }; + + expect(result).toEqual(expected); + }); + }); + + describe('getModifiedAboutDetailsData', () => { + test('returns object with "note" and "description" being those of passed in rule', () => { + const result: AboutStepRuleDetails = getModifiedAboutDetailsData( + mockRuleWithEverything('test-id') + ); + const aboutRuleDataDetailsData = { + note: '# this is some markdown documentation', + description: '24/7', + }; + + expect(result).toEqual(aboutRuleDataDetailsData); + }); + + test('returns "note" with empty string if "note" does not exist', () => { + const { note, ...mockRuleWithoutNote } = { ...mockRuleWithEverything('test-id') }; + const result: AboutStepRuleDetails = getModifiedAboutDetailsData(mockRuleWithoutNote); + + const aboutRuleDetailsData = { note: '', description: mockRuleWithoutNote.description }; + + expect(result).toEqual(aboutRuleDetailsData); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx index 85f3bcbd236e9..1fc8a86a476f2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx @@ -5,19 +5,26 @@ */ import dateMath from '@elastic/datemath'; -import { get, pick } from 'lodash/fp'; +import { get } from 'lodash/fp'; import moment from 'moment'; import { useLocation } from 'react-router-dom'; import { Filter } from '../../../../../../../../src/plugins/data/public'; import { Rule } from '../../../containers/detection_engine/rules'; import { FormData, FormHook, FormSchema } from '../../../shared_imports'; -import { AboutStepRule, DefineStepRule, IMitreEnterpriseAttack, ScheduleStepRule } from './types'; +import { + AboutStepRule, + AboutStepRuleDetails, + DefineStepRule, + IMitreEnterpriseAttack, + ScheduleStepRule, +} from './types'; -interface GetStepsData { - aboutRuleData: AboutStepRule | null; - defineRuleData: DefineStepRule | null; - scheduleRuleData: ScheduleStepRule | null; +export interface GetStepsData { + aboutRuleData: AboutStepRule; + modifiedAboutRuleDetailsData: AboutStepRuleDetails; + defineRuleData: DefineStepRule; + scheduleRuleData: ScheduleStepRule; } export const getStepsData = ({ @@ -27,58 +34,107 @@ export const getStepsData = ({ rule: Rule; detailsView?: boolean; }): GetStepsData => { - const defineRuleData: DefineStepRule | null = - rule != null - ? { - isNew: false, - index: rule.index, - queryBar: { - query: { query: rule.query as string, language: rule.language }, - filters: rule.filters as Filter[], - saved_id: rule.saved_id ?? null, - }, - } - : null; - const aboutRuleData: AboutStepRule | null = - rule != null - ? { - isNew: false, - ...pick(['description', 'name', 'references', 'severity', 'tags', 'threat'], rule), - ...(detailsView ? { name: '' } : {}), - threat: rule.threat as IMitreEnterpriseAttack[], - falsePositives: rule.false_positives, - riskScore: rule.risk_score, - timeline: { - id: rule.timeline_id ?? null, - title: rule.timeline_title ?? null, - }, - } - : null; - - const from = dateMath.parse(rule.from) ?? moment(); - const interval = dateMath.parse(`now-${rule.interval}`) ?? moment(); - - const fromDuration = moment.duration(interval.diff(from)); - let fromHumanize = `${Math.floor(fromDuration.asHours())}h`; + const defineRuleData: DefineStepRule = getDefineStepsData(rule); + const aboutRuleData: AboutStepRule = getAboutStepsData(rule, detailsView); + const modifiedAboutRuleDetailsData: AboutStepRuleDetails = getModifiedAboutDetailsData(rule); + const scheduleRuleData: ScheduleStepRule = getScheduleStepsData(rule); + + return { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData }; +}; + +export const getDefineStepsData = (rule: Rule): DefineStepRule => { + const { index, query, language, filters, saved_id: savedId } = rule; + + return { + isNew: false, + index, + queryBar: { + query: { + query, + language, + }, + filters: filters as Filter[], + saved_id: savedId ?? null, + }, + }; +}; + +export const getScheduleStepsData = (rule: Rule): ScheduleStepRule => { + const { enabled, interval, from } = rule; + const fromHumanizedValue = getHumanizedDuration(from, interval); + + return { + isNew: false, + enabled, + interval, + from: fromHumanizedValue, + }; +}; + +export const getHumanizedDuration = (from: string, interval: string): string => { + const fromValue = dateMath.parse(from) ?? moment(); + const intervalValue = dateMath.parse(`now-${interval}`) ?? moment(); + + const fromDuration = moment.duration(intervalValue.diff(fromValue)); + const fromHumanize = `${Math.floor(fromDuration.asHours())}h`; if (fromDuration.asSeconds() < 60) { - fromHumanize = `${Math.floor(fromDuration.asSeconds())}s`; + return `${Math.floor(fromDuration.asSeconds())}s`; } else if (fromDuration.asMinutes() < 60) { - fromHumanize = `${Math.floor(fromDuration.asMinutes())}m`; + return `${Math.floor(fromDuration.asMinutes())}m`; } - const scheduleRuleData: ScheduleStepRule | null = - rule != null - ? { - isNew: false, - ...pick(['enabled', 'interval'], rule), - from: fromHumanize, - } - : null; + return fromHumanize; +}; + +export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRule => { + const { name, description, note } = determineDetailsValue(rule, detailsView); + const { + references, + severity, + false_positives: falsePositives, + risk_score: riskScore, + tags, + threat, + timeline_id: timelineId, + timeline_title: timelineTitle, + } = rule; + + return { + isNew: false, + name, + description, + note: note!, + references, + severity, + tags, + riskScore, + falsePositives, + threat: threat as IMitreEnterpriseAttack[], + timeline: { + id: timelineId ?? null, + title: timelineTitle ?? null, + }, + }; +}; + +export const determineDetailsValue = ( + rule: Rule, + detailsView: boolean +): Pick => { + const { name, description, note } = rule; + if (detailsView) { + return { name: '', description: '', note: '' }; + } - return { aboutRuleData, defineRuleData, scheduleRuleData }; + return { name, description, note: note ?? '' }; }; +export const getModifiedAboutDetailsData = (rule: Rule): AboutStepRuleDetails => ({ + note: rule.note ?? '', + description: rule.description, +}); + export const useQuery = () => new URLSearchParams(useLocation().search); export type PrePackagedRuleStatus = diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts index 34df20de1e461..aa50626a1231a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts @@ -36,7 +36,7 @@ export interface RuleStepData { export interface RuleStepProps { addPadding?: boolean; - descriptionDirection?: 'row' | 'column'; + descriptionColumns?: 'multi' | 'single' | 'singleSplit'; setStepData?: (step: RuleStep, data: unknown, isValid: boolean) => void; isReadOnlyView: boolean; isUpdateView?: boolean; @@ -58,6 +58,12 @@ export interface AboutStepRule extends StepRuleData { tags: string[]; timeline: FieldValueTimeline; threat: IMitreEnterpriseAttack[]; + note: string; +} + +export interface AboutStepRuleDetails { + note: string; + description: string; } export interface DefineStepRule extends StepRuleData { @@ -91,6 +97,7 @@ export interface AboutStepRuleJson { timeline_id?: string; timeline_title?: string; threat: IMitreEnterpriseAttack[]; + note?: string; } export interface ScheduleStepRuleJson { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts index dd4acaeaf5a02..39277b3d3c77e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts @@ -19,7 +19,7 @@ export const TOTAL_SIGNAL = i18n.translate('xpack.siem.detectionEngine.totalSign }); export const SIGNAL = i18n.translate('xpack.siem.detectionEngine.signalTitle', { - defaultMessage: 'Signals (SIEM Detections)', + defaultMessage: 'Detected signals', }); export const ALERT = i18n.translate('xpack.siem.detectionEngine.alertTitle', { From 79b04547dbc93aafb6b390a830a16628c286eeac Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Tue, 17 Mar 2020 18:14:02 +0100 Subject: [PATCH 52/76] [SIEM] Adds 'Closes one signal when more than one opened signals are selected' test again (#60380) * Revert "Revert "adds new test (#60064)"" This reverts commit 4a8fd0afee9262916348c51e98f2ac955e25b2ac. * waits for having 25 signals displayed --- .../cypress/integration/detections.spec.ts | 44 ++++++++++++++++++- .../siem/cypress/screens/detections.ts | 4 +- .../plugins/siem/cypress/tasks/detections.ts | 7 +++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts index 1624586d4ca14..de17f40a3ac71 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -5,12 +5,14 @@ */ import { NUMBER_OF_SIGNALS, + OPEN_CLOSE_SIGNALS_BTN, SELECTED_SIGNALS, SHOWING_SIGNALS, SIGNALS, } from '../screens/detections'; import { + closeFirstSignal, closeSignals, goToClosedSignals, goToOpenedSignals, @@ -26,7 +28,7 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Detections', () => { - before(() => { + beforeEach(() => { esArchiverLoad('signals'); loginAndWaitForPage(DETECTIONS); }); @@ -53,6 +55,7 @@ describe('Detections', () => { waitForSignals(); cy.reload(); waitForSignals(); + waitForSignalsToBeLoaded(); const expectedNumberOfSignalsAfterClosing = +numberOfSignals - numberOfSignalsToBeClosed; cy.get(NUMBER_OF_SIGNALS) @@ -111,4 +114,43 @@ describe('Detections', () => { .should('eql', expectedNumberOfOpenedSignals.toString()); }); }); + + it('Closes one signal when more than one opened signals are selected', () => { + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + const numberOfSignalsToBeClosed = 1; + const numberOfSignalsToBeSelected = 3; + + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('have.attr', 'disabled'); + selectNumberOfSignals(numberOfSignalsToBeSelected); + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('not.have.attr', 'disabled'); + + closeFirstSignal(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + + const expectedNumberOfSignals = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignals.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignals.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signal`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8b5ba23578807..f388ac1215d01 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -12,7 +12,9 @@ export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; -export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; +export const OPEN_CLOSE_SIGNAL_BTN = '[data-test-subj="update-signal-status-button"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] button'; export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 21a0c136b90df..3416e3eb81de3 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -8,6 +8,7 @@ import { CLOSED_SIGNALS_BTN, LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNAL_BTN, OPEN_CLOSE_SIGNALS_BTN, OPENED_SIGNALS_BTN, SIGNALS, @@ -15,6 +16,12 @@ import { } from '../screens/detections'; import { REFRESH_BUTTON } from '../screens/siem_header'; +export const closeFirstSignal = () => { + cy.get(OPEN_CLOSE_SIGNAL_BTN) + .first() + .click({ force: true }); +}; + export const closeSignals = () => { cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); }; From 4da0cb36844a3ff368dbeda25b4cc0f9738af920 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 17 Mar 2020 10:24:58 -0700 Subject: [PATCH 53/76] [Ingest] Support `show_user` package registry flag (#60338) * Support registry `show_user` var definition property (elastic/package-registry#266) * Add tests --- .../ingest_manager/common/types/models/epm.ts | 1 + .../components/datasource_input_config.tsx | 7 ++- .../datasource_input_stream_config.tsx | 7 ++- .../create_datasource_page/services/index.ts | 6 ++ .../services/is_advanced_var.test.ts | 62 +++++++++++++++++++ .../services/is_advanced_var.ts | 13 ++++ 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index 6b8403b74a759..28786530db018 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -186,6 +186,7 @@ export interface RegistryVarsEntry { description?: string; type: string; required?: boolean; + show_user?: boolean; multi?: boolean; default?: string | string[]; os?: { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx index 39f2f048ab88d..69d2194638441 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx @@ -15,6 +15,7 @@ import { EuiTitle, } from '@elastic/eui'; import { DatasourceInput, RegistryVarsEntry } from '../../../../types'; +import { isAdvancedVar } from '../services'; import { DatasourceInputVarField } from './datasource_input_var_field'; export const DatasourceInputConfig: React.FunctionComponent<{ @@ -30,10 +31,10 @@ export const DatasourceInputConfig: React.FunctionComponent<{ if (packageInputVars) { packageInputVars.forEach(varDef => { - if (varDef.required && !varDef.default) { - requiredVars.push(varDef); - } else { + if (isAdvancedVar(varDef)) { advancedVars.push(varDef); + } else { + requiredVars.push(varDef); } }); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx index e4b138932cb53..1f483f1911bcc 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx @@ -16,6 +16,7 @@ import { EuiButtonEmpty, } from '@elastic/eui'; import { DatasourceInputStream, RegistryStream, RegistryVarsEntry } from '../../../../types'; +import { isAdvancedVar } from '../services'; import { DatasourceInputVarField } from './datasource_input_var_field'; export const DatasourceInputStreamConfig: React.FunctionComponent<{ @@ -31,10 +32,10 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ if (packageInputStream.vars && packageInputStream.vars.length) { packageInputStream.vars.forEach(varDef => { - if (varDef.required && !varDef.default) { - requiredVars.push(varDef); - } else { + if (isAdvancedVar(varDef)) { advancedVars.push(varDef); + } else { + requiredVars.push(varDef); } }); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts new file mode 100644 index 0000000000000..44e5bfa41cb9b --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts @@ -0,0 +1,6 @@ +/* + * 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. + */ +export { isAdvancedVar } from './is_advanced_var'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts new file mode 100644 index 0000000000000..67796d69863fa --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts @@ -0,0 +1,62 @@ +/* + * 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 { isAdvancedVar } from './is_advanced_var'; + +describe('Ingest Manager - isAdvancedVar', () => { + it('returns true for vars that should be show under advanced options', () => { + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + required: true, + default: 'default string', + }) + ).toBe(true); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + default: 'default string', + }) + ).toBe(true); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + }) + ).toBe(true); + }); + + it('returns false for vars that should be show by default', () => { + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + required: true, + default: 'default string', + show_user: true, + }) + ).toBe(false); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + required: true, + }) + ).toBe(false); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + show_user: true, + }) + ).toBe(false); + }); +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts new file mode 100644 index 0000000000000..398f1d675c5df --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { RegistryVarsEntry } from '../../../../types'; + +export const isAdvancedVar = (varDef: RegistryVarsEntry): boolean => { + if (varDef.show_user || (varDef.required && !varDef.default)) { + return false; + } + return true; +}; From 6b7731bb74b1f133a39730c1e4de847c655e9d7a Mon Sep 17 00:00:00 2001 From: Joel Griffith Date: Tue, 17 Mar 2020 10:41:06 -0700 Subject: [PATCH 54/76] [Reporting] Wholesale moves client to newest-platform (#58945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move over to new plugin space, working implementation * Fixing tests for report_listing snapshots * WIP: Fixing react-component tests * Fixing report_info_button tests * Fixing download linksies * WIP: Final working implementation * Fixing attachAction API + API URLs * Let the past die. Kill it if you have to. That’s the only way to become what you were meant to be. * Fixing stream-client for new platform APIs * Fixing types and tests * Fix broken mock * Adds back in warnings to report info button * kibana.json line-breaks on required plugins * Fixing broked snapshots * Fix license checks in client-side components * Adding back in warnings to report_listing component * Fix danglig unused import * Adds license checks for basic to our csv panel action * Fixes issues from prior fork * Move relative pathing to absolute * Fix POST URL copying as we've moved from static methods * Fix layoutId props * Fixes types for layoutId Co-authored-by: Elastic Machine --- .../workpad_header/workpad_export/index.ts | 2 +- x-pack/legacy/plugins/reporting/index.ts | 16 +- .../report_info_button.test.mocks.ts | 8 - .../public/components/report_listing.test.tsx | 77 --------- .../public/constants/job_statuses.tsx | 13 -- .../reporting/public/lib/download_report.ts | 23 --- .../reporting/public/lib/job_queue_client.ts | 89 ---------- .../reporting/public/lib/reporting_client.ts | 37 ----- .../reporting/public/register_feature.ts | 27 ---- .../public/views/management/index.js | 7 - .../public/views/management/jobs.html | 3 - .../reporting/public/views/management/jobs.js | 59 ------- .../public/views/management/management.js | 44 ----- x-pack/legacy/plugins/reporting/types.d.ts | 16 -- x-pack/plugins/reporting/common/poller.ts | 96 +++++++++++ x-pack/plugins/reporting/constants.ts | 25 ++- x-pack/plugins/reporting/index.d.ts | 16 ++ x-pack/plugins/reporting/kibana.json | 10 +- .../report_info_button.test.tsx.snap | 0 .../report_listing.test.tsx.snap | 0 .../public/components/general_error.tsx | 2 +- .../public/components/job_failure.tsx | 2 +- .../components/job_queue_client.test.mocks.ts | 17 ++ .../public/components/job_success.tsx | 2 +- .../components/job_warning_formulas.tsx | 2 +- .../components/job_warning_max_size.tsx | 2 +- .../public/components/report_error_button.tsx | 5 +- .../components/report_info_button.test.tsx | 32 ++-- .../public/components/report_info_button.tsx | 12 +- .../public/components/report_listing.test.tsx | 85 ++++++++++ .../public/components/report_listing.tsx | 83 ++++++---- .../components/reporting_panel_content.tsx | 55 ++++--- .../screen_capture_panel_content.tsx | 6 + x-pack/plugins/reporting/public/index.ts | 4 +- .../__snapshots__/stream_handler.test.ts.snap | 4 +- .../lib/job_completion_notifications.ts | 2 +- .../plugins/reporting/public/lib/job_queue.ts | 27 ---- .../public/lib/license_check.test.ts | 50 ++++++ .../reporting/public/lib/license_check.ts | 52 ++++++ .../public/lib/reporting_api_client.ts | 152 ++++++++++++++++++ .../public/lib/stream_handler.test.ts | 68 +++----- .../reporting/public/lib/stream_handler.ts | 47 +++--- .../panel_actions/get_csv_panel_action.tsx | 65 ++++---- x-pack/plugins/reporting/public/plugin.tsx | 127 +++++++++++++-- .../register_csv_reporting.tsx | 43 +++-- .../register_pdf_png_reporting.tsx} | 120 ++++++++------ 46 files changed, 920 insertions(+), 714 deletions(-) delete mode 100644 x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx delete mode 100644 x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx delete mode 100644 x-pack/legacy/plugins/reporting/public/lib/download_report.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/register_feature.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/index.js delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/jobs.html delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/jobs.js delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/management.js create mode 100644 x-pack/plugins/reporting/common/poller.ts rename x-pack/{legacy => }/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap (100%) create mode 100644 x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts rename x-pack/{legacy => }/plugins/reporting/public/components/report_error_button.tsx (92%) rename x-pack/{legacy => }/plugins/reporting/public/components/report_info_button.test.tsx (60%) rename x-pack/{legacy => }/plugins/reporting/public/components/report_info_button.tsx (95%) create mode 100644 x-pack/plugins/reporting/public/components/report_listing.test.tsx rename x-pack/{legacy => }/plugins/reporting/public/components/report_listing.tsx (85%) rename x-pack/{legacy => }/plugins/reporting/public/components/reporting_panel_content.tsx (88%) rename x-pack/{legacy => }/plugins/reporting/public/components/screen_capture_panel_content.tsx (91%) rename x-pack/{legacy => }/plugins/reporting/public/lib/job_completion_notifications.ts (98%) delete mode 100644 x-pack/plugins/reporting/public/lib/job_queue.ts create mode 100644 x-pack/plugins/reporting/public/lib/license_check.test.ts create mode 100644 x-pack/plugins/reporting/public/lib/license_check.ts create mode 100644 x-pack/plugins/reporting/public/lib/reporting_api_client.ts rename x-pack/{legacy => }/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx (72%) rename x-pack/{legacy => }/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx (61%) rename x-pack/{legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx => plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx} (57%) diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts index 7f81adad6bf9b..949264fcc9fdb 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts @@ -6,7 +6,7 @@ import { connect } from 'react-redux'; import { compose, withProps } from 'recompose'; -import * as jobCompletionNotifications from '../../../../../reporting/public/lib/job_completion_notifications'; +import { jobCompletionNotifications } from '../../../../../../../plugins/reporting/public'; // @ts-ignore Untyped local import { getWorkpad, getPages } from '../../../state/selectors/workpad'; // @ts-ignore Untyped local diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index 9ce4e807f8ef8..89e98302cddc9 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -10,7 +10,7 @@ import { resolve } from 'path'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; import { config as reportingConfig } from './config'; import { legacyInit } from './server/legacy'; -import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types'; +import { ReportingPluginSpecOptions } from './types'; const kbToBase64Length = (kb: number) => { return Math.floor((kb * 1024 * 8) / 6); @@ -25,20 +25,6 @@ export const reporting = (kibana: any) => { config: reportingConfig, uiExports: { - shareContextMenuExtensions: [ - 'plugins/reporting/share_context_menu/register_csv_reporting', - 'plugins/reporting/share_context_menu/register_reporting', - ], - embeddableActions: ['plugins/reporting/panel_actions/get_csv_panel_action'], - home: ['plugins/reporting/register_feature'], - managementSections: ['plugins/reporting/views/management'], - injectDefaultVars(server: Legacy.Server, options?: ReportingConfigOptions) { - const config = server.config(); - return { - reportingPollConfig: options ? options.poll : {}, - enablePanelActionDownload: config.get('xpack.reporting.csv.enablePanelActionDownload'), - }; - }, uiSettingDefaults: { [UI_SETTINGS_CUSTOM_PDF_LOGO]: { name: i18n.translate('xpack.reporting.pdfFooterImageLabel', { diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts b/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts deleted file mode 100644 index 9dd7cbb5fc567..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts +++ /dev/null @@ -1,8 +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. - */ - -export const mockJobQueueClient = { list: jest.fn(), total: jest.fn(), getInfo: jest.fn() }; -jest.mock('../lib/job_queue_client', () => ({ jobQueueClient: mockJobQueueClient })); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx b/x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx deleted file mode 100644 index d78eb5c409c1f..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx +++ /dev/null @@ -1,77 +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. - */ - -interface JobData { - _index: string; - _id: string; - _source: { - browser_type: string; - created_at: string; - jobtype: string; - created_by: string; - payload: { - type: string; - title: string; - }; - kibana_name?: string; // undefined if job is pending (not yet claimed by an instance) - kibana_id?: string; // undefined if job is pending (not yet claimed by an instance) - output?: { content_type: string; size: number }; // undefined if job is incomplete - completed_at?: string; // undefined if job is incomplete - }; -} - -jest.mock('ui/chrome', () => ({ - getInjected() { - return { - jobsRefresh: { - interval: 10, - intervalErrorMultiplier: 2, - }, - }; - }, -})); - -jest.mock('ui/kfetch', () => ({ - kfetch: ({ pathname }: { pathname: string }): Promise => { - if (pathname === '/api/reporting/jobs/list') { - return Promise.resolve([ - { _index: '.reporting-2019.08.18', _id: 'jzoik8dh1q2i89fb5f19znm6', _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.869Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik7tn1q2i89fb5f60e5ve', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.155Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik5tb1q2i89fb5fckchny', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:21.551Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik5a11q2i89fb5f130t2m', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:20.857Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik3ka1q2i89fb5fdx93g7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:18.634Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik2vt1q2i89fb5ffw723n', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:17.753Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik1851q2i89fb5fdge6e7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1080, height: 720 } }, type: 'canvas workpad', title: 'My Canvas Workpad - Dark', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:15.605Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoijyre1q2i89fb5fa7xzvi', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:12.410Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoijv5h1q2i89fb5ffklnhx', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:07.733Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jznhgk7r1bx789fb5f6hxok7', _score: null, _source: { kibana_name: 'spicy.local', browser_type: 'chromium', created_at: '2019-08-23T02:15:47.799Z', jobtype: 'printable_pdf', created_by: 'elastic', kibana_id: 'ca75e26c-2b7d-464f-aef0-babb67c735a0', output: { content_type: 'application/pdf', size: 877114 }, completed_at: '2019-08-23T02:15:57.707Z', payload: { type: 'dashboard (legacy)', title: 'tests-panels', }, max_attempts: 3, started_at: '2019-08-23T02:15:48.794Z', attempts: 1, status: 'completed', }, }, // prettier-ignore - ]); - } - - // query for jobs count - return Promise.resolve(18); - }, -})); - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReportListing } from './report_listing'; - -describe('ReportListing', () => { - it('Report job listing with some items', () => { - const wrapper = mountWithIntl( - - ); - wrapper.update(); - const input = wrapper.find('[data-test-subj="reportJobListing"]'); - expect(input).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx b/x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx deleted file mode 100644 index 29c51217a5c64..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx +++ /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. - */ - -export enum JobStatuses { - PENDING = 'pending', - PROCESSING = 'processing', - COMPLETED = 'completed', - FAILED = 'failed', - CANCELLED = 'cancelled', -} diff --git a/x-pack/legacy/plugins/reporting/public/lib/download_report.ts b/x-pack/legacy/plugins/reporting/public/lib/download_report.ts deleted file mode 100644 index 54194c87afabc..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/lib/download_report.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { API_BASE_URL } from '../../common/constants'; - -const { core } = npStart; - -export function getReportURL(jobId: string) { - const apiBaseUrl = core.http.basePath.prepend(API_BASE_URL); - const downloadLink = `${apiBaseUrl}/jobs/download/${jobId}`; - - return downloadLink; -} - -export function downloadReport(jobId: string) { - const location = getReportURL(jobId); - - window.open(location); -} diff --git a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts deleted file mode 100644 index 87d4174168b7f..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { API_LIST_URL } from '../../common/constants'; - -const { core } = npStart; - -export interface JobQueueEntry { - _id: string; - _source: any; -} - -export interface JobContent { - content: string; - content_type: boolean; -} - -export interface JobInfo { - kibana_name: string; - kibana_id: string; - browser_type: string; - created_at: string; - priority: number; - jobtype: string; - created_by: string; - timeout: number; - output: { - content_type: string; - size: number; - warnings: string[]; - }; - process_expiration: string; - completed_at: string; - payload: { - layout: { id: string; dimensions: { width: number; height: number } }; - objects: Array<{ relativeUrl: string }>; - type: string; - title: string; - forceNow: string; - browserTimezone: string; - }; - meta: { - layout: string; - objectType: string; - }; - max_attempts: number; - started_at: string; - attempts: number; - status: string; -} - -class JobQueueClient { - public list = (page = 0, jobIds: string[] = []): Promise => { - const query = { page } as any; - if (jobIds.length > 0) { - // Only getting the first 10, to prevent URL overflows - query.ids = jobIds.slice(0, 10).join(','); - } - - return core.http.get(`${API_LIST_URL}/list`, { - query, - asSystemRequest: true, - }); - }; - - public total(): Promise { - return core.http.get(`${API_LIST_URL}/count`, { - asSystemRequest: true, - }); - } - - public getContent(jobId: string): Promise { - return core.http.get(`${API_LIST_URL}/output/${jobId}`, { - asSystemRequest: true, - }); - } - - public getInfo(jobId: string): Promise { - return core.http.get(`${API_LIST_URL}/info/${jobId}`, { - asSystemRequest: true, - }); - } -} - -export const jobQueueClient = new JobQueueClient(); diff --git a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts deleted file mode 100644 index d471dc57fc9e1..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { stringify } from 'query-string'; -import { npStart } from 'ui/new_platform'; -// @ts-ignore -import rison from 'rison-node'; -import { add } from './job_completion_notifications'; - -const { core } = npStart; -const API_BASE_URL = '/api/reporting/generate'; - -interface JobParams { - [paramName: string]: any; -} - -export const getReportingJobPath = (exportType: string, jobParams: JobParams) => { - const params = stringify({ jobParams: rison.encode(jobParams) }); - - return `${core.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; -}; - -export const createReportingJob = async (exportType: string, jobParams: any) => { - const jobParamsRison = rison.encode(jobParams); - const resp = await core.http.post(`${API_BASE_URL}/${exportType}`, { - method: 'POST', - body: JSON.stringify({ - jobParams: jobParamsRison, - }), - }); - - add(resp.job.id); - - return resp; -}; diff --git a/x-pack/legacy/plugins/reporting/public/register_feature.ts b/x-pack/legacy/plugins/reporting/public/register_feature.ts deleted file mode 100644 index 4e8d32facfcec..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/register_feature.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; - -const { - plugins: { home }, -} = npSetup; - -home.featureCatalogue.register({ - id: 'reporting', - title: i18n.translate('xpack.reporting.registerFeature.reportingTitle', { - defaultMessage: 'Reporting', - }), - description: i18n.translate('xpack.reporting.registerFeature.reportingDescription', { - defaultMessage: 'Manage your reports generated from Discover, Visualize, and Dashboard.', - }), - icon: 'reportingApp', - path: '/app/kibana#/management/kibana/reporting', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, -}); diff --git a/x-pack/legacy/plugins/reporting/public/views/management/index.js b/x-pack/legacy/plugins/reporting/public/views/management/index.js deleted file mode 100644 index 0ed6fe09ef80a..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './management'; diff --git a/x-pack/legacy/plugins/reporting/public/views/management/jobs.html b/x-pack/legacy/plugins/reporting/public/views/management/jobs.html deleted file mode 100644 index 5471513d64d95..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/jobs.html +++ /dev/null @@ -1,3 +0,0 @@ - -
    -
    \ No newline at end of file diff --git a/x-pack/legacy/plugins/reporting/public/views/management/jobs.js b/x-pack/legacy/plugins/reporting/public/views/management/jobs.js deleted file mode 100644 index 7205fad8cca53..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/jobs.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -import routes from 'ui/routes'; -import template from 'plugins/reporting/views/management/jobs.html'; - -import { ReportListing } from '../../components/report_listing'; -import { i18n } from '@kbn/i18n'; -import { I18nContext } from 'ui/i18n'; -import { MANAGEMENT_BREADCRUMB } from 'ui/management'; - -const REACT_ANCHOR_DOM_ELEMENT_ID = 'reportListingAnchor'; - -routes.when('/management/kibana/reporting', { - template, - k7Breadcrumbs: () => [ - MANAGEMENT_BREADCRUMB, - { - text: i18n.translate('xpack.reporting.breadcrumb', { - defaultMessage: 'Reporting', - }), - }, - ], - controllerAs: 'jobsCtrl', - controller($scope, kbnUrl) { - $scope.$$postDigest(() => { - const node = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID); - if (!node) { - return; - } - - render( - - - , - node - ); - }); - - $scope.$on('$destroy', () => { - const node = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID); - if (node) { - unmountComponentAtNode(node); - } - }); - }, -}); diff --git a/x-pack/legacy/plugins/reporting/public/views/management/management.js b/x-pack/legacy/plugins/reporting/public/views/management/management.js deleted file mode 100644 index 8643e6fa8b8b4..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/management.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { management } from 'ui/management'; -import { i18n } from '@kbn/i18n'; -import routes from 'ui/routes'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -import 'plugins/reporting/views/management/jobs'; - -routes.defaults(/\/management/, { - resolve: { - reportingManagementSection: function() { - const kibanaManagementSection = management.getSection('kibana'); - const showReportingLinks = xpackInfo.get('features.reporting.management.showLinks'); - - kibanaManagementSection.deregister('reporting'); - if (showReportingLinks) { - const enableReportingLinks = xpackInfo.get('features.reporting.management.enableLinks'); - const tooltipMessage = xpackInfo.get('features.reporting.management.message'); - - let url; - let tooltip; - if (enableReportingLinks) { - url = '#/management/kibana/reporting'; - } else { - tooltip = tooltipMessage; - } - - return kibanaManagementSection.register('reporting', { - order: 15, - display: i18n.translate('xpack.reporting.management.reportingTitle', { - defaultMessage: 'Reporting', - }), - url, - tooltip, - }); - } - }, - }, -}); diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index b4d49fd21f230..917e9d7daae40 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -23,22 +23,6 @@ export type Job = EventEmitter & { }; }; -export interface ReportingConfigOptions { - browser: BrowserConfig; - poll: { - jobCompletionNotifier: { - interval: number; - intervalErrorMultiplier: number; - }; - jobsRefresh: { - interval: number; - intervalErrorMultiplier: number; - }; - }; - queue: QueueConfig; - capture: CaptureConfig; -} - export interface NetworkPolicyRule { allow: boolean; protocol: string; diff --git a/x-pack/plugins/reporting/common/poller.ts b/x-pack/plugins/reporting/common/poller.ts new file mode 100644 index 0000000000000..919d7273062a8 --- /dev/null +++ b/x-pack/plugins/reporting/common/poller.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { PollerOptions } from '..'; + +// @TODO Maybe move to observables someday +export class Poller { + private readonly functionToPoll: () => Promise; + private readonly successFunction: (...args: any) => any; + private readonly errorFunction: (error: Error) => any; + private _isRunning: boolean; + private _timeoutId: NodeJS.Timeout | null; + private pollFrequencyInMillis: number; + private trailing: boolean; + private continuePollingOnError: boolean; + private pollFrequencyErrorMultiplier: number; + + constructor(options: PollerOptions) { + this.functionToPoll = options.functionToPoll; // Must return a Promise + this.successFunction = options.successFunction || _.noop; + this.errorFunction = options.errorFunction || _.noop; + this.pollFrequencyInMillis = options.pollFrequencyInMillis; + this.trailing = options.trailing || false; + this.continuePollingOnError = options.continuePollingOnError || false; + this.pollFrequencyErrorMultiplier = options.pollFrequencyErrorMultiplier || 1; + + this._timeoutId = null; + this._isRunning = false; + } + + getPollFrequency() { + return this.pollFrequencyInMillis; + } + + _poll() { + return this.functionToPoll() + .then(this.successFunction) + .then(() => { + if (!this._isRunning) { + return; + } + + this._timeoutId = setTimeout(this._poll.bind(this), this.pollFrequencyInMillis); + }) + .catch(e => { + this.errorFunction(e); + if (!this._isRunning) { + return; + } + + if (this.continuePollingOnError) { + this._timeoutId = setTimeout( + this._poll.bind(this), + this.pollFrequencyInMillis * this.pollFrequencyErrorMultiplier + ); + } else { + this.stop(); + } + }); + } + + start() { + if (this._isRunning) { + return; + } + + this._isRunning = true; + if (this.trailing) { + this._timeoutId = setTimeout(this._poll.bind(this), this.pollFrequencyInMillis); + } else { + this._poll(); + } + } + + stop() { + if (!this._isRunning) { + return; + } + + this._isRunning = false; + + if (this._timeoutId) { + clearTimeout(this._timeoutId); + } + + this._timeoutId = null; + } + + isRunning() { + return this._isRunning; + } +} diff --git a/x-pack/plugins/reporting/constants.ts b/x-pack/plugins/reporting/constants.ts index fe5673a0b74b5..8f47a0a6b2ac1 100644 --- a/x-pack/plugins/reporting/constants.ts +++ b/x-pack/plugins/reporting/constants.ts @@ -14,8 +14,31 @@ export const JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG = { }, }; -export const API_BASE_URL = '/api/reporting/jobs'; +// Routes +export const API_BASE_URL = '/api/reporting'; +export const API_LIST_URL = `${API_BASE_URL}/jobs`; +export const API_BASE_GENERATE = `${API_BASE_URL}/generate`; +export const API_GENERATE_IMMEDIATE = `${API_BASE_URL}/v1/generate/immediate/csv/saved-object`; export const REPORTING_MANAGEMENT_HOME = '/app/kibana#/management/kibana/reporting'; +// Statuses export const JOB_STATUS_FAILED = 'failed'; export const JOB_STATUS_COMPLETED = 'completed'; + +export enum JobStatuses { + PENDING = 'pending', + PROCESSING = 'processing', + COMPLETED = 'completed', + FAILED = 'failed', + CANCELLED = 'cancelled', +} + +// Types +export const PDF_JOB_TYPE = 'printable_pdf'; +export const PNG_JOB_TYPE = 'PNG'; +export const CSV_JOB_TYPE = 'csv'; +export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject'; +export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE, PNG_JOB_TYPE]; + +// Actions +export const CSV_REPORTING_ACTION = 'downloadCsvReport'; diff --git a/x-pack/plugins/reporting/index.d.ts b/x-pack/plugins/reporting/index.d.ts index 9559de4a5bb03..7c1a2ebd7d9de 100644 --- a/x-pack/plugins/reporting/index.d.ts +++ b/x-pack/plugins/reporting/index.d.ts @@ -57,3 +57,19 @@ export type DownloadReportFn = (jobId: JobId) => DownloadLink; type ManagementLink = string; export type ManagementLinkFn = () => ManagementLink; + +export interface PollerOptions { + functionToPoll: () => Promise; + pollFrequencyInMillis: number; + trailing?: boolean; + continuePollingOnError?: boolean; + pollFrequencyErrorMultiplier?: number; + successFunction?: (...args: any) => any; + errorFunction?: (error: Error) => any; +} + +export interface LicenseCheckResults { + enableLinks: boolean; + showLinks: boolean; + message: string; +} diff --git a/x-pack/plugins/reporting/kibana.json b/x-pack/plugins/reporting/kibana.json index 50f552b0d9fb0..a7e2bd288f0b1 100644 --- a/x-pack/plugins/reporting/kibana.json +++ b/x-pack/plugins/reporting/kibana.json @@ -2,7 +2,15 @@ "id": "reporting", "version": "8.0.0", "kibanaVersion": "kibana", - "requiredPlugins": [], + "requiredPlugins": [ + "home", + "management", + "licensing", + "uiActions", + "embeddable", + "share", + "kibanaLegacy" + ], "server": false, "ui": true } diff --git a/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap b/x-pack/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap rename to x-pack/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap b/x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap rename to x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap diff --git a/x-pack/plugins/reporting/public/components/general_error.tsx b/x-pack/plugins/reporting/public/components/general_error.tsx index feb0ea0062ace..bc1ec901cc475 100644 --- a/x-pack/plugins/reporting/public/components/general_error.tsx +++ b/x-pack/plugins/reporting/public/components/general_error.tsx @@ -7,7 +7,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; export const getGeneralErrorToast = (errorText: string, err: Error): ToastInput => ({ diff --git a/x-pack/plugins/reporting/public/components/job_failure.tsx b/x-pack/plugins/reporting/public/components/job_failure.tsx index 7544cbf906458..628ecb56b9c21 100644 --- a/x-pack/plugins/reporting/public/components/job_failure.tsx +++ b/x-pack/plugins/reporting/public/components/job_failure.tsx @@ -8,7 +8,7 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobSummary, ManagementLinkFn } from '../../index.d'; diff --git a/x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts b/x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts new file mode 100644 index 0000000000000..5e9614e27e2fd --- /dev/null +++ b/x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export const mockAPIClient = { + http: jest.fn(), + list: jest.fn(), + total: jest.fn(), + getInfo: jest.fn(), + getContent: jest.fn(), + getReportURL: jest.fn(), + downloadReport: jest.fn(), +}; + +jest.mock('../lib/reporting_api_client', () => mockAPIClient); diff --git a/x-pack/plugins/reporting/public/components/job_success.tsx b/x-pack/plugins/reporting/public/components/job_success.tsx index b538cef030e0d..c2feac382ca7a 100644 --- a/x-pack/plugins/reporting/public/components/job_success.tsx +++ b/x-pack/plugins/reporting/public/components/job_success.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../index.d'; import { ReportLink } from './report_link'; diff --git a/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx b/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx index 7981237c9b781..22f656dbe738c 100644 --- a/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx +++ b/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../index.d'; import { ReportLink } from './report_link'; diff --git a/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx b/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx index caeda6fc01678..1abba8888bb81 100644 --- a/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx +++ b/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../index.d'; import { ReportLink } from './report_link'; diff --git a/x-pack/legacy/plugins/reporting/public/components/report_error_button.tsx b/x-pack/plugins/reporting/public/components/report_error_button.tsx similarity index 92% rename from x-pack/legacy/plugins/reporting/public/components/report_error_button.tsx rename to x-pack/plugins/reporting/public/components/report_error_button.tsx index 3e6fd07847f2c..252dee9c619a9 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_error_button.tsx +++ b/x-pack/plugins/reporting/public/components/report_error_button.tsx @@ -7,11 +7,12 @@ import { EuiButtonIcon, EuiCallOut, EuiPopover } from '@elastic/eui'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component } from 'react'; -import { JobContent, jobQueueClient } from '../lib/job_queue_client'; +import { JobContent, ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { jobId: string; intl: InjectedIntl; + apiClient: ReportingAPIClient; } interface State { @@ -90,7 +91,7 @@ class ReportErrorButtonUi extends Component { private loadError = async () => { this.setState({ isLoading: true }); try { - const reportContent: JobContent = await jobQueueClient.getContent(this.props.jobId); + const reportContent: JobContent = await this.props.apiClient.getContent(this.props.jobId); if (this.mounted) { this.setState({ isLoading: false, error: reportContent.content }); } diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.tsx b/x-pack/plugins/reporting/public/components/report_info_button.test.tsx similarity index 60% rename from x-pack/legacy/plugins/reporting/public/components/report_info_button.test.tsx rename to x-pack/plugins/reporting/public/components/report_info_button.test.tsx index 3b9c2a8485423..2edd59e6de7a3 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.tsx +++ b/x-pack/plugins/reporting/public/components/report_info_button.test.tsx @@ -4,27 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mockJobQueueClient } from './report_info_button.test.mocks'; - import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ReportInfoButton } from './report_info_button'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; -describe('ReportInfoButton', () => { - beforeEach(() => { - mockJobQueueClient.getInfo = jest.fn(() => ({ - payload: { title: 'Test Job' }, - })); - }); +jest.mock('../lib/reporting_api_client'); +const httpSetup = {} as any; +const apiClient = new ReportingAPIClient(httpSetup); + +describe('ReportInfoButton', () => { it('handles button click flyout on click', () => { - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); const input = wrapper.find('[data-test-subj="reportInfoButton"]').hostNodes(); expect(input).toMatchSnapshot(); }); - it('opens flyout with info', () => { - const wrapper = mountWithIntl(); + it('opens flyout with info', async () => { + const wrapper = mountWithIntl(); const input = wrapper.find('[data-test-subj="reportInfoButton"]').hostNodes(); input.simulate('click'); @@ -32,17 +30,17 @@ describe('ReportInfoButton', () => { const flyout = wrapper.find('[data-test-subj="reportInfoFlyout"]'); expect(flyout).toMatchSnapshot(); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledTimes(1); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledWith('abc-456'); + expect(apiClient.getInfo).toHaveBeenCalledTimes(1); + expect(apiClient.getInfo).toHaveBeenCalledWith('abc-456'); }); it('opens flyout with fetch error info', () => { // simulate fetch failure - mockJobQueueClient.getInfo = jest.fn(() => { + apiClient.getInfo = jest.fn(() => { throw new Error('Could not fetch the job info'); }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); const input = wrapper.find('[data-test-subj="reportInfoButton"]').hostNodes(); input.simulate('click'); @@ -50,7 +48,7 @@ describe('ReportInfoButton', () => { const flyout = wrapper.find('[data-test-subj="reportInfoFlyout"]'); expect(flyout).toMatchSnapshot(); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledTimes(1); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledWith('abc-789'); + expect(apiClient.getInfo).toHaveBeenCalledTimes(1); + expect(apiClient.getInfo).toHaveBeenCalledWith('abc-789'); }); }); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx b/x-pack/plugins/reporting/public/components/report_info_button.tsx similarity index 95% rename from x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx rename to x-pack/plugins/reporting/public/components/report_info_button.tsx index 7f5d070948e50..81a5af3b87957 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx +++ b/x-pack/plugins/reporting/public/components/report_info_button.tsx @@ -17,11 +17,12 @@ import { } from '@elastic/eui'; import React, { Component, Fragment } from 'react'; import { get } from 'lodash'; -import { USES_HEADLESS_JOB_TYPES } from '../../common/constants'; -import { JobInfo, jobQueueClient } from '../lib/job_queue_client'; +import { USES_HEADLESS_JOB_TYPES } from '../../constants'; +import { JobInfo, ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { jobId: string; + apiClient: ReportingAPIClient; } interface State { @@ -171,6 +172,7 @@ export class ReportInfoButton extends Component { description: USES_HEADLESS_JOB_TYPES.includes(jobType) ? info.browser_type || UNKNOWN : NA, }, ]; + if (warnings) { jobInfoStatus.push({ title: 'Errors', @@ -261,17 +263,17 @@ export class ReportInfoButton extends Component { private loadInfo = async () => { this.setState({ isLoading: true }); try { - const info: JobInfo = await jobQueueClient.getInfo(this.props.jobId); + const info: JobInfo = await this.props.apiClient.getInfo(this.props.jobId); if (this.mounted) { this.setState({ isLoading: false, info }); } - } catch (kfetchError) { + } catch (err) { if (this.mounted) { this.setState({ isLoading: false, calloutTitle: 'Unable to fetch report info', info: null, - error: kfetchError, + error: err, }); } } diff --git a/x-pack/plugins/reporting/public/components/report_listing.test.tsx b/x-pack/plugins/reporting/public/components/report_listing.test.tsx new file mode 100644 index 0000000000000..5cf894580eae0 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/report_listing.test.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { ReportListing } from './report_listing'; +import { Observable } from 'rxjs'; +import { ILicense } from '../../../licensing/public'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; + +const reportingAPIClient = { + list: () => + Promise.resolve([ + { _index: '.reporting-2019.08.18', _id: 'jzoik8dh1q2i89fb5f19znm6', _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.869Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik7tn1q2i89fb5f60e5ve', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.155Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik5tb1q2i89fb5fckchny', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:21.551Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik5a11q2i89fb5f130t2m', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:20.857Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik3ka1q2i89fb5fdx93g7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:18.634Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik2vt1q2i89fb5ffw723n', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:17.753Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik1851q2i89fb5fdge6e7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1080, height: 720 } }, type: 'canvas workpad', title: 'My Canvas Workpad - Dark', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:15.605Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoijyre1q2i89fb5fa7xzvi', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:12.410Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoijv5h1q2i89fb5ffklnhx', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:07.733Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jznhgk7r1bx789fb5f6hxok7', _score: null, _source: { kibana_name: 'spicy.local', browser_type: 'chromium', created_at: '2019-08-23T02:15:47.799Z', jobtype: 'printable_pdf', created_by: 'elastic', kibana_id: 'ca75e26c-2b7d-464f-aef0-babb67c735a0', output: { content_type: 'application/pdf', size: 877114 }, completed_at: '2019-08-23T02:15:57.707Z', payload: { type: 'dashboard (legacy)', title: 'tests-panels', }, max_attempts: 3, started_at: '2019-08-23T02:15:48.794Z', attempts: 1, status: 'completed', }, }, // prettier-ignore + ]), + total: () => Promise.resolve(18), +} as any; + +const validCheck = { + check: () => ({ + state: 'VALID', + message: '', + }), +}; + +const license$ = { + subscribe: (handler: any) => { + return handler(validCheck); + }, +} as Observable; + +const toasts = { + addDanger: jest.fn(), +} as any; + +describe('ReportListing', () => { + it('Report job listing with some items', () => { + const wrapper = mountWithIntl( + + ); + wrapper.update(); + const input = wrapper.find('[data-test-subj="reportJobListing"]'); + expect(input).toMatchSnapshot(); + }); + + it('subscribes to license changes, and unsubscribes on dismount', () => { + const unsubscribeMock = jest.fn(); + const subMock = { + subscribe: jest.fn().mockReturnValue({ + unsubscribe: unsubscribeMock, + }), + } as any; + + const wrapper = mountWithIntl( + } + redirect={jest.fn()} + toasts={toasts} + /> + ); + wrapper.update(); + expect(subMock.subscribe).toHaveBeenCalled(); + expect(unsubscribeMock).not.toHaveBeenCalled(); + wrapper.unmount(); + expect(unsubscribeMock).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx b/x-pack/plugins/reporting/public/components/report_listing.tsx similarity index 85% rename from x-pack/legacy/plugins/reporting/public/components/report_listing.tsx rename to x-pack/plugins/reporting/public/components/report_listing.tsx index 54061eda94dce..13fca019f3284 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx +++ b/x-pack/plugins/reporting/public/components/report_listing.tsx @@ -6,11 +6,11 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import moment from 'moment'; import { get } from 'lodash'; +import moment from 'moment'; import React, { Component } from 'react'; -import chrome from 'ui/chrome'; -import { toastNotifications } from 'ui/notify'; +import { Subscription } from 'rxjs'; + import { EuiBasicTable, EuiButtonIcon, @@ -21,10 +21,13 @@ import { EuiTitle, EuiToolTip, } from '@elastic/eui'; -import { Poller } from '../../../../common/poller'; -import { JobStatuses } from '../constants/job_statuses'; -import { downloadReport } from '../lib/download_report'; -import { jobQueueClient, JobQueueEntry } from '../lib/job_queue_client'; + +import { ToastsSetup, ApplicationStart } from 'src/core/public'; +import { LicensingPluginSetup, ILicense } from '../../../licensing/public'; +import { Poller } from '../../common/poller'; +import { JobStatuses, JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG } from '../../constants'; +import { ReportingAPIClient, JobQueueEntry } from '../lib/reporting_api_client'; +import { checkLicense } from '../lib/license_check'; import { ReportErrorButton } from './report_error_button'; import { ReportInfoButton } from './report_info_button'; @@ -47,11 +50,11 @@ interface Job { } interface Props { - badLicenseMessage: string; - showLinks: boolean; - enableLinks: boolean; - redirect: (url: string) => void; intl: InjectedIntl; + apiClient: ReportingAPIClient; + license$: LicensingPluginSetup['license$']; + redirect: ApplicationStart['navigateToApp']; + toasts: ToastsSetup; } interface State { @@ -59,6 +62,9 @@ interface State { total: number; jobs: Job[]; isLoading: boolean; + showLinks: boolean; + enableLinks: boolean; + badLicenseMessage: string; } const jobStatusLabelsMap = new Map([ @@ -95,9 +101,10 @@ const jobStatusLabelsMap = new Map([ ]); class ReportListingUi extends Component { + private isInitialJobsFetch: boolean; + private licenseSubscription?: Subscription; private mounted?: boolean; private poller?: any; - private isInitialJobsFetch: boolean; constructor(props: Props) { super(props); @@ -107,6 +114,9 @@ class ReportListingUi extends Component { total: 0, jobs: [], isLoading: false, + showLinks: false, + enableLinks: false, + badLicenseMessage: '', }; this.isInitialJobsFetch = true; @@ -137,23 +147,41 @@ class ReportListingUi extends Component { public componentWillUnmount() { this.mounted = false; this.poller.stop(); + + if (this.licenseSubscription) { + this.licenseSubscription.unsubscribe(); + } } public componentDidMount() { this.mounted = true; - const { jobsRefresh } = chrome.getInjected('reportingPollConfig'); this.poller = new Poller({ functionToPoll: () => { return this.fetchJobs(); }, - pollFrequencyInMillis: jobsRefresh.interval, + pollFrequencyInMillis: + JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG.jobCompletionNotifier.interval, trailing: false, continuePollingOnError: true, - pollFrequencyErrorMultiplier: jobsRefresh.intervalErrorMultiplier, + pollFrequencyErrorMultiplier: + JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG.jobCompletionNotifier.intervalErrorMultiplier, }); this.poller.start(); + this.licenseSubscription = this.props.license$.subscribe(this.licenseHandler); } + private licenseHandler = (license: ILicense) => { + const { enableLinks, showLinks, message: badLicenseMessage } = checkLicense( + license.check('reporting', 'basic') + ); + + this.setState({ + enableLinks, + showLinks, + badLicenseMessage, + }); + }; + private renderTable() { const { intl } = this.props; @@ -275,7 +303,6 @@ class ReportListingUi extends Component {
    {statusLabel} {maxSizeReached} - {warnings}
    ); }, @@ -340,7 +367,7 @@ class ReportListingUi extends Component { const { intl } = this.props; const button = ( downloadReport(record.id)} + onClick={() => this.props.apiClient.downloadReport(record.id)} iconType="importAction" aria-label={intl.formatMessage({ id: 'xpack.reporting.listing.table.downloadReportAriaLabel', @@ -386,11 +413,11 @@ class ReportListingUi extends Component { return; } - return ; + return ; }; private renderInfoButton = (record: Job) => { - return ; + return ; }; private onTableChange = ({ page }: { page: { index: number } }) => { @@ -407,19 +434,19 @@ class ReportListingUi extends Component { let jobs: JobQueueEntry[]; let total: number; try { - jobs = await jobQueueClient.list(this.state.page); - total = await jobQueueClient.total(); + jobs = await this.props.apiClient.list(this.state.page); + total = await this.props.apiClient.total(); this.isInitialJobsFetch = false; - } catch (kfetchError) { + } catch (fetchError) { if (!this.licenseAllowsToShowThisPage()) { - toastNotifications.addDanger(this.props.badLicenseMessage); - this.props.redirect('/management'); + this.props.toasts.addDanger(this.state.badLicenseMessage); + this.props.redirect('kibana#/management'); return; } - if (kfetchError.res.status !== 401 && kfetchError.res.status !== 403) { - toastNotifications.addDanger( - kfetchError.res.statusText || + if (fetchError.message === 'Failed to fetch') { + this.props.toasts.addDanger( + fetchError.message || this.props.intl.formatMessage({ id: 'xpack.reporting.listing.table.requestFailedErrorMessage', defaultMessage: 'Request failed', @@ -463,7 +490,7 @@ class ReportListingUi extends Component { }; private licenseAllowsToShowThisPage = () => { - return this.props.showLinks && this.props.enableLinks; + return this.state.showLinks && this.state.enableLinks; }; private formatDate(timestamp: string) { diff --git a/x-pack/legacy/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx similarity index 88% rename from x-pack/legacy/plugins/reporting/public/components/reporting_panel_content.tsx rename to x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index aaf4021302a97..cf107fd712876 100644 --- a/x-pack/legacy/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -7,12 +7,14 @@ import { EuiButton, EuiCopy, EuiForm, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; -import { toastNotifications } from 'ui/notify'; import url from 'url'; -import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; -import * as reportingClient from '../lib/reporting_client'; +import { ToastsSetup } from 'src/core/public'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; interface Props { + apiClient: ReportingAPIClient; + toasts: ToastsSetup; reportType: string; layoutId: string | undefined; objectId?: string; @@ -31,23 +33,6 @@ interface State { } class ReportingPanelContentUi extends Component { - public static getDerivedStateFromProps(nextProps: Props, prevState: State) { - if (nextProps.layoutId !== prevState.layoutId) { - return { - ...prevState, - absoluteUrl: ReportingPanelContentUi.getAbsoluteReportGenerationUrl(nextProps), - }; - } - return prevState; - } - - private static getAbsoluteReportGenerationUrl = (props: Props) => { - const relativePath = reportingClient.getReportingJobPath( - props.reportType, - props.getJobParams() - ); - return url.resolve(window.location.href, relativePath); - }; private mounted?: boolean; constructor(props: Props) { @@ -55,11 +40,29 @@ class ReportingPanelContentUi extends Component { this.state = { isStale: false, - absoluteUrl: '', + absoluteUrl: this.getAbsoluteReportGenerationUrl(props), layoutId: '', }; } + private getAbsoluteReportGenerationUrl = (props: Props) => { + const relativePath = this.props.apiClient.getReportingJobPath( + props.reportType, + props.getJobParams() + ); + return url.resolve(window.location.href, relativePath); + }; + + public componentDidUpdate(prevProps: Props, prevState: State) { + if (this.props.layoutId && this.props.layoutId !== prevState.layoutId) { + this.setState({ + ...prevState, + absoluteUrl: this.getAbsoluteReportGenerationUrl(this.props), + layoutId: this.props.layoutId, + }); + } + } + public componentWillUnmount() { window.removeEventListener('hashchange', this.markAsStale); window.removeEventListener('resize', this.setAbsoluteReportGenerationUrl); @@ -188,17 +191,17 @@ class ReportingPanelContentUi extends Component { if (!this.mounted) { return; } - const absoluteUrl = ReportingPanelContentUi.getAbsoluteReportGenerationUrl(this.props); + const absoluteUrl = this.getAbsoluteReportGenerationUrl(this.props); this.setState({ absoluteUrl }); }; private createReportingJob = () => { const { intl } = this.props; - return reportingClient + return this.props.apiClient .createReportingJob(this.props.reportType, this.props.getJobParams()) .then(() => { - toastNotifications.addSuccess({ + this.props.toasts.addSuccess({ title: intl.formatMessage( { id: 'xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle', @@ -218,7 +221,7 @@ class ReportingPanelContentUi extends Component { }) .catch((error: any) => { if (error.message === 'not exportable') { - return toastNotifications.addWarning({ + return this.props.toasts.addWarning({ title: intl.formatMessage( { id: 'xpack.reporting.panelContent.whatCanBeExportedWarningTitle', @@ -248,7 +251,7 @@ class ReportingPanelContentUi extends Component { /> ); - toastNotifications.addDanger({ + this.props.toasts.addDanger({ title: intl.formatMessage({ id: 'xpack.reporting.panelContent.notification.reportingErrorTitle', defaultMessage: 'Reporting error', diff --git a/x-pack/legacy/plugins/reporting/public/components/screen_capture_panel_content.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx similarity index 91% rename from x-pack/legacy/plugins/reporting/public/components/screen_capture_panel_content.tsx rename to x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx index cf6bb94876361..9fb74a70ff1ac 100644 --- a/x-pack/legacy/plugins/reporting/public/components/screen_capture_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx @@ -7,9 +7,13 @@ import { EuiSpacer, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; +import { ToastsSetup } from 'src/core/public'; import { ReportingPanelContent } from './reporting_panel_content'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { + apiClient: ReportingAPIClient; + toasts: ToastsSetup; reportType: string; objectId?: string; objectType: string; @@ -38,6 +42,8 @@ export class ScreenCapturePanelContent extends Component { public render() { return ( + "path":
    => { - return http.fetch(`${API_BASE_URL}/list`, { - query: { page: 0, ids: jobIds.join(',') }, - method: 'GET', - }); - }; - - public getContent(http: HttpService, jobId: JobId): Promise { - return http - .fetch(`${API_BASE_URL}/output/${jobId}`, { - method: 'GET', - }) - .then((data: JobContent) => data.content); - } -} - -export const jobQueueClient = new JobQueue(); diff --git a/x-pack/plugins/reporting/public/lib/license_check.test.ts b/x-pack/plugins/reporting/public/lib/license_check.test.ts new file mode 100644 index 0000000000000..24e14969d2c81 --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/license_check.test.ts @@ -0,0 +1,50 @@ +/* + * 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 { checkLicense } from './license_check'; +import { LicenseCheck } from '../../../licensing/public'; + +describe('License check', () => { + it('enables and shows links when licenses are good mkay', () => { + expect(checkLicense({ state: 'VALID' } as LicenseCheck)).toEqual({ + enableLinks: true, + showLinks: true, + message: '', + }); + }); + + it('disables and shows links when licenses are not valid', () => { + expect(checkLicense({ state: 'INVALID' } as LicenseCheck)).toEqual({ + enableLinks: false, + showLinks: false, + message: 'Your license does not support Reporting. Please upgrade your license.', + }); + }); + + it('shows links, but disables them, on expired licenses', () => { + expect(checkLicense({ state: 'EXPIRED' } as LicenseCheck)).toEqual({ + enableLinks: false, + showLinks: true, + message: 'You cannot use Reporting because your license has expired.', + }); + }); + + it('shows links, but disables them, when license checks are unavailable', () => { + expect(checkLicense({ state: 'UNAVAILABLE' } as LicenseCheck)).toEqual({ + enableLinks: false, + showLinks: true, + message: + 'You cannot use Reporting because license information is not available at this time.', + }); + }); + + it('shows and enables links if state is not known', () => { + expect(checkLicense({ state: 'PONYFOO' } as any)).toEqual({ + enableLinks: true, + showLinks: true, + message: '', + }); + }); +}); diff --git a/x-pack/plugins/reporting/public/lib/license_check.ts b/x-pack/plugins/reporting/public/lib/license_check.ts new file mode 100644 index 0000000000000..ca803fb38ef2a --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/license_check.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { LicenseCheckResults } from '../..'; +import { LICENSE_CHECK_STATE, LicenseCheck } from '../../../licensing/public'; + +export const checkLicense = (checkResults: LicenseCheck): LicenseCheckResults => { + switch (checkResults.state) { + case LICENSE_CHECK_STATE.Valid: { + return { + showLinks: true, + enableLinks: true, + message: '', + }; + } + + case LICENSE_CHECK_STATE.Invalid: { + return { + showLinks: false, + enableLinks: false, + message: 'Your license does not support Reporting. Please upgrade your license.', + }; + } + + case LICENSE_CHECK_STATE.Unavailable: { + return { + showLinks: true, + enableLinks: false, + message: + 'You cannot use Reporting because license information is not available at this time.', + }; + } + + case LICENSE_CHECK_STATE.Expired: { + return { + showLinks: true, + enableLinks: false, + message: 'You cannot use Reporting because your license has expired.', + }; + } + + default: { + return { + showLinks: true, + enableLinks: true, + message: '', + }; + } + } +}; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts new file mode 100644 index 0000000000000..ddfeb144d3cd7 --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts @@ -0,0 +1,152 @@ +/* + * 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 { stringify } from 'query-string'; +import rison from 'rison-node'; + +import { HttpSetup } from 'src/core/public'; +import { add } from './job_completion_notifications'; +import { + API_LIST_URL, + API_BASE_URL, + API_BASE_GENERATE, + REPORTING_MANAGEMENT_HOME, +} from '../../constants'; +import { JobId, SourceJob } from '../..'; + +export interface JobQueueEntry { + _id: string; + _source: any; +} + +export interface JobContent { + content: string; + content_type: boolean; +} + +export interface JobInfo { + kibana_name: string; + kibana_id: string; + browser_type: string; + created_at: string; + priority: number; + jobtype: string; + created_by: string; + timeout: number; + output: { + content_type: string; + size: number; + warnings: string[]; + }; + process_expiration: string; + completed_at: string; + payload: { + layout: { id: string; dimensions: { width: number; height: number } }; + objects: Array<{ relativeUrl: string }>; + type: string; + title: string; + forceNow: string; + browserTimezone: string; + }; + meta: { + layout: string; + objectType: string; + }; + max_attempts: number; + started_at: string; + attempts: number; + status: string; +} + +interface JobParams { + [paramName: string]: any; +} + +export class ReportingAPIClient { + private http: HttpSetup; + + constructor(http: HttpSetup) { + this.http = http; + } + + public getReportURL(jobId: string) { + const apiBaseUrl = this.http.basePath.prepend(API_LIST_URL); + const downloadLink = `${apiBaseUrl}/download/${jobId}`; + + return downloadLink; + } + + public downloadReport(jobId: string) { + const location = this.getReportURL(jobId); + + window.open(location); + } + + public list = (page = 0, jobIds: string[] = []): Promise => { + const query = { page } as any; + if (jobIds.length > 0) { + // Only getting the first 10, to prevent URL overflows + query.ids = jobIds.slice(0, 10).join(','); + } + + return this.http.get(`${API_LIST_URL}/list`, { + query, + asSystemRequest: true, + }); + }; + + public total(): Promise { + return this.http.get(`${API_LIST_URL}/count`, { + asSystemRequest: true, + }); + } + + public getContent(jobId: string): Promise { + return this.http.get(`${API_LIST_URL}/output/${jobId}`, { + asSystemRequest: true, + }); + } + + public getInfo(jobId: string): Promise { + return this.http.get(`${API_LIST_URL}/info/${jobId}`, { + asSystemRequest: true, + }); + } + + public findForJobIds = (jobIds: JobId[]): Promise => { + return this.http.fetch(`${API_LIST_URL}/list`, { + query: { page: 0, ids: jobIds.join(',') }, + method: 'GET', + }); + }; + + public getReportingJobPath = (exportType: string, jobParams: JobParams) => { + const params = stringify({ jobParams: rison.encode(jobParams) }); + + return `${this.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; + }; + + public createReportingJob = async (exportType: string, jobParams: any) => { + const jobParamsRison = rison.encode(jobParams); + const resp = await this.http.post(`${API_BASE_GENERATE}/${exportType}`, { + method: 'POST', + body: JSON.stringify({ + jobParams: jobParamsRison, + }), + }); + + add(resp.job.id); + + return resp; + }; + + public getManagementLink = () => this.http.basePath.prepend(REPORTING_MANAGEMENT_HOME); + + public getDownloadLink = (jobId: JobId) => + this.http.basePath.prepend(`${API_LIST_URL}/download/${jobId}`); + + public getBasePath = () => this.http.basePath.get(); +} diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index aeba2ca5406b8..3a2c7de9ad0f0 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -5,9 +5,9 @@ */ import sinon, { stub } from 'sinon'; -import { HttpSetup, NotificationsStart } from '../../../../../src/core/public'; -import { SourceJob, JobSummary, HttpService } from '../../index.d'; -import { JobQueue } from './job_queue'; +import { NotificationsStart } from 'src/core/public'; +import { SourceJob, JobSummary } from '../../index.d'; +import { ReportingAPIClient } from './reporting_api_client'; import { ReportingNotifierStreamHandler } from './stream_handler'; Object.defineProperty(window, 'sessionStorage', { @@ -44,20 +44,16 @@ const mockJobsFound = [ }, ]; -const jobQueueClientMock: JobQueue = { - findForJobIds: async (http: HttpService, jobIds: string[]) => { +const jobQueueClientMock: ReportingAPIClient = { + findForJobIds: async (jobIds: string[]) => { return mockJobsFound as SourceJob[]; }, - getContent: () => { - return Promise.resolve('this is the completed report data'); + getContent: (): Promise => { + return Promise.resolve({ content: 'this is the completed report data' }); }, -}; - -const httpMock: HttpService = ({ - basePath: { - prepend: stub(), - }, -} as unknown) as HttpSetup; + getManagementLink: () => '/#management', + getDownloadLink: () => '/reporting/download/job-123', +} as any; const mockShowDanger = stub(); const mockShowSuccess = stub(); @@ -76,17 +72,13 @@ describe('stream handler', () => { }); it('constructs', () => { - const sh = new ReportingNotifierStreamHandler(httpMock, notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); expect(sh).not.toBe(null); }); describe('findChangedStatusJobs', () => { it('finds no changed status jobs from empty', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); const findJobs = sh.findChangedStatusJobs([]); findJobs.subscribe(data => { expect(data).toEqual({ completed: [], failed: [] }); @@ -95,11 +87,7 @@ describe('stream handler', () => { }); it('finds changed status jobs', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); const findJobs = sh.findChangedStatusJobs([ 'job-source-mock1', 'job-source-mock2', @@ -115,11 +103,7 @@ describe('stream handler', () => { describe('showNotifications', () => { it('show success', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { @@ -140,11 +124,7 @@ describe('stream handler', () => { }); it('show max length warning', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { @@ -166,11 +146,7 @@ describe('stream handler', () => { }); it('show csv formulas warning', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { @@ -192,11 +168,7 @@ describe('stream handler', () => { }); it('show failed job toast', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [], failed: [ @@ -217,11 +189,7 @@ describe('stream handler', () => { }); it('show multiple toast', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index e58e90d3de8ef..1aae30f6fdfb0 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -11,19 +11,16 @@ import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUS_COMPLETED, JOB_STATUS_FAILED, - API_BASE_URL, - REPORTING_MANAGEMENT_HOME, } from '../../constants'; + import { JobId, JobSummary, JobStatusBuckets, - HttpService, NotificationsService, SourceJob, - DownloadReportFn, - ManagementLinkFn, } from '../../index.d'; + import { getSuccessToast, getFailureToast, @@ -31,7 +28,7 @@ import { getWarningMaxSizeToast, getGeneralErrorToast, } from '../components'; -import { jobQueueClient as defaultJobQueueClient } from './job_queue'; +import { ReportingAPIClient } from './reporting_api_client'; function updateStored(jobIds: JobId[]): void { sessionStorage.setItem(JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JSON.stringify(jobIds)); @@ -49,21 +46,7 @@ function summarizeJob(src: SourceJob): JobSummary { } export class ReportingNotifierStreamHandler { - private getManagementLink: ManagementLinkFn; - private getDownloadLink: DownloadReportFn; - - constructor( - private http: HttpService, - private notifications: NotificationsService, - private jobQueueClient = defaultJobQueueClient - ) { - this.getManagementLink = () => { - return http.basePath.prepend(REPORTING_MANAGEMENT_HOME); - }; - this.getDownloadLink = (jobId: JobId) => { - return http.basePath.prepend(`${API_BASE_URL}/download/${jobId}`); - }; - } + constructor(private notifications: NotificationsService, private apiClient: ReportingAPIClient) {} /* * Use Kibana Toast API to show our messages @@ -77,23 +60,33 @@ export class ReportingNotifierStreamHandler { for (const job of completedJobs) { if (job.csvContainsFormulas) { this.notifications.toasts.addWarning( - getWarningFormulasToast(job, this.getManagementLink, this.getDownloadLink) + getWarningFormulasToast( + job, + this.apiClient.getManagementLink, + this.apiClient.getDownloadLink + ) ); } else if (job.maxSizeReached) { this.notifications.toasts.addWarning( - getWarningMaxSizeToast(job, this.getManagementLink, this.getDownloadLink) + getWarningMaxSizeToast( + job, + this.apiClient.getManagementLink, + this.apiClient.getDownloadLink + ) ); } else { this.notifications.toasts.addSuccess( - getSuccessToast(job, this.getManagementLink, this.getDownloadLink) + getSuccessToast(job, this.apiClient.getManagementLink, this.apiClient.getDownloadLink) ); } } // no download link available for (const job of failedJobs) { - const content = await this.jobQueueClient.getContent(this.http, job.id); - this.notifications.toasts.addDanger(getFailureToast(content, job, this.getManagementLink)); + const { content } = await this.apiClient.getContent(job.id); + this.notifications.toasts.addDanger( + getFailureToast(content, job, this.apiClient.getManagementLink) + ); } return { completed: completedJobs, failed: failedJobs }; }; @@ -106,7 +99,7 @@ export class ReportingNotifierStreamHandler { * session storage) but have non-processing job status on the server */ public findChangedStatusJobs(storedJobs: JobId[]): Rx.Observable { - return Rx.from(this.jobQueueClient.findForJobIds(this.http, storedJobs)).pipe( + return Rx.from(this.apiClient.findForJobIds(storedJobs)).pipe( map((jobs: SourceJob[]) => { const completedJobs: JobSummary[] = []; const failedJobs: JobSummary[] = []; diff --git a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx similarity index 72% rename from x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx rename to x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index 4c9cd890ee75b..282ee75815fa5 100644 --- a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -6,24 +6,21 @@ import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; import moment from 'moment-timezone'; - -import { npSetup, npStart } from 'ui/new_platform'; -import { - ActionByType, - IncompatibleActionError, -} from '../../../../../../src/plugins/ui_actions/public'; +import { CoreSetup } from 'src/core/public'; +import { Action, IncompatibleActionError } from '../../../../../src/plugins/ui_actions/public'; +import { LicensingPluginSetup } from '../../../licensing/public'; +import { checkLicense } from '../lib/license_check'; import { ViewMode, IEmbeddable, - CONTEXT_MENU_TRIGGER, -} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; -import { ISearchEmbeddable } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/types'; +} from '../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../common/constants'; +// @TODO: These import paths will need to be updated once discovery moves to non-legacy dir +import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; +import { ISearchEmbeddable } from '../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/types'; -const { core } = npStart; +import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../constants'; function isSavedSearchEmbeddable( embeddable: IEmbeddable | ISearchEmbeddable @@ -31,23 +28,26 @@ function isSavedSearchEmbeddable( return embeddable.type === SEARCH_EMBEDDABLE_TYPE; } -export interface CSVActionContext { +interface ActionContext { embeddable: ISearchEmbeddable; } -declare module '../../../../../../src/plugins/ui_actions/public' { - export interface ActionContextMapping { - [CSV_REPORTING_ACTION]: CSVActionContext; - } -} - -class GetCsvReportPanelAction implements ActionByType { +export class GetCsvReportPanelAction implements Action { private isDownloading: boolean; - public readonly type = CSV_REPORTING_ACTION; + public readonly type = ''; public readonly id = CSV_REPORTING_ACTION; + private canDownloadCSV: boolean = false; + private core: CoreSetup; - constructor() { + constructor(core: CoreSetup, license$: LicensingPluginSetup['license$']) { this.isDownloading = false; + this.core = core; + + license$.subscribe(license => { + const results = license.check('reporting', 'basic'); + const { showLinks } = checkLicense(results); + this.canDownloadCSV = showLinks; + }); } public getIconType() { @@ -73,13 +73,17 @@ class GetCsvReportPanelAction implements ActionByType { + public isCompatible = async (context: ActionContext) => { + if (!this.canDownloadCSV) { + return false; + } + const { embeddable } = context; return embeddable.getInput().viewMode !== ViewMode.EDIT && embeddable.type === 'search'; }; - public execute = async (context: CSVActionContext) => { + public execute = async (context: ActionContext) => { const { embeddable } = context; if (!isSavedSearchEmbeddable(embeddable)) { @@ -97,7 +101,7 @@ class GetCsvReportPanelAction implements ActionByType { this.isDownloading = false; @@ -160,7 +164,7 @@ class GetCsvReportPanelAction implements ActionByType { private readonly stop$ = new Rx.ReplaySubject(1); - // FIXME: License checking: only active, non-expired licenses allowed - // Depends on https://github.com/elastic/kibana/pull/44922 + private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', { + defaultMessage: 'Reporting', + }); + + private readonly breadcrumbText = i18n.translate('xpack.reporting.breadcrumb', { + defaultMessage: 'Reporting', + }); + constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup) {} + public setup( + core: CoreSetup, + { + home, + management, + licensing, + uiActions, + share, + }: { + home: HomePublicPluginSetup; + management: ManagementSetup; + licensing: LicensingPluginSetup; + uiActions: UiActionsSetup; + share: SharePluginSetup; + } + ) { + const { + http, + notifications: { toasts }, + getStartServices, + uiSettings, + } = core; + const { license$ } = licensing; + + const apiClient = new ReportingAPIClient(http); + const action = new GetCsvReportPanelAction(core, license$); + + home.featureCatalogue.register({ + id: 'reporting', + title: i18n.translate('xpack.reporting.registerFeature.reportingTitle', { + defaultMessage: 'Reporting', + }), + description: i18n.translate('xpack.reporting.registerFeature.reportingDescription', { + defaultMessage: 'Manage your reports generated from Discover, Visualize, and Dashboard.', + }), + icon: 'reportingApp', + path: '/app/kibana#/management/kibana/reporting', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, + }); + + management.sections.getSection('kibana')!.registerApp({ + id: 'reporting', + title: this.title, + order: 15, + mount: async params => { + const [start] = await getStartServices(); + params.setBreadcrumbs([{ text: this.breadcrumbText }]); + ReactDOM.render( + + + , + params.element + ); + + return () => { + ReactDOM.unmountComponentAtNode(params.element); + }; + }, + }); + + uiActions.registerAction(action); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, action); + + share.register(csvReportingProvider({ apiClient, toasts, license$ })); + share.register( + reportingPDFPNGProvider({ + apiClient, + toasts, + license$, + uiSettings, + }) + ); + } // FIXME: only perform these actions for authenticated routes // Depends on https://github.com/elastic/kibana/pull/39477 public start(core: CoreStart) { const { http, notifications } = core; - const streamHandler = new StreamHandler(http, notifications); + const apiClient = new ReportingAPIClient(http); + const streamHandler = new StreamHandler(notifications, apiClient); Rx.timer(0, JOBS_REFRESH_INTERVAL) .pipe( diff --git a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx similarity index 61% rename from x-pack/legacy/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx rename to x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index 3c9d1d7262587..9d4f475cde79a 100644 --- a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -5,14 +5,34 @@ */ import { i18n } from '@kbn/i18n'; -// @ts-ignore: implicit any for JS file -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import React from 'react'; -import { npSetup } from 'ui/new_platform'; + +import { ToastsSetup } from 'src/core/public'; import { ReportingPanelContent } from '../components/reporting_panel_content'; -import { ShareContext } from '../../../../../../src/plugins/share/public'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { checkLicense } from '../lib/license_check'; +import { LicensingPluginSetup } from '../../../licensing/public'; +import { ShareContext } from '../../../../../src/plugins/share/public'; + +interface ReportingProvider { + apiClient: ReportingAPIClient; + toasts: ToastsSetup; + license$: LicensingPluginSetup['license$']; +} + +export const csvReportingProvider = ({ apiClient, toasts, license$ }: ReportingProvider) => { + let toolTipContent = ''; + let disabled = true; + let hasCSVReporting = false; + + license$.subscribe(license => { + const { enableLinks, showLinks, message } = checkLicense(license.check('reporting', 'basic')); + + toolTipContent = message; + hasCSVReporting = showLinks; + disabled = !enableLinks; + }); -function reportingProvider() { const getShareMenuItems = ({ objectType, objectId, @@ -32,7 +52,8 @@ function reportingProvider() { }; const shareActions = []; - if (xpackInfo.get('features.reporting.csv.showLinks', false)) { + + if (hasCSVReporting) { const panelTitle = i18n.translate('xpack.reporting.shareContextMenu.csvReportsButtonLabel', { defaultMessage: 'CSV Reports', }); @@ -41,8 +62,8 @@ function reportingProvider() { shareMenuItem: { name: panelTitle, icon: 'document', - toolTipContent: xpackInfo.get('features.reporting.csv.message'), - disabled: !xpackInfo.get('features.reporting.csv.enableLinks', false) ? true : false, + toolTipContent, + disabled, ['data-test-subj']: 'csvReportMenuItem', sortOrder: 1, }, @@ -51,6 +72,8 @@ function reportingProvider() { title: panelTitle, content: ( { + let toolTipContent = ''; + let disabled = true; + let hasPDFPNGReporting = false; -const { core } = npSetup; + license$.subscribe(license => { + const { enableLinks, showLinks, message } = checkLicense(license.check('reporting', 'gold')); + + toolTipContent = message; + hasPDFPNGReporting = showLinks; + disabled = !enableLinks; + }); -async function reportingProvider() { const getShareMenuItems = ({ objectType, objectId, @@ -29,24 +52,22 @@ async function reportingProvider() { } // Dashboard only mode does not currently support reporting // https://github.com/elastic/kibana/issues/18286 - if ( - objectType === 'dashboard' && - npStart.plugins.kibanaLegacy.dashboardConfig.getHideWriteControls() - ) { + // @TODO For NP + if (objectType === 'dashboard' && false) { return []; } const getReportingJobParams = () => { // Replace hashes with original RISON values. const relativeUrl = shareableUrl.replace( - window.location.origin + core.http.basePath.get(), + window.location.origin + apiClient.getBasePath(), '' ); const browserTimezone = - core.uiSettings.get('dateFormat:tz') === 'Browser' + uiSettings.get('dateFormat:tz') === 'Browser' ? moment.tz.guess() - : core.uiSettings.get('dateFormat:tz'); + : uiSettings.get('dateFormat:tz'); return { ...sharingData, @@ -59,14 +80,14 @@ async function reportingProvider() { const getPngJobParams = () => { // Replace hashes with original RISON values. const relativeUrl = shareableUrl.replace( - window.location.origin + core.http.basePath.get(), + window.location.origin + apiClient.getBasePath(), '' ); const browserTimezone = - core.uiSettings.get('dateFormat:tz') === 'Browser' + uiSettings.get('dateFormat:tz') === 'Browser' ? moment.tz.guess() - : core.uiSettings.get('dateFormat:tz'); + : uiSettings.get('dateFormat:tz'); return { ...sharingData, @@ -77,60 +98,69 @@ async function reportingProvider() { }; const shareActions = []; - if (xpackInfo.get('features.reporting.printablePdf.showLinks', false)) { - const panelTitle = i18n.translate('xpack.reporting.shareContextMenu.pdfReportsButtonLabel', { - defaultMessage: 'PDF Reports', - }); + + if (hasPDFPNGReporting) { + const pngPanelTitle = i18n.translate( + 'xpack.reporting.shareContextMenu.pngReportsButtonLabel', + { + defaultMessage: 'PNG Reports', + } + ); + + const pdfPanelTitle = i18n.translate( + 'xpack.reporting.shareContextMenu.pdfReportsButtonLabel', + { + defaultMessage: 'PDF Reports', + } + ); shareActions.push({ shareMenuItem: { - name: panelTitle, + name: pngPanelTitle, icon: 'document', - toolTipContent: xpackInfo.get('features.reporting.printablePdf.message'), - disabled: !xpackInfo.get('features.reporting.printablePdf.enableLinks', false) - ? true - : false, - ['data-test-subj']: 'pdfReportMenuItem', + toolTipContent, + disabled, + ['data-test-subj']: 'pngReportMenuItem', sortOrder: 10, }, panel: { - id: 'reportingPdfPanel', - title: panelTitle, + id: 'reportingPngPanel', + title: pngPanelTitle, content: ( ), }, }); - } - - if (xpackInfo.get('features.reporting.png.showLinks', false)) { - const panelTitle = 'PNG Reports'; shareActions.push({ shareMenuItem: { - name: panelTitle, + name: pdfPanelTitle, icon: 'document', - toolTipContent: xpackInfo.get('features.reporting.png.message'), - disabled: !xpackInfo.get('features.reporting.png.enableLinks', false) ? true : false, - ['data-test-subj']: 'pngReportMenuItem', + toolTipContent, + disabled, + ['data-test-subj']: 'pdfReportMenuItem', sortOrder: 10, }, panel: { - id: 'reportingPngPanel', - title: panelTitle, + id: 'reportingPdfPanel', + title: pdfPanelTitle, content: ( @@ -146,8 +176,4 @@ async function reportingProvider() { id: 'screenCaptureReports', getShareMenuItems, }; -} - -(async () => { - npSetup.plugins.share.register(await reportingProvider()); -})(); +}; From 89f9260da2eb0d54404a93380ea4eef6c4b16597 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Tue, 17 Mar 2020 10:41:23 -0700 Subject: [PATCH 55/76] FTR configurable test users (#52431) * initial implementation of configurable test users * user superuser by default to match master * referenced the configs in reporting and api integration * setting the minimum number of default roles * looking for x-pack tests with users and roles * add testUserService in dashboard mode tests * running only ciGroup7 * uncommenting - addign visualization * re-enabling all CI groups to run on CI * reinstating Jenkinsfile * disable Test user for OIDC config * improved logging and added Roles for OSS tests to get better info on the runs. * disable test_user for auth tests * don't fetch enabledPlugins when testuser disabled * fix es-lint * running oss tests with x-pack enabled * [revertme] build default dist for oss tests * updating NOTICE.txt file as it complained in the kibana intake tests * changed to pick OSS builds * trying a license change to trial * switch back to xpack builds * created a new sample data role and used it in homepage tests * revert test/scripts/jenkins_ci_group.sh * only refresh browser and wait for chrome if we are already on Kibana page * fix large_string test to use minimum set of roles and privileges * fix for date nanos custom timestamp with a configured role * changes to the files with addition of new roles for the test_user * reverting to OSS changes and few additions to the time_zone test to run as a test_user * changes to security * changes to the x-pack test to use elastic superuser * fix for chart_types test * fixes to area chart , input control test * fix for dashboard filtering test and a new config role * changes to handle the x-pack tests * additional role for date nanos mixed * added the logstash role to the accessibility tests * removed telemetry setting * docs+few changes to the tests * removed Page navigation * removed pageNavigation which was unused * test/accessibility/apps/management.ts * update management.ts * aria label, and other changes * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * reverted * unloading of logstash data, fixing aria label * aria-label * added the required role * fix for tsvb chart * fix for sample data test reverted home_page pageobject file * changes to sample data test and visualize index file to incorporate OSS changes * changes to describe() and some more changes to incorporate in settings_page * re-adding the after() * removed unwanted roles * replaced kibana_user with kibana_admin * added the check of deprecated kibana_user * testing with kibana_admin role * fix for discover test * incorporated the review comments * incorporated the review comments * incorporate review comments and added restoreDefaults() * removed describe.only * reverted the OSS logic change I had here- pulled into seperate PR * incorporated the review comments * incorporated review changes * adding hidden=true to find hidden kibanaChrome * change field.test.tsx to be same as that of master branch Co-authored-by: spalger Co-authored-by: Elastic Machine --- .../development-functional-tests.asciidoc | 19 ++ .../lib/config/schema.ts | 15 ++ test/common/services/security/role.ts | 2 - test/common/services/security/security.ts | 12 +- test/common/services/security/test_user.ts | 92 ++++++++++ test/functional/apps/context/_date_nanos.js | 7 +- .../context/_date_nanos_custom_timestamp.js | 11 +- .../apps/dashboard/dashboard_filtering.js | 6 + test/functional/apps/dashboard/index.js | 1 + test/functional/apps/dashboard/time_zones.js | 2 - test/functional/apps/discover/_date_nanos.js | 7 +- .../apps/discover/_date_nanos_mixed.js | 7 +- .../apps/discover/_discover_histogram.js | 7 + .../functional/apps/discover/_large_string.js | 3 + .../apps/getting_started/_shakespeare.js | 6 + test/functional/apps/home/_sample_data.ts | 6 + .../apps/management/_handle_alias.js | 3 + .../apps/management/_test_huge_fields.js | 3 + test/functional/apps/visualize/_area_chart.js | 11 +- .../apps/visualize/_experimental_vis.js | 2 +- .../apps/visualize/_linked_saved_searches.ts | 2 +- .../apps/visualize/_markdown_vis.js | 2 +- test/functional/apps/visualize/_tsvb_chart.ts | 4 + test/functional/apps/visualize/_vega_chart.js | 2 +- .../input_control_vis/input_control_range.ts | 3 + test/functional/config.js | 167 ++++++++++++++++++ test/functional/page_objects/common_page.ts | 17 +- test/functional/services/browser.ts | 2 + test/functional/services/test_subjects.ts | 2 + x-pack/test/api_integration/config.js | 1 + .../dashboard_mode/dashboard_view_mode.js | 21 ++- x-pack/test/functional/config.js | 19 ++ x-pack/test/oidc_api_integration/config.ts | 1 + x-pack/test/pki_api_integration/config.ts | 1 + .../reporting/configs/chromium_functional.js | 1 + x-pack/test/saml_api_integration/config.ts | 1 + x-pack/test/token_api_integration/config.js | 1 + 37 files changed, 433 insertions(+), 36 deletions(-) create mode 100644 test/common/services/security/test_user.ts diff --git a/docs/developer/core/development-functional-tests.asciidoc b/docs/developer/core/development-functional-tests.asciidoc index dcb3d65b8b83f..51b5273851ce7 100644 --- a/docs/developer/core/development-functional-tests.asciidoc +++ b/docs/developer/core/development-functional-tests.asciidoc @@ -178,6 +178,25 @@ To run tests on Firefox locally, use `config.firefox.js`: node scripts/functional_test_runner --config test/functional/config.firefox.js ----------- +[float] +===== Using the test_user service + +Tests should run at the positive security boundry condition, meaning that they should be run with the mimimum privileges required (and documented) and not as the superuser. + This prevents the type of regression where additional privleges accidentally become required to perform the same action. + +The functional UI tests now default to logging in with a user named `test_user` and the roles of this user can be changed dynamically without logging in and out. + +In order to achieve this a new service was introduced called `createTestUserService` (see `test/common/services/security/test_user.ts`). The purpose of this test user service is to create roles defined in the test config files and setRoles() or restoreDefaults(). + +An example of how to set the role like how its defined below: + +`await security.testUser.setRoles(['kibana_user', 'kibana_date_nanos']);` + +Here we are setting the `test_user` to have the `kibana_user` role and also role access to a specific data index (`kibana_date_nanos`). + +Tests should normally setRoles() in the before() and restoreDefaults() in the after(). + + [float] ===== Anatomy of a test file diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 28e8396d0beba..66f17ab579ec3 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -255,5 +255,20 @@ export const schema = Joi.object() fixedHeaderHeight: Joi.number().default(50), }) .default(), + + // settings for the security service if there is no defaultRole defined, then default to superuser role. + security: Joi.object() + .keys({ + roles: Joi.object().default(), + defaultRoles: Joi.array() + .items(Joi.string()) + .when('$primary', { + is: true, + then: Joi.array().min(1), + }) + .default(['superuser']), + disableTestUser: Joi.boolean(), + }) + .default(), }) .default(); diff --git a/test/common/services/security/role.ts b/test/common/services/security/role.ts index 0e7572882f80d..dfc6ff9b164e5 100644 --- a/test/common/services/security/role.ts +++ b/test/common/services/security/role.ts @@ -43,7 +43,6 @@ export class Role { `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(data)}` ); } - this.log.debug(`created role ${name}`); } public async delete(name: string) { @@ -56,6 +55,5 @@ export class Role { )}` ); } - this.log.debug(`deleted role ${name}`); } } diff --git a/test/common/services/security/security.ts b/test/common/services/security/security.ts index 4eebb7b6697e0..6ad0933a2a5a2 100644 --- a/test/common/services/security/security.ts +++ b/test/common/services/security/security.ts @@ -23,15 +23,21 @@ import { Role } from './role'; import { User } from './user'; import { RoleMappings } from './role_mappings'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { createTestUserService } from './test_user'; -export function SecurityServiceProvider({ getService }: FtrProviderContext) { +export async function SecurityServiceProvider(context: FtrProviderContext) { + const { getService } = context; const log = getService('log'); const config = getService('config'); const url = formatUrl(config.get('servers.kibana')); + const role = new Role(url, log); + const user = new User(url, log); + const testUser = await createTestUserService(role, user, context); return new (class SecurityService { - role = new Role(url, log); roleMappings = new RoleMappings(url, log); - user = new User(url, log); + testUser = testUser; + role = role; + user = user; })(); } diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts new file mode 100644 index 0000000000000..7f01c64d291a5 --- /dev/null +++ b/test/common/services/security/test_user.ts @@ -0,0 +1,92 @@ +/* + * 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 { Role } from './role'; +import { User } from './user'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { Browser } from '../../../functional/services/browser'; +import { TestSubjects } from '../../../functional/services/test_subjects'; + +export async function createTestUserService( + role: Role, + user: User, + { getService, hasService }: FtrProviderContext +) { + const log = getService('log'); + const config = getService('config'); + // @ts-ignore browser service is not normally available in common. + const browser: Browser | void = hasService('browser') && getService('browser'); + const testSubjects: TestSubjects | void = + // @ts-ignore testSubject service is not normally available in common. + hasService('testSubjects') && getService('testSubjects'); + const kibanaServer = getService('kibanaServer'); + + const enabledPlugins = config.get('security.disableTestUser') + ? [] + : await kibanaServer.plugins.getEnabledIds(); + const isEnabled = () => { + return enabledPlugins.includes('security') && !config.get('security.disableTestUser'); + }; + if (isEnabled()) { + log.debug('===============creating roles and users==============='); + for (const [name, definition] of Object.entries(config.get('security.roles'))) { + // create the defined roles (need to map array to create roles) + await role.create(name, definition); + } + try { + // delete the test_user if present (will it error if the user doesn't exist?) + await user.delete('test_user'); + } catch (exception) { + log.debug('no test user to delete'); + } + + // create test_user with username and pwd + log.debug(`default roles = ${config.get('security.defaultRoles')}`); + await user.create('test_user', { + password: 'changeme', + roles: config.get('security.defaultRoles'), + full_name: 'test user', + }); + } + + return new (class TestUser { + async restoreDefaults() { + if (isEnabled()) { + await this.setRoles(config.get('security.defaultRoles')); + } + } + + async setRoles(roles: string[]) { + if (isEnabled()) { + log.debug(`set roles = ${roles}`); + await user.create('test_user', { + password: 'changeme', + roles, + full_name: 'test user', + }); + + if (browser && testSubjects) { + if (await testSubjects.exists('kibanaChrome', { allowHidden: true })) { + await browser.refresh(); + await testSubjects.find('kibanaChrome', config.get('timeouts.find') * 10); + } + } + } + } + })(); +} diff --git a/test/functional/apps/context/_date_nanos.js b/test/functional/apps/context/_date_nanos.js index d4acdb0b4d5c0..bd132e3745caa 100644 --- a/test/functional/apps/context/_date_nanos.js +++ b/test/functional/apps/context/_date_nanos.js @@ -26,11 +26,13 @@ const TEST_STEP_SIZE = 3; export default function({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const docTable = getService('docTable'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'context', 'timePicker', 'discover']); const esArchiver = getService('esArchiver'); describe('context view for date_nanos', () => { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos']); await esArchiver.loadIfNeeded('date_nanos'); await kibanaServer.uiSettings.replace({ defaultIndex: TEST_INDEX_PATTERN }); await kibanaServer.uiSettings.update({ @@ -39,8 +41,9 @@ export default function({ getService, getPageObjects }) { }); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos'); + after(async function unloadMakelogs() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('date_nanos'); }); it('displays predessors - anchor - successors in right order ', async function() { diff --git a/test/functional/apps/context/_date_nanos_custom_timestamp.js b/test/functional/apps/context/_date_nanos_custom_timestamp.js index 046cca0aba8c6..7834b29931a65 100644 --- a/test/functional/apps/context/_date_nanos_custom_timestamp.js +++ b/test/functional/apps/context/_date_nanos_custom_timestamp.js @@ -26,12 +26,14 @@ const TEST_STEP_SIZE = 3; export default function({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const docTable = getService('docTable'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'context', 'timePicker', 'discover']); const esArchiver = getService('esArchiver'); // skipped due to a recent change in ES that caused search_after queries with data containing // custom timestamp formats like in the testdata to fail describe.skip('context view for date_nanos with custom timestamp', () => { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos_custom']); await esArchiver.loadIfNeeded('date_nanos_custom'); await kibanaServer.uiSettings.replace({ defaultIndex: TEST_INDEX_PATTERN }); await kibanaServer.uiSettings.update({ @@ -40,10 +42,6 @@ export default function({ getService, getPageObjects }) { }); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos_custom'); - }); - it('displays predessors - anchor - successors in right order ', async function() { await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, '1'); const actualRowsText = await docTable.getRowsText(); @@ -54,5 +52,10 @@ export default function({ getService, getPageObjects }) { ]; expect(actualRowsText).to.eql(expectedRowsText); }); + + after(async function() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('date_nanos_custom'); + }); }); } diff --git a/test/functional/apps/dashboard/dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js index ec8a48ca74911..f388993dcaf7d 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.js +++ b/test/functional/apps/dashboard/dashboard_filtering.js @@ -33,6 +33,7 @@ export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const dashboardPanelActions = getService('dashboardPanelActions'); const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'timePicker']); @@ -41,6 +42,7 @@ export default function({ getService, getPageObjects }) { before(async () => { await esArchiver.load('dashboard/current/kibana'); + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -49,6 +51,10 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + describe('adding a filter that excludes all data', () => { before(async () => { await PageObjects.dashboard.clickNewDashboard(); diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index 13e8631445393..5e96a55b19014 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -23,6 +23,7 @@ export default function({ getService, loadTestFile }) { async function loadCurrentData() { await browser.setWindowSize(1300, 900); + await esArchiver.unload('logstash_functional'); await esArchiver.loadIfNeeded('dashboard/current/data'); } diff --git a/test/functional/apps/dashboard/time_zones.js b/test/functional/apps/dashboard/time_zones.js index f374d6526fcf1..b7698a7d6ac4b 100644 --- a/test/functional/apps/dashboard/time_zones.js +++ b/test/functional/apps/dashboard/time_zones.js @@ -22,7 +22,6 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const pieChart = getService('pieChart'); - const browser = getService('browser'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['dashboard', 'timePicker', 'settings', 'common']); @@ -48,7 +47,6 @@ export default function({ getService, getPageObjects }) { after(async () => { await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'UTC' }); - await browser.refresh(); }); it('Exported dashboard adjusts EST time to UTC', async () => { diff --git a/test/functional/apps/discover/_date_nanos.js b/test/functional/apps/discover/_date_nanos.js index 9b06b9ac84cfd..99a37cc18feaa 100644 --- a/test/functional/apps/discover/_date_nanos.js +++ b/test/functional/apps/discover/_date_nanos.js @@ -23,6 +23,7 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const fromTime = 'Sep 22, 2019 @ 20:31:44.000'; const toTime = 'Sep 23, 2019 @ 03:31:44.000'; @@ -30,12 +31,14 @@ export default function({ getService, getPageObjects }) { before(async function() { await esArchiver.loadIfNeeded('date_nanos'); await kibanaServer.uiSettings.replace({ defaultIndex: 'date-nanos' }); + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos']); await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos'); + after(async function unloadMakelogs() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('date_nanos'); }); it('should show a timestamp with nanoseconds in the first result row', async function() { diff --git a/test/functional/apps/discover/_date_nanos_mixed.js b/test/functional/apps/discover/_date_nanos_mixed.js index 0bb6848db4d10..b88ae87601cc5 100644 --- a/test/functional/apps/discover/_date_nanos_mixed.js +++ b/test/functional/apps/discover/_date_nanos_mixed.js @@ -23,6 +23,7 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const fromTime = 'Jan 1, 2019 @ 00:00:00.000'; const toTime = 'Jan 1, 2019 @ 23:59:59.999'; @@ -30,12 +31,14 @@ export default function({ getService, getPageObjects }) { before(async function() { await esArchiver.loadIfNeeded('date_nanos_mixed'); await kibanaServer.uiSettings.replace({ defaultIndex: 'timestamp-*' }); + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos_mixed']); await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos_mixed'); + after(async () => { + await security.testUser.restoreDefaults(); + esArchiver.unload('date_nanos_mixed'); }); it('shows a list of records of indices with date & date_nanos fields in the right order', async function() { diff --git a/test/functional/apps/discover/_discover_histogram.js b/test/functional/apps/discover/_discover_histogram.js index 9310838666256..f815c505a8c27 100644 --- a/test/functional/apps/discover/_discover_histogram.js +++ b/test/functional/apps/discover/_discover_histogram.js @@ -25,6 +25,7 @@ export default function({ getService, getPageObjects }) { const browser = getService('browser'); const elasticChart = getService('elasticChart'); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const PageObjects = getPageObjects(['settings', 'common', 'discover', 'header', 'timePicker']); const defaultSettings = { defaultIndex: 'long-window-logstash-*', @@ -35,6 +36,11 @@ export default function({ getService, getPageObjects }) { before(async function() { log.debug('load kibana index with default index pattern'); await PageObjects.common.navigateToApp('home'); + await security.testUser.setRoles([ + 'kibana_admin', + 'test_logstash_reader', + 'long_window_logstash', + ]); await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('long_window_logstash'); await esArchiver.load('visualize'); @@ -56,6 +62,7 @@ export default function({ getService, getPageObjects }) { await esArchiver.unload('long_window_logstash'); await esArchiver.unload('visualize'); await esArchiver.unload('discover'); + await security.testUser.restoreDefaults(); }); it('should visualize monthly data with different day intervals', async () => { diff --git a/test/functional/apps/discover/_large_string.js b/test/functional/apps/discover/_large_string.js index a5052b2403074..5e9048e2bc481 100644 --- a/test/functional/apps/discover/_large_string.js +++ b/test/functional/apps/discover/_large_string.js @@ -25,10 +25,12 @@ export default function({ getService, getPageObjects }) { const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); const queryBar = getService('queryBar'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover']); describe('test large strings', function() { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'kibana_large_strings']); await esArchiver.load('empty_kibana'); await esArchiver.loadIfNeeded('hamlet'); await kibanaServer.uiSettings.replace({ defaultIndex: 'testlargestring' }); @@ -77,6 +79,7 @@ export default function({ getService, getPageObjects }) { }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('hamlet'); }); }); diff --git a/test/functional/apps/getting_started/_shakespeare.js b/test/functional/apps/getting_started/_shakespeare.js index 5af1676cf423f..ded4eca908410 100644 --- a/test/functional/apps/getting_started/_shakespeare.js +++ b/test/functional/apps/getting_started/_shakespeare.js @@ -23,6 +23,7 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const esArchiver = getService('esArchiver'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects([ 'console', 'common', @@ -46,11 +47,16 @@ export default function({ getService, getPageObjects }) { 'Load empty_kibana and Shakespeare Getting Started data\n' + 'https://www.elastic.co/guide/en/kibana/current/tutorial-load-dataset.html' ); + await security.testUser.setRoles(['kibana_admin', 'test_shakespeare_reader']); await esArchiver.load('empty_kibana', { skipExisting: true }); log.debug('Load shakespeare data'); await esArchiver.loadIfNeeded('getting_started/shakespeare'); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should create shakespeare index pattern', async function() { log.debug('Create shakespeare index pattern'); await PageObjects.settings.createIndexPattern('shakes', null); diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index 8bc528e045566..5812b9b96e42a 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -25,6 +25,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const find = getService('find'); const log = getService('log'); + const security = getService('security'); const pieChart = getService('pieChart'); const renderable = getService('renderable'); const dashboardExpect = getService('dashboardExpect'); @@ -34,10 +35,15 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { this.tags('smoke'); before(async () => { + await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); await PageObjects.common.navigateToUrl('home', 'tutorial_directory/sampleData'); await PageObjects.header.waitUntilLoadingHasFinished(); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should display registered flights sample data sets', async () => { await retry.try(async () => { const exists = await PageObjects.home.doesSampleDataSetExist('flights'); diff --git a/test/functional/apps/management/_handle_alias.js b/test/functional/apps/management/_handle_alias.js index 55f6b56d9f0d1..4ef02f6c9e873 100644 --- a/test/functional/apps/management/_handle_alias.js +++ b/test/functional/apps/management/_handle_alias.js @@ -23,11 +23,13 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const es = getService('legacyEs'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'timePicker']); // FLAKY: https://github.com/elastic/kibana/issues/59717 describe.skip('Index patterns on aliases', function() { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'test_alias_reader']); await esArchiver.loadIfNeeded('alias'); await esArchiver.load('empty_kibana'); await es.indices.updateAliases({ @@ -84,6 +86,7 @@ export default function({ getService, getPageObjects }) { }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('alias'); }); }); diff --git a/test/functional/apps/management/_test_huge_fields.js b/test/functional/apps/management/_test_huge_fields.js index 643cbcbe89482..bc280e51ae048 100644 --- a/test/functional/apps/management/_test_huge_fields.js +++ b/test/functional/apps/management/_test_huge_fields.js @@ -21,6 +21,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings']); describe('test large number of fields', function() { @@ -28,6 +29,7 @@ export default function({ getService, getPageObjects }) { const EXPECTED_FIELD_COUNT = '10006'; before(async function() { + await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader']); await esArchiver.loadIfNeeded('large_fields'); await PageObjects.settings.createIndexPattern('testhuge', 'date'); }); @@ -38,6 +40,7 @@ export default function({ getService, getPageObjects }) { }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('large_fields'); }); }); diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index 101b2d4f547dd..bf836cfe778b4 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -24,6 +24,7 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const browser = getService('browser'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects([ 'common', 'visualize', @@ -58,7 +59,14 @@ export default function({ getService, getPageObjects }) { return PageObjects.visEditor.clickGo(); }; - before(initAreaChart); + before(async function() { + await security.testUser.setRoles([ + 'kibana_admin', + 'long_window_logstash', + 'test_logstash_reader', + ]); + await initAreaChart(); + }); it('should save and load with special characters', async function() { const vizNamewithSpecialChars = vizName1 + '/?&=%'; @@ -284,6 +292,7 @@ export default function({ getService, getPageObjects }) { .pop() .replace('embed=true', ''); await PageObjects.common.navigateToUrl('visualize', embedUrl); + await security.testUser.restoreDefaults(); }); }); diff --git a/test/functional/apps/visualize/_experimental_vis.js b/test/functional/apps/visualize/_experimental_vis.js index 2ce15cf913eff..c45a95abab86e 100644 --- a/test/functional/apps/visualize/_experimental_vis.js +++ b/test/functional/apps/visualize/_experimental_vis.js @@ -23,7 +23,7 @@ export default ({ getService, getPageObjects }) => { const log = getService('log'); const PageObjects = getPageObjects(['visualize']); - describe('visualize app', function() { + describe('experimental visualizations in visualize app ', function() { this.tags('smoke'); describe('experimental visualizations', () => { diff --git a/test/functional/apps/visualize/_linked_saved_searches.ts b/test/functional/apps/visualize/_linked_saved_searches.ts index 345987a803394..ea42f7c671985 100644 --- a/test/functional/apps/visualize/_linked_saved_searches.ts +++ b/test/functional/apps/visualize/_linked_saved_searches.ts @@ -32,7 +32,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { 'visChart', ]); - describe('visualize app', function describeIndexTests() { + describe('saved search visualizations from visualize app', function describeIndexTests() { describe('linked saved searched', () => { const savedSearchName = 'vis_saved_search'; diff --git a/test/functional/apps/visualize/_markdown_vis.js b/test/functional/apps/visualize/_markdown_vis.js index fee6c074af5d2..649fe0a8e4c2e 100644 --- a/test/functional/apps/visualize/_markdown_vis.js +++ b/test/functional/apps/visualize/_markdown_vis.js @@ -29,7 +29,7 @@ export default function({ getPageObjects, getService }) {

    Inline HTML that should not be rendered as html

    `; - describe('visualize app', () => { + describe('markdown app in visualize app', () => { before(async function() { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickMarkdownWidget(); diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index 6a4bed3ba5892..867db66ac81dc 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -25,11 +25,13 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const log = getService('log'); const inspector = getService('inspector'); + const security = getService('security'); const PageObjects = getPageObjects(['visualize', 'visualBuilder', 'timePicker', 'visChart']); describe('visual builder', function describeIndexTests() { this.tags('smoke'); beforeEach(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisualBuilder(); await PageObjects.visualBuilder.checkVisualBuilderIsPresent(); @@ -111,8 +113,10 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualBuilder.resetPage(); await PageObjects.visualBuilder.clickMetric(); await PageObjects.visualBuilder.checkMetricTabIsPresent(); + await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('kibana_sample_data_flights'); }); diff --git a/test/functional/apps/visualize/_vega_chart.js b/test/functional/apps/visualize/_vega_chart.js index df0603c7f95f5..7a19bde341cdd 100644 --- a/test/functional/apps/visualize/_vega_chart.js +++ b/test/functional/apps/visualize/_vega_chart.js @@ -25,7 +25,7 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const log = getService('log'); - describe('visualize app', () => { + describe('vega chart in visualize app', () => { before(async () => { log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); diff --git a/test/functional/apps/visualize/input_control_vis/input_control_range.ts b/test/functional/apps/visualize/input_control_vis/input_control_range.ts index f48ba7b54daf1..8f079f5cc430d 100644 --- a/test/functional/apps/visualize/input_control_vis/input_control_range.ts +++ b/test/functional/apps/visualize/input_control_vis/input_control_range.ts @@ -25,10 +25,12 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const find = getService('find'); + const security = getService('security'); const { visualize, visEditor } = getPageObjects(['visualize', 'visEditor']); describe('input control range', () => { before(async () => { + await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); await esArchiver.load('kibana_sample_data_flights_index_pattern'); await visualize.navigateToNewVisualization(); await visualize.clickInputControlVis(); @@ -63,6 +65,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.loadIfNeeded('long_window_logstash'); await esArchiver.load('visualize'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + await security.testUser.restoreDefaults(); }); }); } diff --git a/test/functional/config.js b/test/functional/config.js index e84b7e0a98a68..11399bd6187c8 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -103,5 +103,172 @@ export default async function({ readConfigFile }) { browser: { type: 'chrome', }, + + security: { + roles: { + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_shakespeare_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['shakes*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_testhuge_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testhuge*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_alias_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['alias*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + //for sample data - can remove but not add sample data.( not ml)- for ml use built in role. + kibana_sample_admin: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['kibana_sample*'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date-nanos'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_custom: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_custom_timestamp'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_mixed: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_mixed', 'timestamp-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_large_strings: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testlargestring'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + long_window_logstash: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['long-window-logstash-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + animals: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['animals-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + }, + defaultRoles: ['test_logstash_reader', 'kibana_admin'], + }, }; } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 60966511c1f99..5ee3726ddb44f 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -105,13 +105,16 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo const wantedLoginPage = appUrl.includes('/login') || appUrl.includes('/logout'); if (loginPage && !wantedLoginPage) { - log.debug( - `Found login page. Logging in with username = ${config.get('servers.kibana.username')}` - ); - await PageObjects.shield.login( - config.get('servers.kibana.username'), - config.get('servers.kibana.password') - ); + log.debug('Found login page'); + if (config.get('security.disableTestUser')) { + await PageObjects.shield.login( + config.get('servers.kibana.username'), + config.get('servers.kibana.password') + ); + } else { + await PageObjects.shield.login('test_user', 'changeme'); + } + await find.byCssSelector( '[data-test-subj="kibanaChrome"] nav:not(.ng-hide)', 6 * defaultFindTimeout diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 02349b4e6cca2..5017947e95d03 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -21,6 +21,7 @@ import { cloneDeep } from 'lodash'; import { Key, Origin } from 'selenium-webdriver'; // @ts-ignore internal modules are not typed import { LegacyActionSequence } from 'selenium-webdriver/lib/actions'; +import { ProvidedType } from '@kbn/test/types/ftr'; import Jimp from 'jimp'; import { modifyUrl } from '../../../src/core/utils'; @@ -28,6 +29,7 @@ import { WebElementWrapper } from './lib/web_element_wrapper'; import { FtrProviderContext } from '../ftr_provider_context'; import { Browsers } from './remote/browsers'; +export type Browser = ProvidedType; export async function BrowserProvider({ getService }: FtrProviderContext) { const log = getService('log'); const { driver, browserType } = await getService('__webdriver__').init(); diff --git a/test/functional/services/test_subjects.ts b/test/functional/services/test_subjects.ts index d47b838c8d72a..e5c2e61c48a0b 100644 --- a/test/functional/services/test_subjects.ts +++ b/test/functional/services/test_subjects.ts @@ -19,6 +19,7 @@ import testSubjSelector from '@kbn/test-subj-selector'; import { map as mapAsync } from 'bluebird'; +import { ProvidedType } from '@kbn/test/types/ftr'; import { WebElementWrapper } from './lib/web_element_wrapper'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -32,6 +33,7 @@ interface SetValueOptions { typeCharByChar?: boolean; } +export type TestSubjects = ProvidedType; export function TestSubjectsProvider({ getService }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); diff --git a/x-pack/test/api_integration/config.js b/x-pack/test/api_integration/config.js index 182a9105a7df8..b62368bf2d608 100644 --- a/x-pack/test/api_integration/config.js +++ b/x-pack/test/api_integration/config.js @@ -15,6 +15,7 @@ export async function getApiIntegrationConfig({ readConfigFile }) { testFiles: [require.resolve('./apis')], services, servers: xPackFunctionalTestsConfig.get('servers'), + security: xPackFunctionalTestsConfig.get('security'), esArchiver: xPackFunctionalTestsConfig.get('esArchiver'), junit: { reportName: 'X-Pack API Integration Tests', diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js index b9c0b0095b96b..78cef80c7ca87 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js @@ -12,6 +12,7 @@ export default function({ getService, getPageObjects }) { const browser = getService('browser'); const log = getService('log'); const pieChart = getService('pieChart'); + const security = getService('security'); const testSubjects = getService('testSubjects'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); @@ -109,13 +110,15 @@ export default function({ getService, getPageObjects }) { await PageObjects.security.clickSaveEditUser(); }); - after('logout', async () => { - await PageObjects.security.forceLogout(); + after(async () => { + await security.testUser.restoreDefaults(); }); it('shows only the dashboard app link', async () => { + await security.testUser.setRoles(['test_logstash_reader', 'kibana_dashboard_only_user']); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.security.forceLogout(); - await PageObjects.security.login('dashuser', '123456'); + await PageObjects.security.login('test_user', 'changeme'); const appLinks = await appsMenu.readLinks(); expect(appLinks).to.have.length(1); @@ -194,8 +197,12 @@ export default function({ getService, getPageObjects }) { }); it('is loaded for a user who is assigned a non-dashboard mode role', async () => { - await PageObjects.security.forceLogout(); - await PageObjects.security.login('mixeduser', '123456'); + await security.testUser.setRoles([ + 'test_logstash_reader', + 'kibana_dashboard_only_user', + 'kibana_admin', + ]); + await PageObjects.header.waitUntilLoadingHasFinished(); if (await appsMenu.linkExists('Management')) { throw new Error('Expected management nav link to not be shown'); @@ -203,8 +210,8 @@ export default function({ getService, getPageObjects }) { }); it('is not loaded for a user who is assigned a superuser role', async () => { - await PageObjects.security.forceLogout(); - await PageObjects.security.login('mysuperuser', '123456'); + await security.testUser.setRoles(['kibana_dashboard_only_user', 'superuser']); + await PageObjects.header.waitUntilLoadingHasFinished(); if (!(await appsMenu.linkExists('Management'))) { throw new Error('Expected management nav link to be shown'); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 09ec403af7424..1586908d8b5ef 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -217,5 +217,24 @@ export default async function({ readConfigFile }) { junit: { reportName: 'Chrome X-Pack UI Functional Tests', }, + security: { + roles: { + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + }, + defaultRoles: ['superuser'], + }, }; } diff --git a/x-pack/test/oidc_api_integration/config.ts b/x-pack/test/oidc_api_integration/config.ts index 724ffd35cc9e3..557dea4d51b0e 100644 --- a/x-pack/test/oidc_api_integration/config.ts +++ b/x-pack/test/oidc_api_integration/config.ts @@ -17,6 +17,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { testFiles: [require.resolve('./apis/authorization_code_flow')], servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, services, junit: { reportName: 'X-Pack OpenID Connect API Integration Tests', diff --git a/x-pack/test/pki_api_integration/config.ts b/x-pack/test/pki_api_integration/config.ts index a445b3d4943b0..21ae1b40efa16 100644 --- a/x-pack/test/pki_api_integration/config.ts +++ b/x-pack/test/pki_api_integration/config.ts @@ -28,6 +28,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { testFiles: [require.resolve('./apis')], servers, + security: { disableTestUser: true }, services, junit: { reportName: 'X-Pack PKI API Integration Tests', diff --git a/x-pack/test/reporting/configs/chromium_functional.js b/x-pack/test/reporting/configs/chromium_functional.js index 81a51f44c1c5f..05c3b6c142946 100644 --- a/x-pack/test/reporting/configs/chromium_functional.js +++ b/x-pack/test/reporting/configs/chromium_functional.js @@ -33,5 +33,6 @@ export default async function({ readConfigFile }) { }, esArchiver: functionalConfig.get('esArchiver'), esTestCluster: functionalConfig.get('esTestCluster'), + security: { disableTestUser: true }, }; } diff --git a/x-pack/test/saml_api_integration/config.ts b/x-pack/test/saml_api_integration/config.ts index 6ea29b0d9e56e..502d34d4c9e5d 100644 --- a/x-pack/test/saml_api_integration/config.ts +++ b/x-pack/test/saml_api_integration/config.ts @@ -19,6 +19,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { testFiles: [require.resolve('./apis')], servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, services: { randomness: kibanaAPITestsConfig.get('services.randomness'), legacyEs: kibanaAPITestsConfig.get('services.legacyEs'), diff --git a/x-pack/test/token_api_integration/config.js b/x-pack/test/token_api_integration/config.js index db5ee6a9a1cbd..84322ff9473f3 100644 --- a/x-pack/test/token_api_integration/config.js +++ b/x-pack/test/token_api_integration/config.js @@ -10,6 +10,7 @@ export default async function({ readConfigFile }) { return { testFiles: [require.resolve('./auth')], servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, services: { legacyEs: xPackAPITestsConfig.get('services.legacyEs'), supertestWithoutAuth: xPackAPITestsConfig.get('services.supertestWithoutAuth'), From f875b7165e3ea0ba925f9a4dd89c39703a5e6bab Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 17 Mar 2020 13:41:46 -0400 Subject: [PATCH 56/76] do not update cell background if is label cell (#60308) --- .../classification_exploration/column_data.tsx | 4 +++- .../classification_exploration/evaluate_panel.tsx | 14 ++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx index 5a08dd159affb..baf7fd32b0f60 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx @@ -15,6 +15,8 @@ interface ColumnData { error_count?: number; } +export const ACTUAL_CLASS_ID = 'actual_class'; + export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { const colData: Partial = []; @@ -67,7 +69,7 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { const columns: any = [ { - id: 'actual_class', + id: ACTUAL_CLASS_ID, display: , }, ]; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index 23dd1ae288d8e..7bf55f4ecf392 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -39,7 +39,7 @@ import { ANALYSIS_CONFIG_TYPE, } from '../../../../common/analytics'; import { LoadingPanel } from '../loading_panel'; -import { getColumnData } from './column_data'; +import { getColumnData, ACTUAL_CLASS_ID } from './column_data'; const defaultPanelWidth = 500; @@ -205,11 +205,13 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) const cellValue = columnsData[rowIndex][columnId]; // eslint-disable-next-line react-hooks/rules-of-hooks useEffect(() => { - setCellProps({ - style: { - backgroundColor: `rgba(0, 179, 164, ${cellValue})`, - }, - }); + if (columnId !== ACTUAL_CLASS_ID) { + setCellProps({ + style: { + backgroundColor: `rgba(0, 179, 164, ${cellValue})`, + }, + }); + } }, [rowIndex, columnId, setCellProps]); return ( {typeof cellValue === 'number' ? `${Math.round(cellValue * 100)}%` : cellValue} From 928454afa4ce0f7136c90b2a8756e0600b55e360 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Tue, 17 Mar 2020 11:51:17 -0700 Subject: [PATCH 57/76] Update the ems-client dependency to 7.7.0 (#59936) * Update the ems-client dependency This PR adds the `appName` and `appVersion` parameters used by ems-client. The `appVersion` parameter replaces the now deprecated `kbnVersion` parameter in ems-client. * Review feedback * Fix borked merge Co-authored-by: Elastic Machine --- package.json | 2 +- src/legacy/ui/public/vis/map/service_settings.js | 3 ++- x-pack/legacy/plugins/maps/public/meta.js | 4 +++- x-pack/legacy/plugins/maps/server/routes.js | 4 +++- x-pack/package.json | 2 +- x-pack/plugins/maps/common/constants.ts | 1 + yarn.lock | 8 ++++---- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 261b3ad74d9b7..13796dcbf2b76 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "@elastic/apm-rum": "^4.6.0", "@elastic/charts": "^17.1.1", "@elastic/datemath": "5.0.2", - "@elastic/ems-client": "7.6.0", + "@elastic/ems-client": "7.7.0", "@elastic/eui": "20.0.2", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", diff --git a/src/legacy/ui/public/vis/map/service_settings.js b/src/legacy/ui/public/vis/map/service_settings.js index 233ee526c439b..9f3d21831e3da 100644 --- a/src/legacy/ui/public/vis/map/service_settings.js +++ b/src/legacy/ui/public/vis/map/service_settings.js @@ -47,7 +47,8 @@ uiModules this._showZoomMessage = true; this._emsClient = new EMSClient({ language: i18n.getLocale(), - kbnVersion: kbnVersion, + appVersion: kbnVersion, + appName: 'kibana', fileApiUrl: mapConfig.emsFileApiUrl, tileApiUrl: mapConfig.emsTileApiUrl, htmlSanitizer: $sanitize, diff --git a/x-pack/legacy/plugins/maps/public/meta.js b/x-pack/legacy/plugins/maps/public/meta.js index c5cfb582976c1..4d81785ff7a0a 100644 --- a/x-pack/legacy/plugins/maps/public/meta.js +++ b/x-pack/legacy/plugins/maps/public/meta.js @@ -9,6 +9,7 @@ import { EMS_FILES_CATALOGUE_PATH, EMS_TILES_CATALOGUE_PATH, EMS_GLYPHS_PATH, + EMS_APP_NAME, } from '../common/constants'; import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; @@ -56,7 +57,8 @@ export function getEMSClient() { emsClient = new EMSClient({ language: i18n.getLocale(), - kbnVersion: chrome.getInjected('kbnPkgVersion'), + appVersion: chrome.getInjected('kbnPkgVersion'), + appName: EMS_APP_NAME, tileApiUrl, fileApiUrl, landingPageUrl: chrome.getInjected('emsLandingPageUrl'), diff --git a/x-pack/legacy/plugins/maps/server/routes.js b/x-pack/legacy/plugins/maps/server/routes.js index 757750dbb0813..7ca659148449f 100644 --- a/x-pack/legacy/plugins/maps/server/routes.js +++ b/x-pack/legacy/plugins/maps/server/routes.js @@ -5,6 +5,7 @@ */ import { + EMS_APP_NAME, EMS_CATALOGUE_PATH, EMS_FILES_API_PATH, EMS_FILES_CATALOGUE_PATH, @@ -38,7 +39,8 @@ export function initRoutes(server, licenseUid) { if (mapConfig.includeElasticMapsService) { emsClient = new EMSClient({ language: i18n.getLocale(), - kbnVersion: serverConfig.get('pkg.version'), + appVersion: serverConfig.get('pkg.version'), + appName: EMS_APP_NAME, fileApiUrl: mapConfig.emsFileApiUrl, tileApiUrl: mapConfig.emsTileApiUrl, landingPageUrl: mapConfig.emsLandingPageUrl, diff --git a/x-pack/package.json b/x-pack/package.json index 3c8aa435c3e43..20b9dcc6cd0bc 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -179,7 +179,7 @@ "@babel/runtime": "^7.5.5", "@elastic/apm-rum-react": "^0.3.2", "@elastic/datemath": "5.0.2", - "@elastic/ems-client": "7.6.0", + "@elastic/ems-client": "7.7.0", "@elastic/eui": "20.0.2", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.1.0", diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index b1483cefa43bc..b608151d26ae8 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -10,6 +10,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; +export const EMS_APP_NAME = 'kibana'; export const EMS_CATALOGUE_PATH = 'ems/catalogue'; export const EMS_FILES_CATALOGUE_PATH = 'ems/files'; diff --git a/yarn.lock b/yarn.lock index 1e5c160a7eb19..ea153e2a9c63f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1916,10 +1916,10 @@ once "^1.4.0" pump "^3.0.0" -"@elastic/ems-client@7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.6.0.tgz#ca548aba1a1f5170a1892de129b537b5248c74be" - integrity sha512-oBtLH24qIgTaMhlSske49FTd35Y0nv+PlZCZaHkBhOH+ScsTDL3LO2lbIcSmcYQod43Ly34v/xwJvFCTxojVEQ== +"@elastic/ems-client@7.7.0": + version "7.7.0" + resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.7.0.tgz#7d36d716dd941f060b9fcdae94f186a9aecc5cc2" + integrity sha512-JatsSyLik/8MTEOEimzEZ3NYjvGL1YzjbGujuSCgaXhPRqzu/wvMLEL8dlVpmYFZ7ALbGNsVdho4Hr8tngsIMw== dependencies: lodash "^4.17.15" node-fetch "^1.7.3" From 9f31565b885f149c4473ba7408d5625c9b538690 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 17 Mar 2020 19:25:01 +0000 Subject: [PATCH 58/76] [ML] Fixing custom urls to dashboards (#60355) * [ML] Fixing custom urls to dashboards * missing file Co-authored-by: Elastic Machine --- x-pack/plugins/ml/kibana.json | 3 +- x-pack/plugins/ml/public/application/app.tsx | 1 + .../components/custom_url_editor/utils.js | 139 +++++++++--------- .../application/util/dependency_cache.ts | 11 ++ x-pack/plugins/ml/public/plugin.ts | 3 + 5 files changed, 85 insertions(+), 72 deletions(-) diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 2f8863fc1d7cf..b6db289f4be6d 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -12,7 +12,8 @@ "features", "home", "licensing", - "usageCollection" + "usageCollection", + "share" ], "optionalPlugins": [ "security", diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 206189c79696f..2597715488399 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -41,6 +41,7 @@ const App: FC = ({ coreStart, deps, appMountParams }) => { application: coreStart.application, http: coreStart.http, security: deps.security, + urlGenerators: deps.share.urlGenerators, }); const mlLicense = setLicenseCache(deps.licensing); diff --git a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js index e9b39058c23a8..a7734289314ae 100644 --- a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js @@ -7,10 +7,9 @@ import { TIME_RANGE_TYPE, URL_TYPE } from './constants'; import rison from 'rison-node'; -// import url from 'url'; +import url from 'url'; -// import { npStart } from 'ui/new_platform'; -// import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../../src/plugins/dashboard_embeddable_container/public'; +import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../../src/plugins/dashboard/public'; import { ML_RESULTS_INDEX_PATTERN } from '../../../../../common/constants/index_patterns'; import { getPartitioningFieldNames } from '../../../../../common/util/job_utils'; @@ -19,7 +18,7 @@ import { replaceTokensInUrlValue, isValidLabel } from '../../../util/custom_url_ import { ml } from '../../../services/ml_api_service'; import { mlJobService } from '../../../services/job_service'; import { escapeForElasticsearchQuery } from '../../../util/string_utils'; -// import { getSavedObjectsClient } from '../../../util/dependency_cache'; +import { getSavedObjectsClient, getGetUrlGenerator } from '../../../util/dependency_cache'; export function getNewCustomUrlDefaults(job, dashboards, indexPatterns) { // Returns the settings object in the format used by the custom URL editor @@ -119,7 +118,7 @@ export function buildCustomUrlFromSettings(settings) { // Dashboard URL returns a Promise as a query is made to obtain the full dashboard config. // So wrap the other two return types in a Promise for consistent return type. if (settings.type === URL_TYPE.KIBANA_DASHBOARD) { - // return buildDashboardUrlFromSettings(settings); + return buildDashboardUrlFromSettings(settings); } else if (settings.type === URL_TYPE.KIBANA_DISCOVER) { return Promise.resolve(buildDiscoverUrlFromSettings(settings)); } else { @@ -132,72 +131,70 @@ export function buildCustomUrlFromSettings(settings) { } } -// function buildDashboardUrlFromSettings(settings) { -// // Get the complete list of attributes for the selected dashboard (query, filters). -// return new Promise((resolve, reject) => { -// const { dashboardId, queryFieldNames } = settings.kibanaSettings; - -// const savedObjectsClient = getSavedObjectsClient(); -// savedObjectsClient -// .get('dashboard', dashboardId) -// .then(response => { -// // Use the filters from the saved dashboard if there are any. -// // let filters = []; - -// // Use the query from the dashboard only if no job entities are selected. -// let query = undefined; - -// const searchSourceJSON = response.get('kibanaSavedObjectMeta.searchSourceJSON'); -// if (searchSourceJSON !== undefined) { -// const searchSourceData = JSON.parse(searchSourceJSON); -// if (searchSourceData.filter !== undefined) { -// filters = searchSourceData.filter; -// } -// query = searchSourceData.query; -// } - -// const queryFromEntityFieldNames = buildAppStateQueryParam(queryFieldNames); -// if (queryFromEntityFieldNames !== undefined) { -// query = queryFromEntityFieldNames; -// } - -// const generator = npStart.plugins.share.urlGenerators.getUrlGenerator( -// DASHBOARD_APP_URL_GENERATOR -// ); - -// return generator -// .createUrl({ -// dashboardId, -// timeRange: { -// from: '$earliest$', -// to: '$latest$', -// mode: 'absolute', -// }, -// filters, -// query, -// // Don't hash the URL since this string will be 1. shown to the user and 2. used as a -// // template to inject the time parameters. -// useHash: false, -// }) -// .then(urlValue => { -// const urlToAdd = { -// url_name: settings.label, -// url_value: decodeURIComponent(`kibana${url.parse(urlValue).hash}`), -// time_range: TIME_RANGE_TYPE.AUTO, -// }; - -// if (settings.timeRange.type === TIME_RANGE_TYPE.INTERVAL) { -// urlToAdd.time_range = settings.timeRange.interval; -// } - -// resolve(urlToAdd); -// }); -// }) -// .catch(resp => { -// reject(resp); -// }); -// }); -// } +function buildDashboardUrlFromSettings(settings) { + // Get the complete list of attributes for the selected dashboard (query, filters). + return new Promise((resolve, reject) => { + const { dashboardId, queryFieldNames } = settings.kibanaSettings; + + const savedObjectsClient = getSavedObjectsClient(); + savedObjectsClient + .get('dashboard', dashboardId) + .then(response => { + // Use the filters from the saved dashboard if there are any. + let filters = []; + + // Use the query from the dashboard only if no job entities are selected. + let query = undefined; + + const searchSourceJSON = response.get('kibanaSavedObjectMeta.searchSourceJSON'); + if (searchSourceJSON !== undefined) { + const searchSourceData = JSON.parse(searchSourceJSON); + if (searchSourceData.filter !== undefined) { + filters = searchSourceData.filter; + } + query = searchSourceData.query; + } + + const queryFromEntityFieldNames = buildAppStateQueryParam(queryFieldNames); + if (queryFromEntityFieldNames !== undefined) { + query = queryFromEntityFieldNames; + } + + const getUrlGenerator = getGetUrlGenerator(); + const generator = getUrlGenerator(DASHBOARD_APP_URL_GENERATOR); + return generator + .createUrl({ + dashboardId, + timeRange: { + from: '$earliest$', + to: '$latest$', + mode: 'absolute', + }, + filters, + query, + // Don't hash the URL since this string will be 1. shown to the user and 2. used as a + // template to inject the time parameters. + useHash: false, + }) + .then(urlValue => { + const urlToAdd = { + url_name: settings.label, + url_value: decodeURIComponent(`kibana${url.parse(urlValue).hash}`), + time_range: TIME_RANGE_TYPE.AUTO, + }; + + if (settings.timeRange.type === TIME_RANGE_TYPE.INTERVAL) { + urlToAdd.time_range = settings.timeRange.interval; + } + + resolve(urlToAdd); + }); + }) + .catch(resp => { + reject(resp); + }); + }); +} function buildDiscoverUrlFromSettings(settings) { const { discoverIndexPatternId, queryFieldNames } = settings.kibanaSettings; diff --git a/x-pack/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/plugins/ml/public/application/util/dependency_cache.ts index 5343c51b525d2..d5605d3bca65f 100644 --- a/x-pack/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/plugins/ml/public/application/util/dependency_cache.ts @@ -21,6 +21,7 @@ import { ChromeRecentlyAccessed, IBasePath, } from 'kibana/public'; +import { SharePluginStart } from 'src/plugins/share/public'; import { SecurityPluginSetup } from '../../../../security/public'; export interface DependencyCache { @@ -40,6 +41,7 @@ export interface DependencyCache { http: HttpStart | null; security: SecurityPluginSetup | null; i18n: I18nStart | null; + urlGenerators: SharePluginStart['urlGenerators'] | null; } const cache: DependencyCache = { @@ -59,6 +61,7 @@ const cache: DependencyCache = { http: null, security: null, i18n: null, + urlGenerators: null, }; export function setDependencyCache(deps: Partial) { @@ -78,6 +81,7 @@ export function setDependencyCache(deps: Partial) { cache.http = deps.http || null; cache.security = deps.security || null; cache.i18n = deps.i18n || null; + cache.urlGenerators = deps.urlGenerators || null; } export function getTimefilter() { @@ -191,6 +195,13 @@ export function getI18n() { return cache.i18n; } +export function getGetUrlGenerator() { + if (cache.urlGenerators === null) { + throw new Error("urlGenerators hasn't been initialized"); + } + return cache.urlGenerators.getUrlGenerator; +} + export function clearCache() { console.log('clearing dependency cache'); // eslint-disable-line no-console Object.keys(cache).forEach(k => { diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index ef85a36494df5..624e877bda49f 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { Plugin, CoreStart, CoreSetup, AppMountParameters } from 'kibana/public'; import { ManagementSetup } from 'src/plugins/management/public'; +import { SharePluginStart } from 'src/plugins/share/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { SecurityPluginSetup } from '../../security/public'; @@ -17,6 +18,7 @@ import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; export interface MlStartDependencies { data: DataPublicPluginStart; + share: SharePluginStart; } export interface MlSetupDependencies { security: SecurityPluginSetup; @@ -41,6 +43,7 @@ export class MlPlugin implements Plugin { coreStart, { data: pluginsStart.data, + share: pluginsStart.share, security: pluginsSetup.security, licensing: pluginsSetup.licensing, management: pluginsSetup.management, From a8e33a87b57b141a925ddc5c8a7bea72c51832a9 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Tue, 17 Mar 2020 13:27:10 -0600 Subject: [PATCH 59/76] Update app arch CODEOWNERS items. (#60396) --- .github/CODEOWNERS | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index accc99170bb70..132a99fb0a151 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,33 +29,32 @@ /src/plugins/dashboard/ @elastic/kibana-app # App Architecture +/examples/url_generators_examples/ @elastic/kibana-app-arch +/examples/url_generators_explorer/ @elastic/kibana-app-arch /packages/kbn-interpreter/ @elastic/kibana-app-arch -/src/legacy/core_plugins/data/ @elastic/kibana-app-arch -/src/legacy/core_plugins/elasticsearch/lib/create_proxy.js @elastic/kibana-app-arch /src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch /src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch -/src/legacy/core_plugins/kibana/server/field_formats/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch -/src/legacy/core_plugins/kibana/server/routes/api/suggestions/ @elastic/kibana-app-arch /src/legacy/core_plugins/visualizations/ @elastic/kibana-app-arch /src/legacy/server/index_patterns/ @elastic/kibana-app-arch +/src/plugins/advanced_settings/ @elastic/kibana-app-arch /src/plugins/bfetch/ @elastic/kibana-app-arch /src/plugins/data/ @elastic/kibana-app-arch /src/plugins/embeddable/ @elastic/kibana-app-arch /src/plugins/expressions/ @elastic/kibana-app-arch /src/plugins/inspector/ @elastic/kibana-app-arch /src/plugins/kibana_react/ @elastic/kibana-app-arch +/src/plugins/kibana_react/public/code_editor @elastic/kibana-canvas /src/plugins/kibana_utils/ @elastic/kibana-app-arch /src/plugins/management/ @elastic/kibana-app-arch /src/plugins/navigation/ @elastic/kibana-app-arch +/src/plugins/share/ @elastic/kibana-app-arch /src/plugins/ui_actions/ @elastic/kibana-app-arch /src/plugins/visualizations/ @elastic/kibana-app-arch -/src/plugins/share/ @elastic/kibana-app-arch -/examples/url_generators_examples/ @elastic/kibana-app-arch -/examples/url_generators_explorer/ @elastic/kibana-app-arch /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch +/x-pack/plugins/data_enhanced/ @elastic/kibana-app-arch /x-pack/plugins/drilldowns/ @elastic/kibana-app-arch # APM From 631d93da50335a1db1519bb7fc5dfb35210da758 Mon Sep 17 00:00:00 2001 From: Karen Metts <35154725+karenzone@users.noreply.github.com> Date: Tue, 17 Mar 2020 16:11:40 -0400 Subject: [PATCH 60/76] Remove link to old settings (#60326) --- docs/settings/monitoring-settings.asciidoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 8586d26e9a07a..6645f49029a51 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -20,9 +20,7 @@ which support the same values as <>. To control how data is collected from your {es} nodes, you configure {ref}/monitoring-settings.html[`xpack.monitoring.collection` settings] in `elasticsearch.yml`. To control how monitoring data is collected -from Logstash, you configure -{logstash-ref}/monitoring-internal-collection.html#monitoring-settings[`xpack.monitoring` settings] -in `logstash.yml`. +from Logstash, configure monitoring settings in `logstash.yml`. For more information, see {ref}/monitor-elasticsearch-cluster.html[Monitor a cluster]. From 2367d749c102faf1f039a5c03d2d16eff43f06f9 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 17 Mar 2020 15:24:59 -0700 Subject: [PATCH 61/76] upgrade react-use (#60427) Co-authored-by: spalger Co-authored-by: Elastic Machine --- package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 42 +++++++++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 13796dcbf2b76..583e99158da72 100644 --- a/package.json +++ b/package.json @@ -239,7 +239,7 @@ "react-resize-detector": "^4.2.0", "react-router-dom": "^5.1.2", "react-sizeme": "^2.3.6", - "react-use": "^13.13.0", + "react-use": "^13.27.0", "reactcss": "1.2.3", "redux": "^4.0.5", "redux-actions": "^2.6.5", diff --git a/x-pack/package.json b/x-pack/package.json index 20b9dcc6cd0bc..6f15b46e28f53 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -317,7 +317,7 @@ "react-sticky": "^6.0.3", "react-syntax-highlighter": "^5.7.0", "react-tiny-virtual-list": "^2.2.0", - "react-use": "^13.13.0", + "react-use": "^13.27.0", "react-vis": "^1.8.1", "react-visibility-sensor": "^5.1.1", "recompose": "^0.26.0", diff --git a/yarn.lock b/yarn.lock index ea153e2a9c63f..a7e29935c7ab5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4678,6 +4678,11 @@ dependencies: "@types/sizzle" "*" +"@types/js-cookie@2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e" + integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg== + "@types/js-search@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@types/js-search/-/js-search-1.4.0.tgz#f2d4afa176a4fc7b17fb46a1593847887fa1fb7b" @@ -5768,10 +5773,10 @@ dependencies: tslib "^1.9.3" -"@xobotyi/scrollbar-width@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.5.0.tgz#488210bff634548040dc22a72f62722a85b134e1" - integrity sha512-BK+HR1D00F2xh7n4+5en8/dMkG13uvIXLmEbsjtc1702b7+VwXkvlBDKoRPJMbkRN5hD7VqWa3nS9fNT8JG3CA== +"@xobotyi/scrollbar-width@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb" + integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -13667,10 +13672,10 @@ fast-safe-stringify@2.x.x, fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0. resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fast-shallow-equal@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-0.1.1.tgz#44d01324d7fd31e00a67bb02b9396e283d526c22" - integrity sha512-XVP6nhaXLYOH6JZCWBcNaeEer9GJ5/8cJWUP+OLmgwWgEkJp5Kpl/fdpJ01zl0mpLxrk7f5J3hIv+GmjTCi7Mg== +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== fast-stream-to-buffer@^1.0.0: version "1.0.0" @@ -18498,6 +18503,11 @@ js-base64@^2.1.8: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.5.tgz#e293cd3c7c82f070d700fc7a1ca0a2e69f101f92" integrity sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ== +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-levenshtein@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.3.tgz#3ef627df48ec8cf24bacf05c0f184ff30ef413c5" @@ -24919,16 +24929,18 @@ react-transition-group@^2.2.1: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" -react-use@^13.13.0: - version "13.13.0" - resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.13.0.tgz#5d133c4d4d8d3f21f6ccf4ccbe54fbcd6fdafb36" - integrity sha512-J3/h5wvL6vXmecAvEnninCC3DviLMRWcQrEnouTliwws1b376DQKEgIFuTXlF8c3SKpXBQJdDDm1RpluokW6ag== +react-use@^13.27.0: + version "13.27.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa" + integrity sha512-2lyTyqJWyvnaP/woVtDcFS4B5pUYz0FQWI9pVHk/6TBWom2x3/ziJthkEn/LbCA9Twv39xSQU7Dn0zdIWfsNTQ== dependencies: - "@xobotyi/scrollbar-width" "1.5.0" + "@types/js-cookie" "2.2.5" + "@xobotyi/scrollbar-width" "1.9.4" copy-to-clipboard "^3.2.0" - fast-shallow-equal "^0.1.1" + fast-deep-equal "^3.1.1" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" nano-css "^5.2.1" - react-fast-compare "^2.0.4" resize-observer-polyfill "^1.5.1" screenfull "^5.0.0" set-harmonic-interval "^1.0.1" From 32b3861580720503a7864cb92b30ff84e4e7f8de Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 17 Mar 2020 15:25:44 -0700 Subject: [PATCH 62/76] [kbn/pm] don't fail when plugins are outside repo (#60164) * [kbn/pm] don't fail when plugins are outside repo * remove unused import Co-authored-by: spalger Co-authored-by: Elastic Machine --- packages/kbn-pm/dist/index.js | 1244 +++++++++-------- packages/kbn-pm/package.json | 1 + packages/kbn-pm/src/utils/kibana.ts | 12 + .../kbn-pm/src/utils/project_checksums.ts | 26 +- yarn.lock | 2 +- 5 files changed, 681 insertions(+), 604 deletions(-) diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 338d489300526..16fc0d891185f 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(704); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(705); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); @@ -57074,7 +57074,7 @@ async function getChangesForProjects(projects, kbn, log) { log.verbose('getting changed files'); const { stdout - } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['ls-files', '-dmt', '--', ...Array.from(projects.values()).map(p => p.path)], { + } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['ls-files', '-dmt', '--', ...Array.from(projects.values()).filter(p => kbn.isPartOfRepo(p)).map(p => p.path)], { cwd: kbn.getAbsolute() }); const output = stdout.trim(); @@ -57117,6 +57117,11 @@ async function getChangesForProjects(projects, kbn, log) { const changesByProject = new Map(); for (const project of sortedRelevantProjects) { + if (kbn.isOutsideRepo(project)) { + changesByProject.set(project, undefined); + continue; + } + const ownChanges = new Map(); const prefix = kbn.getRelative(project.path); @@ -57141,6 +57146,10 @@ async function getChangesForProjects(projects, kbn, log) { async function getLatestSha(project, kbn) { + if (kbn.isOutsideRepo(project)) { + return; + } + const { stdout } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], { @@ -57200,7 +57209,7 @@ async function getChecksum(project, changes, yarnLock, kbn, log) { log.verbose(`[${project.name}] local sha:`, sha); } - if (Array.from(changes.values()).includes('invalid')) { + if (!changes || Array.from(changes.values()).includes('invalid')) { log.warning(`[${project.name}] unable to determine local changes, caching disabled`); return; } @@ -79162,8 +79171,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(700); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(501); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(579); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(704); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(579); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -79192,6 +79203,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope + /** * Helper class for dealing with a set of projects as children of * the Kibana project. The kbn/pm is currently implemented to be @@ -79206,7 +79218,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope class Kibana { static async loadFrom(rootPath) { - return new Kibana((await Object(_projects__WEBPACK_IMPORTED_MODULE_2__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])({ + return new Kibana((await Object(_projects__WEBPACK_IMPORTED_MODULE_3__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_4__["getProjectPaths"])({ rootPath })))); } @@ -79265,7 +79277,7 @@ class Kibana { getProjectAndDeps(name) { const project = this.getProject(name); - return Object(_projects__WEBPACK_IMPORTED_MODULE_2__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + return Object(_projects__WEBPACK_IMPORTED_MODULE_3__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); } /** filter the projects to just those matching certain paths/include/exclude tags */ @@ -79274,7 +79286,7 @@ class Kibana { const allProjects = this.getAllProjects(); const filteredProjects = new Map(); const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); - const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])(_objectSpread({}, options, { + const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_4__["getProjectPaths"])(_objectSpread({}, options, { rootPath: this.kibanaProject.path })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_1___default()(pkgJsonPaths, filteredPkgJsonGlobs); @@ -79292,6 +79304,14 @@ class Kibana { return filteredProjects; } + isPartOfRepo(project) { + return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_2___default()(project.path, this.kibanaProject.path); + } + + isOutsideRepo(project) { + return !this.isPartOfRepo(project); + } + } /***/ }), @@ -79385,14 +79405,42 @@ module.exports = arrify; /***/ }), /* 704 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const path = __webpack_require__(16); + +module.exports = (childPath, parentPath) => { + childPath = path.resolve(childPath); + parentPath = path.resolve(parentPath); + + if (process.platform === 'win32') { + childPath = childPath.toLowerCase(); + parentPath = parentPath.toLowerCase(); + } + + if (childPath === parentPath) { + return false; + } + + childPath += path.sep; + parentPath += path.sep; + + return childPath.startsWith(parentPath); +}; + + +/***/ }), +/* 705 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(705); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(706); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(928); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(929); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -79417,13 +79465,13 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 705 */ +/* 706 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(706); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(707); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(587); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); @@ -79565,7 +79613,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 706 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79573,13 +79621,13 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(379); const path = __webpack_require__(16); const os = __webpack_require__(11); -const pAll = __webpack_require__(707); -const arrify = __webpack_require__(709); -const globby = __webpack_require__(710); +const pAll = __webpack_require__(708); +const arrify = __webpack_require__(710); +const globby = __webpack_require__(711); const isGlob = __webpack_require__(605); -const cpFile = __webpack_require__(913); -const junk = __webpack_require__(925); -const CpyError = __webpack_require__(926); +const cpFile = __webpack_require__(914); +const junk = __webpack_require__(926); +const CpyError = __webpack_require__(927); const defaultOptions = { ignoreJunk: true @@ -79698,12 +79746,12 @@ module.exports = (source, destination, { /***/ }), -/* 707 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(708); +const pMap = __webpack_require__(709); module.exports = (iterable, options) => pMap(iterable, element => element(), options); // TODO: Remove this for the next major release @@ -79711,7 +79759,7 @@ module.exports.default = module.exports; /***/ }), -/* 708 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79790,7 +79838,7 @@ module.exports.default = pMap; /***/ }), -/* 709 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79820,17 +79868,17 @@ module.exports = arrify; /***/ }), -/* 710 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const arrayUnion = __webpack_require__(711); -const glob = __webpack_require__(713); -const fastGlob = __webpack_require__(718); -const dirGlob = __webpack_require__(906); -const gitignore = __webpack_require__(909); +const arrayUnion = __webpack_require__(712); +const glob = __webpack_require__(714); +const fastGlob = __webpack_require__(719); +const dirGlob = __webpack_require__(907); +const gitignore = __webpack_require__(910); const DEFAULT_FILTER = () => false; @@ -79975,12 +80023,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 711 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(712); +var arrayUniq = __webpack_require__(713); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -79988,7 +80036,7 @@ module.exports = function () { /***/ }), -/* 712 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80057,7 +80105,7 @@ if ('Set' in global) { /***/ }), -/* 713 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -80106,13 +80154,13 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(714) +var inherits = __webpack_require__(715) var EE = __webpack_require__(379).EventEmitter var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var globSync = __webpack_require__(716) -var common = __webpack_require__(717) +var globSync = __webpack_require__(717) +var common = __webpack_require__(718) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -80853,7 +80901,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 714 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -80863,12 +80911,12 @@ try { module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(715); + module.exports = __webpack_require__(716); } /***/ }), -/* 715 */ +/* 716 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -80901,7 +80949,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 716 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -80911,12 +80959,12 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(713).Glob +var Glob = __webpack_require__(714).Glob var util = __webpack_require__(29) var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var common = __webpack_require__(717) +var common = __webpack_require__(718) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -81393,7 +81441,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 717 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -81639,10 +81687,10 @@ function childrenIgnored (self, path) { /***/ }), -/* 718 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(719); +const pkg = __webpack_require__(720); module.exports = pkg.async; module.exports.default = pkg.async; @@ -81655,19 +81703,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 719 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(720); -var taskManager = __webpack_require__(721); -var reader_async_1 = __webpack_require__(877); -var reader_stream_1 = __webpack_require__(901); -var reader_sync_1 = __webpack_require__(902); -var arrayUtils = __webpack_require__(904); -var streamUtils = __webpack_require__(905); +var optionsManager = __webpack_require__(721); +var taskManager = __webpack_require__(722); +var reader_async_1 = __webpack_require__(878); +var reader_stream_1 = __webpack_require__(902); +var reader_sync_1 = __webpack_require__(903); +var arrayUtils = __webpack_require__(905); +var streamUtils = __webpack_require__(906); /** * Synchronous API. */ @@ -81733,7 +81781,7 @@ function isString(source) { /***/ }), -/* 720 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81771,13 +81819,13 @@ exports.prepare = prepare; /***/ }), -/* 721 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(722); +var patternUtils = __webpack_require__(723); /** * Generate tasks based on parent directory of each pattern. */ @@ -81868,16 +81916,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 722 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var globParent = __webpack_require__(723); -var isGlob = __webpack_require__(726); -var micromatch = __webpack_require__(727); +var globParent = __webpack_require__(724); +var isGlob = __webpack_require__(727); +var micromatch = __webpack_require__(728); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -82023,15 +82071,15 @@ exports.matchAny = matchAny; /***/ }), -/* 723 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(16); -var isglob = __webpack_require__(724); -var pathDirname = __webpack_require__(725); +var isglob = __webpack_require__(725); +var pathDirname = __webpack_require__(726); var isWin32 = __webpack_require__(11).platform() === 'win32'; module.exports = function globParent(str) { @@ -82054,7 +82102,7 @@ module.exports = function globParent(str) { /***/ }), -/* 724 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -82085,7 +82133,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 725 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82235,7 +82283,7 @@ module.exports.win32 = win32; /***/ }), -/* 726 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -82287,7 +82335,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 727 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82298,18 +82346,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(29); -var braces = __webpack_require__(728); -var toRegex = __webpack_require__(830); -var extend = __webpack_require__(838); +var braces = __webpack_require__(729); +var toRegex = __webpack_require__(831); +var extend = __webpack_require__(839); /** * Local dependencies */ -var compilers = __webpack_require__(841); -var parsers = __webpack_require__(873); -var cache = __webpack_require__(874); -var utils = __webpack_require__(875); +var compilers = __webpack_require__(842); +var parsers = __webpack_require__(874); +var cache = __webpack_require__(875); +var utils = __webpack_require__(876); var MAX_LENGTH = 1024 * 64; /** @@ -83171,7 +83219,7 @@ module.exports = micromatch; /***/ }), -/* 728 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83181,18 +83229,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(729); -var unique = __webpack_require__(741); -var extend = __webpack_require__(738); +var toRegex = __webpack_require__(730); +var unique = __webpack_require__(742); +var extend = __webpack_require__(739); /** * Local dependencies */ -var compilers = __webpack_require__(742); -var parsers = __webpack_require__(757); -var Braces = __webpack_require__(767); -var utils = __webpack_require__(743); +var compilers = __webpack_require__(743); +var parsers = __webpack_require__(758); +var Braces = __webpack_require__(768); +var utils = __webpack_require__(744); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -83496,15 +83544,15 @@ module.exports = braces; /***/ }), -/* 729 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(730); -var extend = __webpack_require__(738); -var not = __webpack_require__(740); +var define = __webpack_require__(731); +var extend = __webpack_require__(739); +var not = __webpack_require__(741); var MAX_LENGTH = 1024 * 64; /** @@ -83651,7 +83699,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 730 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83664,7 +83712,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(731); +var isDescriptor = __webpack_require__(732); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -83689,7 +83737,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 731 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83702,9 +83750,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(732); -var isAccessor = __webpack_require__(733); -var isData = __webpack_require__(736); +var typeOf = __webpack_require__(733); +var isAccessor = __webpack_require__(734); +var isData = __webpack_require__(737); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -83718,7 +83766,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 732 */ +/* 733 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -83871,7 +83919,7 @@ function isBuffer(val) { /***/ }), -/* 733 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83884,7 +83932,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(734); +var typeOf = __webpack_require__(735); // accessor descriptor properties var accessor = { @@ -83947,10 +83995,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 734 */ +/* 735 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -84069,7 +84117,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 735 */ +/* 736 */ /***/ (function(module, exports) { /*! @@ -84096,7 +84144,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 736 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84109,7 +84157,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(737); +var typeOf = __webpack_require__(738); // data descriptor properties var data = { @@ -84158,10 +84206,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 737 */ +/* 738 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -84280,13 +84328,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 738 */ +/* 739 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(739); +var isObject = __webpack_require__(740); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -84320,7 +84368,7 @@ function hasOwn(obj, key) { /***/ }), -/* 739 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84340,13 +84388,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 740 */ +/* 741 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(738); +var extend = __webpack_require__(739); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -84413,7 +84461,7 @@ module.exports = toRegex; /***/ }), -/* 741 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84463,13 +84511,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 742 */ +/* 743 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(743); +var utils = __webpack_require__(744); module.exports = function(braces, options) { braces.compiler @@ -84752,25 +84800,25 @@ function hasQueue(node) { /***/ }), -/* 743 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(744); +var splitString = __webpack_require__(745); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(738); -utils.flatten = __webpack_require__(750); -utils.isObject = __webpack_require__(748); -utils.fillRange = __webpack_require__(751); -utils.repeat = __webpack_require__(756); -utils.unique = __webpack_require__(741); +utils.extend = __webpack_require__(739); +utils.flatten = __webpack_require__(751); +utils.isObject = __webpack_require__(749); +utils.fillRange = __webpack_require__(752); +utils.repeat = __webpack_require__(757); +utils.unique = __webpack_require__(742); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -85102,7 +85150,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 744 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85115,7 +85163,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(745); +var extend = __webpack_require__(746); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -85280,14 +85328,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 745 */ +/* 746 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(746); -var assignSymbols = __webpack_require__(749); +var isExtendable = __webpack_require__(747); +var assignSymbols = __webpack_require__(750); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -85347,7 +85395,7 @@ function isEnum(obj, key) { /***/ }), -/* 746 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85360,7 +85408,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -85368,7 +85416,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 747 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85381,7 +85429,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(748); +var isObject = __webpack_require__(749); function isObjectObject(o) { return isObject(o) === true @@ -85412,7 +85460,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 748 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85431,7 +85479,7 @@ module.exports = function isObject(val) { /***/ }), -/* 749 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85478,7 +85526,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 750 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85507,7 +85555,7 @@ function flat(arr, res) { /***/ }), -/* 751 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85521,10 +85569,10 @@ function flat(arr, res) { var util = __webpack_require__(29); -var isNumber = __webpack_require__(752); -var extend = __webpack_require__(738); -var repeat = __webpack_require__(754); -var toRegex = __webpack_require__(755); +var isNumber = __webpack_require__(753); +var extend = __webpack_require__(739); +var repeat = __webpack_require__(755); +var toRegex = __webpack_require__(756); /** * Return a range of numbers or letters. @@ -85722,7 +85770,7 @@ module.exports = fillRange; /***/ }), -/* 752 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85735,7 +85783,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(753); +var typeOf = __webpack_require__(754); module.exports = function isNumber(num) { var type = typeOf(num); @@ -85751,10 +85799,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 753 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -85873,7 +85921,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 754 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85950,7 +85998,7 @@ function repeat(str, num) { /***/ }), -/* 755 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85963,8 +86011,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(754); -var isNumber = __webpack_require__(752); +var repeat = __webpack_require__(755); +var isNumber = __webpack_require__(753); var cache = {}; function toRegexRange(min, max, options) { @@ -86251,7 +86299,7 @@ module.exports = toRegexRange; /***/ }), -/* 756 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86276,14 +86324,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 757 */ +/* 758 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(758); -var utils = __webpack_require__(743); +var Node = __webpack_require__(759); +var utils = __webpack_require__(744); /** * Braces parsers @@ -86643,15 +86691,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 758 */ +/* 759 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(748); -var define = __webpack_require__(759); -var utils = __webpack_require__(766); +var isObject = __webpack_require__(749); +var define = __webpack_require__(760); +var utils = __webpack_require__(767); var ownNames; /** @@ -87142,7 +87190,7 @@ exports = module.exports = Node; /***/ }), -/* 759 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87155,7 +87203,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(760); +var isDescriptor = __webpack_require__(761); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -87180,7 +87228,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 760 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87193,9 +87241,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(761); -var isAccessor = __webpack_require__(762); -var isData = __webpack_require__(764); +var typeOf = __webpack_require__(762); +var isAccessor = __webpack_require__(763); +var isData = __webpack_require__(765); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -87209,7 +87257,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 761 */ +/* 762 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87344,7 +87392,7 @@ function isBuffer(val) { /***/ }), -/* 762 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87357,7 +87405,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(763); +var typeOf = __webpack_require__(764); // accessor descriptor properties var accessor = { @@ -87420,7 +87468,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 763 */ +/* 764 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87555,7 +87603,7 @@ function isBuffer(val) { /***/ }), -/* 764 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87568,7 +87616,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(765); +var typeOf = __webpack_require__(766); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -87611,7 +87659,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 765 */ +/* 766 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87746,13 +87794,13 @@ function isBuffer(val) { /***/ }), -/* 766 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(753); +var typeOf = __webpack_require__(754); var utils = module.exports; /** @@ -88772,17 +88820,17 @@ function assert(val, message) { /***/ }), -/* 767 */ +/* 768 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(738); -var Snapdragon = __webpack_require__(768); -var compilers = __webpack_require__(742); -var parsers = __webpack_require__(757); -var utils = __webpack_require__(743); +var extend = __webpack_require__(739); +var Snapdragon = __webpack_require__(769); +var compilers = __webpack_require__(743); +var parsers = __webpack_require__(758); +var utils = __webpack_require__(744); /** * Customize Snapdragon parser and renderer @@ -88883,17 +88931,17 @@ module.exports = Braces; /***/ }), -/* 768 */ +/* 769 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(769); -var define = __webpack_require__(730); -var Compiler = __webpack_require__(798); -var Parser = __webpack_require__(827); -var utils = __webpack_require__(807); +var Base = __webpack_require__(770); +var define = __webpack_require__(731); +var Compiler = __webpack_require__(799); +var Parser = __webpack_require__(828); +var utils = __webpack_require__(808); var regexCache = {}; var cache = {}; @@ -89064,20 +89112,20 @@ module.exports.Parser = Parser; /***/ }), -/* 769 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var define = __webpack_require__(770); -var CacheBase = __webpack_require__(771); -var Emitter = __webpack_require__(772); -var isObject = __webpack_require__(748); -var merge = __webpack_require__(789); -var pascal = __webpack_require__(792); -var cu = __webpack_require__(793); +var define = __webpack_require__(771); +var CacheBase = __webpack_require__(772); +var Emitter = __webpack_require__(773); +var isObject = __webpack_require__(749); +var merge = __webpack_require__(790); +var pascal = __webpack_require__(793); +var cu = __webpack_require__(794); /** * Optionally define a custom `cache` namespace to use. @@ -89506,7 +89554,7 @@ module.exports.namespace = namespace; /***/ }), -/* 770 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89519,7 +89567,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(760); +var isDescriptor = __webpack_require__(761); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -89544,21 +89592,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 771 */ +/* 772 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(748); -var Emitter = __webpack_require__(772); -var visit = __webpack_require__(773); -var toPath = __webpack_require__(776); -var union = __webpack_require__(777); -var del = __webpack_require__(781); -var get = __webpack_require__(779); -var has = __webpack_require__(786); -var set = __webpack_require__(780); +var isObject = __webpack_require__(749); +var Emitter = __webpack_require__(773); +var visit = __webpack_require__(774); +var toPath = __webpack_require__(777); +var union = __webpack_require__(778); +var del = __webpack_require__(782); +var get = __webpack_require__(780); +var has = __webpack_require__(787); +var set = __webpack_require__(781); /** * Create a `Cache` constructor that when instantiated will @@ -89812,7 +89860,7 @@ module.exports.namespace = namespace; /***/ }), -/* 772 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { @@ -89981,7 +90029,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 773 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89994,8 +90042,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(774); -var mapVisit = __webpack_require__(775); +var visit = __webpack_require__(775); +var mapVisit = __webpack_require__(776); module.exports = function(collection, method, val) { var result; @@ -90018,7 +90066,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 774 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90031,7 +90079,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(748); +var isObject = __webpack_require__(749); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -90058,14 +90106,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 775 */ +/* 776 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var visit = __webpack_require__(774); +var visit = __webpack_require__(775); /** * Map `visit` over an array of objects. @@ -90102,7 +90150,7 @@ function isObject(val) { /***/ }), -/* 776 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90115,7 +90163,7 @@ function isObject(val) { -var typeOf = __webpack_require__(753); +var typeOf = __webpack_require__(754); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -90142,16 +90190,16 @@ function filter(arr) { /***/ }), -/* 777 */ +/* 778 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(739); -var union = __webpack_require__(778); -var get = __webpack_require__(779); -var set = __webpack_require__(780); +var isObject = __webpack_require__(740); +var union = __webpack_require__(779); +var get = __webpack_require__(780); +var set = __webpack_require__(781); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -90179,7 +90227,7 @@ function arrayify(val) { /***/ }), -/* 778 */ +/* 779 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90215,7 +90263,7 @@ module.exports = function union(init) { /***/ }), -/* 779 */ +/* 780 */ /***/ (function(module, exports) { /*! @@ -90271,7 +90319,7 @@ function toString(val) { /***/ }), -/* 780 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90284,10 +90332,10 @@ function toString(val) { -var split = __webpack_require__(744); -var extend = __webpack_require__(738); -var isPlainObject = __webpack_require__(747); -var isObject = __webpack_require__(739); +var split = __webpack_require__(745); +var extend = __webpack_require__(739); +var isPlainObject = __webpack_require__(748); +var isObject = __webpack_require__(740); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -90333,7 +90381,7 @@ function isValidKey(key) { /***/ }), -/* 781 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90346,8 +90394,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(748); -var has = __webpack_require__(782); +var isObject = __webpack_require__(749); +var has = __webpack_require__(783); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -90372,7 +90420,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 782 */ +/* 783 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90385,9 +90433,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(783); -var hasValues = __webpack_require__(785); -var get = __webpack_require__(779); +var isObject = __webpack_require__(784); +var hasValues = __webpack_require__(786); +var get = __webpack_require__(780); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -90398,7 +90446,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 783 */ +/* 784 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90411,7 +90459,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(784); +var isArray = __webpack_require__(785); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -90419,7 +90467,7 @@ module.exports = function isObject(val) { /***/ }), -/* 784 */ +/* 785 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -90430,7 +90478,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 785 */ +/* 786 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90473,7 +90521,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 786 */ +/* 787 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90486,9 +90534,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(748); -var hasValues = __webpack_require__(787); -var get = __webpack_require__(779); +var isObject = __webpack_require__(749); +var hasValues = __webpack_require__(788); +var get = __webpack_require__(780); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -90496,7 +90544,7 @@ module.exports = function(val, prop) { /***/ }), -/* 787 */ +/* 788 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90509,8 +90557,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(788); -var isNumber = __webpack_require__(752); +var typeOf = __webpack_require__(789); +var isNumber = __webpack_require__(753); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -90563,10 +90611,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 788 */ +/* 789 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -90688,14 +90736,14 @@ module.exports = function kindOf(val) { /***/ }), -/* 789 */ +/* 790 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(790); -var forIn = __webpack_require__(791); +var isExtendable = __webpack_require__(791); +var forIn = __webpack_require__(792); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -90759,7 +90807,7 @@ module.exports = mixinDeep; /***/ }), -/* 790 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90772,7 +90820,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -90780,7 +90828,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 791 */ +/* 792 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90803,7 +90851,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 792 */ +/* 793 */ /***/ (function(module, exports) { /*! @@ -90830,14 +90878,14 @@ module.exports = pascalcase; /***/ }), -/* 793 */ +/* 794 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var utils = __webpack_require__(794); +var utils = __webpack_require__(795); /** * Expose class utils @@ -91202,7 +91250,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 794 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91216,10 +91264,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(778); -utils.define = __webpack_require__(730); -utils.isObj = __webpack_require__(748); -utils.staticExtend = __webpack_require__(795); +utils.union = __webpack_require__(779); +utils.define = __webpack_require__(731); +utils.isObj = __webpack_require__(749); +utils.staticExtend = __webpack_require__(796); /** @@ -91230,7 +91278,7 @@ module.exports = utils; /***/ }), -/* 795 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91243,8 +91291,8 @@ module.exports = utils; -var copy = __webpack_require__(796); -var define = __webpack_require__(730); +var copy = __webpack_require__(797); +var define = __webpack_require__(731); var util = __webpack_require__(29); /** @@ -91327,15 +91375,15 @@ module.exports = extend; /***/ }), -/* 796 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(753); -var copyDescriptor = __webpack_require__(797); -var define = __webpack_require__(730); +var typeOf = __webpack_require__(754); +var copyDescriptor = __webpack_require__(798); +var define = __webpack_require__(731); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -91508,7 +91556,7 @@ module.exports.has = has; /***/ }), -/* 797 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91596,16 +91644,16 @@ function isObject(val) { /***/ }), -/* 798 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(799); -var define = __webpack_require__(730); -var debug = __webpack_require__(801)('snapdragon:compiler'); -var utils = __webpack_require__(807); +var use = __webpack_require__(800); +var define = __webpack_require__(731); +var debug = __webpack_require__(802)('snapdragon:compiler'); +var utils = __webpack_require__(808); /** * Create a new `Compiler` with the given `options`. @@ -91759,7 +91807,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(826); + var sourcemaps = __webpack_require__(827); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -91780,7 +91828,7 @@ module.exports = Compiler; /***/ }), -/* 799 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91793,7 +91841,7 @@ module.exports = Compiler; -var utils = __webpack_require__(800); +var utils = __webpack_require__(801); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -91908,7 +91956,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 800 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91922,8 +91970,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(730); -utils.isObject = __webpack_require__(748); +utils.define = __webpack_require__(731); +utils.isObject = __webpack_require__(749); utils.isString = function(val) { @@ -91938,7 +91986,7 @@ module.exports = utils; /***/ }), -/* 801 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -91947,14 +91995,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(802); + module.exports = __webpack_require__(803); } else { - module.exports = __webpack_require__(805); + module.exports = __webpack_require__(806); } /***/ }), -/* 802 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -91963,7 +92011,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(803); +exports = module.exports = __webpack_require__(804); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -92145,7 +92193,7 @@ function localstorage() { /***/ }), -/* 803 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { @@ -92161,7 +92209,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(804); +exports.humanize = __webpack_require__(805); /** * The currently active debug mode names, and names to skip. @@ -92353,7 +92401,7 @@ function coerce(val) { /***/ }), -/* 804 */ +/* 805 */ /***/ (function(module, exports) { /** @@ -92511,7 +92559,7 @@ function plural(ms, n, name) { /***/ }), -/* 805 */ +/* 806 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -92527,7 +92575,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(803); +exports = module.exports = __webpack_require__(804); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -92706,7 +92754,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(806); + var net = __webpack_require__(807); stream = new net.Socket({ fd: fd, readable: false, @@ -92765,13 +92813,13 @@ exports.enable(load()); /***/ }), -/* 806 */ +/* 807 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 807 */ +/* 808 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92781,9 +92829,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(738); -exports.SourceMap = __webpack_require__(808); -exports.sourceMapResolve = __webpack_require__(819); +exports.extend = __webpack_require__(739); +exports.SourceMap = __webpack_require__(809); +exports.sourceMapResolve = __webpack_require__(820); /** * Convert backslash in the given string to forward slashes @@ -92826,7 +92874,7 @@ exports.last = function(arr, n) { /***/ }), -/* 808 */ +/* 809 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -92834,13 +92882,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(809).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(815).SourceMapConsumer; -exports.SourceNode = __webpack_require__(818).SourceNode; +exports.SourceMapGenerator = __webpack_require__(810).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(816).SourceMapConsumer; +exports.SourceNode = __webpack_require__(819).SourceNode; /***/ }), -/* 809 */ +/* 810 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -92850,10 +92898,10 @@ exports.SourceNode = __webpack_require__(818).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(810); -var util = __webpack_require__(812); -var ArraySet = __webpack_require__(813).ArraySet; -var MappingList = __webpack_require__(814).MappingList; +var base64VLQ = __webpack_require__(811); +var util = __webpack_require__(813); +var ArraySet = __webpack_require__(814).ArraySet; +var MappingList = __webpack_require__(815).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -93262,7 +93310,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 810 */ +/* 811 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93302,7 +93350,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(811); +var base64 = __webpack_require__(812); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -93408,7 +93456,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 811 */ +/* 812 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93481,7 +93529,7 @@ exports.decode = function (charCode) { /***/ }), -/* 812 */ +/* 813 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93904,7 +93952,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 813 */ +/* 814 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93914,7 +93962,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(812); +var util = __webpack_require__(813); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -94031,7 +94079,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 814 */ +/* 815 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94041,7 +94089,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(812); +var util = __webpack_require__(813); /** * Determine whether mappingB is after mappingA with respect to generated @@ -94116,7 +94164,7 @@ exports.MappingList = MappingList; /***/ }), -/* 815 */ +/* 816 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94126,11 +94174,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(812); -var binarySearch = __webpack_require__(816); -var ArraySet = __webpack_require__(813).ArraySet; -var base64VLQ = __webpack_require__(810); -var quickSort = __webpack_require__(817).quickSort; +var util = __webpack_require__(813); +var binarySearch = __webpack_require__(817); +var ArraySet = __webpack_require__(814).ArraySet; +var base64VLQ = __webpack_require__(811); +var quickSort = __webpack_require__(818).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -95204,7 +95252,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 816 */ +/* 817 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95321,7 +95369,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 817 */ +/* 818 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95441,7 +95489,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 818 */ +/* 819 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95451,8 +95499,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(809).SourceMapGenerator; -var util = __webpack_require__(812); +var SourceMapGenerator = __webpack_require__(810).SourceMapGenerator; +var util = __webpack_require__(813); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -95860,17 +95908,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 819 */ +/* 820 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(820) -var resolveUrl = __webpack_require__(821) -var decodeUriComponent = __webpack_require__(822) -var urix = __webpack_require__(824) -var atob = __webpack_require__(825) +var sourceMappingURL = __webpack_require__(821) +var resolveUrl = __webpack_require__(822) +var decodeUriComponent = __webpack_require__(823) +var urix = __webpack_require__(825) +var atob = __webpack_require__(826) @@ -96168,7 +96216,7 @@ module.exports = { /***/ }), -/* 820 */ +/* 821 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -96231,7 +96279,7 @@ void (function(root, factory) { /***/ }), -/* 821 */ +/* 822 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -96249,13 +96297,13 @@ module.exports = resolveUrl /***/ }), -/* 822 */ +/* 823 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(823) +var decodeUriComponent = __webpack_require__(824) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -96266,7 +96314,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 823 */ +/* 824 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96367,7 +96415,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 824 */ +/* 825 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -96390,7 +96438,7 @@ module.exports = urix /***/ }), -/* 825 */ +/* 826 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96404,7 +96452,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 826 */ +/* 827 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96412,8 +96460,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(23); var path = __webpack_require__(16); -var define = __webpack_require__(730); -var utils = __webpack_require__(807); +var define = __webpack_require__(731); +var utils = __webpack_require__(808); /** * Expose `mixin()`. @@ -96556,19 +96604,19 @@ exports.comment = function(node) { /***/ }), -/* 827 */ +/* 828 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(799); +var use = __webpack_require__(800); var util = __webpack_require__(29); -var Cache = __webpack_require__(828); -var define = __webpack_require__(730); -var debug = __webpack_require__(801)('snapdragon:parser'); -var Position = __webpack_require__(829); -var utils = __webpack_require__(807); +var Cache = __webpack_require__(829); +var define = __webpack_require__(731); +var debug = __webpack_require__(802)('snapdragon:parser'); +var Position = __webpack_require__(830); +var utils = __webpack_require__(808); /** * Create a new `Parser` with the given `input` and `options`. @@ -97096,7 +97144,7 @@ module.exports = Parser; /***/ }), -/* 828 */ +/* 829 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97203,13 +97251,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 829 */ +/* 830 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(730); +var define = __webpack_require__(731); /** * Store position for a node @@ -97224,16 +97272,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 830 */ +/* 831 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(831); -var define = __webpack_require__(837); -var extend = __webpack_require__(838); -var not = __webpack_require__(840); +var safe = __webpack_require__(832); +var define = __webpack_require__(838); +var extend = __webpack_require__(839); +var not = __webpack_require__(841); var MAX_LENGTH = 1024 * 64; /** @@ -97386,10 +97434,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 831 */ +/* 832 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(832); +var parse = __webpack_require__(833); var types = parse.types; module.exports = function (re, opts) { @@ -97435,13 +97483,13 @@ function isRegExp (x) { /***/ }), -/* 832 */ +/* 833 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(833); -var types = __webpack_require__(834); -var sets = __webpack_require__(835); -var positions = __webpack_require__(836); +var util = __webpack_require__(834); +var types = __webpack_require__(835); +var sets = __webpack_require__(836); +var positions = __webpack_require__(837); module.exports = function(regexpStr) { @@ -97723,11 +97771,11 @@ module.exports.types = types; /***/ }), -/* 833 */ +/* 834 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(834); -var sets = __webpack_require__(835); +var types = __webpack_require__(835); +var sets = __webpack_require__(836); // All of these are private and only used by randexp. @@ -97840,7 +97888,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 834 */ +/* 835 */ /***/ (function(module, exports) { module.exports = { @@ -97856,10 +97904,10 @@ module.exports = { /***/ }), -/* 835 */ +/* 836 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(834); +var types = __webpack_require__(835); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -97944,10 +97992,10 @@ exports.anyChar = function() { /***/ }), -/* 836 */ +/* 837 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(834); +var types = __webpack_require__(835); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -97967,7 +98015,7 @@ exports.end = function() { /***/ }), -/* 837 */ +/* 838 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97980,8 +98028,8 @@ exports.end = function() { -var isobject = __webpack_require__(748); -var isDescriptor = __webpack_require__(760); +var isobject = __webpack_require__(749); +var isDescriptor = __webpack_require__(761); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -98012,14 +98060,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 838 */ +/* 839 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(839); -var assignSymbols = __webpack_require__(749); +var isExtendable = __webpack_require__(840); +var assignSymbols = __webpack_require__(750); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -98079,7 +98127,7 @@ function isEnum(obj, key) { /***/ }), -/* 839 */ +/* 840 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98092,7 +98140,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -98100,14 +98148,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 840 */ +/* 841 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(838); -var safe = __webpack_require__(831); +var extend = __webpack_require__(839); +var safe = __webpack_require__(832); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -98179,14 +98227,14 @@ module.exports = toRegex; /***/ }), -/* 841 */ +/* 842 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(842); -var extglob = __webpack_require__(857); +var nanomatch = __webpack_require__(843); +var extglob = __webpack_require__(858); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -98263,7 +98311,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 842 */ +/* 843 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98274,17 +98322,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(29); -var toRegex = __webpack_require__(729); -var extend = __webpack_require__(843); +var toRegex = __webpack_require__(730); +var extend = __webpack_require__(844); /** * Local dependencies */ -var compilers = __webpack_require__(845); -var parsers = __webpack_require__(846); -var cache = __webpack_require__(849); -var utils = __webpack_require__(851); +var compilers = __webpack_require__(846); +var parsers = __webpack_require__(847); +var cache = __webpack_require__(850); +var utils = __webpack_require__(852); var MAX_LENGTH = 1024 * 64; /** @@ -99108,14 +99156,14 @@ module.exports = nanomatch; /***/ }), -/* 843 */ +/* 844 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(844); -var assignSymbols = __webpack_require__(749); +var isExtendable = __webpack_require__(845); +var assignSymbols = __webpack_require__(750); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -99175,7 +99223,7 @@ function isEnum(obj, key) { /***/ }), -/* 844 */ +/* 845 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99188,7 +99236,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -99196,7 +99244,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 845 */ +/* 846 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99542,15 +99590,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 846 */ +/* 847 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(740); -var toRegex = __webpack_require__(729); -var isOdd = __webpack_require__(847); +var regexNot = __webpack_require__(741); +var toRegex = __webpack_require__(730); +var isOdd = __webpack_require__(848); /** * Characters to use in negation regex (we want to "not" match @@ -99936,7 +99984,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 847 */ +/* 848 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99949,7 +99997,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(848); +var isNumber = __webpack_require__(849); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -99963,7 +100011,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 848 */ +/* 849 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99991,14 +100039,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 849 */ +/* 850 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(850))(); +module.exports = new (__webpack_require__(851))(); /***/ }), -/* 850 */ +/* 851 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100011,7 +100059,7 @@ module.exports = new (__webpack_require__(850))(); -var MapCache = __webpack_require__(828); +var MapCache = __webpack_require__(829); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -100133,7 +100181,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 851 */ +/* 852 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100146,14 +100194,14 @@ var path = __webpack_require__(16); * Module dependencies */ -var isWindows = __webpack_require__(852)(); -var Snapdragon = __webpack_require__(768); -utils.define = __webpack_require__(853); -utils.diff = __webpack_require__(854); -utils.extend = __webpack_require__(843); -utils.pick = __webpack_require__(855); -utils.typeOf = __webpack_require__(856); -utils.unique = __webpack_require__(741); +var isWindows = __webpack_require__(853)(); +var Snapdragon = __webpack_require__(769); +utils.define = __webpack_require__(854); +utils.diff = __webpack_require__(855); +utils.extend = __webpack_require__(844); +utils.pick = __webpack_require__(856); +utils.typeOf = __webpack_require__(857); +utils.unique = __webpack_require__(742); /** * Returns true if the given value is effectively an empty string @@ -100519,7 +100567,7 @@ utils.unixify = function(options) { /***/ }), -/* 852 */ +/* 853 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -100547,7 +100595,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 853 */ +/* 854 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100560,8 +100608,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(748); -var isDescriptor = __webpack_require__(760); +var isobject = __webpack_require__(749); +var isDescriptor = __webpack_require__(761); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -100592,7 +100640,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 854 */ +/* 855 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100646,7 +100694,7 @@ function diffArray(one, two) { /***/ }), -/* 855 */ +/* 856 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100659,7 +100707,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(748); +var isObject = __webpack_require__(749); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -100688,7 +100736,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 856 */ +/* 857 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -100823,7 +100871,7 @@ function isBuffer(val) { /***/ }), -/* 857 */ +/* 858 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100833,18 +100881,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(738); -var unique = __webpack_require__(741); -var toRegex = __webpack_require__(729); +var extend = __webpack_require__(739); +var unique = __webpack_require__(742); +var toRegex = __webpack_require__(730); /** * Local dependencies */ -var compilers = __webpack_require__(858); -var parsers = __webpack_require__(869); -var Extglob = __webpack_require__(872); -var utils = __webpack_require__(871); +var compilers = __webpack_require__(859); +var parsers = __webpack_require__(870); +var Extglob = __webpack_require__(873); +var utils = __webpack_require__(872); var MAX_LENGTH = 1024 * 64; /** @@ -101161,13 +101209,13 @@ module.exports = extglob; /***/ }), -/* 858 */ +/* 859 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(859); +var brackets = __webpack_require__(860); /** * Extglob compilers @@ -101337,7 +101385,7 @@ module.exports = function(extglob) { /***/ }), -/* 859 */ +/* 860 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101347,17 +101395,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(860); -var parsers = __webpack_require__(862); +var compilers = __webpack_require__(861); +var parsers = __webpack_require__(863); /** * Module dependencies */ -var debug = __webpack_require__(864)('expand-brackets'); -var extend = __webpack_require__(738); -var Snapdragon = __webpack_require__(768); -var toRegex = __webpack_require__(729); +var debug = __webpack_require__(865)('expand-brackets'); +var extend = __webpack_require__(739); +var Snapdragon = __webpack_require__(769); +var toRegex = __webpack_require__(730); /** * Parses the given POSIX character class `pattern` and returns a @@ -101555,13 +101603,13 @@ module.exports = brackets; /***/ }), -/* 860 */ +/* 861 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(861); +var posix = __webpack_require__(862); module.exports = function(brackets) { brackets.compiler @@ -101649,7 +101697,7 @@ module.exports = function(brackets) { /***/ }), -/* 861 */ +/* 862 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101678,14 +101726,14 @@ module.exports = { /***/ }), -/* 862 */ +/* 863 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(863); -var define = __webpack_require__(730); +var utils = __webpack_require__(864); +var define = __webpack_require__(731); /** * Text regex @@ -101904,14 +101952,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 863 */ +/* 864 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(729); -var regexNot = __webpack_require__(740); +var toRegex = __webpack_require__(730); +var regexNot = __webpack_require__(741); var cached; /** @@ -101945,7 +101993,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 864 */ +/* 865 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -101954,14 +102002,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(865); + module.exports = __webpack_require__(866); } else { - module.exports = __webpack_require__(868); + module.exports = __webpack_require__(869); } /***/ }), -/* 865 */ +/* 866 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -101970,7 +102018,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(866); +exports = module.exports = __webpack_require__(867); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -102152,7 +102200,7 @@ function localstorage() { /***/ }), -/* 866 */ +/* 867 */ /***/ (function(module, exports, __webpack_require__) { @@ -102168,7 +102216,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(867); +exports.humanize = __webpack_require__(868); /** * The currently active debug mode names, and names to skip. @@ -102360,7 +102408,7 @@ function coerce(val) { /***/ }), -/* 867 */ +/* 868 */ /***/ (function(module, exports) { /** @@ -102518,7 +102566,7 @@ function plural(ms, n, name) { /***/ }), -/* 868 */ +/* 869 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -102534,7 +102582,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(866); +exports = module.exports = __webpack_require__(867); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -102713,7 +102761,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(806); + var net = __webpack_require__(807); stream = new net.Socket({ fd: fd, readable: false, @@ -102772,15 +102820,15 @@ exports.enable(load()); /***/ }), -/* 869 */ +/* 870 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(859); -var define = __webpack_require__(870); -var utils = __webpack_require__(871); +var brackets = __webpack_require__(860); +var define = __webpack_require__(871); +var utils = __webpack_require__(872); /** * Characters to use in text regex (we want to "not" match @@ -102935,7 +102983,7 @@ module.exports = parsers; /***/ }), -/* 870 */ +/* 871 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102948,7 +102996,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(760); +var isDescriptor = __webpack_require__(761); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -102973,14 +103021,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 871 */ +/* 872 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(740); -var Cache = __webpack_require__(850); +var regex = __webpack_require__(741); +var Cache = __webpack_require__(851); /** * Utils @@ -103049,7 +103097,7 @@ utils.createRegex = function(str) { /***/ }), -/* 872 */ +/* 873 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103059,16 +103107,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(768); -var define = __webpack_require__(870); -var extend = __webpack_require__(738); +var Snapdragon = __webpack_require__(769); +var define = __webpack_require__(871); +var extend = __webpack_require__(739); /** * Local dependencies */ -var compilers = __webpack_require__(858); -var parsers = __webpack_require__(869); +var compilers = __webpack_require__(859); +var parsers = __webpack_require__(870); /** * Customize Snapdragon parser and renderer @@ -103134,16 +103182,16 @@ module.exports = Extglob; /***/ }), -/* 873 */ +/* 874 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(857); -var nanomatch = __webpack_require__(842); -var regexNot = __webpack_require__(740); -var toRegex = __webpack_require__(830); +var extglob = __webpack_require__(858); +var nanomatch = __webpack_require__(843); +var regexNot = __webpack_require__(741); +var toRegex = __webpack_require__(831); var not; /** @@ -103224,14 +103272,14 @@ function textRegex(pattern) { /***/ }), -/* 874 */ +/* 875 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(850))(); +module.exports = new (__webpack_require__(851))(); /***/ }), -/* 875 */ +/* 876 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103244,13 +103292,13 @@ var path = __webpack_require__(16); * Module dependencies */ -var Snapdragon = __webpack_require__(768); -utils.define = __webpack_require__(837); -utils.diff = __webpack_require__(854); -utils.extend = __webpack_require__(838); -utils.pick = __webpack_require__(855); -utils.typeOf = __webpack_require__(876); -utils.unique = __webpack_require__(741); +var Snapdragon = __webpack_require__(769); +utils.define = __webpack_require__(838); +utils.diff = __webpack_require__(855); +utils.extend = __webpack_require__(839); +utils.pick = __webpack_require__(856); +utils.typeOf = __webpack_require__(877); +utils.unique = __webpack_require__(742); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -103547,7 +103595,7 @@ utils.unixify = function(options) { /***/ }), -/* 876 */ +/* 877 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -103682,7 +103730,7 @@ function isBuffer(val) { /***/ }), -/* 877 */ +/* 878 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103701,9 +103749,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_stream_1 = __webpack_require__(895); +var readdir = __webpack_require__(879); +var reader_1 = __webpack_require__(892); +var fs_stream_1 = __webpack_require__(896); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -103764,15 +103812,15 @@ exports.default = ReaderAsync; /***/ }), -/* 878 */ +/* 879 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(879); -const readdirAsync = __webpack_require__(887); -const readdirStream = __webpack_require__(890); +const readdirSync = __webpack_require__(880); +const readdirAsync = __webpack_require__(888); +const readdirStream = __webpack_require__(891); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -103856,7 +103904,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 879 */ +/* 880 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103864,11 +103912,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(880); +const DirectoryReader = __webpack_require__(881); let syncFacade = { - fs: __webpack_require__(885), - forEach: __webpack_require__(886), + fs: __webpack_require__(886), + forEach: __webpack_require__(887), sync: true }; @@ -103897,7 +103945,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 880 */ +/* 881 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103906,9 +103954,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(27).Readable; const EventEmitter = __webpack_require__(379).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(881); -const stat = __webpack_require__(883); -const call = __webpack_require__(884); +const normalizeOptions = __webpack_require__(882); +const stat = __webpack_require__(884); +const call = __webpack_require__(885); /** * Asynchronously reads the contents of a directory and streams the results @@ -104284,14 +104332,14 @@ module.exports = DirectoryReader; /***/ }), -/* 881 */ +/* 882 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(882); +const globToRegExp = __webpack_require__(883); module.exports = normalizeOptions; @@ -104468,7 +104516,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 882 */ +/* 883 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -104605,13 +104653,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 883 */ +/* 884 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(884); +const call = __webpack_require__(885); module.exports = stat; @@ -104686,7 +104734,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 884 */ +/* 885 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104747,14 +104795,14 @@ function callOnce (fn) { /***/ }), -/* 885 */ +/* 886 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(884); +const call = __webpack_require__(885); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -104818,7 +104866,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 886 */ +/* 887 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104847,7 +104895,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 887 */ +/* 888 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104855,12 +104903,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(888); -const DirectoryReader = __webpack_require__(880); +const maybe = __webpack_require__(889); +const DirectoryReader = __webpack_require__(881); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(889), + forEach: __webpack_require__(890), async: true }; @@ -104902,7 +104950,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 888 */ +/* 889 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104929,7 +104977,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 889 */ +/* 890 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104965,7 +105013,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 890 */ +/* 891 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104973,11 +105021,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(880); +const DirectoryReader = __webpack_require__(881); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(889), + forEach: __webpack_require__(890), async: true }; @@ -104997,16 +105045,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 891 */ +/* 892 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(892); -var entry_1 = __webpack_require__(894); -var pathUtil = __webpack_require__(893); +var deep_1 = __webpack_require__(893); +var entry_1 = __webpack_require__(895); +var pathUtil = __webpack_require__(894); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -105072,14 +105120,14 @@ exports.default = Reader; /***/ }), -/* 892 */ +/* 893 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(893); -var patternUtils = __webpack_require__(722); +var pathUtils = __webpack_require__(894); +var patternUtils = __webpack_require__(723); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -105162,7 +105210,7 @@ exports.default = DeepFilter; /***/ }), -/* 893 */ +/* 894 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105193,14 +105241,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 894 */ +/* 895 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(893); -var patternUtils = __webpack_require__(722); +var pathUtils = __webpack_require__(894); +var patternUtils = __webpack_require__(723); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -105285,7 +105333,7 @@ exports.default = EntryFilter; /***/ }), -/* 895 */ +/* 896 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105305,8 +105353,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var fsStat = __webpack_require__(896); -var fs_1 = __webpack_require__(900); +var fsStat = __webpack_require__(897); +var fs_1 = __webpack_require__(901); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -105356,14 +105404,14 @@ exports.default = FileSystemStream; /***/ }), -/* 896 */ +/* 897 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(897); -const statProvider = __webpack_require__(899); +const optionsManager = __webpack_require__(898); +const statProvider = __webpack_require__(900); /** * Asynchronous API. */ @@ -105394,13 +105442,13 @@ exports.statSync = statSync; /***/ }), -/* 897 */ +/* 898 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(898); +const fsAdapter = __webpack_require__(899); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -105413,7 +105461,7 @@ exports.prepare = prepare; /***/ }), -/* 898 */ +/* 899 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105436,7 +105484,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 899 */ +/* 900 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105488,7 +105536,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 900 */ +/* 901 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105519,7 +105567,7 @@ exports.default = FileSystem; /***/ }), -/* 901 */ +/* 902 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105539,9 +105587,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_stream_1 = __webpack_require__(895); +var readdir = __webpack_require__(879); +var reader_1 = __webpack_require__(892); +var fs_stream_1 = __webpack_require__(896); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -105609,7 +105657,7 @@ exports.default = ReaderStream; /***/ }), -/* 902 */ +/* 903 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105628,9 +105676,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_sync_1 = __webpack_require__(903); +var readdir = __webpack_require__(879); +var reader_1 = __webpack_require__(892); +var fs_sync_1 = __webpack_require__(904); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -105690,7 +105738,7 @@ exports.default = ReaderSync; /***/ }), -/* 903 */ +/* 904 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105709,8 +105757,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(896); -var fs_1 = __webpack_require__(900); +var fsStat = __webpack_require__(897); +var fs_1 = __webpack_require__(901); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -105756,7 +105804,7 @@ exports.default = FileSystemSync; /***/ }), -/* 904 */ +/* 905 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105772,7 +105820,7 @@ exports.flatten = flatten; /***/ }), -/* 905 */ +/* 906 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105793,13 +105841,13 @@ exports.merge = merge; /***/ }), -/* 906 */ +/* 907 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(907); +const pathType = __webpack_require__(908); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -105865,13 +105913,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 907 */ +/* 908 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(908); +const pify = __webpack_require__(909); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -105914,7 +105962,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 908 */ +/* 909 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106005,17 +106053,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 909 */ +/* 910 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); const path = __webpack_require__(16); -const fastGlob = __webpack_require__(718); -const gitIgnore = __webpack_require__(910); -const pify = __webpack_require__(911); -const slash = __webpack_require__(912); +const fastGlob = __webpack_require__(719); +const gitIgnore = __webpack_require__(911); +const pify = __webpack_require__(912); +const slash = __webpack_require__(913); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -106113,7 +106161,7 @@ module.exports.sync = options => { /***/ }), -/* 910 */ +/* 911 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -106582,7 +106630,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 911 */ +/* 912 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106657,7 +106705,7 @@ module.exports = (input, options) => { /***/ }), -/* 912 */ +/* 913 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106675,17 +106723,17 @@ module.exports = input => { /***/ }), -/* 913 */ +/* 914 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const pEvent = __webpack_require__(914); -const CpFileError = __webpack_require__(917); -const fs = __webpack_require__(921); -const ProgressEmitter = __webpack_require__(924); +const pEvent = __webpack_require__(915); +const CpFileError = __webpack_require__(918); +const fs = __webpack_require__(922); +const ProgressEmitter = __webpack_require__(925); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -106799,12 +106847,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 914 */ +/* 915 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(915); +const pTimeout = __webpack_require__(916); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -107095,12 +107143,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 915 */ +/* 916 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(916); +const pFinally = __webpack_require__(917); class TimeoutError extends Error { constructor(message) { @@ -107146,7 +107194,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 916 */ +/* 917 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107168,12 +107216,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 917 */ +/* 918 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(918); +const NestedError = __webpack_require__(919); class CpFileError extends NestedError { constructor(message, nested) { @@ -107187,10 +107235,10 @@ module.exports = CpFileError; /***/ }), -/* 918 */ +/* 919 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(919); +var inherits = __webpack_require__(920); var NestedError = function (message, nested) { this.nested = nested; @@ -107241,7 +107289,7 @@ module.exports = NestedError; /***/ }), -/* 919 */ +/* 920 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -107249,12 +107297,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(920); + module.exports = __webpack_require__(921); } /***/ }), -/* 920 */ +/* 921 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -107283,16 +107331,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 921 */ +/* 922 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(29); const fs = __webpack_require__(22); -const makeDir = __webpack_require__(922); -const pEvent = __webpack_require__(914); -const CpFileError = __webpack_require__(917); +const makeDir = __webpack_require__(923); +const pEvent = __webpack_require__(915); +const CpFileError = __webpack_require__(918); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -107389,7 +107437,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 922 */ +/* 923 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107397,7 +107445,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const {promisify} = __webpack_require__(29); -const semver = __webpack_require__(923); +const semver = __webpack_require__(924); const defaults = { mode: 0o777 & (~process.umask()), @@ -107546,7 +107594,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 923 */ +/* 924 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -109148,7 +109196,7 @@ function coerce (version, options) { /***/ }), -/* 924 */ +/* 925 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109189,7 +109237,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 925 */ +/* 926 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109235,12 +109283,12 @@ exports.default = module.exports; /***/ }), -/* 926 */ +/* 927 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(927); +const NestedError = __webpack_require__(928); class CpyError extends NestedError { constructor(message, nested) { @@ -109254,7 +109302,7 @@ module.exports = CpyError; /***/ }), -/* 927 */ +/* 928 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -109310,7 +109358,7 @@ module.exports = NestedError; /***/ }), -/* 928 */ +/* 929 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 444d46307b059..278fdbd2bc9a4 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -48,6 +48,7 @@ "globby": "^8.0.1", "has-ansi": "^3.0.0", "indent-string": "^3.2.0", + "is-path-inside": "^3.0.2", "lodash.clonedeepwith": "^4.5.0", "log-symbols": "^2.2.0", "multimatch": "^4.0.0", diff --git a/packages/kbn-pm/src/utils/kibana.ts b/packages/kbn-pm/src/utils/kibana.ts index 36f697d19fc1f..58af98b2a92db 100644 --- a/packages/kbn-pm/src/utils/kibana.ts +++ b/packages/kbn-pm/src/utils/kibana.ts @@ -20,6 +20,7 @@ import Path from 'path'; import multimatch from 'multimatch'; +import isPathInside from 'is-path-inside'; import { ProjectMap, getProjects, includeTransitiveProjects } from './projects'; import { Project } from './project'; @@ -121,4 +122,15 @@ export class Kibana { return filteredProjects; } + + isPartOfRepo(project: Project) { + return ( + project.path === this.kibanaProject.path || + isPathInside(project.path, this.kibanaProject.path) + ); + } + + isOutsideRepo(project: Project) { + return !this.isPartOfRepo(project); + } } diff --git a/packages/kbn-pm/src/utils/project_checksums.ts b/packages/kbn-pm/src/utils/project_checksums.ts index 2fd24c8fc9577..572f2adb19bd9 100644 --- a/packages/kbn-pm/src/utils/project_checksums.ts +++ b/packages/kbn-pm/src/utils/project_checksums.ts @@ -43,7 +43,14 @@ async function getChangesForProjects(projects: ProjectMap, kbn: Kibana, log: Too const { stdout } = await execa( 'git', - ['ls-files', '-dmt', '--', ...Array.from(projects.values()).map(p => p.path)], + [ + 'ls-files', + '-dmt', + '--', + ...Array.from(projects.values()) + .filter(p => kbn.isPartOfRepo(p)) + .map(p => p.path), + ], { cwd: kbn.getAbsolute(), } @@ -84,9 +91,14 @@ async function getChangesForProjects(projects: ProjectMap, kbn: Kibana, log: Too } const sortedRelevantProjects = Array.from(projects.values()).sort(projectBySpecificitySorter); - const changesByProject = new Map(); + const changesByProject = new Map(); for (const project of sortedRelevantProjects) { + if (kbn.isOutsideRepo(project)) { + changesByProject.set(project, undefined); + continue; + } + const ownChanges: Changes = new Map(); const prefix = kbn.getRelative(project.path); @@ -114,6 +126,10 @@ async function getChangesForProjects(projects: ProjectMap, kbn: Kibana, log: Too /** Get the latest commit sha for a project */ async function getLatestSha(project: Project, kbn: Kibana) { + if (kbn.isOutsideRepo(project)) { + return; + } + const { stdout } = await execa( 'git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], @@ -175,7 +191,7 @@ function resolveDepsForProject(project: Project, yarnLock: YarnLock, kbn: Kibana */ async function getChecksum( project: Project, - changes: Changes, + changes: Changes | undefined, yarnLock: YarnLock, kbn: Kibana, log: ToolingLog @@ -185,7 +201,7 @@ async function getChecksum( log.verbose(`[${project.name}] local sha:`, sha); } - if (Array.from(changes.values()).includes('invalid')) { + if (!changes || Array.from(changes.values()).includes('invalid')) { log.warning(`[${project.name}] unable to determine local changes, caching disabled`); return; } @@ -248,7 +264,7 @@ export async function getAllChecksums(kbn: Kibana, log: ToolingLog) { Array.from(projects.values()).map(async project => { cacheKeys.set( project.name, - await getChecksum(project, changesByProject.get(project)!, yarnLock, kbn, log) + await getChecksum(project, changesByProject.get(project), yarnLock, kbn, log) ); }) ); diff --git a/yarn.lock b/yarn.lock index a7e29935c7ab5..eaee706101a7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17576,7 +17576,7 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" -is-path-inside@^3.0.1: +is-path-inside@^3.0.1, is-path-inside@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== From f168b6abb844c6f4c76db0d7bb3018f60ccba89d Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 17 Mar 2020 15:26:29 -0700 Subject: [PATCH 63/76] Add additional safeguards for data source wizard step 2 (#60426) Co-authored-by: Elastic Machine --- .../create_datasource_page/step_configure_datasource.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx index 484ea3f1d94a0..b45beef4a8b5e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx @@ -183,7 +183,10 @@ export const StepConfigureDatasource: React.FunctionComponent<{ // Step B, configure inputs (and their streams) // Assume packages only export one datasource for now const ConfigureInputs = - packageInfo.datasources && packageInfo.datasources[0] ? ( + packageInfo.datasources && + packageInfo.datasources[0] && + packageInfo.datasources[0].inputs && + packageInfo.datasources[0].inputs.length ? ( {packageInfo.datasources[0].inputs.map(packageInput => { const datasourceInput = datasource.inputs.find(input => input.type === packageInput.type); From 3e0b6fb65d916104c3d80874bab6dd2f5844f193 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 17 Mar 2020 18:55:56 -0400 Subject: [PATCH 64/76] [IM] Use EuiCodeBlock to render index mapping (#60420) --- .../detail_panel/show_json/show_json.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js index 50c0e331e3dbc..7b7ca08427087 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { EuiCodeEditor } from '@elastic/eui'; +import { EuiCodeBlock } from '@elastic/eui'; import 'brace/theme/textmate'; @@ -25,17 +25,6 @@ export class ShowJson extends React.PureComponent { return null; } const json = JSON.stringify(data, null, 2); - return ( - - ); + return {json}; } } From bc16fcd984d772edcb381f28ce654d73d0ae7a08 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 17 Mar 2020 16:15:27 -0700 Subject: [PATCH 65/76] Update ingest management team handle (#60457) --- .github/CODEOWNERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 132a99fb0a151..df3a56dd35130 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -74,9 +74,9 @@ # Observability UIs /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui /x-pack/plugins/infra/ @elastic/logs-metrics-ui -/x-pack/plugins/ingest_manager/ @elastic/ingest -/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest -/x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest +/x-pack/plugins/ingest_manager/ @elastic/ingest-management +/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest-management +/x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest-management /x-pack/legacy/plugins/monitoring/ @elastic/stack-monitoring-ui # Machine Learning From 0d23c516ce4da540bacef2ab5536f8fd35bd1198 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Mar 2020 00:23:50 +0100 Subject: [PATCH 66/76] [Console] Fix bool filter autocompletions and refactor (#60361) The autocomplete lib component was controlling state that belongs to the legacy editor model and so it was moved there. The fix for filter autocompletion inside of "bool" was just adding "[" around the filter scoped entry. * Remove reference to model code from autocomplete lib * Also renamed the __tests__ dir to __jest__ to avoid re-running in mocha. --- .../legacy_core_editor/legacy_core_editor.ts | 56 ++++++- .../__tests__/integration.test.js | 145 +++++++++--------- .../models/sense_editor/sense_editor.ts | 1 + .../url_autocomplete.test.js | 1 - .../url_params.test.js | 4 - .../public/lib/autocomplete/autocomplete.ts | 71 ++------- .../public/lib/autocomplete/body_completer.js | 1 - .../console/public/lib/autocomplete/engine.js | 2 +- .../console/public/types/core_editor.ts | 12 ++ .../lib/spec_definitions/es_6_0/query/dsl.js | 8 +- 10 files changed, 161 insertions(+), 140 deletions(-) rename src/plugins/console/public/lib/autocomplete/{__tests__ => __jest__}/url_autocomplete.test.js (99%) rename src/plugins/console/public/lib/autocomplete/{__tests__ => __jest__}/url_params.test.js (95%) diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 47947e985092b..49093dd3527b5 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -18,9 +18,17 @@ */ import ace from 'brace'; -import { Editor as IAceEditor } from 'brace'; +import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace'; import $ from 'jquery'; -import { CoreEditor, Position, Range, Token, TokensProvider, EditorEvent } from '../../../types'; +import { + CoreEditor, + Position, + Range, + Token, + TokensProvider, + EditorEvent, + AutoCompleterFunction, +} from '../../../types'; import { AceTokensProvider } from '../../../lib/ace_token_provider'; import * as curl from '../sense_editor/curl'; import smartResize from './smart_resize'; @@ -354,4 +362,48 @@ export class LegacyCoreEditor implements CoreEditor { } } } + + registerAutocompleter(getCompletions: AutoCompleterFunction): void { + // Hook into Ace + + // disable standard context based autocompletion. + // @ts-ignore + ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( + require: any, + exports: any + ) { + exports.getCompletions = function( + innerEditor: any, + session: any, + pos: any, + prefix: any, + callback: any + ) { + callback(null, []); + }; + }); + + const langTools = ace.acequire('ace/ext/language_tools'); + + langTools.setCompleters([ + { + identifierRegexps: [ + /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character + ], + getCompletions: ( + DO_NOT_USE_1: IAceEditor, + DO_NOT_USE_2: IAceEditSession, + pos: { row: number; column: number }, + prefix: string, + callback: (...args: any[]) => void + ) => { + const position: Position = { + lineNumber: pos.row + 1, + column: pos.column + 1, + }; + getCompletions(position, prefix, callback); + }, + }, + ]); + } } diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index 1a09b6b00da9c..c5a0c2ebddf71 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -84,93 +84,90 @@ describe('Integration', () => { changeListener: function() {}, }; // mimic auto complete - senseEditor.autocomplete._test.getCompletions( - senseEditor, - null, - { row: cursor.lineNumber - 1, column: cursor.column - 1 }, - '', - function(err, terms) { - if (testToRun.assertThrows) { - done(); - return; - } + senseEditor.autocomplete._test.getCompletions(senseEditor, null, cursor, '', function( + err, + terms + ) { + if (testToRun.assertThrows) { + done(); + return; + } - if (err) { - throw err; - } + if (err) { + throw err; + } - if (testToRun.no_context) { - expect(!terms || terms.length === 0).toBeTruthy(); - } else { - expect(terms).not.toBeNull(); - expect(terms.length).toBeGreaterThan(0); - } + if (testToRun.no_context) { + expect(!terms || terms.length === 0).toBeTruthy(); + } else { + expect(terms).not.toBeNull(); + expect(terms.length).toBeGreaterThan(0); + } - if (!terms || terms.length === 0) { - done(); - return; - } + if (!terms || terms.length === 0) { + done(); + return; + } - if (testToRun.autoCompleteSet) { - const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { - if (typeof t !== 'object') { - t = { name: t }; - } - return t; - }); - if (terms.length !== expectedTerms.length) { - expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); - } else { - const filteredActualTerms = _.map(terms, function(actualTerm, i) { - const expectedTerm = expectedTerms[i]; - const filteredTerm = {}; - _.each(expectedTerm, function(v, p) { - filteredTerm[p] = actualTerm[p]; - }); - return filteredTerm; - }); - expect(filteredActualTerms).toEqual(expectedTerms); + if (testToRun.autoCompleteSet) { + const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { + if (typeof t !== 'object') { + t = { name: t }; } + return t; + }); + if (terms.length !== expectedTerms.length) { + expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); + } else { + const filteredActualTerms = _.map(terms, function(actualTerm, i) { + const expectedTerm = expectedTerms[i]; + const filteredTerm = {}; + _.each(expectedTerm, function(v, p) { + filteredTerm[p] = actualTerm[p]; + }); + return filteredTerm; + }); + expect(filteredActualTerms).toEqual(expectedTerms); } + } - const context = terms[0].context; - const { - cursor: { lineNumber, column }, - } = testToRun; - senseEditor.autocomplete._test.addReplacementInfoToContext( - context, - { lineNumber, column }, - terms[0].value - ); + const context = terms[0].context; + const { + cursor: { lineNumber, column }, + } = testToRun; + senseEditor.autocomplete._test.addReplacementInfoToContext( + context, + { lineNumber, column }, + terms[0].value + ); - function ac(prop, propTest) { - if (typeof testToRun[prop] !== 'undefined') { - if (propTest) { - propTest(context[prop], testToRun[prop], prop); - } else { - expect(context[prop]).toEqual(testToRun[prop]); - } + function ac(prop, propTest) { + if (typeof testToRun[prop] !== 'undefined') { + if (propTest) { + propTest(context[prop], testToRun[prop], prop); + } else { + expect(context[prop]).toEqual(testToRun[prop]); } } + } - function posCompare(actual, expected) { - expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); - expect(actual.column).toEqual(expected.column); - } - - function rangeCompare(actual, expected, name) { - posCompare(actual.start, expected.start, name + '.start'); - posCompare(actual.end, expected.end, name + '.end'); - } + function posCompare(actual, expected) { + expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); + expect(actual.column).toEqual(expected.column); + } - ac('prefixToAdd'); - ac('suffixToAdd'); - ac('addTemplate'); - ac('textBoxPosition', posCompare); - ac('rangeToReplace', rangeCompare); - done(); + function rangeCompare(actual, expected, name) { + posCompare(actual.start, expected.start, name + '.start'); + posCompare(actual.end, expected.end, name + '.end'); } - ); + + ac('prefixToAdd'); + ac('suffixToAdd'); + ac('addTemplate'); + ac('textBoxPosition', posCompare); + ac('rangeToReplace', rangeCompare); + done(); + }); }); } diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index f559f5dfcd707..b1444bdf2bbab 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -44,6 +44,7 @@ export class SenseEditor { coreEditor, parser: this.parser, }); + this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions); this.coreEditor.on( 'tokenizerUpdate', this.highlightCurrentRequestsAndUpdateActionBar.bind(this) diff --git a/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js similarity index 99% rename from src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js rename to src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js index 40fcd551fb6f7..0758a75695566 100644 --- a/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import '../../../application/models/sense_editor/sense_editor.test.mocks'; const _ = require('lodash'); import { diff --git a/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js similarity index 95% rename from src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js rename to src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js index ce2a2553b19ee..72fce53c4f1fe 100644 --- a/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js @@ -16,10 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import '../../../application/models/sense_editor/sense_editor.test.mocks'; -import 'brace'; -import 'brace/mode/javascript'; -import 'brace/mode/json'; const _ = require('lodash'); import { UrlParams } from '../../autocomplete/url_params'; import { populateContext } from '../../autocomplete/engine'; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index e09024ccfc859..d4f10ff4e4277 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -18,9 +18,9 @@ */ import _ from 'lodash'; -import ace, { Editor as AceEditor, IEditSession } from 'brace'; import { i18n } from '@kbn/i18n'; +// TODO: All of these imports need to be moved to the core editor so that it can inject components from there. import { getTopLevelUrlCompleteComponents, getEndpointBodyCompleteComponents, @@ -39,7 +39,7 @@ import { createTokenIterator } from '../../application/factories'; import { Position, Token, Range, CoreEditor } from '../../types'; -let LAST_EVALUATED_TOKEN: any = null; +let lastEvaluatedToken: any = null; function isUrlParamsToken(token: any) { switch ((token || {}).type) { @@ -889,7 +889,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (!currentToken) { if (pos.lineNumber === 1) { - LAST_EVALUATED_TOKEN = null; + lastEvaluatedToken = null; return; } currentToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; // empty row @@ -902,26 +902,26 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (parser.isEmptyToken(nextToken)) { // Empty line, or we're not on the edge of current token. Save the current position as base currentToken.position.column = pos.column; - LAST_EVALUATED_TOKEN = currentToken; + lastEvaluatedToken = currentToken; } else { nextToken.position.lineNumber = pos.lineNumber; - LAST_EVALUATED_TOKEN = nextToken; + lastEvaluatedToken = nextToken; } return; } - if (!LAST_EVALUATED_TOKEN) { - LAST_EVALUATED_TOKEN = currentToken; + if (!lastEvaluatedToken) { + lastEvaluatedToken = currentToken; return; // wait for the next typing. } if ( - LAST_EVALUATED_TOKEN.position.column !== currentToken.position.column || - LAST_EVALUATED_TOKEN.position.lineNumber !== currentToken.position.lineNumber || - LAST_EVALUATED_TOKEN.value === currentToken.value + lastEvaluatedToken.position.column !== currentToken.position.column || + lastEvaluatedToken.position.lineNumber !== currentToken.position.lineNumber || + lastEvaluatedToken.value === currentToken.value ) { // not on the same place or nothing changed, cache and wait for the next time - LAST_EVALUATED_TOKEN = currentToken; + lastEvaluatedToken = currentToken; return; } @@ -935,7 +935,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor return; } - LAST_EVALUATED_TOKEN = currentToken; + lastEvaluatedToken = currentToken; editor.execCommand('startAutocomplete'); }, 100); @@ -947,17 +947,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor } } - function getCompletions( - DO_NOT_USE: AceEditor, - DO_NOT_USE_SESSION: IEditSession, - pos: { row: number; column: number }, - prefix: string, - callback: (...args: any[]) => void - ) { - const position: Position = { - lineNumber: pos.row + 1, - column: pos.column + 1, - }; + function getCompletions(position: Position, prefix: string, callback: (...args: any[]) => void) { try { const context = getAutoCompleteContext(editor, position); if (!context) { @@ -1028,39 +1018,12 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor editor.on('changeSelection', editorChangeListener); - // Hook into Ace - - // disable standard context based autocompletion. - // @ts-ignore - ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( - require: any, - exports: any - ) { - exports.getCompletions = function( - innerEditor: any, - session: any, - pos: any, - prefix: any, - callback: any - ) { - callback(null, []); - }; - }); - - const langTools = ace.acequire('ace/ext/language_tools'); - - langTools.setCompleters([ - { - identifierRegexps: [ - /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character - ], - getCompletions, - }, - ]); - return { + getCompletions, + // TODO: This needs to be cleaned up _test: { - getCompletions, + getCompletions: (_editor: any, _editSession: any, pos: any, prefix: any, callback: any) => + getCompletions(pos, prefix, callback), addReplacementInfoToContext, addChangeListener: () => editor.on('changeSelection', editorChangeListener), removeChangeListener: () => editor.off('changeSelection', editorChangeListener), diff --git a/src/plugins/console/public/lib/autocomplete/body_completer.js b/src/plugins/console/public/lib/autocomplete/body_completer.js index e23a58780a362..1aa315c50b9bf 100644 --- a/src/plugins/console/public/lib/autocomplete/body_completer.js +++ b/src/plugins/console/public/lib/autocomplete/body_completer.js @@ -115,7 +115,6 @@ class ScopeResolver extends SharedComponent { next: [], }; const components = this.resolveLinkToComponents(context, editor); - _.each(components, function(component) { const componentResult = component.match(token, context, editor); if (componentResult && componentResult.next) { diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js index f4df8af871eba..7b64d91c95374 100644 --- a/src/plugins/console/public/lib/autocomplete/engine.js +++ b/src/plugins/console/public/lib/autocomplete/engine.js @@ -43,7 +43,7 @@ export function wrapComponentWithDefaults(component, defaults) { const tracer = function() { if (window.engine_trace) { - console.log.call(console, arguments); + console.log.call(console, ...arguments); } }; diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index 79dc3ca74200b..84a2c64a80888 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -29,6 +29,12 @@ export type EditorEvent = | 'change' | 'changeSelection'; +export type AutoCompleterFunction = ( + pos: Position, + prefix: string, + callback: (...args: any[]) => void +) => void; + export interface Position { /** * The line number, not zero-indexed. @@ -256,4 +262,10 @@ export interface CoreEditor { * Register a keyboard shortcut and provide a function to be called. */ registerKeyboardShortcut(opts: { keys: any; fn: () => void; name: string }): void; + + /** + * Register a completions function that will be called when the editor + * detects a change + */ + registerAutocompleter(getCompletions: AutoCompleterFunction): void; } diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js index a5f0d15dee0e9..16b952fe0fe4f 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js +++ b/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js @@ -281,9 +281,11 @@ export function queryDsl(api) { __scope_link: '.', }, ], - filter: { - __scope_link: 'GLOBAL.filter', - }, + filter: [ + { + __scope_link: 'GLOBAL.filter', + }, + ], minimum_should_match: 1, boost: 1.0, }, From 8412ab61b4f7ecbdca6bb05225c80023d9bd4745 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 17 Mar 2020 16:27:45 -0700 Subject: [PATCH 67/76] Revert "[Console] Fix bool filter autocompletions and refactor (#60361)" This reverts commit 0d23c516ce4da540bacef2ab5536f8fd35bd1198. --- .../legacy_core_editor/legacy_core_editor.ts | 56 +------ .../__tests__/integration.test.js | 145 +++++++++--------- .../models/sense_editor/sense_editor.ts | 1 - .../url_autocomplete.test.js | 1 + .../url_params.test.js | 4 + .../public/lib/autocomplete/autocomplete.ts | 71 +++++++-- .../public/lib/autocomplete/body_completer.js | 1 + .../console/public/lib/autocomplete/engine.js | 2 +- .../console/public/types/core_editor.ts | 12 -- .../lib/spec_definitions/es_6_0/query/dsl.js | 8 +- 10 files changed, 140 insertions(+), 161 deletions(-) rename src/plugins/console/public/lib/autocomplete/{__jest__ => __tests__}/url_autocomplete.test.js (99%) rename src/plugins/console/public/lib/autocomplete/{__jest__ => __tests__}/url_params.test.js (95%) diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 49093dd3527b5..47947e985092b 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -18,17 +18,9 @@ */ import ace from 'brace'; -import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace'; +import { Editor as IAceEditor } from 'brace'; import $ from 'jquery'; -import { - CoreEditor, - Position, - Range, - Token, - TokensProvider, - EditorEvent, - AutoCompleterFunction, -} from '../../../types'; +import { CoreEditor, Position, Range, Token, TokensProvider, EditorEvent } from '../../../types'; import { AceTokensProvider } from '../../../lib/ace_token_provider'; import * as curl from '../sense_editor/curl'; import smartResize from './smart_resize'; @@ -362,48 +354,4 @@ export class LegacyCoreEditor implements CoreEditor { } } } - - registerAutocompleter(getCompletions: AutoCompleterFunction): void { - // Hook into Ace - - // disable standard context based autocompletion. - // @ts-ignore - ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( - require: any, - exports: any - ) { - exports.getCompletions = function( - innerEditor: any, - session: any, - pos: any, - prefix: any, - callback: any - ) { - callback(null, []); - }; - }); - - const langTools = ace.acequire('ace/ext/language_tools'); - - langTools.setCompleters([ - { - identifierRegexps: [ - /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character - ], - getCompletions: ( - DO_NOT_USE_1: IAceEditor, - DO_NOT_USE_2: IAceEditSession, - pos: { row: number; column: number }, - prefix: string, - callback: (...args: any[]) => void - ) => { - const position: Position = { - lineNumber: pos.row + 1, - column: pos.column + 1, - }; - getCompletions(position, prefix, callback); - }, - }, - ]); - } } diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index c5a0c2ebddf71..1a09b6b00da9c 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -84,90 +84,93 @@ describe('Integration', () => { changeListener: function() {}, }; // mimic auto complete - senseEditor.autocomplete._test.getCompletions(senseEditor, null, cursor, '', function( - err, - terms - ) { - if (testToRun.assertThrows) { - done(); - return; - } + senseEditor.autocomplete._test.getCompletions( + senseEditor, + null, + { row: cursor.lineNumber - 1, column: cursor.column - 1 }, + '', + function(err, terms) { + if (testToRun.assertThrows) { + done(); + return; + } - if (err) { - throw err; - } + if (err) { + throw err; + } - if (testToRun.no_context) { - expect(!terms || terms.length === 0).toBeTruthy(); - } else { - expect(terms).not.toBeNull(); - expect(terms.length).toBeGreaterThan(0); - } + if (testToRun.no_context) { + expect(!terms || terms.length === 0).toBeTruthy(); + } else { + expect(terms).not.toBeNull(); + expect(terms.length).toBeGreaterThan(0); + } - if (!terms || terms.length === 0) { - done(); - return; - } + if (!terms || terms.length === 0) { + done(); + return; + } - if (testToRun.autoCompleteSet) { - const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { - if (typeof t !== 'object') { - t = { name: t }; - } - return t; - }); - if (terms.length !== expectedTerms.length) { - expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); - } else { - const filteredActualTerms = _.map(terms, function(actualTerm, i) { - const expectedTerm = expectedTerms[i]; - const filteredTerm = {}; - _.each(expectedTerm, function(v, p) { - filteredTerm[p] = actualTerm[p]; - }); - return filteredTerm; + if (testToRun.autoCompleteSet) { + const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { + if (typeof t !== 'object') { + t = { name: t }; + } + return t; }); - expect(filteredActualTerms).toEqual(expectedTerms); + if (terms.length !== expectedTerms.length) { + expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); + } else { + const filteredActualTerms = _.map(terms, function(actualTerm, i) { + const expectedTerm = expectedTerms[i]; + const filteredTerm = {}; + _.each(expectedTerm, function(v, p) { + filteredTerm[p] = actualTerm[p]; + }); + return filteredTerm; + }); + expect(filteredActualTerms).toEqual(expectedTerms); + } } - } - const context = terms[0].context; - const { - cursor: { lineNumber, column }, - } = testToRun; - senseEditor.autocomplete._test.addReplacementInfoToContext( - context, - { lineNumber, column }, - terms[0].value - ); + const context = terms[0].context; + const { + cursor: { lineNumber, column }, + } = testToRun; + senseEditor.autocomplete._test.addReplacementInfoToContext( + context, + { lineNumber, column }, + terms[0].value + ); - function ac(prop, propTest) { - if (typeof testToRun[prop] !== 'undefined') { - if (propTest) { - propTest(context[prop], testToRun[prop], prop); - } else { - expect(context[prop]).toEqual(testToRun[prop]); + function ac(prop, propTest) { + if (typeof testToRun[prop] !== 'undefined') { + if (propTest) { + propTest(context[prop], testToRun[prop], prop); + } else { + expect(context[prop]).toEqual(testToRun[prop]); + } } } - } - function posCompare(actual, expected) { - expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); - expect(actual.column).toEqual(expected.column); - } + function posCompare(actual, expected) { + expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); + expect(actual.column).toEqual(expected.column); + } - function rangeCompare(actual, expected, name) { - posCompare(actual.start, expected.start, name + '.start'); - posCompare(actual.end, expected.end, name + '.end'); - } + function rangeCompare(actual, expected, name) { + posCompare(actual.start, expected.start, name + '.start'); + posCompare(actual.end, expected.end, name + '.end'); + } - ac('prefixToAdd'); - ac('suffixToAdd'); - ac('addTemplate'); - ac('textBoxPosition', posCompare); - ac('rangeToReplace', rangeCompare); - done(); - }); + ac('prefixToAdd'); + ac('suffixToAdd'); + ac('addTemplate'); + ac('textBoxPosition', posCompare); + ac('rangeToReplace', rangeCompare); + done(); + } + ); }); } diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index b1444bdf2bbab..f559f5dfcd707 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -44,7 +44,6 @@ export class SenseEditor { coreEditor, parser: this.parser, }); - this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions); this.coreEditor.on( 'tokenizerUpdate', this.highlightCurrentRequestsAndUpdateActionBar.bind(this) diff --git a/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js b/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js similarity index 99% rename from src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js rename to src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js index 0758a75695566..40fcd551fb6f7 100644 --- a/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js +++ b/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import '../../../application/models/sense_editor/sense_editor.test.mocks'; const _ = require('lodash'); import { diff --git a/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js b/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js similarity index 95% rename from src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js rename to src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js index 72fce53c4f1fe..ce2a2553b19ee 100644 --- a/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js +++ b/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js @@ -16,6 +16,10 @@ * specific language governing permissions and limitations * under the License. */ +import '../../../application/models/sense_editor/sense_editor.test.mocks'; +import 'brace'; +import 'brace/mode/javascript'; +import 'brace/mode/json'; const _ = require('lodash'); import { UrlParams } from '../../autocomplete/url_params'; import { populateContext } from '../../autocomplete/engine'; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index d4f10ff4e4277..e09024ccfc859 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -18,9 +18,9 @@ */ import _ from 'lodash'; +import ace, { Editor as AceEditor, IEditSession } from 'brace'; import { i18n } from '@kbn/i18n'; -// TODO: All of these imports need to be moved to the core editor so that it can inject components from there. import { getTopLevelUrlCompleteComponents, getEndpointBodyCompleteComponents, @@ -39,7 +39,7 @@ import { createTokenIterator } from '../../application/factories'; import { Position, Token, Range, CoreEditor } from '../../types'; -let lastEvaluatedToken: any = null; +let LAST_EVALUATED_TOKEN: any = null; function isUrlParamsToken(token: any) { switch ((token || {}).type) { @@ -889,7 +889,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (!currentToken) { if (pos.lineNumber === 1) { - lastEvaluatedToken = null; + LAST_EVALUATED_TOKEN = null; return; } currentToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; // empty row @@ -902,26 +902,26 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (parser.isEmptyToken(nextToken)) { // Empty line, or we're not on the edge of current token. Save the current position as base currentToken.position.column = pos.column; - lastEvaluatedToken = currentToken; + LAST_EVALUATED_TOKEN = currentToken; } else { nextToken.position.lineNumber = pos.lineNumber; - lastEvaluatedToken = nextToken; + LAST_EVALUATED_TOKEN = nextToken; } return; } - if (!lastEvaluatedToken) { - lastEvaluatedToken = currentToken; + if (!LAST_EVALUATED_TOKEN) { + LAST_EVALUATED_TOKEN = currentToken; return; // wait for the next typing. } if ( - lastEvaluatedToken.position.column !== currentToken.position.column || - lastEvaluatedToken.position.lineNumber !== currentToken.position.lineNumber || - lastEvaluatedToken.value === currentToken.value + LAST_EVALUATED_TOKEN.position.column !== currentToken.position.column || + LAST_EVALUATED_TOKEN.position.lineNumber !== currentToken.position.lineNumber || + LAST_EVALUATED_TOKEN.value === currentToken.value ) { // not on the same place or nothing changed, cache and wait for the next time - lastEvaluatedToken = currentToken; + LAST_EVALUATED_TOKEN = currentToken; return; } @@ -935,7 +935,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor return; } - lastEvaluatedToken = currentToken; + LAST_EVALUATED_TOKEN = currentToken; editor.execCommand('startAutocomplete'); }, 100); @@ -947,7 +947,17 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor } } - function getCompletions(position: Position, prefix: string, callback: (...args: any[]) => void) { + function getCompletions( + DO_NOT_USE: AceEditor, + DO_NOT_USE_SESSION: IEditSession, + pos: { row: number; column: number }, + prefix: string, + callback: (...args: any[]) => void + ) { + const position: Position = { + lineNumber: pos.row + 1, + column: pos.column + 1, + }; try { const context = getAutoCompleteContext(editor, position); if (!context) { @@ -1018,12 +1028,39 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor editor.on('changeSelection', editorChangeListener); + // Hook into Ace + + // disable standard context based autocompletion. + // @ts-ignore + ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( + require: any, + exports: any + ) { + exports.getCompletions = function( + innerEditor: any, + session: any, + pos: any, + prefix: any, + callback: any + ) { + callback(null, []); + }; + }); + + const langTools = ace.acequire('ace/ext/language_tools'); + + langTools.setCompleters([ + { + identifierRegexps: [ + /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character + ], + getCompletions, + }, + ]); + return { - getCompletions, - // TODO: This needs to be cleaned up _test: { - getCompletions: (_editor: any, _editSession: any, pos: any, prefix: any, callback: any) => - getCompletions(pos, prefix, callback), + getCompletions, addReplacementInfoToContext, addChangeListener: () => editor.on('changeSelection', editorChangeListener), removeChangeListener: () => editor.off('changeSelection', editorChangeListener), diff --git a/src/plugins/console/public/lib/autocomplete/body_completer.js b/src/plugins/console/public/lib/autocomplete/body_completer.js index 1aa315c50b9bf..e23a58780a362 100644 --- a/src/plugins/console/public/lib/autocomplete/body_completer.js +++ b/src/plugins/console/public/lib/autocomplete/body_completer.js @@ -115,6 +115,7 @@ class ScopeResolver extends SharedComponent { next: [], }; const components = this.resolveLinkToComponents(context, editor); + _.each(components, function(component) { const componentResult = component.match(token, context, editor); if (componentResult && componentResult.next) { diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js index 7b64d91c95374..f4df8af871eba 100644 --- a/src/plugins/console/public/lib/autocomplete/engine.js +++ b/src/plugins/console/public/lib/autocomplete/engine.js @@ -43,7 +43,7 @@ export function wrapComponentWithDefaults(component, defaults) { const tracer = function() { if (window.engine_trace) { - console.log.call(console, ...arguments); + console.log.call(console, arguments); } }; diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index 84a2c64a80888..79dc3ca74200b 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -29,12 +29,6 @@ export type EditorEvent = | 'change' | 'changeSelection'; -export type AutoCompleterFunction = ( - pos: Position, - prefix: string, - callback: (...args: any[]) => void -) => void; - export interface Position { /** * The line number, not zero-indexed. @@ -262,10 +256,4 @@ export interface CoreEditor { * Register a keyboard shortcut and provide a function to be called. */ registerKeyboardShortcut(opts: { keys: any; fn: () => void; name: string }): void; - - /** - * Register a completions function that will be called when the editor - * detects a change - */ - registerAutocompleter(getCompletions: AutoCompleterFunction): void; } diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js index 16b952fe0fe4f..a5f0d15dee0e9 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js +++ b/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js @@ -281,11 +281,9 @@ export function queryDsl(api) { __scope_link: '.', }, ], - filter: [ - { - __scope_link: 'GLOBAL.filter', - }, - ], + filter: { + __scope_link: 'GLOBAL.filter', + }, minimum_should_match: 1, boost: 1.0, }, From 2e6c76fda7517314543fabe5e4aa1999aab4c631 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 17 Mar 2020 16:33:37 -0700 Subject: [PATCH 68/76] Disabled edit alert button on management ui for non registered UI alert types (#60439) --- x-pack/plugins/alerting/server/plugin.ts | 10 ---------- .../alerts_list/components/alerts_list.test.tsx | 14 +++++++++++++- .../alerts_list/components/alerts_list.tsx | 4 +++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 885391325fcd6..b4b2de19ef24f 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -173,16 +173,6 @@ export class AlertingPlugin { muteAlertInstanceRoute(router, this.licenseState); unmuteAlertInstanceRoute(router, this.licenseState); - alertTypeRegistry.register({ - id: 'test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - name: 'Test', - executor: async options => { - return { status: 'ok' }; - }, - }); - return { registerType: alertTypeRegistry.register.bind(alertTypeRegistry), }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 865ab6ea04cea..f8f0c278c81e2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -242,6 +242,8 @@ describe('alerts_list component with items', () => { alertTypeRegistry: alertTypeRegistry as any, }; + alertTypeRegistry.has.mockReturnValue(true); + wrapper = mountWithIntl( @@ -257,11 +259,15 @@ describe('alerts_list component with items', () => { expect(loadActionTypes).toHaveBeenCalled(); } - it('renders table of connectors', async () => { + it('renders table of alerts', async () => { await setup(); expect(wrapper.find('EuiBasicTable')).toHaveLength(1); expect(wrapper.find('EuiTableRow')).toHaveLength(2); }); + it('renders edit button for registered alert types', async () => { + await setup(); + expect(wrapper.find('[data-test-subj="alertsTableCell-editLink"]').length).toBeGreaterThan(0); + }); }); describe('alerts_list component empty with show only capability', () => { @@ -455,6 +461,8 @@ describe('alerts_list with show only capability', () => { alertTypeRegistry: alertTypeRegistry as any, }; + alertTypeRegistry.has.mockReturnValue(false); + wrapper = mountWithIntl( @@ -473,4 +481,8 @@ describe('alerts_list with show only capability', () => { expect(wrapper.find('EuiTableRow')).toHaveLength(2); // TODO: check delete button }); + it('not renders edit button for non registered alert types', async () => { + await setup(); + expect(wrapper.find('[data-test-subj="alertsTableCell-editLink"]').length).toBe(0); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 2975b1ef6eba2..8d8fc177b57a0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -225,7 +225,7 @@ export const AlertsList: React.FunctionComponent = () => { ? [ { render: (item: AlertTableItem) => { - return ( + return alertTypeRegistry.has(item.alertTypeId) ? ( { id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editLinkTitle" /> + ) : ( + <> ); }, }, From 4deea08f23c63870daedc5612698be8ea17103d2 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 17 Mar 2020 18:15:26 -0700 Subject: [PATCH 69/76] Publish getIsNavDrawerLocked$ method on core chrome service. (#60191) * Remove isCollapsed, getIsCollapsed, and global_nav_state. --- ...blic.chromestart.getisnavdrawerlocked_.md} | 8 ++-- .../kibana-plugin-core-public.chromestart.md | 3 +- ...-core-public.chromestart.setiscollapsed.md | 24 ---------- src/core/public/chrome/chrome_service.mock.ts | 5 +-- src/core/public/chrome/chrome_service.test.ts | 40 ++--------------- src/core/public/chrome/chrome_service.tsx | 43 ++++++++---------- src/core/public/chrome/ui/header/header.tsx | 27 ++++++++--- .../chrome/ui/header/header_wrapper.tsx | 45 ------------------- src/core/public/chrome/ui/header/index.ts | 1 - src/core/public/chrome/ui/index.ts | 1 - src/core/public/public.api.md | 3 +- src/legacy/ui/public/chrome/chrome.js | 1 - .../chrome/services/global_nav_state.js | 45 ------------------- src/legacy/ui/public/chrome/services/index.js | 20 --------- 14 files changed, 50 insertions(+), 216 deletions(-) rename docs/development/core/public/{kibana-plugin-core-public.chromestart.getiscollapsed_.md => kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md} (51%) delete mode 100644 docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md delete mode 100644 src/core/public/chrome/ui/header/header_wrapper.tsx delete mode 100644 src/legacy/ui/public/chrome/services/global_nav_state.js delete mode 100644 src/legacy/ui/public/chrome/services/index.js diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.getiscollapsed_.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md similarity index 51% rename from docs/development/core/public/kibana-plugin-core-public.chromestart.getiscollapsed_.md rename to docs/development/core/public/kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md index 205f863526e22..78a4442a651e6 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.getiscollapsed_.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md @@ -1,15 +1,15 @@ -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getIsCollapsed$](./kibana-plugin-core-public.chromestart.getiscollapsed_.md) +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getIsNavDrawerLocked$](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) -## ChromeStart.getIsCollapsed$() method +## ChromeStart.getIsNavDrawerLocked$() method -Get an observable of the current collapsed state of the chrome. +Get an observable of the current locked state of the nav drawer. Signature: ```typescript -getIsCollapsed$(): Observable; +getIsNavDrawerLocked$(): Observable; ``` Returns: diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.md index 7d9d47df544d0..c179e089d7cfd 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.md @@ -56,7 +56,7 @@ core.chrome.setHelpExtension(elem => { | [getBrand$()](./kibana-plugin-core-public.chromestart.getbrand_.md) | Get an observable of the current brand information. | | [getBreadcrumbs$()](./kibana-plugin-core-public.chromestart.getbreadcrumbs_.md) | Get an observable of the current list of breadcrumbs | | [getHelpExtension$()](./kibana-plugin-core-public.chromestart.gethelpextension_.md) | Get an observable of the current custom help conttent | -| [getIsCollapsed$()](./kibana-plugin-core-public.chromestart.getiscollapsed_.md) | Get an observable of the current collapsed state of the chrome. | +| [getIsNavDrawerLocked$()](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) | Get an observable of the current locked state of the nav drawer. | | [getIsVisible$()](./kibana-plugin-core-public.chromestart.getisvisible_.md) | Get an observable of the current visibility state of the chrome. | | [removeApplicationClass(className)](./kibana-plugin-core-public.chromestart.removeapplicationclass.md) | Remove a className added with addApplicationClass(). If className is unknown it is ignored. | | [setAppTitle(appTitle)](./kibana-plugin-core-public.chromestart.setapptitle.md) | Sets the current app's title | @@ -65,6 +65,5 @@ core.chrome.setHelpExtension(elem => { | [setBreadcrumbs(newBreadcrumbs)](./kibana-plugin-core-public.chromestart.setbreadcrumbs.md) | Override the current set of breadcrumbs | | [setHelpExtension(helpExtension)](./kibana-plugin-core-public.chromestart.sethelpextension.md) | Override the current set of custom help content | | [setHelpSupportUrl(url)](./kibana-plugin-core-public.chromestart.sethelpsupporturl.md) | Override the default support URL shown in the help menu | -| [setIsCollapsed(isCollapsed)](./kibana-plugin-core-public.chromestart.setiscollapsed.md) | Set the collapsed state of the chrome navigation. | | [setIsVisible(isVisible)](./kibana-plugin-core-public.chromestart.setisvisible.md) | Set the temporary visibility for the chrome. This does nothing if the chrome is hidden by default and should be used to hide the chrome for things like full-screen modes with an exit button. | diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md deleted file mode 100644 index b1843ef326d96..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [setIsCollapsed](./kibana-plugin-core-public.chromestart.setiscollapsed.md) - -## ChromeStart.setIsCollapsed() method - -Set the collapsed state of the chrome navigation. - -Signature: - -```typescript -setIsCollapsed(isCollapsed: boolean): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| isCollapsed | boolean | | - -Returns: - -`void` - diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index bd932c5961eca..89007461b63e6 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -61,8 +61,6 @@ const createStartContractMock = () => { getBrand$: jest.fn(), setIsVisible: jest.fn(), getIsVisible$: jest.fn(), - setIsCollapsed: jest.fn(), - getIsCollapsed$: jest.fn(), addApplicationClass: jest.fn(), removeApplicationClass: jest.fn(), getApplicationClasses$: jest.fn(), @@ -73,15 +71,16 @@ const createStartContractMock = () => { getHelpExtension$: jest.fn(), setHelpExtension: jest.fn(), setHelpSupportUrl: jest.fn(), + getIsNavDrawerLocked$: jest.fn(), }; startContract.navLinks.getAll.mockReturnValue([]); startContract.getBrand$.mockReturnValue(new BehaviorSubject({} as ChromeBrand)); startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false)); - startContract.getIsCollapsed$.mockReturnValue(new BehaviorSubject(false)); startContract.getApplicationClasses$.mockReturnValue(new BehaviorSubject(['class-name'])); startContract.getBadge$.mockReturnValue(new BehaviorSubject({} as ChromeBadge)); startContract.getBreadcrumbs$.mockReturnValue(new BehaviorSubject([{} as ChromeBreadcrumb])); startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined)); + startContract.getIsNavDrawerLocked$.mockReturnValue(new BehaviorSubject(false)); return startContract; }; diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 9018b21973634..bf531aaa00fac 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -259,40 +259,6 @@ describe('start', () => { }); }); - describe('is collapsed', () => { - it('updates/emits isCollapsed', async () => { - const { chrome, service } = await start(); - const promise = chrome - .getIsCollapsed$() - .pipe(toArray()) - .toPromise(); - - chrome.setIsCollapsed(true); - chrome.setIsCollapsed(false); - chrome.setIsCollapsed(true); - service.stop(); - - await expect(promise).resolves.toMatchInlineSnapshot(` - Array [ - false, - true, - false, - true, - ] - `); - }); - - it('only stores true in localStorage', async () => { - const { chrome } = await start(); - - chrome.setIsCollapsed(true); - expect(store.size).toBe(1); - - chrome.setIsCollapsed(false); - expect(store.size).toBe(0); - }); - }); - describe('application classes', () => { it('updates/emits the application classes', async () => { const { chrome, service } = await start(); @@ -442,12 +408,12 @@ describe('start', () => { }); describe('stop', () => { - it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => { + it('completes applicationClass$, getIsNavDrawerLocked, breadcrumbs$, isVisible$, and brand$ observables', async () => { const { chrome, service } = await start(); const promise = Rx.combineLatest( chrome.getBrand$(), chrome.getApplicationClasses$(), - chrome.getIsCollapsed$(), + chrome.getIsNavDrawerLocked$(), chrome.getBreadcrumbs$(), chrome.getIsVisible$(), chrome.getHelpExtension$() @@ -465,7 +431,7 @@ describe('stop', () => { Rx.combineLatest( chrome.getBrand$(), chrome.getApplicationClasses$(), - chrome.getIsCollapsed$(), + chrome.getIsNavDrawerLocked$(), chrome.getBreadcrumbs$(), chrome.getIsVisible$(), chrome.getHelpExtension$() diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 2b0b115ce068e..7c9b644b8b984 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -34,14 +34,14 @@ import { ChromeNavLinks, NavLinksService } from './nav_links'; import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed'; import { NavControlsService, ChromeNavControls } from './nav_controls'; import { DocTitleService, ChromeDocTitle } from './doc_title'; -import { LoadingIndicator, HeaderWrapper as Header } from './ui'; +import { LoadingIndicator, Header } from './ui'; import { DocLinksStart } from '../doc_links'; import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu'; import { KIBANA_ASK_ELASTIC_LINK } from './constants'; import { IUiSettingsClient } from '../ui_settings'; export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle }; -const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed'; +const IS_LOCKED_KEY = 'core.chrome.isLocked'; /** @public */ export interface ChromeBadge { @@ -146,18 +146,25 @@ export class ChromeService { const appTitle$ = new BehaviorSubject('Kibana'); const brand$ = new BehaviorSubject({}); - const isCollapsed$ = new BehaviorSubject(!!localStorage.getItem(IS_COLLAPSED_KEY)); const applicationClasses$ = new BehaviorSubject>(new Set()); const helpExtension$ = new BehaviorSubject(undefined); const breadcrumbs$ = new BehaviorSubject([]); const badge$ = new BehaviorSubject(undefined); const helpSupportUrl$ = new BehaviorSubject(KIBANA_ASK_ELASTIC_LINK); + const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true'); const navControls = this.navControls.start(); const navLinks = this.navLinks.start({ application, http }); const recentlyAccessed = await this.recentlyAccessed.start({ http }); const docTitle = this.docTitle.start({ document: window.document }); + const setIsNavDrawerLocked = (isLocked: boolean) => { + isNavDrawerLocked$.next(isLocked); + localStorage.setItem(IS_LOCKED_KEY, `${isLocked}`); + }; + + const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$)); + if (!this.params.browserSupportsCsp && injectedMetadata.getCspConfig().warnLegacyBrowsers) { notifications.toasts.addWarning( i18n.translate('core.chrome.legacyBrowserWarning', { @@ -193,6 +200,8 @@ export class ChromeService { recentlyAccessed$={recentlyAccessed.get$()} navControlsLeft$={navControls.getLeft$()} navControlsRight$={navControls.getRight$()} + onIsLockedUpdate={setIsNavDrawerLocked} + isLocked$={getIsNavDrawerLocked$} /> ), @@ -214,17 +223,6 @@ export class ChromeService { setIsVisible: (isVisible: boolean) => this.toggleHidden$.next(!isVisible), - getIsCollapsed$: () => isCollapsed$.pipe(takeUntil(this.stop$)), - - setIsCollapsed: (isCollapsed: boolean) => { - isCollapsed$.next(isCollapsed); - if (isCollapsed) { - localStorage.setItem(IS_COLLAPSED_KEY, 'true'); - } else { - localStorage.removeItem(IS_COLLAPSED_KEY); - } - }, - getApplicationClasses$: () => applicationClasses$.pipe( map(set => [...set]), @@ -262,6 +260,8 @@ export class ChromeService { }, setHelpSupportUrl: (url: string) => helpSupportUrl$.next(url), + + getIsNavDrawerLocked$: () => getIsNavDrawerLocked$, }; } @@ -353,16 +353,6 @@ export interface ChromeStart { */ setIsVisible(isVisible: boolean): void; - /** - * Get an observable of the current collapsed state of the chrome. - */ - getIsCollapsed$(): Observable; - - /** - * Set the collapsed state of the chrome navigation. - */ - setIsCollapsed(isCollapsed: boolean): void; - /** * Get the current set of classNames that will be set on the application container. */ @@ -413,6 +403,11 @@ export interface ChromeStart { * @param url The updated support URL */ setHelpSupportUrl(url: string): void; + + /** + * Get an observable of the current locked state of the nav drawer. + */ + getIsNavDrawerLocked$(): Observable; } /** @internal */ diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index c9a583f39b30c..4dec084fd8a83 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -30,6 +30,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Component, createRef } from 'react'; +import classnames from 'classnames'; import * as Rx from 'rxjs'; import { ChromeBadge, @@ -68,8 +69,8 @@ export interface HeaderProps { navControlsLeft$: Rx.Observable; navControlsRight$: Rx.Observable; basePath: HttpStart['basePath']; - isLocked?: boolean; - onIsLockedUpdate?: OnIsLockedUpdate; + isLocked$: Rx.Observable; + onIsLockedUpdate: OnIsLockedUpdate; } interface State { @@ -81,6 +82,7 @@ interface State { navControlsLeft: readonly ChromeNavControl[]; navControlsRight: readonly ChromeNavControl[]; currentAppId: string | undefined; + isLocked: boolean; } export class Header extends Component { @@ -99,6 +101,7 @@ export class Header extends Component { navControlsLeft: [], navControlsRight: [], currentAppId: '', + isLocked: false, }; } @@ -109,11 +112,12 @@ export class Header extends Component { this.props.forceAppSwitcherNavigation$, this.props.navLinks$, this.props.recentlyAccessed$, - // Types for combineLatest only handle up to 6 inferred types so we combine these two separately. + // Types for combineLatest only handle up to 6 inferred types so we combine these separately. Rx.combineLatest( this.props.navControlsLeft$, this.props.navControlsRight$, - this.props.application.currentAppId$ + this.props.application.currentAppId$, + this.props.isLocked$ ) ).subscribe({ next: ([ @@ -122,7 +126,7 @@ export class Header extends Component { forceNavigation, navLinks, recentlyAccessed, - [navControlsLeft, navControlsRight, currentAppId], + [navControlsLeft, navControlsRight, currentAppId, isLocked], ]) => { this.setState({ appTitle, @@ -133,6 +137,7 @@ export class Header extends Component { navControlsLeft, navControlsRight, currentAppId, + isLocked, }); }, }); @@ -181,8 +186,16 @@ export class Header extends Component { return null; } + const className = classnames( + 'chrHeaderWrapper', + { + 'chrHeaderWrapper--navIsLocked': this.state.isLocked, + }, + 'hide-for-sharing' + ); + return ( -
    +
    @@ -220,7 +233,7 @@ export class Header extends Component { = props => { - const initialIsLocked = localStorage.getItem(IS_LOCKED_KEY); - const [isLocked, setIsLocked] = useState(initialIsLocked === 'true'); - const setIsLockedStored = (locked: boolean) => { - localStorage.setItem(IS_LOCKED_KEY, `${locked}`); - setIsLocked(locked); - }; - const className = classnames( - 'chrHeaderWrapper', - { - 'chrHeaderWrapper--navIsLocked': isLocked, - }, - 'hide-for-sharing' - ); - return ( -
    -
    -
    - ); -}; diff --git a/src/core/public/chrome/ui/header/index.ts b/src/core/public/chrome/ui/header/index.ts index 4521f1f74b31b..49e002a66d939 100644 --- a/src/core/public/chrome/ui/header/index.ts +++ b/src/core/public/chrome/ui/header/index.ts @@ -18,7 +18,6 @@ */ export { Header, HeaderProps } from './header'; -export { HeaderWrapper } from './header_wrapper'; export { ChromeHelpExtensionMenuLink, ChromeHelpExtensionMenuCustomLink, diff --git a/src/core/public/chrome/ui/index.ts b/src/core/public/chrome/ui/index.ts index 81b2fdfb0fcc0..460e19b7d9780 100644 --- a/src/core/public/chrome/ui/index.ts +++ b/src/core/public/chrome/ui/index.ts @@ -20,7 +20,6 @@ export { LoadingIndicator } from './loading_indicator'; export { Header, - HeaderWrapper, ChromeHelpExtensionMenuLink, ChromeHelpExtensionMenuCustomLink, ChromeHelpExtensionMenuDiscussLink, diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index fa5dc745e6931..7428280b2dccb 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -337,7 +337,7 @@ export interface ChromeStart { getBrand$(): Observable; getBreadcrumbs$(): Observable; getHelpExtension$(): Observable; - getIsCollapsed$(): Observable; + getIsNavDrawerLocked$(): Observable; getIsVisible$(): Observable; navControls: ChromeNavControls; navLinks: ChromeNavLinks; @@ -349,7 +349,6 @@ export interface ChromeStart { setBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]): void; setHelpExtension(helpExtension?: ChromeHelpExtension): void; setHelpSupportUrl(url: string): void; - setIsCollapsed(isCollapsed: boolean): void; setIsVisible(isVisible: boolean): void; } diff --git a/src/legacy/ui/public/chrome/chrome.js b/src/legacy/ui/public/chrome/chrome.js index 3355870eabfe7..7a75ad906a870 100644 --- a/src/legacy/ui/public/chrome/chrome.js +++ b/src/legacy/ui/public/chrome/chrome.js @@ -28,7 +28,6 @@ import '../private'; import '../promises'; import '../directives/storage'; import '../directives/watch_multi'; -import './services'; import '../react_components'; import '../i18n'; diff --git a/src/legacy/ui/public/chrome/services/global_nav_state.js b/src/legacy/ui/public/chrome/services/global_nav_state.js deleted file mode 100644 index 5a67806852fe8..0000000000000 --- a/src/legacy/ui/public/chrome/services/global_nav_state.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 { distinctUntilChanged } from 'rxjs/operators'; -import { npStart } from 'ui/new_platform'; -import { uiModules } from '../../modules'; - -const newPlatformChrome = npStart.core.chrome; - -uiModules.get('kibana').service('globalNavState', $rootScope => { - let isOpen = false; - newPlatformChrome - .getIsCollapsed$() - .pipe(distinctUntilChanged()) - .subscribe(isCollapsed => { - $rootScope.$evalAsync(() => { - isOpen = !isCollapsed; - $rootScope.$broadcast('globalNavState:change'); - }); - }); - - return { - isOpen: () => isOpen, - - setOpen: newValue => { - newPlatformChrome.setIsCollapsed(!newValue); - }, - }; -}); diff --git a/src/legacy/ui/public/chrome/services/index.js b/src/legacy/ui/public/chrome/services/index.js deleted file mode 100644 index 3b3967f51b2ff..0000000000000 --- a/src/legacy/ui/public/chrome/services/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 './global_nav_state'; From c1435db29f8d90688ac2a645c5778ad2f438823f Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 17 Mar 2020 18:15:58 -0700 Subject: [PATCH 70/76] Edits UI text for ML nodes and job button (#60184) * Edits UI text for ML nodes and job button * Update x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js Co-Authored-By: Brandon Morelli * Update x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js Co-Authored-By: Brandon Morelli Co-authored-by: Elastic Machine Co-authored-by: Brandon Morelli --- .../ServiceIntegrations/MachineLearningFlyout/view.tsx | 2 +- .../__snapshots__/explorer_no_jobs_found.test.js.snap | 2 +- .../explorer_no_jobs_found/explorer_no_jobs_found.js | 5 +---- .../jobs_list/components/jobs_stats_bar/jobs_stats_bar.js | 2 +- .../jobs_list/components/new_job_button/new_job_button.js | 2 +- .../overview/components/anomaly_detection_panel/utils.ts | 2 +- x-pack/plugins/ml/public/application/services/job_service.js | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx index 31fc4db8f1a2f..cff190cd98a11 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx @@ -209,7 +209,7 @@ export function MachineLearningFlyoutView({ {i18n.translate( 'xpack.apm.serviceDetails.enableAnomalyDetectionPanel.createNewJobButtonLabel', { - defaultMessage: 'Create new job' + defaultMessage: 'Create job' } )} diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap index 8aec3c8336da9..c6503a639997d 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap @@ -9,7 +9,7 @@ exports[`ExplorerNoInfluencersFound snapshot 1`] = ` href="ml#/jobs" > diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js index 5cce2e1eece95..6f391f9746f23 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js @@ -23,10 +23,7 @@ export const ExplorerNoJobsFound = () => ( } actions={ - + } data-test-subj="mlNoJobsFound" diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js index 08155f3f4edba..3c791ff658978 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js @@ -15,7 +15,7 @@ function createJobStats(jobsSummaryList) { const jobStats = { activeNodes: { label: i18n.translate('xpack.ml.jobsList.statsBar.activeMLNodesLabel', { - defaultMessage: 'Active ML Nodes', + defaultMessage: 'Active ML nodes', }), value: 0, show: true, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js index cacca839a4f59..1297ca5b9afd1 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js @@ -29,7 +29,7 @@ export function NewJobButton() { > ); diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts index eab40c0f577f8..b030a1ef45ab0 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts @@ -72,7 +72,7 @@ export function getStatsBarData(jobsList: any) { const jobStats = { activeNodes: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { - defaultMessage: 'Active ML Nodes', + defaultMessage: 'Active ML nodes', }), value: 0, show: true, diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index f092e85bef5ce..e087740ec0e99 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -45,7 +45,7 @@ class JobService { this.jobStats = { activeNodes: { label: i18n.translate('xpack.ml.jobService.activeMLNodesLabel', { - defaultMessage: 'Active ML Nodes', + defaultMessage: 'Active ML nodes', }), value: 0, show: true, From 2207e0ab265fe0a7c204fdd54a3edbea732b283f Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 17 Mar 2020 18:20:00 -0700 Subject: [PATCH 71/76] Index Action - Moved index params fields to connector config (#60349) * Moved index params fields to connector config * Fixed type check issue * Fixing functional tests * Fixed due to comments * Fixed functional tests * Fixed tests and type check --- .../builtin_action_types/es_index.test.ts | 121 +++---- .../server/builtin_action_types/es_index.ts | 34 +- .../public/application/boot.tsx | 2 +- .../builtin_action_types/es_index.test.tsx | 39 +-- .../builtin_action_types/es_index.tsx | 297 ++++++++++++++---- .../components/builtin_action_types/types.ts | 7 +- .../threshold/expression.tsx | 95 +----- .../builtin_alert_types/threshold/types.ts | 6 - .../threshold/visualization.tsx | 2 +- .../action_connector_form.test.tsx | 1 + .../action_connector_form.tsx | 4 + .../connector_add_flyout.tsx | 1 + .../connector_add_modal.tsx | 1 + .../connector_edit_flyout.tsx | 1 + .../public/common/index_controls/index.ts | 90 ++++++ .../lib/index_threshold_api.ts} | 3 +- .../triggers_actions_ui/public/types.ts | 6 +- .../actions/builtin_action_types/es_index.ts | 119 ++++--- .../actions/builtin_action_types/es_index.ts | 32 +- 19 files changed, 528 insertions(+), 333 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts rename x-pack/plugins/triggers_actions_ui/public/{application/components/builtin_alert_types/threshold/lib/api.ts => common/lib/index_threshold_api.ts} (96%) diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts index 0be1983477256..7eded9bb40964 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts @@ -43,18 +43,46 @@ describe('actionTypeRegistry.get() works', () => { describe('config validation', () => { test('config validation succeeds when config is valid', () => { - const config: Record = {}; + const config: Record = { + index: 'testing-123', + refresh: false, + }; expect(validateConfig(actionType, config)).toEqual({ ...config, - index: null, + index: 'testing-123', + refresh: false, }); - config.index = 'testing-123'; + config.executionTimeField = 'field-123'; expect(validateConfig(actionType, config)).toEqual({ ...config, index: 'testing-123', + refresh: false, + executionTimeField: 'field-123', }); + + delete config.index; + + expect(() => { + validateConfig(actionType, { index: 666 }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type config: [index]: expected value of type [string] but got [number]"` + ); + delete config.executionTimeField; + + expect(() => { + validateConfig(actionType, { index: 'testing-123', executionTimeField: true }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type config: [executionTimeField]: expected value of type [string] but got [boolean]"` + ); + + delete config.refresh; + expect(() => { + validateConfig(actionType, { index: 'testing-123', refresh: 'foo' }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type config: [refresh]: expected value of type [boolean] but got [string]"` + ); }); test('config validation fails when config is not valid', () => { @@ -65,46 +93,16 @@ describe('config validation', () => { expect(() => { validateConfig(actionType, baseConfig); }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type config: [indeX]: definition for this key is missing"` + `"error validating action type config: [index]: expected value of type [string] but got [undefined]"` ); - - delete baseConfig.user; - baseConfig.index = 666; - - expect(() => { - validateConfig(actionType, baseConfig); - }).toThrowErrorMatchingInlineSnapshot(` -"error validating action type config: [index]: types that failed validation: -- [index.0]: expected value of type [string] but got [number] -- [index.1]: expected value to equal [null]" -`); }); }); describe('params validation', () => { test('params validation succeeds when params is valid', () => { const params: Record = { - index: 'testing-123', - executionTimeField: 'field-used-for-time', - refresh: true, documents: [{ rando: 'thing' }], }; - expect(validateParams(actionType, params)).toMatchInlineSnapshot(` - Object { - "documents": Array [ - Object { - "rando": "thing", - }, - ], - "executionTimeField": "field-used-for-time", - "index": "testing-123", - "refresh": true, - } - `); - - delete params.index; - delete params.refresh; - delete params.executionTimeField; expect(validateParams(actionType, params)).toMatchInlineSnapshot(` Object { "documents": Array [ @@ -129,24 +127,6 @@ describe('params validation', () => { `"error validating action params: [documents]: expected value of type [array] but got [undefined]"` ); - expect(() => { - validateParams(actionType, { index: 666 }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [index]: expected value of type [string] but got [number]"` - ); - - expect(() => { - validateParams(actionType, { executionTimeField: true }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [executionTimeField]: expected value of type [string] but got [boolean]"` - ); - - expect(() => { - validateParams(actionType, { refresh: 'foo' }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [refresh]: expected value of type [boolean] but got [string]"` - ); - expect(() => { validateParams(actionType, { documents: ['should be an object'] }); }).toThrowErrorMatchingInlineSnapshot( @@ -162,13 +142,10 @@ describe('execute()', () => { let params: ActionParamsType; let executorOptions: ActionTypeExecutorOptions; - // minimal params, index via param - config = { index: null }; + // minimal params + config = { index: 'index-value', refresh: false, executionTimeField: undefined }; params = { - index: 'index-via-param', documents: [{ jim: 'bob' }], - executionTimeField: undefined, - refresh: undefined, }; const actionId = 'some-id'; @@ -190,19 +167,17 @@ describe('execute()', () => { "jim": "bob", }, ], - "index": "index-via-param", + "index": "index-value", + "refresh": false, }, ], ] `); - // full params (except index), index via config - config = { index: 'index-via-config' }; + // full params + config = { index: 'index-value', executionTimeField: 'field_to_use_for_time', refresh: true }; params = { - index: undefined, documents: [{ jimbob: 'jr' }], - executionTimeField: 'field_to_use_for_time', - refresh: true, }; executorOptions = { actionId, config, secrets, params, services }; @@ -226,20 +201,17 @@ describe('execute()', () => { "jimbob": "jr", }, ], - "index": "index-via-config", + "index": "index-value", "refresh": true, }, ], ] `); - // minimal params, index via config and param - config = { index: 'index-via-config' }; + // minimal params + config = { index: 'index-value', executionTimeField: undefined, refresh: false }; params = { - index: 'index-via-param', documents: [{ jim: 'bob' }], - executionTimeField: undefined, - refresh: undefined, }; executorOptions = { actionId, config, secrets, params, services }; @@ -259,19 +231,17 @@ describe('execute()', () => { "jim": "bob", }, ], - "index": "index-via-config", + "index": "index-value", + "refresh": false, }, ], ] `); // multiple documents - config = { index: null }; + config = { index: 'index-value', executionTimeField: undefined, refresh: false }; params = { - index: 'index-via-param', documents: [{ a: 1 }, { b: 2 }], - executionTimeField: undefined, - refresh: undefined, }; executorOptions = { actionId, config, secrets, params, services }; @@ -297,7 +267,8 @@ describe('execute()', () => { "b": 2, }, ], - "index": "index-via-param", + "index": "index-value", + "refresh": false, }, ], ] diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts index f8217046b2ea5..b1fe5e3af2d11 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts @@ -8,7 +8,6 @@ import { curry } from 'lodash'; import { i18n } from '@kbn/i18n'; import { schema, TypeOf } from '@kbn/config-schema'; -import { nullableType } from './lib/nullable'; import { Logger } from '../../../../../src/core/server'; import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../types'; @@ -17,7 +16,9 @@ import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from export type ActionTypeConfigType = TypeOf; const ConfigSchema = schema.object({ - index: nullableType(schema.string()), + index: schema.string(), + refresh: schema.boolean({ defaultValue: false }), + executionTimeField: schema.maybe(schema.string()), }); // params definition @@ -28,9 +29,6 @@ export type ActionParamsType = TypeOf; // - timeout not added here, as this seems to be a generic thing we want to do // eventually: https://github.com/elastic/kibana/projects/26#card-24087404 const ParamsSchema = schema.object({ - index: schema.maybe(schema.string()), - executionTimeField: schema.maybe(schema.string()), - refresh: schema.maybe(schema.boolean()), documents: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), }); @@ -60,27 +58,12 @@ async function executor( const params = execOptions.params as ActionParamsType; const services = execOptions.services; - if (config.index == null && params.index == null) { - const message = i18n.translate('xpack.actions.builtin.esIndex.indexParamRequiredErrorMessage', { - defaultMessage: 'index param needs to be set because not set in config for action', - }); - return { - status: 'error', - actionId, - message, - }; - } - - if (config.index != null && params.index != null) { - logger.debug(`index passed in params overridden by index set in config for action ${actionId}`); - } - - const index = config.index || params.index; + const index = config.index; const bulkBody = []; for (const document of params.documents) { - if (params.executionTimeField != null) { - document[params.executionTimeField] = new Date(); + if (config.executionTimeField != null) { + document[config.executionTimeField] = new Date(); } bulkBody.push({ index: {} }); @@ -92,9 +75,7 @@ async function executor( body: bulkBody, }; - if (params.refresh != null) { - bulkParams.refresh = params.refresh; - } + bulkParams.refresh = config.refresh; let result; try { @@ -103,6 +84,7 @@ async function executor( const message = i18n.translate('xpack.actions.builtin.esIndex.errorIndexingErrorMessage', { defaultMessage: 'error indexing documents', }); + logger.error(`error indexing documents: ${err.message}`); return { status: 'error', actionId, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx index a458472c6d753..c157f923e4447 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx @@ -9,7 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { SavedObjectsClientContract } from 'src/core/public'; import { App, AppDeps } from './app'; -import { setSavedObjectsClient } from '../application/components/builtin_alert_types/threshold/lib/api'; +import { setSavedObjectsClient } from '../common/lib/index_threshold_api'; interface BootDeps extends AppDeps { element: HTMLElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx index d44787f0c4ed6..f1d4790e67bbe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx @@ -9,6 +9,7 @@ import { TypeRegistry } from '../../type_registry'; import { registerBuiltInActionTypes } from './index'; import { ActionTypeModel, ActionParamsProps } from '../../../types'; import { IndexActionParams, EsIndexActionConnector } from './types'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; const ACTION_TYPE_ID = '.index'; let actionTypeModel: ActionTypeModel; @@ -38,16 +39,15 @@ describe('index connector validation', () => { name: 'es_index', config: { index: 'test_es_index', + refresh: false, + executionTimeField: '1', }, } as EsIndexActionConnector; expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: {}, - }); - - delete actionConnector.config.index; - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: {}, + errors: { + index: [], + }, }); }); }); @@ -55,9 +55,6 @@ describe('index connector validation', () => { describe('action params validation', () => { test('action params validation succeeds when action params is valid', () => { const actionParams = { - index: 'test', - refresh: false, - executionTimeField: '1', documents: ['test'], }; @@ -75,6 +72,8 @@ describe('action params validation', () => { describe('IndexActionConnectorFields renders', () => { test('all connector fields is rendered', () => { + const mocks = coreMock.createSetup(); + expect(actionTypeModel.actionConnectorFields).not.toBeNull(); if (!actionTypeModel.actionConnectorFields) { return; @@ -87,23 +86,21 @@ describe('IndexActionConnectorFields renders', () => { name: 'es_index', config: { index: 'test', + refresh: false, + executionTimeField: 'test1', }, } as EsIndexActionConnector; const wrapper = mountWithIntl( {}} editActionSecrets={() => {}} + http={mocks.http} /> ); - expect(wrapper.find('[data-test-subj="indexInput"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="indexInput"]') - .first() - .prop('value') - ).toBe('test'); + expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy(); }); }); @@ -117,8 +114,6 @@ describe('IndexParamsFields renders', () => { ActionParamsProps >; const actionParams = { - index: 'test_index', - refresh: false, documents: ['test'], }; const wrapper = mountWithIntl( @@ -129,13 +124,11 @@ describe('IndexParamsFields renders', () => { index={0} /> ); - expect(wrapper.find('[data-test-subj="indexInput"]').length > 0).toBeTruthy(); expect( wrapper - .find('[data-test-subj="indexInput"]') + .find('[data-test-subj="actionIndexDoc"]') .first() .prop('value') - ).toBe('test_index'); - expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy(); + ).toBe('"test"'); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx index 6af54d2bf15b4..b3e62e022c412 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx @@ -3,8 +3,18 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; -import { EuiFieldText, EuiFormRow, EuiSwitch, EuiSpacer } from '@elastic/eui'; +import React, { Fragment, useState, useEffect } from 'react'; +import { + EuiFormRow, + EuiSwitch, + EuiSpacer, + EuiCodeEditor, + EuiComboBox, + EuiComboBoxOptionOption, + EuiSelect, + EuiTitle, + EuiIconTip, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { @@ -14,6 +24,13 @@ import { ActionParamsProps, } from '../../../types'; import { IndexActionParams, EsIndexActionConnector } from './types'; +import { getTimeFieldOptions } from '../../../common/lib/get_time_options'; +import { + firstFieldOption, + getFields, + getIndexOptions, + getIndexPatterns, +} from '../../../common/index_controls'; export function getActionType(): ActionTypeModel { return { @@ -25,8 +42,23 @@ export function getActionType(): ActionTypeModel { defaultMessage: 'Index data into Elasticsearch.', } ), - validateConnector: (): ValidationResult => { - return { errors: {} }; + validateConnector: (action: EsIndexActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + index: new Array(), + }; + validationResult.errors = errors; + if (!action.config.index) { + errors.index.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText', + { + defaultMessage: 'Index is required.', + } + ) + ); + } + return validationResult; }, actionConnectorFields: IndexActionConnectorFields, actionParamsFields: IndexParamsFields, @@ -38,33 +70,189 @@ export function getActionType(): ActionTypeModel { const IndexActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig }) => { - const { index } = action.config; +>> = ({ action, editActionConfig, errors, http }) => { + const { index, refresh, executionTimeField } = action.config; + const [hasTimeFieldCheckbox, setTimeFieldCheckboxState] = useState( + executionTimeField !== undefined + ); + + const [indexPatterns, setIndexPatterns] = useState([]); + const [indexOptions, setIndexOptions] = useState([]); + const [timeFieldOptions, setTimeFieldOptions] = useState([firstFieldOption]); + const [isIndiciesLoading, setIsIndiciesLoading] = useState(false); + + useEffect(() => { + const indexPatternsFunction = async () => { + setIndexPatterns(await getIndexPatterns()); + if (index) { + const currentEsFields = await getFields(http!, [index]); + const timeFields = getTimeFieldOptions(currentEsFields as any); + setTimeFieldOptions([firstFieldOption, ...timeFields]); + } + }; + indexPatternsFunction(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( - - + +
    + +
    +
    + + ) => { - editActionConfig('index', e.target.value); - }} - onBlur={() => { - if (!index) { - editActionConfig('index', ''); + label={ + + } + isInvalid={errors.index.length > 0 && index !== undefined} + error={errors.index} + helpText={ + + } + > + 0 && index !== undefined} + noSuggestions={!indexOptions.length} + options={indexOptions} + data-test-subj="connectorIndexesComboBox" + selectedOptions={ + index + ? [ + { + value: index, + label: index, + }, + ] + : [] } + onChange={async (selected: EuiComboBoxOptionOption[]) => { + editActionConfig('index', selected[0].value); + const indices = selected.map(s => s.value as string); + + // reset time field and expression fields if indices are deleted + if (indices.length === 0) { + setTimeFieldOptions([]); + return; + } + const currentEsFields = await getFields(http!, indices); + const timeFields = getTimeFieldOptions(currentEsFields as any); + + setTimeFieldOptions([firstFieldOption, ...timeFields]); + }} + onSearchChange={async search => { + setIsIndiciesLoading(true); + setIndexOptions(await getIndexOptions(http!, search, indexPatterns)); + setIsIndiciesLoading(false); + }} + onBlur={() => { + if (!index) { + editActionConfig('index', ''); + } + }} + /> + + + { + editActionConfig('refresh', e.target.checked); }} + label={ + <> + {' '} + + + } + /> + + { + setTimeFieldCheckboxState(!hasTimeFieldCheckbox); + }} + label={ + <> + + + + } /> -
    + + {hasTimeFieldCheckbox ? ( + <> + + } + > + { + editActionConfig('executionTimeField', e.target.value); + }} + onBlur={() => { + if (executionTimeField === undefined) { + editActionConfig('executionTimeField', ''); + } + }} + /> + + + ) : null} + ); }; @@ -73,47 +261,48 @@ const IndexParamsFields: React.FunctionComponent { - const { refresh } = actionParams; + const { documents } = actionParams; + + function onDocumentsChange(updatedDocuments: string) { + try { + const documentsJSON = JSON.parse(updatedDocuments); + editAction('documents', [documentsJSON], index); + // eslint-disable-next-line no-empty + } catch (e) {} + } return ( - ) => { - editAction('index', e.target.value, index); + 0 ? documents[0] : {}, null, 2)} + onChange={onDocumentsChange} + width="100%" + height="auto" + minLines={6} + maxLines={30} + isReadOnly={false} + setOptions={{ + showLineNumbers: true, + tabSize: 2, }} - onBlur={() => { - if (!actionParams.index) { - editAction('index', '', index); - } + editorProps={{ + $blockScrolling: Infinity, }} + showGutter={true} /> - - { - editAction('refresh', e.target.checked, index); - }} - label={ - - } - /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts index 45a08b2d5263a..c0ddd6791e90e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts @@ -39,9 +39,6 @@ export interface PagerDutyActionParams { } export interface IndexActionParams { - index?: string; - refresh?: boolean; - executionTimeField?: string; documents: string[]; } @@ -85,7 +82,9 @@ export interface EmailActionConnector extends ActionConnector { } interface EsIndexConfig { - index?: string; + index: string; + executionTimeField?: string; + refresh?: boolean; } export interface EsIndexActionConnector extends ActionConnector { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx index 2bf779e550618..5c7f48de81f75 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx @@ -23,12 +23,13 @@ import { EuiEmptyPrompt, EuiText, } from '@elastic/eui'; -import { COMPARATORS, builtInComparators } from '../../../../common/constants'; import { - getMatchingIndicesForThresholdAlertType, - getThresholdAlertTypeFields, - loadIndexPatterns, -} from './lib/api'; + firstFieldOption, + getIndexPatterns, + getIndexOptions, + getFields, +} from '../../../../common/index_controls'; +import { COMPARATORS, builtInComparators } from '../../../../common/constants'; import { getTimeFieldOptions } from '../../../../common/lib/get_time_options'; import { ThresholdVisualization } from './visualization'; import { WhenExpression } from '../../../../common'; @@ -95,15 +96,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 ); - const getIndexPatterns = async () => { - const indexPatternObjects = await loadIndexPatterns(); - const titles = indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); - setIndexPatterns(titles); - }; - const expressionErrorMessage = i18n.translate( 'xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage', { @@ -150,7 +136,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent 0) { - const currentEsFields = await getFields(index); + const currentEsFields = await getFields(http, index); const timeFields = getTimeFieldOptions(currentEsFields as any); setEsFields(currentEsFields); @@ -158,12 +144,11 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent { - return await getThresholdAlertTypeFields({ indexes, http }); - }; - useEffect(() => { - getIndexPatterns(); + const indexPatternsFunction = async () => { + setIndexPatterns(await getIndexPatterns()); + }; + indexPatternsFunction(); }, []); useEffect(() => { @@ -171,60 +156,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent; - } - - const getIndexOptions = async (pattern: string, indexPatternsParam: string[]) => { - const options: IOption[] = []; - - if (!pattern) { - return options; - } - - const matchingIndices = (await getMatchingIndicesForThresholdAlertType({ - pattern, - http, - })) as string[]; - const matchingIndexPatterns = indexPatternsParam.filter(anIndexPattern => { - return anIndexPattern.includes(pattern); - }) as string[]; - - if (matchingIndices.length || matchingIndexPatterns.length) { - const matchingOptions = _.uniq([...matchingIndices, ...matchingIndexPatterns]); - - options.push({ - label: i18n.translate( - 'xpack.triggersActionsUI.sections.alertAdd.threshold.indicesAndIndexPatternsLabel', - { - defaultMessage: 'Based on your indices and index patterns', - } - ), - options: matchingOptions.map(match => { - return { - label: match, - value: match, - }; - }), - }); - } - - options.push({ - label: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.threshold.chooseLabel', { - defaultMessage: 'Choose…', - }), - options: [ - { - value: pattern, - label: pattern, - }, - ], - }); - - return options; - }; - const indexPopover = ( @@ -285,7 +216,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent { setIsIndiciesLoading(true); - setIndexOptions(await getIndexOptions(search, indexPatterns)); + setIndexOptions(await getIndexOptions(http, search, indexPatterns)); setIsIndiciesLoading(false); }} onBlur={() => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts index d5b64f1489b8d..356b0fbbc0845 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - TimeSeriesResult, - TimeSeriesResultRow, - MetricResult, -} from '../../../../../../alerting_builtins/common/alert_types/index_threshold'; - export interface Comparator { text: string; value: string; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx index f27e35fe7609d..0bcaa83127468 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx @@ -23,7 +23,7 @@ import { import moment from 'moment-timezone'; import { EuiCallOut, EuiLoadingChart, EuiSpacer, EuiEmptyPrompt, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getThresholdAlertVisualizationData } from './lib/api'; +import { getThresholdAlertVisualizationData } from '../../../../common/lib/index_threshold_api'; import { AggregationType, Comparator } from '../../../../common/types'; import { AlertsContextValue } from '../../../context/alerts_context'; import { IndexThresholdAlertParams } from './types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx index f68cc5759fb54..1c70e42e7ae72 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx @@ -68,6 +68,7 @@ describe('action_connector_form', () => { dispatch={() => {}} errors={{ name: [] }} actionTypeRegistry={deps.actionTypeRegistry} + http={deps.http} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index e221fff64048e..57333d8032793 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -15,6 +15,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { HttpSetup } from 'kibana/public'; import { ReducerAction } from './connector_reducer'; import { ActionConnector, IErrorObject, ActionTypeModel } from '../../../types'; import { TypeRegistry } from '../../type_registry'; @@ -47,6 +48,7 @@ interface ActionConnectorProps { }; errors: IErrorObject; actionTypeRegistry: TypeRegistry; + http: HttpSetup; } export const ActionConnectorForm = ({ @@ -56,6 +58,7 @@ export const ActionConnectorForm = ({ serverError, errors, actionTypeRegistry, + http, }: ActionConnectorProps) => { const setActionProperty = (key: string, value: any) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); @@ -148,6 +151,7 @@ export const ActionConnectorForm = ({ errors={errors} editActionConfig={setActionConfigProperty} editActionSecrets={setActionSecretsProperty} + http={http} /> ) : null} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx index f265a1de6f56a..9aea2419ec619 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx @@ -104,6 +104,7 @@ export const ConnectorAddFlyout = ({ dispatch={dispatch} errors={errors} actionTypeRegistry={actionTypeRegistry} + http={http} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx index c7f52fb462cc0..977a908fd86f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx @@ -152,6 +152,7 @@ export const ConnectorAddModal = ({ serverError={serverError} errors={errors} actionTypeRegistry={actionTypeRegistry} + http={http} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index d0dcff9ef6a94..39c0b7255a7b9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -135,6 +135,7 @@ export const ConnectorEditFlyout = ({ actionTypeName={connector.actionType} dispatch={dispatch} actionTypeRegistry={actionTypeRegistry} + http={http} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts new file mode 100644 index 0000000000000..32fb35d6adebb --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HttpSetup } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { + loadIndexPatterns, + getMatchingIndicesForThresholdAlertType, + getThresholdAlertTypeFields, +} from '../lib/index_threshold_api'; + +export interface IOption { + label: string; + options: Array<{ value: string; label: string }>; +} + +export const getIndexPatterns = async () => { + const indexPatternObjects = await loadIndexPatterns(); + return indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); +}; + +export const getIndexOptions = async ( + http: HttpSetup, + pattern: string, + indexPatternsParam: string[] +) => { + const options: IOption[] = []; + + if (!pattern) { + return options; + } + + const matchingIndices = (await getMatchingIndicesForThresholdAlertType({ + pattern, + http, + })) as string[]; + const matchingIndexPatterns = indexPatternsParam.filter(anIndexPattern => { + return anIndexPattern.includes(pattern); + }) as string[]; + + if (matchingIndices.length || matchingIndexPatterns.length) { + const matchingOptions = _.uniq([...matchingIndices, ...matchingIndexPatterns]); + + options.push({ + label: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel', + { + defaultMessage: 'Based on your index patterns', + } + ), + options: matchingOptions.map(match => { + return { + label: match, + value: match, + }; + }), + }); + } + + options.push({ + label: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel', + { + defaultMessage: 'Choose…', + } + ), + options: [ + { + value: pattern, + label: pattern, + }, + ], + }); + + return options; +}; + +export const getFields = async (http: HttpSetup, indexes: string[]) => { + return await getThresholdAlertTypeFields({ indexes, http }); +}; + +export const firstFieldOption = { + text: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldOptionLabel', { + defaultMessage: 'Select a field', + }), + value: '', +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/lib/api.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/lib/api.ts rename to x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts index 064f05b415d42..9ec198a43646f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/lib/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { HttpSetup } from 'kibana/public'; -import { TimeSeriesResult } from '../types'; -export { TimeSeriesResult } from '../types'; +import { TimeSeriesResult } from '../../../../alerting_builtins/common/alert_types/index_threshold'; const INDEX_THRESHOLD_API_ROOT = '/api/alerting_builtins/index_threshold'; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index d9681e2474f00..900521830571c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { HttpSetup } from 'kibana/public'; import { ActionGroup } from '../../alerting/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; @@ -20,11 +21,12 @@ export type AlertTypeIndex = Record; export type ActionTypeRegistryContract = PublicMethodsOf>; export type AlertTypeRegistryContract = PublicMethodsOf>; -export interface ActionConnectorFieldsProps { - action: TActionCOnnector; +export interface ActionConnectorFieldsProps { + action: TActionConnector; editActionConfig: (property: string, value: any) => void; editActionSecrets: (property: string, value: any) => void; errors: { [key: string]: string[] }; + http?: HttpSetup; } export interface ActionParamsProps { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts index 1aa0f8e2c9f16..6d76a00d39b97 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts @@ -31,7 +31,9 @@ export default function indexTest({ getService }: FtrProviderContext) { .send({ name: 'An index action', actionTypeId: '.index', - config: {}, + config: { + index: ES_TEST_INDEX_NAME, + }, secrets: {}, }) .expect(200); @@ -41,7 +43,8 @@ export default function indexTest({ getService }: FtrProviderContext) { name: 'An index action', actionTypeId: '.index', config: { - index: null, + index: ES_TEST_INDEX_NAME, + refresh: false, }, }); createdActionID = createdAction.id; @@ -55,10 +58,10 @@ export default function indexTest({ getService }: FtrProviderContext) { id: fetchedAction.id, name: 'An index action', actionTypeId: '.index', - config: { index: null }, + config: { index: ES_TEST_INDEX_NAME, refresh: false }, }); - // create action with index config + // create action with all config props const { body: createdActionWithIndex } = await supertest .post('/api/action') .set('kbn-xsrf', 'foo') @@ -67,6 +70,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }) .expect(200); @@ -77,6 +82,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); createdActionIDWithIndex = createdActionWithIndex.id; @@ -92,6 +99,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); }); @@ -111,20 +120,31 @@ export default function indexTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type config: [index]: types that failed validation:\n- [index.0]: expected value of type [string] but got [number]\n- [index.1]: expected value to equal [null]', + 'error validating action type config: [index]: expected value of type [string] but got [number]', }); }); }); it('should execute successly when expected for a single body', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: true, + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ testing: [1, 2, 3] }], - refresh: true, }, }) .expect(200); @@ -136,14 +156,25 @@ export default function indexTest({ getService }: FtrProviderContext) { }); it('should execute successly when expected for with multiple bodies', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: true, + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ testing: [1, 2, 3] }, { Testing: [4, 5, 6] }], - refresh: true, }, }) .expect(200); @@ -169,12 +200,25 @@ export default function indexTest({ getService }: FtrProviderContext) { }); it('should execute successly with refresh false', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: false, + executionTimeField: 'test', + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ refresh: 'not set' }], }, }) @@ -185,57 +229,32 @@ export default function indexTest({ getService }: FtrProviderContext) { items = await getTestIndexItems(es); expect(items.length).to.be.lessThan(2); - const { body: result2 } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + const { body: createdActionWithRefresh } = await supertest + .post('/api/action') .set('kbn-xsrf', 'foo') .send({ - params: { + name: 'An index action', + actionTypeId: '.index', + config: { index: ES_TEST_INDEX_NAME, - documents: [{ refresh: 'true' }], refresh: true, }, + secrets: {}, }) .expect(200); - expect(result2.status).to.eql('ok'); - - items = await getTestIndexItems(es); - expect(items.length).to.eql(2); - }); - - it('should execute unsuccessfully when expected', async () => { - let response; - let result; - - response = await supertest - .post(`/api/action/${createdActionID}/_execute`) + const { body: result2 } = await supertest + .post(`/api/action/${createdActionWithRefresh.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - indeX: ES_TEST_INDEX_NAME, - documents: [{ testing: [1, 2, 3] }], + documents: [{ refresh: 'true' }], }, }) .expect(200); - result = response.body; - expect(result.status).to.equal('error'); - expect(result.message).to.eql( - 'error validating action params: [indeX]: definition for this key is missing' - ); + expect(result2.status).to.eql('ok'); - response = await supertest - .post(`/api/action/${createdActionID}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - documents: [{ testing: [1, 2, 3] }], - }, - }) - .expect(200); - result = response.body; - expect(result.status).to.equal('error'); - expect(result.message).to.eql( - 'index param needs to be set because not set in config for action' - ); + items = await getTestIndexItems(es); + expect(items.length).to.eql(2); }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts index 79e0da3a4c68a..5cc3d7275a7bd 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts @@ -31,7 +31,7 @@ export default function indexTest({ getService }: FtrProviderContext) { .send({ name: 'An index action', actionTypeId: '.index', - config: {}, + config: { index: ES_TEST_INDEX_NAME }, secrets: {}, }) .expect(200); @@ -41,7 +41,8 @@ export default function indexTest({ getService }: FtrProviderContext) { name: 'An index action', actionTypeId: '.index', config: { - index: null, + index: ES_TEST_INDEX_NAME, + refresh: false, }, }); createdActionID = createdAction.id; @@ -55,10 +56,10 @@ export default function indexTest({ getService }: FtrProviderContext) { id: fetchedAction.id, name: 'An index action', actionTypeId: '.index', - config: { index: null }, + config: { index: ES_TEST_INDEX_NAME, refresh: false }, }); - // create action with index config + // create action with all config props const { body: createdActionWithIndex } = await supertest .post('/api/action') .set('kbn-xsrf', 'foo') @@ -67,6 +68,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }) .expect(200); @@ -77,6 +80,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); createdActionIDWithIndex = createdActionWithIndex.id; @@ -92,19 +97,32 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); }); it('should execute successly when expected for a single body', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: true, + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ testing: [1, 2, 3] }], - refresh: true, }, }) .expect(200); From ac5e323af872f0db601724c3c22455bbcd946c45 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Tue, 17 Mar 2020 18:47:54 -0700 Subject: [PATCH 72/76] [Search service] Asynchronous ES search strategy (#53538) * Add async search strategy * Add async search * Fix async strategy and add tests * Move types to separate file * Revert changes to demo search * Update demo search strategy to use async * Add async es search strategy * Return response as rawResponse * Poll after initial request * Add cancellation to search strategies * Add tests * Simplify async search strategy * Move loadingCount to search strategy * Update abort controller library * Bootstrap * Abort when the request is aborted * Add utility and update value suggestions route * Fix bad merge conflict * Update tests * Move to data_enhanced plugin * Remove bad merge * Revert switching abort controller libraries * Revert package.json in lib * Move to previous abort controller * Add support for frozen indices * Fix test to use fake timers to run debounced handlers * Revert changes to example plugin * Fix loading bar not going away when cancelling * Call getSearchStrategy instead of passing directly * Add async demo search strategy * Fix error with setting state * Update how aborting works * Fix type checks * Add test for loading count * Attempt to fix broken example test * Revert changes to test * Fix test * Update name to camelCase * Fix failing test * Don't require data_enhanced in example plugin * Actually send DELETE request * Use waitForCompletion parameter * Use default search params * Add support for rollups * Only make changes needed for frozen indices/rollups * Only make changes needed for frozen indices/rollups * Add back in async functionality * Fix tests/types * Fix issue with sending empty body in GET * Don't include skipped in loaded/total * Don't wait before polling the next time * Simplify search logic * Fix merge error * Review feedback * Fix issue with hits.total Co-authored-by: Elastic Machine --- ...bana-plugin-plugins-data-server.icancel.md | 11 ---- ...lugin-plugins-data-server.isearchcancel.md | 11 ++++ .../kibana-plugin-plugins-data-server.md | 2 +- src/plugins/data/server/index.ts | 2 +- .../search/i_route_handler_search_context.ts | 4 +- src/plugins/data/server/search/i_search.ts | 4 +- .../data/server/search/i_search_strategy.ts | 4 +- src/plugins/data/server/search/index.ts | 8 ++- src/plugins/data/server/server.api.md | 12 ++-- .../public/search/es_search_strategy.ts | 16 ++++-- .../server/search/es_search_strategy.ts | 56 ++++++++++++++++--- 11 files changed, 90 insertions(+), 40 deletions(-) delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md deleted file mode 100644 index 27141c68ae1a7..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ICancel](./kibana-plugin-plugins-data-server.icancel.md) - -## ICancel type - -Signature: - -```typescript -export declare type ICancel = (id: string) => Promise; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md new file mode 100644 index 0000000000000..99c30515e8da6 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchCancel](./kibana-plugin-plugins-data-server.isearchcancel.md) + +## ISearchCancel type + +Signature: + +```typescript +export declare type ISearchCancel = (id: string) => Promise; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 12d53f1a35ea0..e756eb9b72905 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -67,9 +67,9 @@ | Type Alias | Description | | --- | --- | | [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md) | | -| [ICancel](./kibana-plugin-plugins-data-server.icancel.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | | | [ISearch](./kibana-plugin-plugins-data-server.isearch.md) | | +| [ISearchCancel](./kibana-plugin-plugins-data-server.isearchcancel.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | | | [TSearchStrategyProvider](./kibana-plugin-plugins-data-server.tsearchstrategyprovider.md) | Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. | diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 0165486fc2de7..5038b4226fad8 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -166,7 +166,7 @@ export { ParsedInterval } from '../common'; export { ISearch, - ICancel, + ISearchCancel, ISearchOptions, IRequestTypesMap, IResponseTypesMap, diff --git a/src/plugins/data/server/search/i_route_handler_search_context.ts b/src/plugins/data/server/search/i_route_handler_search_context.ts index 89862781b826e..9888c774ea104 100644 --- a/src/plugins/data/server/search/i_route_handler_search_context.ts +++ b/src/plugins/data/server/search/i_route_handler_search_context.ts @@ -17,9 +17,9 @@ * under the License. */ -import { ISearchGeneric, ICancelGeneric } from './i_search'; +import { ISearchGeneric, ISearchCancelGeneric } from './i_search'; export interface IRouteHandlerSearchContext { search: ISearchGeneric; - cancel: ICancelGeneric; + cancel: ISearchCancelGeneric; } diff --git a/src/plugins/data/server/search/i_search.ts b/src/plugins/data/server/search/i_search.ts index ea014c5e136d9..fa4aa72ac7287 100644 --- a/src/plugins/data/server/search/i_search.ts +++ b/src/plugins/data/server/search/i_search.ts @@ -42,7 +42,7 @@ export type ISearchGeneric = Promise; -export type ICancelGeneric = ( +export type ISearchCancelGeneric = ( id: string, strategy?: T ) => Promise; @@ -52,4 +52,4 @@ export type ISearch = ( options?: ISearchOptions ) => Promise; -export type ICancel = (id: string) => Promise; +export type ISearchCancel = (id: string) => Promise; diff --git a/src/plugins/data/server/search/i_search_strategy.ts b/src/plugins/data/server/search/i_search_strategy.ts index 4cfc9608383a9..9b405034f883f 100644 --- a/src/plugins/data/server/search/i_search_strategy.ts +++ b/src/plugins/data/server/search/i_search_strategy.ts @@ -18,7 +18,7 @@ */ import { APICaller } from 'kibana/server'; -import { ISearch, ICancel, ISearchGeneric } from './i_search'; +import { ISearch, ISearchCancel, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; import { ISearchContext } from './i_search_context'; @@ -28,7 +28,7 @@ import { ISearchContext } from './i_search_context'; */ export interface ISearchStrategy { search: ISearch; - cancel?: ICancel; + cancel?: ISearchCancel; } /** diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index 385e96ee803b6..15738a3befb27 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -21,7 +21,13 @@ export { ISearchSetup } from './i_search_setup'; export { ISearchContext } from './i_search_context'; -export { ISearch, ICancel, ISearchOptions, IRequestTypesMap, IResponseTypesMap } from './i_search'; +export { + ISearch, + ISearchCancel, + ISearchOptions, + IRequestTypesMap, + IResponseTypesMap, +} from './i_search'; export { TStrategyTypes } from './strategy_types'; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 2a2d9bb414c14..178b2949a9456 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -329,12 +329,6 @@ export function getDefaultSearchParams(config: SharedGlobalConfig): { restTotalHitsAsInt: boolean; }; -// Warning: (ae-forgotten-export) The symbol "TStrategyTypes" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "ICancel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type ICancel = (id: string) => Promise; - // Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -507,11 +501,17 @@ export interface IResponseTypesMap { [ES_SEARCH_STRATEGY]: IEsSearchResponse; } +// Warning: (ae-forgotten-export) The symbol "TStrategyTypes" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ISearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export type ISearch = (request: IRequestTypesMap[T], options?: ISearchOptions) => Promise; +// Warning: (ae-missing-release-tag) "ISearchCancel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ISearchCancel = (id: string) => Promise; + // Warning: (ae-missing-release-tag) "ISearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts index 25c6a789cca93..c493e8ce86781 100644 --- a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts @@ -10,16 +10,17 @@ import { TSearchStrategyProvider, ISearchContext, ISearch, - SYNC_SEARCH_STRATEGY, getEsPreference, } from '../../../../../src/plugins/data/public'; import { IEnhancedEsSearchRequest, EnhancedSearchParams } from '../../common'; +import { ASYNC_SEARCH_STRATEGY } from './async_search_strategy'; +import { IAsyncSearchOptions } from './types'; export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext ) => { - const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); - const { search: syncSearch } = syncStrategyProvider(context); + const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY); + const { search: asyncSearch } = asyncStrategyProvider(context); const search: ISearch = ( request: IEnhancedEsSearchRequest, @@ -32,9 +33,12 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider; + const asyncOptions: IAsyncSearchOptions = { pollInterval: 0, ...options }; + + return asyncSearch( + { ...request, serverStrategy: ES_SEARCH_STRATEGY }, + asyncOptions + ) as Observable; }; return { search }; diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 69b357196dc32..11f0b9a0dc83c 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -14,10 +14,16 @@ import { TSearchStrategyProvider, ISearch, ISearchOptions, + ISearchCancel, getDefaultSearchParams, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; +export interface AsyncSearchResponse { + id: string; + response: SearchResponse; +} + export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext, caller: APICaller @@ -28,28 +34,62 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider { const config = await context.config$.pipe(first()).toPromise(); const defaultParams = getDefaultSearchParams(config); - const params = { ...defaultParams, ...request.params }; + const params = { ...defaultParams, trackTotalHits: true, ...request.params }; - const rawResponse = (await (request.indexType === 'rollup' + const response = await (request.indexType === 'rollup' ? rollupSearch(caller, { ...request, params }, options) - : caller('search', params, options))) as SearchResponse; + : asyncSearch(caller, { ...request, params }, options)); + + const rawResponse = + request.indexType === 'rollup' + ? (response as SearchResponse) + : (response as AsyncSearchResponse).response; + + if (typeof rawResponse.hits.total !== 'number') { + // @ts-ignore This should be fixed as part of https://github.com/elastic/kibana/issues/26356 + rawResponse.hits.total = rawResponse.hits.total.value; + } + const id = (response as AsyncSearchResponse).id; const { total, failed, successful } = rawResponse._shards; const loaded = failed + successful; - return { total, loaded, rawResponse }; + return { id, total, loaded, rawResponse }; }; - return { search }; + const cancel: ISearchCancel = async id => { + const method = 'DELETE'; + const path = `_async_search/${id}`; + await caller('transport.request', { method, path }); + }; + + return { search, cancel }; }; -function rollupSearch( +function asyncSearch( + caller: APICaller, + request: IEnhancedEsSearchRequest, + options?: ISearchOptions +) { + const { body = undefined, index = undefined, ...params } = request.id ? {} : request.params; + + // If we have an ID, then just poll for that ID, otherwise send the entire request body + const method = request.id ? 'GET' : 'POST'; + const path = request.id ? `_async_search/${request.id}` : `${index}/_async_search`; + + // Wait up to 1s for the response to return + const query = toSnakeCase({ waitForCompletion: '1s', ...params }); + + return caller('transport.request', { method, path, body, query }, options); +} + +async function rollupSearch( caller: APICaller, request: IEnhancedEsSearchRequest, options?: ISearchOptions ) { + const { body, index, ...params } = request.params; const method = 'POST'; - const path = `${request.params.index}/_rollup_search`; - const { body, ...params } = request.params; + const path = `${index}/_rollup_search`; const query = toSnakeCase(params); return caller('transport.request', { method, path, body, query }, options); } From 65a111f189d371e7c67f562eb78df29b45dafcc7 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Tue, 17 Mar 2020 23:31:41 -0400 Subject: [PATCH 73/76] Task/host enhancements (#59671) functional tests and ui updates to endpoint host details --- .../endpoint/common/generate_data.test.ts | 12 +- .../plugins/endpoint/common/generate_data.ts | 8 +- x-pack/plugins/endpoint/common/types.ts | 12 +- .../endpoint/components/header_nav.tsx | 8 +- .../public/applications/endpoint/index.tsx | 86 ++++++------ .../applications/endpoint/store/action.ts | 4 +- .../endpoint/store/hosts/action.ts | 40 ++++++ .../endpoint/store/hosts/index.test.ts | 73 ++++++++++ .../store/{managing => hosts}/index.ts | 6 +- .../{managing => hosts}/middleware.test.ts | 37 ++--- .../store/{managing => hosts}/middleware.ts | 33 ++--- .../mock_host_result_list.ts | 12 +- .../store/{managing => hosts}/reducer.ts | 22 ++- .../endpoint/store/hosts/selectors.ts | 60 ++++++++ .../applications/endpoint/store/index.ts | 6 +- .../endpoint/store/managing/action.ts | 39 ------ .../endpoint/store/managing/index.test.ts | 93 ------------- .../endpoint/store/managing/selectors.ts | 60 -------- .../applications/endpoint/store/reducer.ts | 4 +- .../public/applications/endpoint/types.ts | 16 +-- .../endpoint/view/formatted_date_time.tsx | 24 ++++ .../view/{managing => hosts}/details.tsx | 71 ++++++---- .../view/{managing => hosts}/hooks.ts | 8 +- .../view/{managing => hosts}/index.test.tsx | 31 +++-- .../view/{managing => hosts}/index.tsx | 129 ++++++++++-------- .../url_from_query_params.ts | 4 +- .../endpoint/view/policy/policy_list.tsx | 25 +--- .../endpoint/scripts/resolver_generator.ts | 16 +-- .../endpoint/server/routes/metadata.test.ts | 28 ++-- .../endpoint/server/routes/metadata.ts | 26 ++-- .../api_integration/apis/endpoint/metadata.ts | 42 +++--- .../feature_controls/endpoint_spaces.ts | 19 +-- .../functional/apps/endpoint/header_nav.ts | 14 +- .../endpoint/{management.ts => host_list.ts} | 63 ++++++++- x-pack/test/functional/apps/endpoint/index.ts | 2 +- .../functional/page_objects/endpoint_page.ts | 35 ++++- 36 files changed, 623 insertions(+), 545 deletions(-) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/index.ts (60%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/middleware.test.ts (63%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/middleware.ts (59%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/mock_host_result_list.ts (82%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/reducer.ts (66%) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/details.tsx (62%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/hooks.ts (61%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/index.test.tsx (75%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/index.tsx (55%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/url_from_query_params.ts (78%) rename x-pack/test/functional/apps/endpoint/{management.ts => host_list.ts} (58%) diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts index a687d7af1c590..dfb906c7af606 100644 --- a/x-pack/plugins/endpoint/common/generate_data.test.ts +++ b/x-pack/plugins/endpoint/common/generate_data.test.ts @@ -21,8 +21,8 @@ describe('data generator', () => { const generator1 = new EndpointDocGenerator('seed'); const generator2 = new EndpointDocGenerator('seed'); const timestamp = new Date().getTime(); - const metadata1 = generator1.generateEndpointMetadata(timestamp); - const metadata2 = generator2.generateEndpointMetadata(timestamp); + const metadata1 = generator1.generateHostMetadata(timestamp); + const metadata2 = generator2.generateHostMetadata(timestamp); expect(metadata1).toEqual(metadata2); }); @@ -30,14 +30,14 @@ describe('data generator', () => { const generator1 = new EndpointDocGenerator('seed'); const generator2 = new EndpointDocGenerator('different seed'); const timestamp = new Date().getTime(); - const metadata1 = generator1.generateEndpointMetadata(timestamp); - const metadata2 = generator2.generateEndpointMetadata(timestamp); + const metadata1 = generator1.generateHostMetadata(timestamp); + const metadata2 = generator2.generateHostMetadata(timestamp); expect(metadata1).not.toEqual(metadata2); }); - it('creates endpoint metadata documents', () => { + it('creates host metadata documents', () => { const timestamp = new Date().getTime(); - const metadata = generator.generateEndpointMetadata(timestamp); + const metadata = generator.generateHostMetadata(timestamp); expect(metadata['@timestamp']).toEqual(timestamp); expect(metadata.event.created).toEqual(timestamp); expect(metadata.endpoint).not.toBeNull(); diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 36896e5af6810..2e1d6074d0c2f 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -6,7 +6,7 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; -import { AlertEvent, EndpointEvent, EndpointMetadata, OSFields, HostFields } from './types'; +import { AlertEvent, EndpointEvent, HostMetadata, OSFields, HostFields } from './types'; export type Event = AlertEvent | EndpointEvent; @@ -104,8 +104,8 @@ export class EndpointDocGenerator { this.commonInfo = this.createHostData(); } - // This function will create new values for all the host fields, so documents from a different endpoint can be created - // This provides a convenient way to make documents from multiple endpoints that are all tied to a single seed value + // This function will create new values for all the host fields, so documents from a different host can be created + // This provides a convenient way to make documents from multiple hosts that are all tied to a single seed value public randomizeHostData() { this.commonInfo = this.createHostData(); } @@ -129,7 +129,7 @@ export class EndpointDocGenerator { }; } - public generateEndpointMetadata(ts = new Date().getTime()): EndpointMetadata { + public generateHostMetadata(ts = new Date().getTime()): HostMetadata { return { '@timestamp': ts, event: { diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index aa326c663965d..e423de56bf817 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -83,10 +83,10 @@ export interface AlertResultList { prev: string | null; } -export interface EndpointResultList { - /* the endpoints restricted by the page size */ - endpoints: EndpointMetadata[]; - /* the total number of unique endpoints in the index */ +export interface HostResultList { + /* the hosts restricted by the page size */ + hosts: HostMetadata[]; + /* the total number of unique hosts in the index */ total: number; /* the page size requested */ request_page_size: number; @@ -243,7 +243,7 @@ interface AlertMetadata { */ export type AlertData = AlertEvent & AlertMetadata; -export interface EndpointMetadata { +export type HostMetadata = Immutable<{ '@timestamp': number; event: { created: number; @@ -258,7 +258,7 @@ export interface EndpointMetadata { version: string; }; host: HostFields; -} +}>; /** * Represents `total` response from Elasticsearch after ES 7.0. diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx index f7d6551f9093b..1bafcbec93f5f 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx @@ -24,11 +24,11 @@ export const navTabs: NavTabs[] = [ href: '/', }, { - id: 'management', - name: i18n.translate('xpack.endpoint.headerNav.management', { - defaultMessage: 'Management', + id: 'hosts', + name: i18n.translate('xpack.endpoint.headerNav.hosts', { + defaultMessage: 'Hosts', }), - href: '/management', + href: '/hosts', }, { id: 'alerts', diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx index cec51f570f95d..997113754f95d 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx @@ -11,15 +11,17 @@ import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { Route, Switch, BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import { Store } from 'redux'; +import { useObservable } from 'react-use'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { RouteCapture } from './view/route_capture'; import { EndpointPluginStartDependencies } from '../../plugin'; import { appStoreFactory } from './store'; import { AlertIndex } from './view/alerts'; -import { ManagementList } from './view/managing'; +import { HostList } from './view/hosts'; import { PolicyList } from './view/policy'; import { PolicyDetails } from './view/policy'; import { HeaderNavigation } from './components/header_nav'; +import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components'; /** * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle. @@ -48,43 +50,49 @@ interface RouterProps { } const AppRoot: React.FunctionComponent = React.memo( - ({ basename, store, coreStart: { http, notifications }, depsStart: { data } }) => ( - - - - - - - - ( -

    - -

    - )} - /> - - - - - ( - { + const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); + + return ( + + + + + + + + + ( +

    + +

    + )} + /> + + + + + ( + + )} /> - )} - /> -
    -
    -
    -
    -
    -
    - ) +
    +
    +
    + +
    +
    +
    + ); + } ); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts index 85215238dbefc..2dce8ead38584 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ManagementAction } from './managing'; +import { HostAction } from './hosts'; import { AlertAction } from './alerts'; import { RoutingAction } from './routing'; import { PolicyListAction } from './policy_list'; import { PolicyDetailsAction } from './policy_details'; export type AppAction = - | ManagementAction + | HostAction | AlertAction | RoutingAction | PolicyListAction diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts new file mode 100644 index 0000000000000..dee35aa3b895a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts @@ -0,0 +1,40 @@ +/* + * 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 { HostListPagination, ServerApiError } from '../../types'; +import { HostResultList, HostMetadata } from '../../../../../common/types'; + +interface ServerReturnedHostList { + type: 'serverReturnedHostList'; + payload: HostResultList; +} + +interface ServerReturnedHostDetails { + type: 'serverReturnedHostDetails'; + payload: HostMetadata; +} + +interface ServerFailedToReturnHostDetails { + type: 'serverFailedToReturnHostDetails'; + payload: ServerApiError; +} + +interface UserPaginatedHostList { + type: 'userPaginatedHostList'; + payload: HostListPagination; +} + +// Why is FakeActionWithNoPayload here, see: https://github.com/elastic/endpoint-app-team/issues/273 +interface FakeActionWithNoPayload { + type: 'fakeActionWithNoPayLoad'; +} + +export type HostAction = + | ServerReturnedHostList + | ServerReturnedHostDetails + | ServerFailedToReturnHostDetails + | UserPaginatedHostList + | FakeActionWithNoPayload; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts new file mode 100644 index 0000000000000..9aff66cdfb75e --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createStore, Dispatch, Store } from 'redux'; +import { HostAction, hostListReducer } from './index'; +import { HostListState } from '../../types'; +import { listData } from './selectors'; +import { mockHostResultList } from './mock_host_result_list'; + +describe('HostList store concerns', () => { + let store: Store; + let dispatch: Dispatch; + const createTestStore = () => { + store = createStore(hostListReducer); + dispatch = store.dispatch; + }; + + const loadDataToStore = () => { + dispatch({ + type: 'serverReturnedHostList', + payload: mockHostResultList({ request_page_size: 1, request_page_index: 1, total: 10 }), + }); + }; + + describe('# Reducers', () => { + beforeEach(() => { + createTestStore(); + }); + + test('it creates default state', () => { + expect(store.getState()).toEqual({ + hosts: [], + pageSize: 10, + pageIndex: 0, + total: 0, + loading: false, + }); + }); + + test('it handles `serverReturnedHostList', () => { + const payload = mockHostResultList({ + request_page_size: 1, + request_page_index: 1, + total: 10, + }); + dispatch({ + type: 'serverReturnedHostList', + payload, + }); + + const currentState = store.getState(); + expect(currentState.hosts).toEqual(payload.hosts); + expect(currentState.pageSize).toEqual(payload.request_page_size); + expect(currentState.pageIndex).toEqual(payload.request_page_index); + expect(currentState.total).toEqual(payload.total); + }); + }); + + describe('# Selectors', () => { + beforeEach(() => { + createTestStore(); + loadDataToStore(); + }); + + test('it selects `hostListData`', () => { + const currentState = store.getState(); + expect(listData(currentState)).toEqual(currentState.hosts); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts similarity index 60% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts index f0bfe27c9e30f..e80d7a82dc8cb 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { managementListReducer } from './reducer'; -export { ManagementAction } from './action'; -export { managementMiddlewareFactory } from './middleware'; +export { hostListReducer } from './reducer'; +export { HostAction } from './action'; +export { hostMiddlewareFactory } from './middleware'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts similarity index 63% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts index 459a1789a58da..a1973a38b6534 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts @@ -7,51 +7,40 @@ import { CoreStart, HttpSetup } from 'kibana/public'; import { applyMiddleware, createStore, Dispatch, Store } from 'redux'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { History, createBrowserHistory } from 'history'; -import { managementListReducer, managementMiddlewareFactory } from './index'; -import { EndpointMetadata, EndpointResultList } from '../../../../../common/types'; -import { EndpointDocGenerator } from '../../../../../common/generate_data'; -import { ManagementListState } from '../../types'; +import { hostListReducer, hostMiddlewareFactory } from './index'; +import { HostResultList } from '../../../../../common/types'; +import { HostListState } from '../../types'; import { AppAction } from '../action'; import { listData } from './selectors'; import { DepsStartMock, depsStartMock } from '../../mocks'; +import { mockHostResultList } from './mock_host_result_list'; -describe('endpoint list saga', () => { +describe('host list middleware', () => { const sleep = (ms = 100) => new Promise(wakeup => setTimeout(wakeup, ms)); let fakeCoreStart: jest.Mocked; let depsStart: DepsStartMock; let fakeHttpServices: jest.Mocked; - let store: Store; + let store: Store; let getState: typeof store['getState']; let dispatch: Dispatch; - const generator = new EndpointDocGenerator(); - // https://github.com/elastic/endpoint-app-team/issues/131 - const generateEndpoint = (): EndpointMetadata => { - return generator.generateEndpointMetadata(); - }; - let history: History; - const getEndpointListApiResponse = (): EndpointResultList => { - return { - endpoints: [generateEndpoint()], - request_page_size: 1, - request_page_index: 1, - total: 10, - }; + const getEndpointListApiResponse = (): HostResultList => { + return mockHostResultList({ request_page_size: 1, request_page_index: 1, total: 10 }); }; beforeEach(() => { fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); depsStart = depsStartMock(); fakeHttpServices = fakeCoreStart.http as jest.Mocked; store = createStore( - managementListReducer, - applyMiddleware(managementMiddlewareFactory(fakeCoreStart, depsStart)) + hostListReducer, + applyMiddleware(hostMiddlewareFactory(fakeCoreStart, depsStart)) ); getState = store.getState; dispatch = store.dispatch; history = createBrowserHistory(); }); - test('it handles `userChangedUrl`', async () => { + test('handles `userChangedUrl`', async () => { const apiResponse = getEndpointListApiResponse(); fakeHttpServices.post.mockResolvedValue(apiResponse); expect(fakeHttpServices.post).not.toHaveBeenCalled(); @@ -60,7 +49,7 @@ describe('endpoint list saga', () => { type: 'userChangedUrl', payload: { ...history.location, - pathname: '/management', + pathname: '/hosts', }, }); await sleep(); @@ -69,6 +58,6 @@ describe('endpoint list saga', () => { paging_properties: [{ page_index: 0 }, { page_size: 10 }], }), }); - expect(listData(getState())).toEqual(apiResponse.endpoints); + expect(listData(getState())).toEqual(apiResponse.hosts); }); }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts similarity index 59% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts index 1131e8d769fcf..9481b6633f12e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts @@ -5,39 +5,30 @@ */ import { MiddlewareFactory } from '../../types'; -import { - pageIndex, - pageSize, - isOnManagementPage, - hasSelectedHost, - uiQueryParams, -} from './selectors'; -import { ManagementListState } from '../../types'; +import { pageIndex, pageSize, isOnHostPage, hasSelectedHost, uiQueryParams } from './selectors'; +import { HostListState } from '../../types'; import { AppAction } from '../action'; -export const managementMiddlewareFactory: MiddlewareFactory = coreStart => { +export const hostMiddlewareFactory: MiddlewareFactory = coreStart => { return ({ getState, dispatch }) => next => async (action: AppAction) => { next(action); const state = getState(); if ( (action.type === 'userChangedUrl' && - isOnManagementPage(state) && + isOnHostPage(state) && hasSelectedHost(state) !== true) || - action.type === 'userPaginatedManagementList' + action.type === 'userPaginatedHostList' ) { - const managementPageIndex = pageIndex(state); - const managementPageSize = pageSize(state); + const hostPageIndex = pageIndex(state); + const hostPageSize = pageSize(state); const response = await coreStart.http.post('/api/endpoint/metadata', { body: JSON.stringify({ - paging_properties: [ - { page_index: managementPageIndex }, - { page_size: managementPageSize }, - ], + paging_properties: [{ page_index: hostPageIndex }, { page_size: hostPageSize }], }), }); - response.request_page_index = managementPageIndex; + response.request_page_index = hostPageIndex; dispatch({ - type: 'serverReturnedManagementList', + type: 'serverReturnedHostList', payload: response, }); } @@ -46,12 +37,12 @@ export const managementMiddlewareFactory: MiddlewareFactory try { const response = await coreStart.http.get(`/api/endpoint/metadata/${selectedHost}`); dispatch({ - type: 'serverReturnedManagementDetails', + type: 'serverReturnedHostDetails', payload: response, }); } catch (error) { dispatch({ - type: 'serverFailedToReturnManagementDetails', + type: 'serverFailedToReturnHostDetails', payload: error, }); } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts similarity index 82% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts index 61833d1dfb957..db39ecf448312 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EndpointResultList } from '../../../../../common/types'; +import { HostResultList } from '../../../../../common/types'; import { EndpointDocGenerator } from '../../../../../common/generate_data'; export const mockHostResultList: (options?: { total?: number; request_page_size?: number; request_page_index?: number; -}) => EndpointResultList = (options = {}) => { +}) => HostResultList = (options = {}) => { const { total = 1, request_page_size: requestPageSize = 10, @@ -24,13 +24,13 @@ export const mockHostResultList: (options?: { // total - numberToSkip is the count of non-skipped ones, but return no more than a pageSize, and no less than 0 const actualCountToReturn = Math.max(Math.min(total - numberToSkip, requestPageSize), 0); - const endpoints = []; + const hosts = []; for (let index = 0; index < actualCountToReturn; index++) { const generator = new EndpointDocGenerator('seed'); - endpoints.push(generator.generateEndpointMetadata()); + hosts.push(generator.generateHostMetadata()); } - const mock: EndpointResultList = { - endpoints, + const mock: HostResultList = { + hosts, total, request_page_size: requestPageSize, request_page_index: requestPageIndex, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts similarity index 66% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts index 582aa6b7138c9..fd70317a9f37e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts @@ -5,12 +5,12 @@ */ import { Reducer } from 'redux'; -import { ManagementListState } from '../../types'; +import { HostListState } from '../../types'; import { AppAction } from '../action'; -const initialState = (): ManagementListState => { +const initialState = (): HostListState => { return { - endpoints: [], + hosts: [], pageSize: 10, pageIndex: 0, total: 0, @@ -21,38 +21,36 @@ const initialState = (): ManagementListState => { }; }; -export const managementListReducer: Reducer = ( +export const hostListReducer: Reducer = ( state = initialState(), action ) => { - if (action.type === 'serverReturnedManagementList') { + if (action.type === 'serverReturnedHostList') { const { - endpoints, + hosts, total, request_page_size: pageSize, request_page_index: pageIndex, } = action.payload; return { ...state, - endpoints, + hosts, total, pageSize, pageIndex, loading: false, }; - } else if (action.type === 'serverReturnedManagementDetails') { + } else if (action.type === 'serverReturnedHostDetails') { return { ...state, details: action.payload, }; - } else if (action.type === 'serverFailedToReturnManagementDetails') { + } else if (action.type === 'serverFailedToReturnHostDetails') { return { ...state, detailsError: action.payload, }; - } else if (action.type === 'userExitedManagementList') { - return initialState(); - } else if (action.type === 'userPaginatedManagementList') { + } else if (action.type === 'userPaginatedHostList') { return { ...state, ...action.payload, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts new file mode 100644 index 0000000000000..ebe310cb51190 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts @@ -0,0 +1,60 @@ +/* + * 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 querystring from 'querystring'; +import { createSelector } from 'reselect'; +import { Immutable } from '../../../../../common/types'; +import { HostListState, HostIndexUIQueryParams } from '../../types'; + +export const listData = (state: HostListState) => state.hosts; + +export const pageIndex = (state: HostListState) => state.pageIndex; + +export const pageSize = (state: HostListState) => state.pageSize; + +export const totalHits = (state: HostListState) => state.total; + +export const isLoading = (state: HostListState) => state.loading; + +export const detailsError = (state: HostListState) => state.detailsError; + +export const detailsData = (state: HostListState) => { + return state.details; +}; + +export const isOnHostPage = (state: HostListState) => + state.location ? state.location.pathname === '/hosts' : false; + +export const uiQueryParams: ( + state: HostListState +) => Immutable = createSelector( + (state: HostListState) => state.location, + (location: HostListState['location']) => { + const data: HostIndexUIQueryParams = {}; + if (location) { + // Removes the `?` from the beginning of query string if it exists + const query = querystring.parse(location.search.slice(1)); + + const keys: Array = ['selected_host']; + + for (const key of keys) { + const value = query[key]; + if (typeof value === 'string') { + data[key] = value; + } else if (Array.isArray(value)) { + data[key] = value[value.length - 1]; + } + } + } + return data; + } +); + +export const hasSelectedHost: (state: HostListState) => boolean = createSelector( + uiQueryParams, + ({ selected_host: selectedHost }) => { + return selectedHost !== undefined; + } +); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts index c051be2bb83cb..efa79b163d3b6 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts @@ -16,7 +16,7 @@ import { import { CoreStart } from 'kibana/public'; import { appReducer } from './reducer'; import { alertMiddlewareFactory } from './alerts/middleware'; -import { managementMiddlewareFactory } from './managing'; +import { hostMiddlewareFactory } from './hosts'; import { policyListMiddlewareFactory } from './policy_list'; import { policyDetailsMiddlewareFactory } from './policy_details'; import { GlobalState } from '../types'; @@ -69,8 +69,8 @@ export const appStoreFactory: (middlewareDeps?: { middleware = composeWithReduxDevTools( applyMiddleware( substateMiddlewareFactory( - globalState => globalState.managementList, - managementMiddlewareFactory(coreStart, depsStart) + globalState => globalState.hostList, + hostMiddlewareFactory(coreStart, depsStart) ), substateMiddlewareFactory( globalState => globalState.policyList, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts deleted file mode 100644 index a42e23e57d107..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ManagementListPagination, ServerApiError } from '../../types'; -import { EndpointResultList, EndpointMetadata } from '../../../../../common/types'; - -interface ServerReturnedManagementList { - type: 'serverReturnedManagementList'; - payload: EndpointResultList; -} - -interface ServerReturnedManagementDetails { - type: 'serverReturnedManagementDetails'; - payload: EndpointMetadata; -} - -interface ServerFailedToReturnManagementDetails { - type: 'serverFailedToReturnManagementDetails'; - payload: ServerApiError; -} - -interface UserExitedManagementList { - type: 'userExitedManagementList'; -} - -interface UserPaginatedManagementList { - type: 'userPaginatedManagementList'; - payload: ManagementListPagination; -} - -export type ManagementAction = - | ServerReturnedManagementList - | ServerReturnedManagementDetails - | ServerFailedToReturnManagementDetails - | UserExitedManagementList - | UserPaginatedManagementList; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts deleted file mode 100644 index e435fded13f4c..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createStore, Dispatch, Store } from 'redux'; -import { ManagementAction, managementListReducer } from './index'; -import { EndpointMetadata } from '../../../../../common/types'; -import { EndpointDocGenerator } from '../../../../../common/generate_data'; -import { ManagementListState } from '../../types'; -import { listData } from './selectors'; - -describe('endpoint_list store concerns', () => { - let store: Store; - let dispatch: Dispatch; - const generator = new EndpointDocGenerator(); - const createTestStore = () => { - store = createStore(managementListReducer); - dispatch = store.dispatch; - }; - const generateEndpoint = (): EndpointMetadata => { - return generator.generateEndpointMetadata(); - }; - const loadDataToStore = () => { - dispatch({ - type: 'serverReturnedManagementList', - payload: { - endpoints: [generateEndpoint()], - request_page_size: 1, - request_page_index: 1, - total: 10, - }, - }); - }; - - describe('# Reducers', () => { - beforeEach(() => { - createTestStore(); - }); - - test('it creates default state', () => { - expect(store.getState()).toEqual({ - endpoints: [], - pageSize: 10, - pageIndex: 0, - total: 0, - loading: false, - }); - }); - - test('it handles `serverReturnedManagementList', () => { - const payload = { - endpoints: [generateEndpoint()], - request_page_size: 1, - request_page_index: 1, - total: 10, - }; - dispatch({ - type: 'serverReturnedManagementList', - payload, - }); - - const currentState = store.getState(); - expect(currentState.endpoints).toEqual(payload.endpoints); - expect(currentState.pageSize).toEqual(payload.request_page_size); - expect(currentState.pageIndex).toEqual(payload.request_page_index); - expect(currentState.total).toEqual(payload.total); - }); - - test('it handles `userExitedManagementListPage`', () => { - loadDataToStore(); - - expect(store.getState().total).toEqual(10); - - dispatch({ type: 'userExitedManagementList' }); - expect(store.getState().endpoints.length).toEqual(0); - expect(store.getState().pageIndex).toEqual(0); - }); - }); - - describe('# Selectors', () => { - beforeEach(() => { - createTestStore(); - loadDataToStore(); - }); - - test('it selects `managementListData`', () => { - const currentState = store.getState(); - expect(listData(currentState)).toEqual(currentState.endpoints); - }); - }); -}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts deleted file mode 100644 index a7776f09fe2b8..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import querystring from 'querystring'; -import { createSelector } from 'reselect'; -import { Immutable } from '../../../../../common/types'; -import { ManagementListState, ManagingIndexUIQueryParams } from '../../types'; - -export const listData = (state: ManagementListState) => state.endpoints; - -export const pageIndex = (state: ManagementListState) => state.pageIndex; - -export const pageSize = (state: ManagementListState) => state.pageSize; - -export const totalHits = (state: ManagementListState) => state.total; - -export const isLoading = (state: ManagementListState) => state.loading; - -export const detailsError = (state: ManagementListState) => state.detailsError; - -export const detailsData = (state: ManagementListState) => { - return state.details; -}; - -export const isOnManagementPage = (state: ManagementListState) => - state.location ? state.location.pathname === '/management' : false; - -export const uiQueryParams: ( - state: ManagementListState -) => Immutable = createSelector( - (state: ManagementListState) => state.location, - (location: ManagementListState['location']) => { - const data: ManagingIndexUIQueryParams = {}; - if (location) { - // Removes the `?` from the beginning of query string if it exists - const query = querystring.parse(location.search.slice(1)); - - const keys: Array = ['selected_host']; - - for (const key of keys) { - const value = query[key]; - if (typeof value === 'string') { - data[key] = value; - } else if (Array.isArray(value)) { - data[key] = value[value.length - 1]; - } - } - } - return data; - } -); - -export const hasSelectedHost: (state: ManagementListState) => boolean = createSelector( - uiQueryParams, - ({ selected_host: selectedHost }) => { - return selectedHost !== undefined; - } -); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts index e655a8d5e46db..c8b2d08676724 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { combineReducers, Reducer } from 'redux'; -import { managementListReducer } from './managing'; +import { hostListReducer } from './hosts'; import { AppAction } from './action'; import { alertListReducer } from './alerts'; import { GlobalState } from '../types'; @@ -12,7 +12,7 @@ import { policyListReducer } from './policy_list'; import { policyDetailsReducer } from './policy_details'; export const appReducer: Reducer = combineReducers({ - managementList: managementListReducer, + hostList: hostListReducer, alertList: alertListReducer, policyList: policyListReducer, policyDetails: policyDetailsReducer, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 91be6e4936dbe..3045f42a93fe2 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -7,7 +7,7 @@ import { Dispatch, MiddlewareAPI } from 'redux'; import { IIndexPattern } from 'src/plugins/data/public'; import { - EndpointMetadata, + HostMetadata, AlertData, AlertResultList, Immutable, @@ -25,22 +25,22 @@ export type MiddlewareFactory = ( api: MiddlewareAPI, S> ) => (next: Dispatch) => (action: AppAction) => unknown; -export interface ManagementListState { - endpoints: EndpointMetadata[]; - total: number; +export interface HostListState { + hosts: HostMetadata[]; pageSize: number; pageIndex: number; + total: number; loading: boolean; detailsError?: ServerApiError; - details?: Immutable; + details?: Immutable; location?: Immutable; } -export interface ManagementListPagination { +export interface HostListPagination { pageIndex: number; pageSize: number; } -export interface ManagingIndexUIQueryParams { +export interface HostIndexUIQueryParams { selected_host?: string; } @@ -92,7 +92,7 @@ export interface PolicyDetailsState { } export interface GlobalState { - readonly managementList: ManagementListState; + readonly hostList: HostListState; readonly alertList: AlertListState; readonly policyList: PolicyListState; readonly policyDetails: PolicyDetailsState; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx new file mode 100644 index 0000000000000..dcf97b4b2b226 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n/react'; + +export const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { + // If date is greater than or equal to 1h (ago), then show it as a date + // else, show it as relative to "now" + return Date.now() - date.getTime() >= 3.6e6 ? ( + <> + + {' @'} + + + ) : ( + <> + + + ); +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx similarity index 62% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx index 9f2a732042719..37080e8568350 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx @@ -13,38 +13,46 @@ import { EuiDescriptionList, EuiLoadingContent, EuiHorizontalRule, + EuiHealth, EuiSpacer, + EuiListGroup, + EuiListGroupItem, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; +import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { useManagementListSelector } from './hooks'; +import { HostMetadata } from '../../../../../common/types'; +import { useHostListSelector } from './hooks'; import { urlFromQueryParams } from './url_from_query_params'; -import { uiQueryParams, detailsData, detailsError } from './../../store/managing/selectors'; +import { FormattedDateAndTime } from '../formatted_date_time'; +import { uiQueryParams, detailsData, detailsError } from './../../store/hosts/selectors'; -const HostDetails = memo(() => { - const details = useManagementListSelector(detailsData); - if (details === undefined) { - return null; +const HostIds = styled(EuiListGroupItem)` + margin-top: 0; + .euiListGroupItem__text { + padding: 0; } +`; +const HostDetails = memo(({ details }: { details: HostMetadata }) => { const detailsResultsUpper = useMemo(() => { return [ { - title: i18n.translate('xpack.endpoint.management.details.os', { + title: i18n.translate('xpack.endpoint.host.details.os', { defaultMessage: 'OS', }), description: details.host.os.full, }, { - title: i18n.translate('xpack.endpoint.management.details.lastSeen', { + title: i18n.translate('xpack.endpoint.host.details.lastSeen', { defaultMessage: 'Last Seen', }), - description: details['@timestamp'], + description: , }, { - title: i18n.translate('xpack.endpoint.management.details.alerts', { + title: i18n.translate('xpack.endpoint.host.details.alerts', { defaultMessage: 'Alerts', }), description: '0', @@ -55,62 +63,67 @@ const HostDetails = memo(() => { const detailsResultsLower = useMemo(() => { return [ { - title: i18n.translate('xpack.endpoint.management.details.policy', { + title: i18n.translate('xpack.endpoint.host.details.policy', { defaultMessage: 'Policy', }), description: details.endpoint.policy.id, }, { - title: i18n.translate('xpack.endpoint.management.details.policyStatus', { + title: i18n.translate('xpack.endpoint.host.details.policyStatus', { defaultMessage: 'Policy Status', }), - description: 'active', + description: active, }, { - title: i18n.translate('xpack.endpoint.management.details.ipAddress', { + title: i18n.translate('xpack.endpoint.host.details.ipAddress', { defaultMessage: 'IP Address', }), - description: details.host.ip, + description: ( + + {details.host.ip.map((ip: string, index: number) => ( + + ))} + + ), }, { - title: i18n.translate('xpack.endpoint.management.details.hostname', { + title: i18n.translate('xpack.endpoint.host.details.hostname', { defaultMessage: 'Hostname', }), description: details.host.hostname, }, { - title: i18n.translate('xpack.endpoint.management.details.sensorVersion', { + title: i18n.translate('xpack.endpoint.host.details.sensorVersion', { defaultMessage: 'Sensor Version', }), description: details.agent.version, }, ]; }, [details.agent.version, details.endpoint.policy.id, details.host.hostname, details.host.ip]); - return ( <> ); }); -export const ManagementDetails = () => { +export const HostDetailsFlyout = () => { const history = useHistory(); const { notifications } = useKibana(); - const queryParams = useManagementListSelector(uiQueryParams); + const queryParams = useHostListSelector(uiQueryParams); const { selected_host: selectedHost, ...queryParamsWithoutSelectedHost } = queryParams; - const details = useManagementListSelector(detailsData); - const error = useManagementListSelector(detailsError); + const details = useHostListSelector(detailsData); + const error = useHostListSelector(detailsError); const handleFlyoutClose = useCallback(() => { history.push(urlFromQueryParams(queryParamsWithoutSelectedHost)); @@ -121,13 +134,13 @@ export const ManagementDetails = () => { notifications.toasts.danger({ title: ( ), body: ( ), @@ -137,10 +150,10 @@ export const ManagementDetails = () => { }, [error, notifications.toasts]); return ( - + -

    +

    {details === undefined ? : details.host.hostname}

    @@ -151,7 +164,7 @@ export const ManagementDetails = () => { ) : ( - + )}
    diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts similarity index 61% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts index a0720fbd8aeeb..99a0073f46c74 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts @@ -5,12 +5,10 @@ */ import { useSelector } from 'react-redux'; -import { GlobalState, ManagementListState } from '../../types'; +import { GlobalState, HostListState } from '../../types'; -export function useManagementListSelector( - selector: (state: ManagementListState) => TSelected -) { +export function useHostListSelector(selector: (state: HostListState) => TSelected) { return useSelector(function(state: GlobalState) { - return selector(state.managementList); + return selector(state.hostList); }); } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx similarity index 75% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx index ced27ae8945b5..f6dfae99c1b11 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx @@ -8,15 +8,16 @@ import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; import { Provider } from 'react-redux'; import { I18nProvider } from '@kbn/i18n/react'; +import { EuiThemeProvider } from '../../../../../../../legacy/common/eui_styled_components'; import { appStoreFactory } from '../../store'; import { RouteCapture } from '../route_capture'; import { createMemoryHistory, MemoryHistory } from 'history'; import { Router } from 'react-router-dom'; import { AppAction } from '../../types'; -import { ManagementList } from './index'; -import { mockHostResultList } from '../../store/managing/mock_host_result_list'; +import { HostList } from './index'; +import { mockHostResultList } from '../../store/hosts/mock_host_result_list'; -describe('when on the managing page', () => { +describe('when on the hosts page', () => { let render: () => reactTestingLibrary.RenderResult; let history: MemoryHistory; let store: ReturnType; @@ -28,11 +29,13 @@ describe('when on the managing page', () => { return reactTestingLibrary.render( - - - - - + + + + + + + ); @@ -41,7 +44,7 @@ describe('when on the managing page', () => { it('should show a table', async () => { const renderResult = render(); - const table = await renderResult.findByTestId('managementListTable'); + const table = await renderResult.findByTestId('hostListTable'); expect(table).not.toBeNull(); }); @@ -49,7 +52,7 @@ describe('when on the managing page', () => { it('should not show the flyout', () => { const renderResult = render(); expect.assertions(1); - return renderResult.findByTestId('managementDetailsFlyout').catch(e => { + return renderResult.findByTestId('hostDetailsFlyout').catch(e => { expect(e).not.toBeNull(); }); }); @@ -57,14 +60,14 @@ describe('when on the managing page', () => { beforeEach(() => { reactTestingLibrary.act(() => { const action: AppAction = { - type: 'serverReturnedManagementList', + type: 'serverReturnedHostList', payload: mockHostResultList(), }; store.dispatch(action); }); }); - it('should render the management summary row in the table', async () => { + it('should render the host summary row in the table', async () => { const renderResult = render(); const rows = await renderResult.findAllByRole('row'); expect(rows).toHaveLength(2); @@ -81,7 +84,7 @@ describe('when on the managing page', () => { }); it('should show the flyout', () => { - return renderResult.findByTestId('managementDetailsFlyout').then(flyout => { + return renderResult.findByTestId('hostDetailsFlyout').then(flyout => { expect(flyout).not.toBeNull(); }); }); @@ -100,7 +103,7 @@ describe('when on the managing page', () => { }); it('should show the flyout', () => { const renderResult = render(); - return renderResult.findByTestId('managementDetailsFlyout').then(flyout => { + return renderResult.findByTestId('hostDetailsFlyout').then(flyout => { expect(flyout).not.toBeNull(); }); }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx similarity index 55% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx index ba9a931a233b2..94625b8c66191 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx @@ -10,28 +10,29 @@ import { useHistory } from 'react-router-dom'; import { EuiPage, EuiPageBody, + EuiPageHeader, EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageContentHeaderSection, + EuiHorizontalRule, EuiTitle, EuiBasicTable, - EuiTextColor, + EuiText, EuiLink, + EuiHealth, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { createStructuredSelector } from 'reselect'; -import { ManagementDetails } from './details'; -import * as selectors from '../../store/managing/selectors'; -import { ManagementAction } from '../../store/managing/action'; -import { useManagementListSelector } from './hooks'; +import { HostDetailsFlyout } from './details'; +import * as selectors from '../../store/hosts/selectors'; +import { HostAction } from '../../store/hosts/action'; +import { useHostListSelector } from './hooks'; import { CreateStructuredSelector } from '../../types'; import { urlFromQueryParams } from './url_from_query_params'; const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); -export const ManagementList = () => { - const dispatch = useDispatch<(a: ManagementAction) => void>(); +export const HostList = () => { + const dispatch = useDispatch<(a: HostAction) => void>(); const history = useHistory(); const { listData, @@ -41,7 +42,7 @@ export const ManagementList = () => { isLoading, uiQueryParams: queryParams, hasSelectedHost, - } = useManagementListSelector(selector); + } = useHostListSelector(selector); const paginationSetup = useMemo(() => { return { @@ -57,7 +58,7 @@ export const ManagementList = () => { ({ page }: { page: { index: number; size: number } }) => { const { index, size } = page; dispatch({ - type: 'userPaginatedManagementList', + type: 'userPaginatedHostList', payload: { pageIndex: index, pageSize: size }, }); }, @@ -68,7 +69,7 @@ export const ManagementList = () => { return [ { field: '', - name: i18n.translate('xpack.endpoint.management.list.host', { + name: i18n.translate('xpack.endpoint.host.list.hostname', { defaultMessage: 'Hostname', }), render: ({ host: { hostname, id } }: { host: { hostname: string; id: string } }) => { @@ -89,7 +90,7 @@ export const ManagementList = () => { }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.policy', { + name: i18n.translate('xpack.endpoint.host.list.policy', { defaultMessage: 'Policy', }), render: () => { @@ -98,37 +99,38 @@ export const ManagementList = () => { }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.policyStatus', { + name: i18n.translate('xpack.endpoint.host.list.policyStatus', { defaultMessage: 'Policy Status', }), render: () => { - return 'Policy Status'; + return Policy Status; }, }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.alerts', { + name: i18n.translate('xpack.endpoint.host.list.alerts', { defaultMessage: 'Alerts', }), + dataType: 'number', render: () => { return '0'; }, }, { field: 'host.os.name', - name: i18n.translate('xpack.endpoint.management.list.os', { + name: i18n.translate('xpack.endpoint.host.list.os', { defaultMessage: 'Operating System', }), }, { field: 'host.ip', - name: i18n.translate('xpack.endpoint.management.list.ip', { + name: i18n.translate('xpack.endpoint.host.list.ip', { defaultMessage: 'IP Address', }), }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.sensorVersion', { + name: i18n.translate('xpack.endpoint.host.list.sensorVersion', { defaultMessage: 'Sensor Version', }), render: () => { @@ -137,9 +139,10 @@ export const ManagementList = () => { }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.lastActive', { + name: i18n.translate('xpack.endpoint.host.list.lastActive', { defaultMessage: 'Last Active', }), + dataType: 'date', render: () => { return 'xxxx'; }, @@ -148,45 +151,59 @@ export const ManagementList = () => { }, [queryParams, history]); return ( - <> - {hasSelectedHost && } - + + {hasSelectedHost && } + - - - - -

    - -

    -
    -

    - - - -

    -
    -
    - - + +

    + +

    +
    + + + + + -
    + + +
    - +
    ); }; + +const HostPage = styled.div` + .hostPage { + padding: 0; + } + .hostHeader { + background-color: ${props => props.theme.eui.euiColorLightestShade}; + border-bottom: ${props => props.theme.eui.euiBorderThin}; + padding: ${props => + props.theme.eui.euiSizeXL + + ' ' + + 0 + + props.theme.eui.euiSizeXL + + ' ' + + props.theme.eui.euiSizeL}; + margin-bottom: 0; + } + .hostPageContent { + border: none; + } +`; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts similarity index 78% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts index ea6a4c6f684ad..225aad8cab020 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts @@ -5,10 +5,10 @@ */ import querystring from 'querystring'; -import { EndpointAppLocation, ManagingIndexUIQueryParams } from '../../types'; +import { EndpointAppLocation, HostIndexUIQueryParams } from '../../types'; export function urlFromQueryParams( - queryParams: ManagingIndexUIQueryParams + queryParams: HostIndexUIQueryParams ): Partial { const search = querystring.stringify(queryParams); return { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx index cf573da3703cc..e7ce53679bbe7 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx @@ -20,17 +20,12 @@ import { EuiLink, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - FormattedMessage, - FormattedDate, - FormattedTime, - FormattedNumber, - FormattedRelative, -} from '@kbn/i18n/react'; +import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { useHistory } from 'react-router-dom'; import { usePageId } from '../use_page_id'; +import { FormattedDateAndTime } from '../formatted_date_time'; import { selectIsLoading, selectPageIndex, @@ -56,22 +51,6 @@ const TruncateTooltipText = styled(TruncateText)` } `; -const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { - // If date is greater than or equal to 24h (ago), then show it as a date - // else, show it as relative to "now" - return Date.now() - date.getTime() >= 8.64e7 ? ( - <> - - {' @'} - - - ) : ( - <> - - - ); -}; - const PolicyLink: React.FC<{ name: string; route: string }> = ({ name, route }) => { const history = useHistory(); diff --git a/x-pack/plugins/endpoint/scripts/resolver_generator.ts b/x-pack/plugins/endpoint/scripts/resolver_generator.ts index a3e56497f0790..503999daec587 100644 --- a/x-pack/plugins/endpoint/scripts/resolver_generator.ts +++ b/x-pack/plugins/endpoint/scripts/resolver_generator.ts @@ -32,7 +32,7 @@ async function main() { }, metadataIndex: { alias: 'mi', - describe: 'index to store endpoint metadata in', + describe: 'index to store host metadata in', default: 'endpoint-agent-1', type: 'string', }, @@ -76,15 +76,15 @@ async function main() { type: 'number', default: 30, }, - numEndpoints: { + numHosts: { alias: 'ne', - describe: 'number of different endpoints to generate alerts for', + describe: 'number of different hosts to generate alerts for', type: 'number', default: 1, }, - alertsPerEndpoint: { + alertsPerHost: { alias: 'ape', - describe: 'number of resolver trees to make for each endpoint', + describe: 'number of resolver trees to make for each host', type: 'number', default: 1, }, @@ -133,12 +133,12 @@ async function main() { } const generator = new EndpointDocGenerator(argv.seed); - for (let i = 0; i < argv.numEndpoints; i++) { + for (let i = 0; i < argv.numHosts; i++) { await client.index({ index: argv.metadataIndex, - body: generator.generateEndpointMetadata(), + body: generator.generateHostMetadata(), }); - for (let j = 0; j < argv.alertsPerEndpoint; j++) { + for (let j = 0; j < argv.alertsPerHost; j++) { const resolverDocs = generator.generateFullResolverTree( argv.ancestors, argv.generations, diff --git a/x-pack/plugins/endpoint/server/routes/metadata.test.ts b/x-pack/plugins/endpoint/server/routes/metadata.test.ts index ee374bc1b57d6..65e07edbcde24 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata.test.ts @@ -18,7 +18,7 @@ import { httpServiceMock, loggingServiceMock, } from '../../../../../src/core/server/mocks'; -import { EndpointMetadata, EndpointResultList } from '../../common/types'; +import { HostMetadata, HostResultList } from '../../common/types'; import { SearchResponse } from 'elasticsearch'; import { registerEndpointRoutes } from './metadata'; import { EndpointConfigSchema } from '../config'; @@ -49,8 +49,8 @@ describe('test endpoint route', () => { it('test find the latest of all endpoints', async () => { const mockRequest = httpServerMock.createKibanaRequest({}); - const response: SearchResponse = (data as unknown) as SearchResponse< - EndpointMetadata + const response: SearchResponse = (data as unknown) as SearchResponse< + HostMetadata >; mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => @@ -72,8 +72,8 @@ describe('test endpoint route', () => { expect(mockScopedClient.callAsCurrentUser).toBeCalled(); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; - expect(endpointResultList.endpoints.length).toEqual(2); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; + expect(endpointResultList.hosts.length).toEqual(2); expect(endpointResultList.total).toEqual(2); expect(endpointResultList.request_page_index).toEqual(0); expect(endpointResultList.request_page_size).toEqual(10); @@ -93,7 +93,7 @@ describe('test endpoint route', () => { }, }); mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => - Promise.resolve((data as unknown) as SearchResponse) + Promise.resolve((data as unknown) as SearchResponse) ); [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => path.startsWith('/api/endpoint/metadata') @@ -117,8 +117,8 @@ describe('test endpoint route', () => { }); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; - expect(endpointResultList.endpoints.length).toEqual(2); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; + expect(endpointResultList.hosts.length).toEqual(2); expect(endpointResultList.total).toEqual(2); expect(endpointResultList.request_page_index).toEqual(10); expect(endpointResultList.request_page_size).toEqual(10); @@ -140,7 +140,7 @@ describe('test endpoint route', () => { }, }); mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => - Promise.resolve((data as unknown) as SearchResponse) + Promise.resolve((data as unknown) as SearchResponse) ); [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => path.startsWith('/api/endpoint/metadata') @@ -177,8 +177,8 @@ describe('test endpoint route', () => { }); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; - expect(endpointResultList.endpoints.length).toEqual(2); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; + expect(endpointResultList.hosts.length).toEqual(2); expect(endpointResultList.total).toEqual(2); expect(endpointResultList.request_page_index).toEqual(10); expect(endpointResultList.request_page_size).toEqual(10); @@ -234,8 +234,8 @@ describe('test endpoint route', () => { const mockRequest = httpServerMock.createKibanaRequest({ params: { id: (data as any).hits.hits[0]._id }, }); - const response: SearchResponse = (data as unknown) as SearchResponse< - EndpointMetadata + const response: SearchResponse = (data as unknown) as SearchResponse< + HostMetadata >; mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => @@ -257,7 +257,7 @@ describe('test endpoint route', () => { expect(mockScopedClient.callAsCurrentUser).toBeCalled(); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const result = mockResponse.ok.mock.calls[0][0]?.body as EndpointMetadata; + const result = mockResponse.ok.mock.calls[0][0]?.body as HostMetadata; expect(result).toHaveProperty('endpoint'); }); }); diff --git a/x-pack/plugins/endpoint/server/routes/metadata.ts b/x-pack/plugins/endpoint/server/routes/metadata.ts index 278cfac020a3b..463a071ab0c77 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata.ts @@ -12,11 +12,11 @@ import { kibanaRequestToMetadataListESQuery, kibanaRequestToMetadataGetESQuery, } from '../services/endpoint/metadata_query_builders'; -import { EndpointMetadata, EndpointResultList } from '../../common/types'; +import { HostMetadata, HostResultList } from '../../common/types'; import { EndpointAppContext } from '../types'; interface HitSource { - _source: EndpointMetadata; + _source: HostMetadata; } export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { @@ -57,8 +57,8 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( 'search', queryParams - )) as SearchResponse; - return res.ok({ body: mapToEndpointResultList(queryParams, response) }); + )) as SearchResponse; + return res.ok({ body: mapToHostResultList(queryParams, response) }); } catch (err) { return res.internalError({ body: err }); } @@ -79,7 +79,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( 'search', query - )) as SearchResponse; + )) as SearchResponse; if (response.hits.hits.length === 0) { return res.notFound({ body: 'Endpoint Not Found' }); @@ -93,27 +93,27 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp ); } -function mapToEndpointResultList( +function mapToHostResultList( queryParams: Record, - searchResponse: SearchResponse -): EndpointResultList { - const totalNumberOfEndpoints = searchResponse?.aggregations?.total?.value || 0; + searchResponse: SearchResponse +): HostResultList { + const totalNumberOfHosts = searchResponse?.aggregations?.total?.value || 0; if (searchResponse.hits.hits.length > 0) { return { request_page_size: queryParams.size, request_page_index: queryParams.from, - endpoints: searchResponse.hits.hits + hosts: searchResponse.hits.hits .map(response => response.inner_hits.most_recent.hits.hits) .flatMap(data => data as HitSource) .map(entry => entry._source), - total: totalNumberOfEndpoints, + total: totalNumberOfHosts, }; } else { return { request_page_size: queryParams.size, request_page_index: queryParams.from, - total: totalNumberOfEndpoints, - endpoints: [], + total: totalNumberOfHosts, + hosts: [], }; } } diff --git a/x-pack/test/api_integration/apis/endpoint/metadata.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts index 5f18bdd9bea02..49e527fa3e7e8 100644 --- a/x-pack/test/api_integration/apis/endpoint/metadata.ts +++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; /** - * The number of alert documents in the es archive. + * The number of host documents in the es archive. */ -const numberOfEndpointsInFixture = 3; +const numberOfHostsInFixture = 3; export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -24,7 +24,7 @@ export default function({ getService }: FtrProviderContext) { .send() .expect(200); expect(body.total).to.eql(0); - expect(body.endpoints.length).to.eql(0); + expect(body.hosts.length).to.eql(0); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -33,14 +33,14 @@ export default function({ getService }: FtrProviderContext) { describe('POST /api/endpoint/metadata when index is not empty', () => { before(() => esArchiver.load('endpoint/metadata/api_feature')); after(() => esArchiver.unload('endpoint/metadata/api_feature')); - it('metadata api should return one entry for each endpoint with default paging', async () => { + it('metadata api should return one entry for each host with default paging', async () => { const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') .send() .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(numberOfEndpointsInFixture); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(numberOfHostsInFixture); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -60,8 +60,8 @@ export default function({ getService }: FtrProviderContext) { ], }) .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(1); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(1); expect(body.request_page_index).to.eql(1); }); @@ -84,8 +84,8 @@ export default function({ getService }: FtrProviderContext) { ], }) .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(0); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(0); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(30); }); @@ -115,7 +115,7 @@ export default function({ getService }: FtrProviderContext) { .send({ filter: 'not host.ip:10.100.170.247' }) .expect(200); expect(body.total).to.eql(2); - expect(body.endpoints.length).to.eql(2); + expect(body.hosts.length).to.eql(2); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -139,7 +139,7 @@ export default function({ getService }: FtrProviderContext) { .expect(200); expect(body.total).to.eql(2); const resultIps: string[] = [].concat( - ...body.endpoints.map((metadata: Record) => metadata.host.ip) + ...body.hosts.map((metadata: Record) => metadata.host.ip) ); expect(resultIps).to.eql([ '10.48.181.222', @@ -150,7 +150,7 @@ export default function({ getService }: FtrProviderContext) { '10.128.235.38', ]); expect(resultIps).not.include.eql(notIncludedIp); - expect(body.endpoints.length).to.eql(2); + expect(body.hosts.length).to.eql(2); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -166,10 +166,10 @@ export default function({ getService }: FtrProviderContext) { .expect(200); expect(body.total).to.eql(1); const resultOsVariantValue: Set = new Set( - body.endpoints.map((metadata: Record) => metadata.host.os.variant) + body.hosts.map((metadata: Record) => metadata.host.os.variant) ); expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); - expect(body.endpoints.length).to.eql(1); + expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -184,17 +184,17 @@ export default function({ getService }: FtrProviderContext) { }) .expect(200); expect(body.total).to.eql(1); - const resultIp: string = body.endpoints[0].host.ip.filter( + const resultIp: string = body.hosts[0].host.ip.filter( (ip: string) => ip === targetEndpointIp ); expect(resultIp).to.eql([targetEndpointIp]); - expect(body.endpoints[0].event.created).to.eql(1584044335459); - expect(body.endpoints.length).to.eql(1); + expect(body.hosts[0].event.created).to.eql(1584044335459); + expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); - it('metadata api should return all endpoints when filter is empty string', async () => { + it('metadata api should return all hosts when filter is empty string', async () => { const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') @@ -202,8 +202,8 @@ export default function({ getService }: FtrProviderContext) { filter: '', }) .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(numberOfEndpointsInFixture); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(numberOfHostsInFixture); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); diff --git a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts index 287892903dd2b..bf3d642307d8c 100644 --- a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts +++ b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts @@ -41,18 +41,13 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('welcomeTitle'); }); - it(`endpoint management shows 'Manage Endpoints'`, async () => { - await pageObjects.common.navigateToUrlWithBrowserHistory( - 'endpoint', - '/management', - undefined, - { - basePath: '/s/custom_space', - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); - await testSubjects.existOrFail('managementViewTitle'); + it(`endpoint management shows 'Hosts'`, async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/hosts', undefined, { + basePath: '/s/custom_space', + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + }); + await testSubjects.existOrFail('hostListTitle'); }); }); diff --git a/x-pack/test/functional/apps/endpoint/header_nav.ts b/x-pack/test/functional/apps/endpoint/header_nav.ts index 2368ad077cf64..d1fa7311d61e8 100644 --- a/x-pack/test/functional/apps/endpoint/header_nav.ts +++ b/x-pack/test/functional/apps/endpoint/header_nav.ts @@ -19,19 +19,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the tabs when the app loads', async () => { const homeTabText = await testSubjects.getVisibleText('homeEndpointTab'); - const managementTabText = await testSubjects.getVisibleText('managementEndpointTab'); + const hostsTabText = await testSubjects.getVisibleText('hostsEndpointTab'); const alertsTabText = await testSubjects.getVisibleText('alertsEndpointTab'); const policiesTabText = await testSubjects.getVisibleText('policiesEndpointTab'); expect(homeTabText.trim()).to.be('Home'); - expect(managementTabText.trim()).to.be('Management'); + expect(hostsTabText.trim()).to.be('Hosts'); expect(alertsTabText.trim()).to.be('Alerts'); expect(policiesTabText.trim()).to.be('Policies'); }); - it('renders the management page when the Management tab is selected', async () => { - await (await testSubjects.find('managementEndpointTab')).click(); - await testSubjects.existOrFail('managementViewTitle'); + it('renders the hosts page when the Hosts tab is selected', async () => { + await (await testSubjects.find('hostsEndpointTab')).click(); + await testSubjects.existOrFail('hostListTitle'); }); it('renders the alerts page when the Alerts tab is selected', async () => { @@ -45,8 +45,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('renders the home page when Home tab is selected after selecting another tab', async () => { - await (await testSubjects.find('managementEndpointTab')).click(); - await testSubjects.existOrFail('managementViewTitle'); + await (await testSubjects.find('hostsEndpointTab')).click(); + await testSubjects.existOrFail('hostListTitle'); await (await testSubjects.find('homeEndpointTab')).click(); await testSubjects.existOrFail('welcomeTitle'); diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/host_list.ts similarity index 58% rename from x-pack/test/functional/apps/endpoint/management.ts rename to x-pack/test/functional/apps/endpoint/host_list.ts index 640f6264c3a09..baace0f7670e1 100644 --- a/x-pack/test/functional/apps/endpoint/management.ts +++ b/x-pack/test/functional/apps/endpoint/host_list.ts @@ -12,15 +12,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); - describe('Endpoint Management List', function() { + describe('host list', function() { this.tags('ciGroup7'); before(async () => { await esArchiver.load('endpoint/metadata/api_feature'); - await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management'); + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/hosts'); }); it('finds title', async () => { - const title = await testSubjects.getVisibleText('managementViewTitle'); + const title = await testSubjects.getVisibleText('hostListTitle'); expect(title).to.equal('Hosts'); }); @@ -67,21 +67,70 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'xxxx', ], ]; - const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable'); + const tableData = await pageObjects.endpoint.getEndpointAppTableData('hostListTable'); expect(tableData).to.eql(expectedData); }); - it('displays no items found', async () => { + it('display details flyout when the hostname is clicked on', async () => { + await (await testSubjects.find('hostnameCellLink')).click(); + await testSubjects.existOrFail('hostDetailsUpperList'); + await testSubjects.existOrFail('hostDetailsLowerList'); + }); + + it('displays no items found when empty', async () => { // clear out the data and reload the page await esArchiver.unload('endpoint/metadata/api_feature'); - await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management'); + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/hosts'); // get the table data and verify no entries appear - const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable'); + const tableData = await pageObjects.endpoint.getEndpointAppTableData('hostListTable'); expect(tableData[1][0]).to.equal('No items found'); // reload the data so the other tests continue to pass await esArchiver.load('endpoint/metadata/api_feature'); }); + describe('has a url with a host id', () => { + before(async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'endpoint', + '/hosts', + 'selected_host=cbe80003-6964-4e0f-aba1-f94c32b44e95' + ); + }); + + it('shows a flyout', async () => { + await testSubjects.existOrFail('hostDetailsFlyout'); + }); + + it('displays details row headers', async () => { + const expectedData = [ + 'OS', + 'Last Seen', + 'Alerts', + 'Policy', + 'Policy Status', + 'IP Address', + 'Hostname', + 'Sensor Version', + ]; + const keys = await pageObjects.endpoint.hostFlyoutDescriptionKeys('hostDetailsFlyout'); + expect(keys).to.eql(expectedData); + }); + + it('displays details row descriptions', async () => { + const values = await pageObjects.endpoint.hostFlyoutDescriptionValues('hostDetailsFlyout'); + + expect(values).to.eql([ + 'Windows Server 2012', + '', + '0', + 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A', + 'active', + '10.48.181.22210.116.62.6210.102.83.30', + 'Host-cxz5glsoup', + '6.6.9', + ]); + }); + }); after(async () => { await esArchiver.unload('endpoint/metadata/api_feature'); }); diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts index 15ce522ce56ba..4d55b3af4956e 100644 --- a/x-pack/test/functional/apps/endpoint/index.ts +++ b/x-pack/test/functional/apps/endpoint/index.ts @@ -12,7 +12,7 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./landing_page')); loadTestFile(require.resolve('./header_nav')); - loadTestFile(require.resolve('./management')); + loadTestFile(require.resolve('./host_list')); loadTestFile(require.resolve('./policy_list')); loadTestFile(require.resolve('./policy_details')); loadTestFile(require.resolve('./alerts')); diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts index 6350f51f707f4..4becbf797abc0 100644 --- a/x-pack/test/functional/page_objects/endpoint_page.ts +++ b/x-pack/test/functional/page_objects/endpoint_page.ts @@ -63,9 +63,42 @@ export function EndpointPageProvider({ getService }: FtrProviderContext) { async waitForTableToHaveData(dataTestSubj: string) { await retry.waitForWithTimeout('table to have data', 2000, async () => { const tableData = await this.getEndpointAppTableData(dataTestSubj); - if (tableData[1][0] === 'No items found') return false; + if (tableData[1][0] === 'No items found') { + return false; + } return true; }); }, + + async hostFlyoutDescriptionKeys(dataTestSubj: string) { + await testSubjects.exists(dataTestSubj); + const detailsData: WebElementWrapper = await testSubjects.find(dataTestSubj); + const $ = await detailsData.parseDomContent(); + return $('dt') + .toArray() + .map(key => + $(key) + .text() + .replace(/ /g, '') + .trim() + ); + }, + + async hostFlyoutDescriptionValues(dataTestSubj: string) { + await testSubjects.exists(dataTestSubj); + const detailsData: WebElementWrapper = await testSubjects.find(dataTestSubj); + const $ = await detailsData.parseDomContent(); + return $('dd') + .toArray() + .map((value, index) => { + if (index === 1) { + return ''; + } + return $(value) + .text() + .replace(/ /g, '') + .trim(); + }); + }, }; } From fae93176e2a0d3d251d90a35bd80f2493bf7325e Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Mar 2020 08:11:36 +0100 Subject: [PATCH 74/76] [Console] Fix for `_settings` and x-pack autocomplete (#60246) * Add settings completion to index create endpoint and clean up. The cleanup is largely for moving settings data completion to JS and removing the dynamic logic for loading different ES versions. This is unused and unnecessary at this point. * Add new settings JS files and move BOOLEAN to shared file. * Important fix for loading x-pack console extensions. After migrating the x-pack console extensions were being loaded too late and were not being served to the client. * Reorder imports to convention --- src/plugins/console/public/lib/kb/kb.js | 10 +- src/plugins/console/server/lib/index.ts | 2 +- .../lib/spec_definitions/{es_6_0.js => es.js} | 44 +++---- .../server/lib/spec_definitions/index.d.ts | 9 +- .../server/lib/spec_definitions/index.js | 6 +- .../{es_6_0 => js}/aggregations.js | 0 .../{es_6_0 => js}/aliases.js | 0 .../{es_6_0 => js}/document.js | 0 .../spec_definitions/{es_6_0 => js}/filter.js | 0 .../{es_6_0 => js}/globals.js | 0 .../spec_definitions/{es_6_0 => js}/ingest.js | 0 .../{es_6_0 => js}/mappings.js | 4 +- .../{es_6_0 => js}/query/dsl.js | 0 .../{es_6_0 => js}/query/index.js | 0 .../{es_6_0 => js}/query/templates.js | 0 .../{es_6_0 => js}/reindex.js | 0 .../spec_definitions/{es_6_0 => js}/search.js | 0 .../lib/spec_definitions/js/settings.js | 74 ++++++++++++ .../server/lib/spec_definitions/js/shared.js | 22 ++++ .../spec_definitions/{spec => json}/.eslintrc | 0 .../{spec => json}/generated/_common.json | 0 .../{spec => json}/generated/bulk.json | 0 .../{spec => json}/generated/cat.aliases.json | 0 .../generated/cat.allocation.json | 0 .../{spec => json}/generated/cat.count.json | 0 .../generated/cat.fielddata.json | 0 .../{spec => json}/generated/cat.health.json | 0 .../{spec => json}/generated/cat.help.json | 0 .../{spec => json}/generated/cat.indices.json | 0 .../{spec => json}/generated/cat.master.json | 0 .../generated/cat.nodeattrs.json | 0 .../{spec => json}/generated/cat.nodes.json | 0 .../generated/cat.pending_tasks.json | 0 .../{spec => json}/generated/cat.plugins.json | 0 .../generated/cat.recovery.json | 0 .../generated/cat.repositories.json | 0 .../generated/cat.segments.json | 0 .../{spec => json}/generated/cat.shards.json | 0 .../generated/cat.snapshots.json | 0 .../{spec => json}/generated/cat.tasks.json | 0 .../generated/cat.templates.json | 0 .../generated/cat.thread_pool.json | 0 .../generated/clear_scroll.json | 0 .../generated/cluster.allocation_explain.json | 0 .../generated/cluster.get_settings.json | 0 .../generated/cluster.health.json | 0 .../generated/cluster.pending_tasks.json | 0 .../generated/cluster.put_settings.json | 0 .../generated/cluster.remote_info.json | 0 .../generated/cluster.reroute.json | 0 .../generated/cluster.state.json | 0 .../generated/cluster.stats.json | 0 .../{spec => json}/generated/count.json | 0 .../{spec => json}/generated/create.json | 0 .../{spec => json}/generated/delete.json | 0 .../generated/delete_by_query.json | 0 .../generated/delete_by_query_rethrottle.json | 0 .../generated/delete_script.json | 0 .../{spec => json}/generated/exists.json | 0 .../generated/exists_source.json | 0 .../{spec => json}/generated/explain.json | 0 .../{spec => json}/generated/field_caps.json | 0 .../{spec => json}/generated/get.json | 0 .../{spec => json}/generated/get_script.json | 0 .../generated/get_script_context.json | 0 .../generated/get_script_languages.json | 0 .../{spec => json}/generated/get_source.json | 0 .../{spec => json}/generated/index.json | 0 .../generated/indices.analyze.json | 0 .../generated/indices.clear_cache.json | 0 .../generated/indices.clone.json | 0 .../generated/indices.close.json | 0 .../generated/indices.create.json | 0 .../generated/indices.delete.json | 0 .../generated/indices.delete_alias.json | 0 .../generated/indices.delete_template.json | 0 .../generated/indices.exists.json | 0 .../generated/indices.exists_alias.json | 0 .../generated/indices.exists_template.json | 0 .../generated/indices.exists_type.json | 0 .../generated/indices.flush.json | 0 .../generated/indices.flush_synced.json | 0 .../generated/indices.forcemerge.json | 0 .../{spec => json}/generated/indices.get.json | 0 .../generated/indices.get_alias.json | 0 .../generated/indices.get_field_mapping.json | 0 .../generated/indices.get_mapping.json | 0 .../generated/indices.get_settings.json | 0 .../generated/indices.get_template.json | 0 .../generated/indices.get_upgrade.json | 0 .../generated/indices.open.json | 0 .../generated/indices.put_alias.json | 0 .../generated/indices.put_mapping.json | 0 .../generated/indices.put_settings.json | 0 .../generated/indices.put_template.json | 0 .../generated/indices.recovery.json | 0 .../generated/indices.refresh.json | 0 .../generated/indices.rollover.json | 0 .../generated/indices.segments.json | 0 .../generated/indices.shard_stores.json | 0 .../generated/indices.shrink.json | 0 .../generated/indices.split.json | 0 .../generated/indices.stats.json | 0 .../generated/indices.update_aliases.json | 0 .../generated/indices.upgrade.json | 0 .../generated/indices.validate_query.json | 0 .../{spec => json}/generated/info.json | 0 .../generated/ingest.delete_pipeline.json | 0 .../generated/ingest.get_pipeline.json | 0 .../generated/ingest.processor_grok.json | 0 .../generated/ingest.put_pipeline.json | 0 .../generated/ingest.simulate.json | 0 .../{spec => json}/generated/mget.json | 0 .../{spec => json}/generated/msearch.json | 0 .../generated/msearch_template.json | 0 .../generated/mtermvectors.json | 0 .../generated/nodes.hot_threads.json | 0 .../{spec => json}/generated/nodes.info.json | 0 .../nodes.reload_secure_settings.json | 0 .../{spec => json}/generated/nodes.stats.json | 0 .../{spec => json}/generated/nodes.usage.json | 0 .../{spec => json}/generated/ping.json | 0 .../{spec => json}/generated/put_script.json | 0 .../{spec => json}/generated/rank_eval.json | 0 .../{spec => json}/generated/reindex.json | 0 .../generated/reindex_rethrottle.json | 0 .../generated/render_search_template.json | 0 .../generated/scripts_painless_execute.json | 0 .../{spec => json}/generated/scroll.json | 0 .../{spec => json}/generated/search.json | 0 .../generated/search_shards.json | 0 .../generated/search_template.json | 0 .../snapshot.cleanup_repository.json | 0 .../generated/snapshot.create.json | 0 .../generated/snapshot.create_repository.json | 0 .../generated/snapshot.delete.json | 0 .../generated/snapshot.delete_repository.json | 0 .../generated/snapshot.get.json | 0 .../generated/snapshot.get_repository.json | 0 .../generated/snapshot.restore.json | 0 .../generated/snapshot.status.json | 0 .../generated/snapshot.verify_repository.json | 0 .../generated/tasks.cancel.json | 0 .../{spec => json}/generated/tasks.get.json | 0 .../{spec => json}/generated/tasks.list.json | 0 .../{spec => json}/generated/termvectors.json | 0 .../{spec => json}/generated/update.json | 0 .../generated/update_by_query.json | 0 .../generated/update_by_query_rethrottle.json | 0 .../spec_definitions/{spec => json}/index.js | 0 .../overrides/clear_scroll.json | 0 .../overrides/cluster.health.json | 0 .../overrides/cluster.put_settings.json | 0 .../overrides/cluster.reroute.json | 0 .../{spec => json}/overrides/count.json | 0 .../overrides/indices.analyze.json | 0 .../overrides/indices.clone.json | 0 .../overrides/indices.create.json | 0 .../overrides/indices.delete_template.json | 0 .../overrides/indices.exists_template.json | 0 .../overrides/indices.get_field_mapping.json | 0 .../overrides/indices.get_mapping.json | 0 .../overrides/indices.get_template.json | 0 .../overrides/indices.put_alias.json | 0 .../json/overrides/indices.put_settings.json | 7 ++ .../overrides/indices.put_template.json | 0 .../overrides/indices.rollover.json | 0 .../overrides/indices.update_aliases.json | 0 .../overrides/indices.validate_query.json | 0 .../overrides/snapshot.create.json | 0 .../overrides/snapshot.create_repository.json | 0 .../overrides/snapshot.restore.json | 0 .../server/lib/spec_definitions/server.js | 21 +--- .../lib/spec_definitions/server.test.js | 51 --------- .../spec/overrides/indices.put_settings.json | 108 ------------------ src/plugins/console/server/plugin.ts | 11 +- .../api/console/spec_definitions/index.ts | 22 +--- .../generated/ml.estimate_memory_usage.json | 2 +- 178 files changed, 163 insertions(+), 230 deletions(-) rename src/plugins/console/server/lib/spec_definitions/{es_6_0.js => es.js} (54%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/aggregations.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/aliases.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/document.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/filter.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/globals.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/ingest.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/mappings.js (99%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/query/dsl.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/query/index.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/query/templates.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/reindex.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/search.js (100%) create mode 100644 src/plugins/console/server/lib/spec_definitions/js/settings.js create mode 100644 src/plugins/console/server/lib/spec_definitions/js/shared.js rename src/plugins/console/server/lib/spec_definitions/{spec => json}/.eslintrc (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/_common.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/bulk.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.aliases.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.allocation.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.count.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.fielddata.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.health.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.help.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.indices.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.master.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.nodeattrs.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.nodes.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.pending_tasks.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.plugins.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.recovery.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.repositories.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.segments.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.shards.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.snapshots.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.tasks.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.templates.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.thread_pool.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/clear_scroll.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.allocation_explain.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.get_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.health.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.pending_tasks.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.put_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.remote_info.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.reroute.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.state.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.stats.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/count.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete_by_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete_by_query_rethrottle.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete_script.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/exists.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/exists_source.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/explain.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/field_caps.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_script.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_script_context.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_script_languages.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_source.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/index.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.analyze.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.clear_cache.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.clone.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.close.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.delete.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.delete_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.delete_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists_type.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.flush.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.flush_synced.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.forcemerge.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_field_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_upgrade.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.open.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.recovery.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.refresh.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.rollover.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.segments.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.shard_stores.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.shrink.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.split.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.stats.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.update_aliases.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.upgrade.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.validate_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/info.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.delete_pipeline.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.get_pipeline.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.processor_grok.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.put_pipeline.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.simulate.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/mget.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/msearch.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/msearch_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/mtermvectors.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.hot_threads.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.info.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.reload_secure_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.stats.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.usage.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/put_script.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/rank_eval.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/reindex.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/reindex_rethrottle.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/render_search_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/scripts_painless_execute.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/scroll.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/search.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/search_shards.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/search_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.cleanup_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.create_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.delete.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.delete_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.get_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.restore.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.status.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.verify_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/tasks.cancel.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/tasks.get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/tasks.list.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/termvectors.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/update.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/update_by_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/update_by_query_rethrottle.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/index.js (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/clear_scroll.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/cluster.health.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/cluster.put_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/cluster.reroute.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/count.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.analyze.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.clone.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.delete_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.exists_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.get_field_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.get_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.get_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.put_alias.json (100%) create mode 100644 src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.put_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.rollover.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.update_aliases.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.validate_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/snapshot.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/snapshot.create_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/snapshot.restore.json (100%) delete mode 100644 src/plugins/console/server/lib/spec_definitions/server.test.js delete mode 100644 src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json diff --git a/src/plugins/console/public/lib/kb/kb.js b/src/plugins/console/public/lib/kb/kb.js index 95896bed02988..053b82bd81d0a 100644 --- a/src/plugins/console/public/lib/kb/kb.js +++ b/src/plugins/console/public/lib/kb/kb.js @@ -147,13 +147,9 @@ function loadApisFromJson( } export function setActiveApi(api) { - if (_.isString(api)) { + if (!api) { $.ajax({ - url: - '../api/console/api_server?sense_version=' + - encodeURIComponent('@@SENSE_VERSION') + - '&apis=' + - encodeURIComponent(api), + url: '../api/console/api_server', dataType: 'json', // disable automatic guessing }).then( function(data) { @@ -169,7 +165,7 @@ export function setActiveApi(api) { ACTIVE_API = api; } -setActiveApi('es_6_0'); +setActiveApi(); export const _test = { loadApisFromJson: loadApisFromJson, diff --git a/src/plugins/console/server/lib/index.ts b/src/plugins/console/server/lib/index.ts index 98004768f880b..2347084b73a66 100644 --- a/src/plugins/console/server/lib/index.ts +++ b/src/plugins/console/server/lib/index.ts @@ -22,4 +22,4 @@ export { ProxyConfigCollection } from './proxy_config_collection'; export { proxyRequest } from './proxy_request'; export { getElasticsearchProxyConfig } from './elasticsearch_proxy_config'; export { setHeaders } from './set_headers'; -export { addProcessorDefinition, addExtensionSpecFilePath } from './spec_definitions'; +export { addProcessorDefinition, addExtensionSpecFilePath, loadSpec } from './spec_definitions'; diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0.js b/src/plugins/console/server/lib/spec_definitions/es.js similarity index 54% rename from src/plugins/console/server/lib/spec_definitions/es_6_0.js rename to src/plugins/console/server/lib/spec_definitions/es.js index 171d232407956..fc24a64f8a6f4 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0.js +++ b/src/plugins/console/server/lib/spec_definitions/es.js @@ -18,26 +18,30 @@ */ import Api from './api'; -import { getSpec } from './spec'; -import { register } from './es_6_0/ingest'; -const ES_6_0 = new Api('es_6_0'); -const spec = getSpec(); +import { getSpec } from './json'; +import { register } from './js/ingest'; +const ES = new Api('es'); -// adding generated specs -Object.keys(spec).forEach(endpoint => { - ES_6_0.addEndpointDescription(endpoint, spec[endpoint]); -}); +export const loadSpec = () => { + const spec = getSpec(); -//adding globals and custom API definitions -require('./es_6_0/aliases')(ES_6_0); -require('./es_6_0/aggregations')(ES_6_0); -require('./es_6_0/document')(ES_6_0); -require('./es_6_0/filter')(ES_6_0); -require('./es_6_0/globals')(ES_6_0); -register(ES_6_0); -require('./es_6_0/mappings')(ES_6_0); -require('./es_6_0/query')(ES_6_0); -require('./es_6_0/reindex')(ES_6_0); -require('./es_6_0/search')(ES_6_0); + // adding generated specs + Object.keys(spec).forEach(endpoint => { + ES.addEndpointDescription(endpoint, spec[endpoint]); + }); -export default ES_6_0; + // adding globals and custom API definitions + require('./js/aliases')(ES); + require('./js/aggregations')(ES); + require('./js/document')(ES); + require('./js/filter')(ES); + require('./js/globals')(ES); + register(ES); + require('./js/mappings')(ES); + require('./js/settings')(ES); + require('./js/query')(ES); + require('./js/reindex')(ES); + require('./js/search')(ES); +}; + +export default ES; diff --git a/src/plugins/console/server/lib/spec_definitions/index.d.ts b/src/plugins/console/server/lib/spec_definitions/index.d.ts index 0a79d3fb386f1..da0125a186c15 100644 --- a/src/plugins/console/server/lib/spec_definitions/index.d.ts +++ b/src/plugins/console/server/lib/spec_definitions/index.d.ts @@ -19,6 +19,13 @@ export declare function addProcessorDefinition(...args: any[]): any; -export declare function resolveApi(senseVersion: string, apis: string[]): object; +export declare function resolveApi(): object; export declare function addExtensionSpecFilePath(...args: any[]): any; + +/** + * A function that synchronously reads files JSON from disk and builds + * the autocomplete structures served to the client. This must be called + * after any extensions have been loaded. + */ +export declare function loadSpec(): any; diff --git a/src/plugins/console/server/lib/spec_definitions/index.js b/src/plugins/console/server/lib/spec_definitions/index.js index 3fe1913d5a193..abf55639fbee8 100644 --- a/src/plugins/console/server/lib/spec_definitions/index.js +++ b/src/plugins/console/server/lib/spec_definitions/index.js @@ -17,8 +17,10 @@ * under the License. */ -export { addProcessorDefinition } from './es_6_0/ingest'; +export { addProcessorDefinition } from './js/ingest'; -export { addExtensionSpecFilePath } from './spec'; +export { addExtensionSpecFilePath } from './json'; + +export { loadSpec } from './es'; export { resolveApi } from './server'; diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/aggregations.js b/src/plugins/console/server/lib/spec_definitions/js/aggregations.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/aggregations.js rename to src/plugins/console/server/lib/spec_definitions/js/aggregations.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/aliases.js b/src/plugins/console/server/lib/spec_definitions/js/aliases.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/aliases.js rename to src/plugins/console/server/lib/spec_definitions/js/aliases.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/document.js b/src/plugins/console/server/lib/spec_definitions/js/document.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/document.js rename to src/plugins/console/server/lib/spec_definitions/js/document.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/filter.js b/src/plugins/console/server/lib/spec_definitions/js/filter.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/filter.js rename to src/plugins/console/server/lib/spec_definitions/js/filter.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/globals.js b/src/plugins/console/server/lib/spec_definitions/js/globals.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/globals.js rename to src/plugins/console/server/lib/spec_definitions/js/globals.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/ingest.js b/src/plugins/console/server/lib/spec_definitions/js/ingest.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/ingest.js rename to src/plugins/console/server/lib/spec_definitions/js/ingest.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/mappings.js b/src/plugins/console/server/lib/spec_definitions/js/mappings.js similarity index 99% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/mappings.js rename to src/plugins/console/server/lib/spec_definitions/js/mappings.js index 8c31e5bc6fbb2..5884d14d4dc8b 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0/mappings.js +++ b/src/plugins/console/server/lib/spec_definitions/js/mappings.js @@ -19,9 +19,7 @@ const _ = require('lodash'); -const BOOLEAN = { - __one_of: [true, false], -}; +import { BOOLEAN } from './shared'; export default function(api) { api.addEndpointDescription('put_mapping', { diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/js/query/dsl.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js rename to src/plugins/console/server/lib/spec_definitions/js/query/dsl.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/index.js b/src/plugins/console/server/lib/spec_definitions/js/query/index.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/query/index.js rename to src/plugins/console/server/lib/spec_definitions/js/query/index.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/templates.js b/src/plugins/console/server/lib/spec_definitions/js/query/templates.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/query/templates.js rename to src/plugins/console/server/lib/spec_definitions/js/query/templates.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/reindex.js b/src/plugins/console/server/lib/spec_definitions/js/reindex.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/reindex.js rename to src/plugins/console/server/lib/spec_definitions/js/reindex.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/search.js b/src/plugins/console/server/lib/spec_definitions/js/search.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/search.js rename to src/plugins/console/server/lib/spec_definitions/js/search.js diff --git a/src/plugins/console/server/lib/spec_definitions/js/settings.js b/src/plugins/console/server/lib/spec_definitions/js/settings.js new file mode 100644 index 0000000000000..26cd0987c34a5 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/js/settings.js @@ -0,0 +1,74 @@ +/* + * 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 { BOOLEAN } from './shared'; + +export default function(api) { + api.addEndpointDescription('put_settings', { + data_autocomplete_rules: { + refresh_interval: '1s', + number_of_shards: 1, + number_of_replicas: 1, + 'blocks.read_only': BOOLEAN, + 'blocks.read': BOOLEAN, + 'blocks.write': BOOLEAN, + 'blocks.metadata': BOOLEAN, + term_index_interval: 32, + term_index_divisor: 1, + 'translog.flush_threshold_ops': 5000, + 'translog.flush_threshold_size': '200mb', + 'translog.flush_threshold_period': '30m', + 'translog.disable_flush': BOOLEAN, + 'cache.filter.max_size': '2gb', + 'cache.filter.expire': '2h', + 'gateway.snapshot_interval': '10s', + routing: { + allocation: { + include: { + tag: '', + }, + exclude: { + tag: '', + }, + require: { + tag: '', + }, + total_shards_per_node: -1, + }, + }, + 'recovery.initial_shards': { + __one_of: ['quorum', 'quorum-1', 'half', 'full', 'full-1'], + }, + 'ttl.disable_purge': BOOLEAN, + analysis: { + analyzer: {}, + tokenizer: {}, + filter: {}, + char_filter: {}, + }, + 'cache.query.enable': BOOLEAN, + shadow_replicas: BOOLEAN, + shared_filesystem: BOOLEAN, + data_path: 'path', + codec: { + __one_of: ['default', 'best_compression', 'lucene_default'], + }, + }, + }); +} diff --git a/src/plugins/console/server/lib/spec_definitions/js/shared.js b/src/plugins/console/server/lib/spec_definitions/js/shared.js new file mode 100644 index 0000000000000..ace189e2d0913 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/js/shared.js @@ -0,0 +1,22 @@ +/* + * 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. + */ + +export const BOOLEAN = Object.freeze({ + __one_of: [true, false], +}); diff --git a/src/plugins/console/server/lib/spec_definitions/spec/.eslintrc b/src/plugins/console/server/lib/spec_definitions/json/.eslintrc similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/.eslintrc rename to src/plugins/console/server/lib/spec_definitions/json/.eslintrc diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/_common.json b/src/plugins/console/server/lib/spec_definitions/json/generated/_common.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/_common.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/_common.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/bulk.json b/src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/bulk.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.aliases.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.aliases.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.aliases.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.aliases.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.allocation.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.allocation.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.allocation.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.allocation.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.count.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.count.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.count.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.count.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.fielddata.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.fielddata.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.fielddata.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.fielddata.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.health.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.health.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.health.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.health.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.help.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.help.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.help.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.help.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.indices.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.indices.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.master.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.master.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.master.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.master.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodeattrs.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodeattrs.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodeattrs.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodeattrs.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodes.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodes.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.pending_tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.pending_tasks.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.pending_tasks.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.pending_tasks.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.plugins.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.plugins.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.recovery.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.recovery.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.recovery.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.recovery.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.repositories.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.repositories.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.repositories.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.repositories.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.segments.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.segments.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.segments.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.segments.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.shards.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.shards.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.snapshots.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.snapshots.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.snapshots.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.snapshots.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.tasks.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.templates.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.templates.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.templates.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.templates.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.thread_pool.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.thread_pool.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/clear_scroll.json b/src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/clear_scroll.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.allocation_explain.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.allocation_explain.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.allocation_explain.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.allocation_explain.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.get_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.get_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.health.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.health.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.health.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.health.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.pending_tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.pending_tasks.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.pending_tasks.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.pending_tasks.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.put_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.remote_info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.remote_info.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.remote_info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.remote_info.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.reroute.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.reroute.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.reroute.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.reroute.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.state.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.state.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.state.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.state.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.stats.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/count.json b/src/plugins/console/server/lib/spec_definitions/json/generated/count.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/count.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/count.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/create.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query_rethrottle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query_rethrottle.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query_rethrottle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query_rethrottle.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete_script.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_script.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete_script.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete_script.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/exists.json b/src/plugins/console/server/lib/spec_definitions/json/generated/exists.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/exists.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/exists.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/exists_source.json b/src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/exists_source.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/explain.json b/src/plugins/console/server/lib/spec_definitions/json/generated/explain.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/explain.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/explain.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/field_caps.json b/src/plugins/console/server/lib/spec_definitions/json/generated/field_caps.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/field_caps.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/field_caps.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_script.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_script.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_script.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_context.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_context.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_languages.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_languages.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_source.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_source.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/index.json b/src/plugins/console/server/lib/spec_definitions/json/generated/index.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/index.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/index.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.analyze.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.analyze.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.analyze.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.analyze.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clear_cache.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.clear_cache.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clear_cache.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.clear_cache.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clone.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.clone.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clone.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.clone.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.close.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.close.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.close.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.close.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.create.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_type.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_type.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_type.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_type.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush_synced.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush_synced.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush_synced.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush_synced.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.forcemerge.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.forcemerge.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.forcemerge.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.forcemerge.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_field_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_field_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_upgrade.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_upgrade.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_upgrade.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_upgrade.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.open.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.open.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.open.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.open.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.recovery.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.recovery.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.recovery.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.recovery.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.refresh.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.refresh.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.refresh.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.refresh.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.rollover.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.rollover.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.segments.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.segments.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.segments.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.segments.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shard_stores.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shard_stores.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shard_stores.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.shard_stores.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shrink.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shrink.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.split.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.split.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.update_aliases.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.update_aliases.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.update_aliases.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.update_aliases.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.upgrade.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.upgrade.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.upgrade.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.upgrade.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.validate_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.validate_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.validate_query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.validate_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/info.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/info.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.delete_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.delete_pipeline.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.delete_pipeline.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.delete_pipeline.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.get_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.get_pipeline.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.processor_grok.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.processor_grok.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.processor_grok.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.processor_grok.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.put_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.put_pipeline.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.put_pipeline.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.put_pipeline.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.simulate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.simulate.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.simulate.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.simulate.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/mget.json b/src/plugins/console/server/lib/spec_definitions/json/generated/mget.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/mget.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/mget.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/msearch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/msearch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/msearch_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/msearch_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/mtermvectors.json b/src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/mtermvectors.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.hot_threads.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.hot_threads.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.hot_threads.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.hot_threads.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.info.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.info.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.reload_secure_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.reload_secure_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.reload_secure_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.reload_secure_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.usage.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.usage.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.usage.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.usage.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/put_script.json b/src/plugins/console/server/lib/spec_definitions/json/generated/put_script.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/put_script.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/put_script.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/rank_eval.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rank_eval.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/rank_eval.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rank_eval.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/reindex.json b/src/plugins/console/server/lib/spec_definitions/json/generated/reindex.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/reindex.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/reindex.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/reindex_rethrottle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/reindex_rethrottle.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/reindex_rethrottle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/reindex_rethrottle.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/render_search_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/render_search_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/scripts_painless_execute.json b/src/plugins/console/server/lib/spec_definitions/json/generated/scripts_painless_execute.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/scripts_painless_execute.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/scripts_painless_execute.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/scroll.json b/src/plugins/console/server/lib/spec_definitions/json/generated/scroll.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/scroll.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/scroll.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/search.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/search.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/search.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/search_shards.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search_shards.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/search_shards.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/search_shards.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/search_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/search_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.cleanup_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.cleanup_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.restore.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.restore.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.restore.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.restore.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.status.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.status.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.status.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.verify_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.verify_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.verify_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.verify_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.cancel.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.cancel.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/tasks.get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.list.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.list.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.list.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/tasks.list.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/termvectors.json b/src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/termvectors.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/update.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/update.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/update.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query_rethrottle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query_rethrottle.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query_rethrottle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query_rethrottle.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/index.js b/src/plugins/console/server/lib/spec_definitions/json/index.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/index.js rename to src/plugins/console/server/lib/spec_definitions/json/index.js diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/clear_scroll.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/clear_scroll.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/clear_scroll.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/clear_scroll.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.health.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.health.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.health.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.health.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.put_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.reroute.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.reroute.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.reroute.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.reroute.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/count.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/count.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/count.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/count.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.analyze.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.analyze.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.analyze.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.analyze.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.clone.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.clone.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.clone.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.clone.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.create.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.create.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.delete_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.delete_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.delete_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.delete_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.exists_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.exists_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.exists_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.exists_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_field_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_field_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_field_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_field_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_alias.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json new file mode 100644 index 0000000000000..2ae8fd82be4d8 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json @@ -0,0 +1,7 @@ +{ + "indices.put_settings": { + "data_autocomplete_rules": { + "__scope_link": "put_settings" + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.rollover.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.rollover.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.rollover.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.rollover.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.update_aliases.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.update_aliases.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.update_aliases.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.update_aliases.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.validate_query.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.validate_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.validate_query.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.validate_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create_repository.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.restore.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.restore.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.restore.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.restore.json diff --git a/src/plugins/console/server/lib/spec_definitions/server.js b/src/plugins/console/server/lib/spec_definitions/server.js index dd700bf019507..cb855958d403a 100644 --- a/src/plugins/console/server/lib/spec_definitions/server.js +++ b/src/plugins/console/server/lib/spec_definitions/server.js @@ -17,21 +17,10 @@ * under the License. */ -import _ from 'lodash'; +import es from './es'; -const KNOWN_APIS = ['es_6_0']; - -export function resolveApi(senseVersion, apis) { - const result = {}; - _.each(apis, function(name) { - { - if (KNOWN_APIS.includes(name)) { - // for now we ignore sense_version. might add it in the api name later - const api = require('./' + name); // eslint-disable-line import/no-dynamic-require - result[name] = api.asJson(); - } - } - }); - - return result; +export function resolveApi() { + return { + es: es.asJson(), + }; } diff --git a/src/plugins/console/server/lib/spec_definitions/server.test.js b/src/plugins/console/server/lib/spec_definitions/server.test.js deleted file mode 100644 index 747689237c177..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/server.test.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 { resolveApi } from './server'; - -describe('resolveApi', () => { - it('allows known APIs to be resolved', () => { - const mockReply = jest.fn(result => ({ type: () => result })); - const result = resolveApi('Sense Version', ['es_6_0'], { response: mockReply }); - expect(result).toMatchObject({ - es_6_0: { - endpoints: expect.any(Object), - globals: expect.any(Object), - name: expect.any(String), - }, - }); - }); - - it('does not resolve APIs that are not known', () => { - const mockReply = jest.fn(result => ({ type: () => result })); - const result = resolveApi('Sense Version', ['unknown'], { response: mockReply }); - expect(result).toEqual({}); - }); - - it('handles request for apis that are known and unknown', () => { - const mockReply = jest.fn(result => ({ type: () => result })); - const result = resolveApi('Sense Version', ['es_6_0'], { response: mockReply }); - expect(result).toMatchObject({ - es_6_0: { - endpoints: expect.any(Object), - globals: expect.any(Object), - name: expect.any(String), - }, - }); - }); -}); diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json b/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json deleted file mode 100644 index 2e1e3024665a4..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "indices.put_settings": { - "data_autocomplete_rules": { - "refresh_interval": "1s", - "number_of_shards": 1, - "number_of_replicas": 1, - "blocks.read_only": { - "__one_of": [ - false, - true - ] - }, - "blocks.read": { - "__one_of": [ - true, - false - ] - }, - "blocks.write": { - "__one_of": [ - true, - false - ] - }, - "blocks.metadata": { - "__one_of": [ - true, - false - ] - }, - "term_index_interval": 32, - "term_index_divisor": 1, - "translog.flush_threshold_ops": 5000, - "translog.flush_threshold_size": "200mb", - "translog.flush_threshold_period": "30m", - "translog.disable_flush": { - "__one_of": [ - true, - false - ] - }, - "cache.filter.max_size": "2gb", - "cache.filter.expire": "2h", - "gateway.snapshot_interval": "10s", - "routing": { - "allocation": { - "include": { - "tag": "" - }, - "exclude": { - "tag": "" - }, - "require": { - "tag": "" - }, - "total_shards_per_node": -1 - } - }, - "recovery.initial_shards": { - "__one_of": [ - "quorum", - "quorum-1", - "half", - "full", - "full-1" - ] - }, - "ttl.disable_purge": { - "__one_of": [ - true, - false - ] - }, - "analysis": { - "analyzer": {}, - "tokenizer": {}, - "filter": {}, - "char_filter": {} - }, - "cache.query.enable": { - "__one_of": [ - true, - false - ] - }, - "shadow_replicas": { - "__one_of": [ - true, - false - ] - }, - "shared_filesystem": { - "__one_of": [ - true, - false - ] - }, - "data_path": "path", - "codec": { - "__one_of": [ - "default", - "best_compression", - "lucene_default" - ] - } - } - } -} diff --git a/src/plugins/console/server/plugin.ts b/src/plugins/console/server/plugin.ts index 65647bd5acb7c..1954918f4d74f 100644 --- a/src/plugins/console/server/plugin.ts +++ b/src/plugins/console/server/plugin.ts @@ -21,7 +21,12 @@ import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/serv import { readLegacyEsConfig } from '../../../legacy/core_plugins/console_legacy'; -import { ProxyConfigCollection, addExtensionSpecFilePath, addProcessorDefinition } from './lib'; +import { + ProxyConfigCollection, + addExtensionSpecFilePath, + addProcessorDefinition, + loadSpec, +} from './lib'; import { ConfigType } from './config'; import { registerProxyRoute } from './routes/api/console/proxy'; import { registerSpecDefinitionsRoute } from './routes/api/console/spec_definitions'; @@ -75,5 +80,7 @@ export class ConsoleServerPlugin implements Plugin { }; } - start() {} + start() { + loadSpec(); + } } diff --git a/src/plugins/console/server/routes/api/console/spec_definitions/index.ts b/src/plugins/console/server/routes/api/console/spec_definitions/index.ts index e2ece37f407ac..88bc250bbfce6 100644 --- a/src/plugins/console/server/routes/api/console/spec_definitions/index.ts +++ b/src/plugins/console/server/routes/api/console/spec_definitions/index.ts @@ -16,33 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -import { schema, TypeOf } from '@kbn/config-schema'; import { IRouter, RequestHandler } from 'kibana/server'; import { resolveApi } from '../../../../lib/spec_definitions'; export const registerSpecDefinitionsRoute = ({ router }: { router: IRouter }) => { - const handler: RequestHandler> = async ( - ctx, - request, - response - ) => { - const { sense_version: version, apis } = request.query; - + const handler: RequestHandler = async (ctx, request, response) => { return response.ok({ - body: resolveApi(version, apis.split(',')), + body: resolveApi(), headers: { 'Content-Type': 'application/json', }, }); }; - const validate = { - query: schema.object({ - sense_version: schema.string({ defaultValue: '' }), - apis: schema.string(), - }), - }; - - router.get({ path: '/api/console/api_server', validate }, handler); - router.post({ path: '/api/console/api_server', validate }, handler); + router.get({ path: '/api/console/api_server', validate: false }, handler); + router.post({ path: '/api/console/api_server', validate: false }, handler); }; diff --git a/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json b/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json index a6ec31465392a..2195b74640c79 100644 --- a/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json +++ b/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json @@ -1,7 +1,7 @@ { "ml.estimate_memory_usage": { "methods": [ - "POST" + "PUT" ], "patterns": [ "_ml/data_frame/analytics/_estimate_memory_usage" From 2fbf38b57ade3585cf092359e51208178a72a461 Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Wed, 18 Mar 2020 10:25:30 +0300 Subject: [PATCH 75/76] [NP] Use local helper shortenDottedString for discover (#60271) * Move shortenDottedString into kibana_utils * Move helper back to data utils * Use local helper for discover * Clean up --- .../kibana/public/discover/kibana_services.ts | 2 -- .../angular/directives/field_name/field_name.tsx | 2 +- .../components/table_header/helpers.tsx | 3 ++- .../discover/np_ready/helpers/index.ts} | 16 +--------------- .../np_ready/helpers/shorten_dotted_string.ts} | 7 +------ 5 files changed, 5 insertions(+), 25 deletions(-) rename src/legacy/core_plugins/kibana/{common/utils/__tests__/shorten_dotted_string.js => public/discover/np_ready/helpers/index.ts} (60%) rename src/legacy/core_plugins/kibana/{common/utils/shorten_dotted_string.js => public/discover/np_ready/helpers/shorten_dotted_string.ts} (81%) diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 8202ba13b30cc..5f3dbb65fd8ff 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -53,8 +53,6 @@ export { wrapInI18nContext } from 'ui/i18n'; import { search } from '../../../../../plugins/data/public'; export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search; // @ts-ignore -export { shortenDottedString } from '../../common/utils/shorten_dotted_string'; -// @ts-ignore export { intervalOptions } from 'ui/agg_types'; export { subscribeWithScope } from '../../../../../plugins/kibana_legacy/public'; // @ts-ignore diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx index 26d8a5abb2471..1b3b16332fa4f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx @@ -21,7 +21,7 @@ import classNames from 'classnames'; import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { FieldIcon, FieldIconProps } from '../../../../../../../../../plugins/kibana_react/public'; -import { shortenDottedString } from '../../../../kibana_services'; +import { shortenDottedString } from '../../../helpers'; import { getFieldTypeName } from './field_type_name'; // property field is provided at discover's field chooser diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx index a2ad18d59d935..bd48b1e083871 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx @@ -16,7 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { IndexPattern, shortenDottedString } from '../../../../../kibana_services'; +import { IndexPattern } from '../../../../../kibana_services'; +import { shortenDottedString } from '../../../../helpers'; export type SortOrder = [string, string]; export interface ColumnProps { diff --git a/src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts similarity index 60% rename from src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts index 267ca74c7c42a..7196c96989e97 100644 --- a/src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts @@ -17,18 +17,4 @@ * under the License. */ -import expect from '@kbn/expect'; -import { shortenDottedString } from '../shorten_dotted_string'; - -describe('shortenDottedString', () => { - it('Convert a dot.notated.string into a short string', () => { - expect(shortenDottedString('dot.notated.string')).to.equal('d.n.string'); - }); - - it('Ignores non-string values', () => { - expect(shortenDottedString(true)).to.equal(true); - expect(shortenDottedString(123)).to.equal(123); - const obj = { key: 'val' }; - expect(shortenDottedString(obj)).to.equal(obj); - }); -}); +export { shortenDottedString } from './shorten_dotted_string'; diff --git a/src/legacy/core_plugins/kibana/common/utils/shorten_dotted_string.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/shorten_dotted_string.ts similarity index 81% rename from src/legacy/core_plugins/kibana/common/utils/shorten_dotted_string.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/shorten_dotted_string.ts index ca76a2a537742..9d78a96784339 100644 --- a/src/legacy/core_plugins/kibana/common/utils/shorten_dotted_string.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/shorten_dotted_string.ts @@ -22,10 +22,5 @@ const DOT_PREFIX_RE = /(.).+?\./g; /** * Convert a dot.notated.string into a short * version (d.n.string) - * - * @param {string} str - the long string to convert - * @return {string} */ -export function shortenDottedString(input) { - return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.'); -} +export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.'); From fd16c461289700eca91e0929851a5051f22c4523 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 18 Mar 2020 08:33:53 +0000 Subject: [PATCH 76/76] [ML] Re-enabling file upload telemetry (#60418) * [ML] Re-enabling file upload telemetry * small refactor * removing exported function * removing commented out code * removing commented out include * cleaning up types --- x-pack/plugins/ml/mappings.json | 13 -- x-pack/plugins/ml/public/plugin.ts | 3 + .../ml/server/lib/ml_telemetry/index.ts | 15 -- .../ml_telemetry/make_ml_usage_collector.ts | 41 ------ .../lib/ml_telemetry/ml_telemetry.test.ts | 128 ------------------ .../server/lib/ml_telemetry/ml_telemetry.ts | 72 ---------- .../plugins/ml/server/lib/telemetry/index.ts | 8 ++ .../lib/telemetry/internal_repository.ts | 15 ++ .../ml/server/lib/telemetry/mappings.ts | 25 ++++ .../lib/telemetry/ml_usage_collector.ts | 32 +++++ .../ml/server/lib/telemetry/telemetry.test.ts | 49 +++++++ .../ml/server/lib/telemetry/telemetry.ts | 81 +++++++++++ x-pack/plugins/ml/server/plugin.ts | 6 +- .../ml/server/routes/file_data_visualizer.ts | 4 +- 14 files changed, 217 insertions(+), 275 deletions(-) delete mode 100644 x-pack/plugins/ml/mappings.json delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/index.ts delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/index.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/mappings.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/telemetry.ts diff --git a/x-pack/plugins/ml/mappings.json b/x-pack/plugins/ml/mappings.json deleted file mode 100644 index 041b85dbea4a1..0000000000000 --- a/x-pack/plugins/ml/mappings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type" : "long" - } - } - } - } - } -} diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 624e877bda49f..79aebece85af2 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { Plugin, CoreStart, CoreSetup, AppMountParameters } from 'kibana/public'; import { ManagementSetup } from 'src/plugins/management/public'; import { SharePluginStart } from 'src/plugins/share/public'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { SecurityPluginSetup } from '../../security/public'; @@ -24,6 +25,7 @@ export interface MlSetupDependencies { security: SecurityPluginSetup; licensing: LicensingPluginSetup; management: ManagementSetup; + usageCollection: UsageCollectionSetup; } export class MlPlugin implements Plugin { @@ -47,6 +49,7 @@ export class MlPlugin implements Plugin { security: pluginsSetup.security, licensing: pluginsSetup.licensing, management: pluginsSetup.management, + usageCollection: pluginsSetup.usageCollection, }, { element: params.element, diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/index.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/index.ts deleted file mode 100644 index dffd95f50e0d9..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/index.ts +++ /dev/null @@ -1,15 +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. - */ - -export { - createMlTelemetry, - incrementFileDataVisualizerIndexCreationCount, - storeMlTelemetry, - MlTelemetry, - MlTelemetrySavedObject, - ML_TELEMETRY_DOC_ID, -} from './ml_telemetry'; -export { makeMlUsageCollector } from './make_ml_usage_collector'; diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts deleted file mode 100644 index 15a430a08eac1..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { SavedObjectsServiceStart } from 'kibana/server'; -import { - createMlTelemetry, - ML_TELEMETRY_DOC_ID, - MlTelemetry, - MlTelemetrySavedObject, -} from './ml_telemetry'; - -export function makeMlUsageCollector( - usageCollection: UsageCollectionSetup | undefined, - savedObjects: SavedObjectsServiceStart -): void { - if (!usageCollection) { - return; - } - - const mlUsageCollector = usageCollection.makeUsageCollector({ - type: 'ml', - isReady: () => true, - fetch: async (): Promise => { - try { - const mlTelemetrySavedObject: MlTelemetrySavedObject = await savedObjects - .createInternalRepository() - .get('ml-telemetry', ML_TELEMETRY_DOC_ID); - - return mlTelemetrySavedObject.attributes; - } catch (err) { - return createMlTelemetry(); - } - }, - }); - - usageCollection.registerCollector(mlUsageCollector); -} diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts deleted file mode 100644 index cda160877f7ae..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// import { -// createMlTelemetry, -// incrementFileDataVisualizerIndexCreationCount, -// ML_TELEMETRY_DOC_ID, -// MlTelemetry, -// storeMlTelemetry, -// } from './ml_telemetry'; - -describe('ml_telemetry', () => { - describe('createMlTelemetry', () => { - it('should create a MlTelemetry object', () => { - // const mlTelemetry = createMlTelemetry(1); - // expect(mlTelemetry.file_data_visualizer.index_creation_count).toBe(1); - }); - it('should ignore undefined or unknown values', () => { - // const mlTelemetry = createMlTelemetry(undefined); - // expect(mlTelemetry.file_data_visualizer.index_creation_count).toBe(0); - }); - }); - - describe('storeMlTelemetry', () => { - // let mlTelemetry: MlTelemetry; - // let internalRepository: any; - - // beforeEach(() => { - // internalRepository = { create: jest.fn(), get: jest.fn() }; - // mlTelemetry = { - // file_data_visualizer: { - // index_creation_count: 1, - // }, - // }; - // }); - - it('should call internalRepository create with the given MlTelemetry object', () => { - // storeMlTelemetry(internalRepository, mlTelemetry); - // expect(internalRepository.create.mock.calls[0][1]).toBe(mlTelemetry); - }); - - it('should call internalRepository create with the ml-telemetry document type and ID', () => { - // storeMlTelemetry(internalRepository, mlTelemetry); - // expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry'); - // expect(internalRepository.create.mock.calls[0][2].id).toBe(ML_TELEMETRY_DOC_ID); - }); - - it('should call internalRepository create with overwrite: true', () => { - // storeMlTelemetry(internalRepository, mlTelemetry); - // expect(internalRepository.create.mock.calls[0][2].overwrite).toBe(true); - }); - }); - - describe('incrementFileDataVisualizerIndexCreationCount', () => { - // let savedObjectsClient: any; - - // function createSavedObjectsClientInstance( - // telemetryEnabled?: boolean, - // indexCreationCount?: number - // ) { - // return { - // create: jest.fn(), - // get: jest.fn(obj => { - // switch (obj) { - // case 'telemetry': - // if (telemetryEnabled === undefined) { - // throw Error; - // } - // return { - // attributes: { - // enabled: telemetryEnabled, - // }, - // }; - // case 'ml-telemetry': - // // emulate that a non-existing saved object will throw an error - // if (indexCreationCount === undefined) { - // throw Error; - // } - // return { - // attributes: { - // file_data_visualizer: { - // index_creation_count: indexCreationCount, - // }, - // }, - // }; - // } - // }), - // }; - // } - - // function mockInit(telemetryEnabled?: boolean, indexCreationCount?: number): void { - // savedObjectsClient = createSavedObjectsClientInstance(telemetryEnabled, indexCreationCount); - // } - - it('should not increment if telemetry status cannot be determined', async () => { - // mockInit(); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls).toHaveLength(0); - }); - - it('should not increment if telemetry status is disabled', async () => { - // mockInit(false); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls).toHaveLength(0); - }); - - it('should initialize index_creation_count with 1', async () => { - // mockInit(true); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls[0][0]).toBe('ml-telemetry'); - // expect(savedObjectsClient.create.mock.calls[0][1]).toEqual({ - // file_data_visualizer: { index_creation_count: 1 }, - // }); - }); - - it('should increment index_creation_count to 2', async () => { - // mockInit(true, 1); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls[0][0]).toBe('ml-telemetry'); - // expect(savedObjectsClient.create.mock.calls[0][1]).toEqual({ - // file_data_visualizer: { index_creation_count: 2 }, - // }); - }); - }); -}); diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts deleted file mode 100644 index 1ca155582db11..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SavedObjectAttributes, SavedObjectsClientContract } from 'kibana/server'; - -export interface MlTelemetry extends SavedObjectAttributes { - file_data_visualizer: { - index_creation_count: number; - }; -} - -export interface MlTelemetrySavedObject { - attributes: MlTelemetry; -} - -export const ML_TELEMETRY_DOC_ID = 'ml-telemetry'; - -export function createMlTelemetry(count: number = 0): MlTelemetry { - return { - file_data_visualizer: { - index_creation_count: count, - }, - }; -} -// savedObjects -export function storeMlTelemetry( - savedObjectsClient: SavedObjectsClientContract, - mlTelemetry: MlTelemetry -): void { - savedObjectsClient.create('ml-telemetry', mlTelemetry, { - id: ML_TELEMETRY_DOC_ID, - overwrite: true, - }); -} - -export async function incrementFileDataVisualizerIndexCreationCount( - savedObjectsClient: SavedObjectsClientContract -): Promise { - return; - try { - const { attributes } = await savedObjectsClient.get<{ enabled: boolean }>( - 'telemetry', - 'telemetry' - ); - - if (attributes.enabled === false) { - return; - } - } catch (error) { - // if we aren't allowed to get the telemetry document, - // we assume we couldn't opt in to telemetry and won't increment the index count. - return; - } - - let indicesCount = 1; - - try { - const { attributes } = (await savedObjectsClient.get( - 'ml-telemetry', - ML_TELEMETRY_DOC_ID - )) as MlTelemetrySavedObject; - indicesCount = attributes.file_data_visualizer.index_creation_count + 1; - } catch (e) { - /* silently fail, this will happen if the saved object doesn't exist yet. */ - } - - const mlTelemetry = createMlTelemetry(indicesCount); - storeMlTelemetry(savedObjectsClient, mlTelemetry); -} diff --git a/x-pack/plugins/ml/server/lib/telemetry/index.ts b/x-pack/plugins/ml/server/lib/telemetry/index.ts new file mode 100644 index 0000000000000..b5ec80daf1787 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { initMlTelemetry } from './ml_usage_collector'; +export { updateTelemetry } from './telemetry'; diff --git a/x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts b/x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts new file mode 100644 index 0000000000000..a273ea4baadfa --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsServiceStart, ISavedObjectsRepository } from 'kibana/server'; + +let internalRepository: ISavedObjectsRepository | null = null; +export const setInternalRepository = ( + createInternalRepository: SavedObjectsServiceStart['createInternalRepository'] +) => { + internalRepository = createInternalRepository(); +}; +export const getInternalRepository = () => internalRepository; diff --git a/x-pack/plugins/ml/server/lib/telemetry/mappings.ts b/x-pack/plugins/ml/server/lib/telemetry/mappings.ts new file mode 100644 index 0000000000000..87e2243328422 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/mappings.ts @@ -0,0 +1,25 @@ +/* + * 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 { SavedObjectsType } from 'src/core/server'; +import { TELEMETRY_DOC_ID } from './telemetry'; + +export const mlTelemetryMappingsType: SavedObjectsType = { + name: TELEMETRY_DOC_ID, + hidden: false, + namespaceAgnostic: true, + mappings: { + properties: { + file_data_visualizer: { + properties: { + index_creation_count: { + type: 'long', + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts b/x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts new file mode 100644 index 0000000000000..21e5dce8e4706 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup } from 'kibana/server'; + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { getTelemetry, initTelemetry } from './telemetry'; +import { mlTelemetryMappingsType } from './mappings'; +import { setInternalRepository } from './internal_repository'; + +const TELEMETRY_TYPE = 'mlTelemetry'; + +export function initMlTelemetry(coreSetup: CoreSetup, usageCollection: UsageCollectionSetup) { + coreSetup.savedObjects.registerType(mlTelemetryMappingsType); + registerMlUsageCollector(usageCollection); + coreSetup.getStartServices().then(([core]) => { + setInternalRepository(core.savedObjects.createInternalRepository); + }); +} + +function registerMlUsageCollector(usageCollection: UsageCollectionSetup): void { + const mlUsageCollector = usageCollection.makeUsageCollector({ + type: TELEMETRY_TYPE, + isReady: () => true, + fetch: async () => (await getTelemetry()) || initTelemetry(), + }); + + usageCollection.registerCollector(mlUsageCollector); +} diff --git a/x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts b/x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts new file mode 100644 index 0000000000000..f41c4fda93a54 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getTelemetry, updateTelemetry } from './telemetry'; + +const internalRepository = () => ({ + get: jest.fn(() => null), + create: jest.fn(() => ({ attributes: 'test' })), + update: jest.fn(() => ({ attributes: 'test' })), +}); + +function mockInit(getVal: any = { attributes: {} }): any { + return { + ...internalRepository(), + get: jest.fn(() => getVal), + }; +} + +describe('ml plugin telemetry', () => { + describe('getTelemetry', () => { + it('should get existing telemetry', async () => { + const internalRepo = mockInit(); + await getTelemetry(internalRepo); + expect(internalRepo.update.mock.calls.length).toBe(0); + expect(internalRepo.get.mock.calls.length).toBe(1); + expect(internalRepo.create.mock.calls.length).toBe(0); + }); + }); + + describe('updateTelemetry', () => { + it('should update existing telemetry', async () => { + const internalRepo = mockInit({ + attributes: { + file_data_visualizer: { + index_creation_count: 2, + }, + }, + }); + + await updateTelemetry(internalRepo); + expect(internalRepo.update.mock.calls.length).toBe(1); + expect(internalRepo.get.mock.calls.length).toBe(1); + expect(internalRepo.create.mock.calls.length).toBe(0); + }); + }); +}); diff --git a/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts b/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts new file mode 100644 index 0000000000000..bc56e8b2a4372 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { ISavedObjectsRepository } from 'kibana/server'; + +import { getInternalRepository } from './internal_repository'; + +export const TELEMETRY_DOC_ID = 'ml-telemetry'; + +interface Telemetry { + file_data_visualizer: { + index_creation_count: number; + }; +} + +export interface TelemetrySavedObject { + attributes: Telemetry; +} + +export function initTelemetry(): Telemetry { + return { + file_data_visualizer: { + index_creation_count: 0, + }, + }; +} + +export async function getTelemetry( + internalRepository?: ISavedObjectsRepository +): Promise { + if (internalRepository === undefined) { + return null; + } + + let telemetrySavedObject; + + try { + telemetrySavedObject = await internalRepository.get( + TELEMETRY_DOC_ID, + TELEMETRY_DOC_ID + ); + } catch (e) { + // Fail silently + } + + return telemetrySavedObject ? telemetrySavedObject.attributes : null; +} + +export async function updateTelemetry(internalRepo?: ISavedObjectsRepository) { + const internalRepository = internalRepo || getInternalRepository(); + if (internalRepository === null) { + return; + } + + let telemetry = await getTelemetry(internalRepository); + // Create if doesn't exist + if (telemetry === null || _.isEmpty(telemetry)) { + const newTelemetrySavedObject = await internalRepository.create( + TELEMETRY_DOC_ID, + initTelemetry(), + { id: TELEMETRY_DOC_ID } + ); + telemetry = newTelemetrySavedObject.attributes; + } + + if (telemetry !== null) { + await internalRepository.update(TELEMETRY_DOC_ID, TELEMETRY_DOC_ID, incrementCounts(telemetry)); + } +} + +function incrementCounts(telemetry: Telemetry) { + return { + file_data_visualizer: { + index_creation_count: telemetry.file_data_visualizer.index_creation_count + 1, + }, + }; +} diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 01d0bcc867019..8948d232b9e5e 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -16,7 +16,7 @@ import { PluginsSetup, RouteInitialization } from './types'; import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; import { elasticsearchJsPlugin } from './client/elasticsearch_ml'; -import { makeMlUsageCollector } from './lib/ml_telemetry'; +import { initMlTelemetry } from './lib/telemetry'; import { initMlServerLog } from './client/log'; import { initSampleDataSets } from './lib/sample_data_sets'; @@ -130,9 +130,7 @@ export class MlServerPlugin implements Plugin { - makeMlUsageCollector(plugins.usageCollection, core.savedObjects); - }); + initMlTelemetry(coreSetup, plugins.usageCollection); return createSharedServices(this.mlLicense, plugins.spaces, plugins.cloud); } diff --git a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts b/x-pack/plugins/ml/server/routes/file_data_visualizer.ts index a14d51ae61b05..fcfd6e121c9f1 100644 --- a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts +++ b/x-pack/plugins/ml/server/routes/file_data_visualizer.ts @@ -19,7 +19,7 @@ import { } from '../models/file_data_visualizer'; import { RouteInitialization } from '../types'; -import { incrementFileDataVisualizerIndexCreationCount } from '../lib/ml_telemetry'; +import { updateTelemetry } from '../lib/telemetry'; function analyzeFiles(context: RequestHandlerContext, data: InputData, overrides: InputOverrides) { const { analyzeFile } = fileDataVisualizerProvider(context.ml!.mlClient.callAsCurrentUser); @@ -132,7 +132,7 @@ export function fileDataVisualizerRoutes({ router, mlLicense }: RouteInitializat // follow-up import calls to just add additional data will include the `id` of the created // index, we'll ignore those and don't increment the counter. if (id === undefined) { - await incrementFileDataVisualizerIndexCreationCount(context.core.savedObjects.client); + await updateTelemetry(); } const result = await importData(

    x(v`!BtEr^b;>wW+JJQRxHA>Gb&L3 zsq7DwRm@+Gj#8E_O=_&6%uIwvrcO0Y%Fx~@RF|d>LLFGBj$(tF2*cE52kL+C4$c%w zMQAwuLyeg*oH1x>{mpH<7>wtZ2FIKh(|w|^?7c{c>OV&)HpzWVx`i#mwttk($6DDE zvm<35&xgC95Z{M@zno1{hf@Bi!bQDS_Bt&G8^+akeKNUW zCa@UVLNbhdzeE8y7*gTz^7)IP9h?ng^p3QcOaA^OBQcjutW;8)FG>TUIs}U#WmP#fq`k z%|{U_C;>U8)fE#4#JH>-YRN7JwAMAMGh>v=A;wZ}K@F%`EjgHg=3J3v>m~s5Nq3)M z)bYEm%n!eyzK_1e>BYH4A1;D=#&f#2TFobgO|8fyVOf7N6(Ql<^#yCJK5M&Zu&=qc z488de*jIFp!JAw)Shc6OWoX}kojf|;VambOZok0P4f)WL_MM6>=6R?%THzobZaTQ@ z3ipD;`O4Vhg%PW1#8bnAA%PDJvoS?r$>poprndH_smDn-Ju*am9qK+dMAkGUhY0Y1 zBE9;iq5kBDgO-B`1!z2k35j02u0t;^ez>n$@uZ{VQ7T)jL*b>0F>tVEh!i?X0sh2z z#!;5-qPNvAB;ITF-X~18)Pva+lw<)Xldy9bJJpN5imv=3X5we%tJ~->Vrww{s;sJx zk=FOplf?0~)ik(TaG}vuM!Ki{fm0Y6)reB$p(npQ?_K9`kX$05pb?sWIXn5dSq{3p z*tpn8ZLw(2<`GH$*Z^>8|7(yq;||X(ocq`Ra5r;9)>HvTJU^PMbYG+{K`4Rx%8@O> z6=#gAhNH$($qFX$5BxonSZ|%X@}Ftjc%P-8-N9CW^n(JAl?KDhvU>b*YiAxPs)K(4 zp7B4$dBdqeo1aDh#HV5ze@o-A;U2IH<9VG(Rv7KiQ-!1MqZXrP5OqR*vi3gQOkKM0 z_6|Ft>g*!c>sdhzJ@;03An1H#{AenJ2ztQEU`XCHw;2!I>j!540unwj3mp&8Gwgp#9LSa8Vc5xk@#1)Ud3re>uSI zsqmk!(y;pUGtj84v+;khe$x|ZA%YwRwVoxlKfSzvKKld;$O{<5^PF8x*A*VQ zvXAO&Ji++u5Z< z@A=TN=JaSLak6b`8=ZZyy+FxxH}TJK_mogU)*dwGMlfU+JHQOk4xRcmUyB zGCNiyM7`Udp9`9Jd;YT*Pv+8AD$-?q981=z^DCsTS) z9=oydNB(ac*}Fq7g`U()_T1qY&qH$#7NU>4xH|{6{{*IvbwNp2=Aqa#qfjp#?KC$; z>yuV2%0=w2xI52Cf?HFgdc36A46l|=B0n-INFk>LmdP@2y_$U8Y=KMm_fcXlj&+vn07<-QrhqptyfK zU)xzS9l7vMFhCD3Osh6UMI6klYw01b3rmNTytV=!UL4Z7Q>;YAdVr%MSKYQ@7MU(L zSE_^d0AX=-59ffl%G$i&ELj=$#ckfO0_WpGb-43&R`Wv05-$knCqQ8D$Qp@;H_{6e zVGg0OdR~?4h{l7bM^}JO34>K!B~oof$3IFsVAh_JIm;X{1mG{EkUN#gU-fdcD%FKf zg68j=WS}0W5S}~5o4#4B=V$y1eQH{1(x`jD^#gfcBI71?0XfD0`TOfVvc`?+--jE| zDueK>DOCl;t>gOcMDOkmxj0-H+^j% zVQI{_aW210%$T_`U0;!PexJITE2qhw#wn#;A^TB>%#pRSk>a~9u)GJDsM2k^5p2qs zw_CZmOnAGn|DBUiHECaf%t+d6S!ds(@G|-1{C;XE@w{h?dwFf+gy;lh_3HVO3^Ezi z`tX^=^~)5vpj5+?fUbDOVph)5-+WAnqt(n|vLm|uaRU4U9PzTa0!-UESbRQ7ZnV1B zw-x1O--t^1Ikfa%!Yw_H*%R4{lYQ<&#BN^M5nYXz)6SCSM7@ZT0*M^z?h$-%y7S^B za#1HeFnGQeDT8lXUw&*LQ@eN6ps4@j=Jo@9<&%F{HmIWmkUsx7$>`<7yZ+m+xVPmk z0uaIwUqvwWSH}C%tl2Fi5tnn1*rUw--pc_*W3P-%>r){zdGAl;zdGi#ren4M4yg6- zT~Y|wnVX}}Jjw`o&uapgk3v0(QObK9k>KY3uYEB6R?qI!waoeGOUy&;0d%s7ew;_! z=8uW9_hh#j*xu*cD#u3-3sSb}58vIJ9>NQ<@%^laCaEm*vN<*bjlUEUz-+&P=H;{- zh8eds&NiLLi!=SJ^NQmqJV#eFUA9gjA3&pS8e)$wCAYDxcjopKczD$6=i1E@*Tn7d zNU3o&+2KHV{v!bY#OsC1_j&d6#F+*t^?^1GxLK4hMPZM9vL)lV?x;-5& zjGT5hb?$sY@`%t^_PM&10^=F;0h_V$4f=C7yhgQcET_>a!Ap0j&wQODw!OODkJ? z7$zXlL#p%B1=(~r{i#pi>pXMLJ5hn&P-=4*}Nn9RY6js*>`3 z!!SsvRS^$JP|=b^U>-x?l4d-q9L<7t=P`6MEt=YYmFFZRj5Tw4AIpI z`MUOs&u#yC?{X6Z$1T9;aNcD5hlkzK_3%K;%l^KN+Z$`~RSfRf*ao^R-CoVE zZ=CkETbrxh5^Gge4E6#H6H6c3j(JW6{S!rDlsX)FzTBGn`hGXh8>^QauUSNAu`N^m zS$XUlpyL7iVjy5I;!QHmU_FF#rtQ+M_M%&WF6)&;ozQQy^Y!uKr7YHlXesxSKj7}) zM)zkpJ1A|_f3woYV{_AgKW>Y`?*&D}ev?;QIw@9F&Jug7l- z=mp(hp6;w&czc&`b~^+Y>?gx_?H+-rp_~+7T_s-ZQ6D5*??lzhcC( zbC{Y1oFs2_{yNLW#>xxW+?<@yxi*cm_oHL*z+NxIm!SoP_`NyNa`w2rK2Eg6rVf-) z5-t7Y)A_izB*Jnhe0OuX7>b$6B8o%S*ll{WcRG6n#CIuR|GQiqiS_Vx*;-8<>U=-% z1I(<&^M@nz#DFeVCA?dk+MTa1S0jqTYI+Iz^m|&{+EOk^{B(WqjV~;__)?BuXzvR_cym`qy50yezn=%i^A@Z6949D_NEU z<6`IZzWCDh8c-9!?d|E~)b#`md5^!kx_kULpG9={V&^*s?~q35`*h4<_BwDl-v2mv zH*^HbwOwzxsk2cqJdod5{q)?s`4H;bvcKA7j)-}6yBQkEIT*=f=NDjcctgBPYtR#{;*@^ ze*HZZ)`~zgPIh-E;~QDfjg2oTj~Yf-*SHQ_n;kCgZ-;e55_xMa2f!gSkbhfGz+(pg zA->tk)X3TaUp3vnjl|jrJT1t>fP0hM_ImsIb>{lE?{Vkf-P@aG%V-w|v>DMO@_P&q z5#hw{UFrd_WcZ4X&~9YUXGDi$OMl(Bp;d9*hu`1d(Igpbm8}cpxMvvPLb~DE+x5A$ z`}&?5hK#&^xVn4#+QsmC#_;mF(_e%Jx;O9osMCo#*x9{|K)!#vn>xy}k*GR5A-8kb zT;}H1m5Er(Vqm)B*U|Uyd_4>z*wA~cZ+UR_;9)qhfS$NG-iFzBdB3&=DcbXejKuT$ z2iAzJ3v|N1D(sD{9UiY9fiA}SjzT~VjDI|cas_leJMa5?cN|~syQUc01kR>-A019x z6y{nFN?LRrKDc{Ao}VwSXLmO54tQuL&W=C|w~57BL|eQYo+MXvEglZ`11`PwnC@}* z&qG-=0&B=NOQ%N%B)}yKlS!j<4GFdHok{ZFOe;UAucDa+@8HXKkCkC+WM_ zLpk^}ZBNs;Ex7- zy}!4MOVrz=a+pF8|FVt8A`$=d_5D_n|b2zH#{ex?Jx*Pe!DFLZFTBWarHUJWr=eWS`pt zc;um_Eb3|N-0Jr(T)c=)%AQ&XKLuMy_=nF_D0P&nru8{+t~+gj1`2bbXTU#}3pt|A zTjumRd<$CG7Yfg>*me5fC5|54RUJh3_MNdR&Rv*O;Wc#y!a&d=k2 z)GeW_2&gI4^?0kno0n7QFNv6?B8S&mxPN#Xrh|EItxe|rXqK5?GT0dD1+=*I>|_B3 zu3QyA$0|O0T<%_w{Ch5?87o(Nq|{bci6&_8DCd$gS1T07-s; zY2w#|st#$V;5>SjyNic*Q?JZCIyn}$s{87j*}e1Pja36TerWsWy^mIT(Duy1(Zm*= zq;IT@k2`4RVZ`%pj)qr>;nKGMxOttM>j#J@m5W_j1tNe3Fqdk2rW~D;fsZeZOC=m> zykXjRRlcNdKi8q3zkxSmsohFWwihe1m*X^N)~srJaVx=)I(c2k2FUt{X@HRRCd z@44|FQg~+aSK^ZOWf*-_lwYRr4rR{a%J}TGwh;7Cld<5@FDP61uowxA9+|0kg>HVG zh`Qcd0X=u}?GZFrO3EYs>_0r@)SxWPoEz>{0W$uNMKy1_g;f9aGt^exJ$GIGk%ua^~|^XJ7$`6DLy&URgok z1av@jd*yQ-$!`tLd?HT!cj(>uaHYMnq?@Pt_#-%(Oe>F5t%kC{g{0xFc_~DLvQfT8 z`IcZfHS5pzXHbBP6s1?b97?)B~J)E zh?53a6=f8PX1=h$@ zC*9d9^)|c|sbacQw3S+m{WzrpPuFYD@ZGczMsNM4-P|d*~zq}mF~bx zPTVZw7ej|Fa#Mnomh^E(Q04(4@ z9B#Cg{>D=RBB?ipVwVHng4E^f2@RU?B(AKC^)aCQc}P3+EW=WN12l#R?5!6fOOza~ zFv~JA9S1yvI(uMlK66xmzA$#rIGxT%n{!e-(E66BZYYyQPI3J1n^59G(17_-|=d6k~zk)sn7SD&81K&b(zwZYxKxCSTQY z1;Y)bl`3=X&#WWwW9>!6*KXbDq7Q4HBf~bF_5h$>CE+rsOf80p3L2`MIS=j1r?Oq1 z!o>wc3*piD-PLm;@j6Wj?JNC=nr z+;hMB{=u$VwfA0g%<+sd^r9Rv9j)!ZFdPzP8NJ^wJ)D2Zb!+Re7ijT#r!zg@bvU@1 zyZn|suaD`S*8No)_5hm9`Yisp)iRDu)jYgo;LY)B`Dw&YoTvKEHsveQ!5R3_nG%!` z`m<;7`lyZ%QK{1T{pMlfIW03ewQ4CrkuIYlfaSAOiXAKgatS^@y0D?VGB$Wg7)9(& zIvfh`XXi1O!2+^ljfx|w%YWWjXoP+Ww1iGsv}zO-g}SyJZQ7zGaD86`tkD?7a2arj zk?VAON}I++S?Yo=l@*O(38~xas_Y?BhsZ_}5Jw?U0>+9FrcvaNaZ12$y>DHT$1N?K z$!d;uQn_E@=O|YWaUknTAWl5W@n`&;$sA%~6`v0}EM!VmmTeFL?v#c8!}RPE99-VG z+9C7CMIb<9`%Q0zbww|b?L2M+wOmtgVYGd|Rmq3#LS?f#!samKFNIA@w|lUy1JU$w zWG7Jf)BbzTj4E4#@2H`uLept={Rdtd3ypa_>w6}J+(vm+i9o$Rq9xB*=l8k5U)W@J z*4j^T84+{R3{6m_G+_g+6tjoRGHn9W1`=U$j3T>Kzm%zboBMhMQY_ZQe@`oNMRy}| zi2U_qlhwSLhZ}+cW|r6oGu!G4R1O!GsB^hiU{|eF;z**XVl7=XIMrZk?T&;cR@gAg z-d1rQXgWkK|Mi$<`zVejU|IGf%$uQA-4>7GSD_)6j8g?3QZ1(>;FhqqF%*r+uFFBx+0wpB43W{%jT`Q@; zhvOgnHV#Wq*OLOL|K^H?C-tSwTmr0F)xb;7o&RR@9IOg+gYB>PCCD*m)RuIN#|;=V zia#`VVNJN;Qb+|if#|hcTRjtkWVT?82HJ06K zv!sbpRtoOoWS6^k=vPxWcZ4I7G`!qNP| z3#Z@gK81+|;N&V(A}Wo|8gZrWXp*i^3+!6Fyqr&fT8~;y98oc&P<&({XE!4eehC8T z`#An3M4^XieWGESbpkJGIy}@*#nfnoFf)f09>hd_T%u~^41E=4GpxRS(mfXFSX|smgqA@Yb(*dr3|ggSjS%8N zsg6L}!Q;Yn_s0eaaV%{Z(?WrzI480*=+*gUXL%ZBMaW^_*1L;I3=fz z22vHMCf(aml?02nj;McoLJdPjeI`f4=4je9T{oC1O$uP?O^E!z(3?X`L1Vqgh9d8} zm9z%$eFSk@mA>f&u*?MJNyj@6U6QSLT)lCgfmWT^#uU6DJxjrxk&k;gPMaS>ZQ-nk z4O305inEN0wx?WMEW~Q%sG?j1B*`iKcGS|0psm{!!hMc*f{Hc;4GQ;|{gFA~_B3wt`V)(epM^PeA|Xxr^KT7(ZT(+q zz=U}V7j<9bHtA%FX$JlVi>B76 z$9L87noFiSAO9BddEqf#E+MeNgf1#q+!2)#*;#%bXQ>mn8Luxm1A~%k(*l<1Dc@Nd zzq{}oCl{61*ycAoRZ+&Rm9h)5J|%+)(|2RmX*~B`Ame= z%oMS7QI}FGg=KMMigo1Se&C}bfshkxJj|WaE}37o!H-{CagNy}Vl(4Md8R$N0gv-7 zt;?+n;Z8(DdS5vOrj$B>#W$bkK81vT2U^nGxQRO#>x~m6JmSmHh5Nc|@N0j0I0h6O zgQO2~{3B}0So-9ubcXa)2x*}*$q{IHz@#2td)T!oiO1DGK@>XA1Os6k0f!Up-)&G; zYO~hdQKW@Q7Ew#APOl0=WxTmX#ZyM;%p+s++Iu4{US}4ac-L351q*92Y&rf1k-~Q$ z2&~syNoFJak34vMu*Tao=A=$!>eV!z5vrStw5C_$Dw%}UVs%vEp&lsoBPYMtZUirr_9C0kAP10(fvkN(gHyPUvLSi3Z$fePunP4N?nssRisrxr zIZULM6ymzOq#pb?86M`AnFzJ&L8b$t;}mk?JpY%TLYI@pp3DKs-PH@byiUzer`kQ7(^y+u5{c{90>!=S1Op(Z5rp7 zj^x@gWDiiJm*`vN=}@P~TO(O4yNvvFz8On_(PycTy49%5(W8mjfejMxJ= zW$wmFP8=^0GE5w1s>umdH2;oJS8-G`ak5{ASzfF4vg&(0eG; z-Y*N2+SeQI!Jlix`(c7=z!)0b^3VNkxdx)s8}Rn5u>W23)uYAb%9WC`?+K4N0CDE= zFnhy%xagKK0Q5O$pm%3`Gjnyqv0_4Qq*6Ef|BuQ4;|#c2p<8u7^y_=B2>DgejO2sa zKeecxU3iQk*MzJj-n(h-j7>p7qu;^Lk-=wozr6}_qn6dBAD6D~E*49PVbQfM#67Vo zfB#Dv-0cyg734}YnS558kt7a!_FOxmDArK~(M#ZY_hIkmPnM~jD5uT`LZqypb&X@R zmx{f*s2~1TpN9@w#UUtcgBMJIWo{*A*sJNj-7_v666^jACYG11N3p2h&_uj?-&Nz= zQrEXUG43)CP0(VC>Wi5YVW=Ks&5UpTF&hS+O?ZP1Idpzc+ACbX77t2=cPqk%o50Z{ zz?7Mv+A?6;B&&5OqZRN?o3!QuQqDutNy96jlv??X7Rm)V1XM3&-622o`yi|4J-B;@ z%6j_ocA%@=izegoh6;z`Dz7kH8r}KH^Rum=suCrSt#(HFKN)F`;?ap4Gjx*lF0H4x zi(7{x=Pd}!sQLwm4~8Aw-akUArs>?fvQyPAmVWHNOXAy_8Y(W+KZFcb{8pSO)>9DF9T@d>q%Od0i;>Xq9UY zOk+r}x;9uU1^m1i=X9odd})N@O&7XtXXDfRDfANzwJH;cY{zvryD zDMluvVq{a}(}lXla}B97epVsgsYGl&T`aF6C0tMzJYgF@?F zhu?LSqj&ECN%EC~LmbV-0>I1xG$p|t@TL8QW*I>lr+Z>@KcD`i=r#QZXQ8;CuiE$A zdy+oNCiNdy{pV?)Hq^d;REV=<9&l1nNX3QoBRXDFIW8Ukc}f1nps3|Gu6j1#7N=M< zZSF|4x`(e-j}7G-h9?HsE#U0D>fmZD%tu(%#^__7uKw7zv%yt9Lc~lGr&1j*Ksbxz zVQ+r~%>XqgGEM?AN)>rtZq$e}bJR-X1JQZfxRZxBa^}iZPpEI`%|{`3xD>kqt^})T zti^BImM$pYXr`+v0jfWamwt>ypl;;@@mF2+#q|33ya8q_A!zre=ci*lwG> zFO7nmmS>PzDE{@3pE$z}yT;WGb73WpY}lL>Rl#waw>#PebA~k)+x9~jJl?y^bs%Wl zTP7Wbtx`R8d<1*;;xWNLBESob z&fBJveV)TGh<7~Ns%M8Ei+gqfaa?0y&IX%KKmvEFm^Wy=t`~B zuR6x+TUl}O86Qg+pLt)|rDc&b4|rd>#$`i&0m?~Jbql#I^;OwelY~rqA5=OBj{=X=2~VQ=N+Rx- z&B5o9QJqRa*il~t5En#^WDUh~qV07H3tKVssms$XI$q7@=%K**p%1G-%(@&3znCLq z=l`5vqm`Az)oaQ-|61@9afiIPjb{Aa9eWEm1Ofs!nKj#M%lty27~U+jnP>b_T5u*K z!s*P&{9atx+3?KS_uH?pj@)@@RKINiLzmojP4YVTV`yH5 zXUMi_9yNOnWW;uES}Mv@x@j=(b_o;eWzQ@|z#*GNav%5Vn~etMqr+6dq7ry^P8Tgm z19$opZ6)u<*_Hu^#9Z8$rJv4RjC4TMT-M}qO$3}o)8d&W>1pWB& z5zn<05`40Tkt{=9tLP#nk_IQHD4~{?v>fJkt9X6!LbqZQH}Vy_l~D#@v2PfAUN5~m|#T=q=fX1kSOdy{=|mew(y z3Lg`p@@gR01Fnezl3dr#zW5dJm=E@P5Hry;Zl!s$0aix+XY~vP&*X`qr_$nk zI5dU>W!&Ln5r|4l6a#cxyPulKvQ~1gTx4LGm1bPH5j%|X()NA*uS_;(x1iC5ygRsf zbr1DrfO>(L9!hb2)OZP8IH7uHdaktwf1_!3TY*Ey{f9v- za>J)YM1rK#JB7$dl?0BwZJJ+-vS2nHjdavH)?!q(lC(mxL|p5AJ9k6caVzOeg{mot zJ+mSvk*0>He%z9vHOBzTc9SQH$y{PZN1ARNlG?}N zDIyCaFze7(ahp5(Z@YV_E};t{15re!e&&6WZwIi*{k;Yj9g#$0<2&t(Ys5Rh!|z#b zjJi8^nkgA_<*o%OUbBl4{-EEv7`~hAX<6H~WXAqi(q=&#m-)PELDiwe^z@Qy(gmk| z8#Rs=ZgBGtgQ6Nd;L#*Gwm3WvU=lO3x(jXOBLg%4s8vXxc6b@ex9+ZP`SYm2c@y4U z*uBc>vLTq!9aQ<8$sWjcjjrnLl(52V;j{1GY3N96(lgKkDj|+e%BH98`7+-zn@VG{ z=fVjQd%**EPQfgAdi0+o(WR2O1dAdn@b6CM(B=zMXajdYRT=!4PM zxzc|HgT>3H|k|%)IJ)*#n@d6^X#TXLD;J$)*PT# z#(?^Z?zTP#X@KMPzNiamua<9g$D2n3#MxY;tysSQ;LTqJ-Ic7T%kMcp;0;*ZJJ%bD zD{M`9`FHU0hY|B;_kCKc!P|*62(l@kjiaU6DJOHC)<&MG35SaC;Mtld$*M={@d+A{ z-@Fwpn#AXm-7Pf-e7(@(_l11nC5u{4sw>}OQg7rf%4+Bnf>+)pRe+41A~C9bdwyuw z6YDuEBcOFd_h2)?Rb%0833<-012(EHR3ixMnjDhTuG1^#&ech7ErT`Zo!S zibx>A^35&Qu<_X&IT(&WRdf!heS90+OQh-u`;1ngAJA>bd#V!u?@fc42&5+t@ZUoC$WWz+qPs=V4XSuCEs2zcJ6 zz}B&-)e2DKhWJ1>l8g|`2yhn1c?_((ASo~*z_8zFh8^z&sQv#QIS)2xleU(uQO{_t zhew>Kw`p#erplCd^st95W#nI1AqV}8Uv#o7=C|A0Unurz*V@ElB1x7GWiKub z?L1!#>rG9IMlNn*wPN&5({r(11@c({mVoC07?+QDGi3GmqCBnoo2lpq%r(z|fQ&6*)C7`ZG?N z85_raMQ(LGe@R%rDksglwf6{a8hXHT!3i8bJ?K%QvmNy2J(L=7MIhEXUq}=Lz@MJW zm27Hh-f!6rcE6+|Hxzi33ck!rcZO^IssJEb^!4Sw z;nSbH>>n5lP2mq(N>P(*QaD}H7}~?|N%J5djCulkIq6_V1Kc%rv{zMUt*o%i?}bf^ z#-awJ`c{uLrW$Ckkize0j^3>q_)1}|A}8sK@q&OxQC`o1U>^2Xt0s{ki@nn2JDioU z+&kF&v6uKtxW1mX3D)uWdjT{kbVLjDtqwLat}OYo6FubZ&wpReT0XrW4GdD_OR$ic zmg-gp#=|XBuy3R-A+$cPXRovLJzG#!S40Y9n-%wd#nX-S0f%F#?V5G)_mb56(X7>u z^}<)^TCjF20T=1yesn@&6FcoiM2ohhs+ALMyf{r}FTX?v{!`w!_$XiFpcg>0Q;z#9 z)q4)h8WH2AZc39q5+xp1x!i&l8r&{(4*GW~_i4_JDKZ%aJ-Y<2c8@*xCp$M$H>cT> zh^Z4WLFDdT;(M1FE>Ex;EQ+yXMqpU$jtVnx;-6Yw2X~Ugi)glf77NxIKRwoXd%n5A zu$n9Jn+y~=`Ow~ZOGfMI#VPOKmKM0|ay}gYT-s3@L+QXqSDLVn1&tuQ9fiKX1@2U8 zRQ+5eTruvVuKgcQ{-4=JL6INh*nd-^r~>S=cl;}y1HCi6QZA|@%#o%YOqe;G|z0-zq^GPw&O-CQ}w~Vb!FuiaQ`n}Khn*;mtMesiM z>z|5ngdc75*p_{poANepsC;D=J9;4|_Y5r_&20vP>%`hA zo4BYc3w@QmKysa(VyRY7S558IGWKNdg+7&33^K9%7+VI<-%%{ls}8pgEjsgZsW9{q ze>@ejW;)=OHy}TxVWX^`ijlLM-RUA+4SF;(5>MNg+Oluz1P|qN;J>5y-+w2+{+%9- zl3^xO-oIvsA2!LT&73O~fEe5kAkVR%*NWJ=CJn6O(e;08S*a=OmN4@+Ilnf>1~T3n zrED;Ri-ml!^>*p)49xTv#$Cb89ZkRRJ$g6Pz0Hju2ke3i&(R>>JaS)+YB`m); zdv`?FuO;GP^=H}Tkq%jp?V8qtG2n_LxDR%Fu|RPP@z835g?41pa1gdih8|-O^}=zm zgH)TjfV%web)QzBCnp9G*`_=tJ{pJHri0+%-!q#vv+8t&4ca<3PBw@`l0;D5@~^Ic zIIic*2&0BcEe0^EDVGSV2J+ubmPm8gdl!&7Jd1 z>h9M4vE$#y z@9I@6+U~ZnP3D(R57WLgS^ZXHhVFJra)p|XtSuY!tM}&L_j&v>u z|9BGq1Nbg)R~o_8zR{tP64dF%f#ke^lJeF^^%?Pk7x8~Q9Z1&`Efn2Ud#_nu4I)O8 zg{dV@t2@y~kbRFq)krQ;G;(Gf`Ou5*s=li)bal72yzt1EfQ3U^dFo%&pz0vAeb2G(tHr=yKdnA>`skw?7<%=;3s+y)3Db^;Q{8>bj^T8o z;tg*KEof)ETAf{u|2&PTfVGcAL)Ka0BOarAMsJ-17T5T(qTb$8 zEC47MuyB;6Utbu?C2kE>@gJf>fqMuA!>@>mJQ?R?8L%c!jAYVVtZz5Wbh$p9z6_rj zV)A@U$5<#n>P$`)e?5s3=tZZxdLH=d!1A7lk?dp98XZPVWo0qi14gh*;r1GNqCVLo z`^>A4V*#+?>)LpiOAKz)xGN}nT>)LqIQ&ADOZb}VGjmnxcMrp2Plny#bu+~uZ4 zL(~{p0ZQ=(fYC*_g+buAmA00Wk7Ek4`hIi88f@qag4A#(Qw>3f!y@=#A^^~j2(WE; z5Wd}^VjQZswh5PFsbr4bxI_#NG>vAI*SY<681aB#3eb4(+gX}=&E!!$vV`(Op*zb@ zqmk!#ElII&3$;*_)hH*Vmczc-LR778VIb1fLwLr6QCEt_gYi0)yER6A06LMn-@|EA zm~o;bq;Pt$GiW1ctMyjfr>4>r&n-I=m^|_}_db^NHGOWm^-Ky4z|c%d{}}a^s{-SdVl; zI;v!~X$U!4t}vodaa=xBbdZrhkCI)Qf|DFGtrp za};m#;1~zmO+}biuJorw&k7*-!-gedAkwcb+$er%vv6es<1xi1?eKH5>HnBti5#T* z*u?oYsQ(JLxV>8+R?wiJ{D1Sfr0e{HzeYdYnSoKR(*wnW#bP#YnHGs zMb?=t`ENkZvwYZAg4Mh`122bwO5-9Nef>?|S#KqGl*BSAYjxOu`RFjlJi?Ls7zV4# zrhKi7Q*mVqVR!@)fkFu(n2QB!6?QWqAP(=YcORz^eS2&4<@}&QI@m2L0my zol)`NHb|<;@$o@ zi)CjWvKF8N6;jCtX$dREAu)lvC0cPMYJV#C)4CgG3amk5hOBJMu9!How=S-v|u{-R*&f2I=)FpmxgdpoR^8)N~Hp z)Phwiv?5KT>n~mxd%xAFYK7=2yR!E_^krQV!vmr#4SP9$-+I^cu!GwK^StZAw8_Er zM9SD{NMGhMDBa9j5^mPGEUt_kQ$%1#O}9Oha&mykMuZcPz%%h1W%6!1n$+UD@YVB` z$!3*!CffFiz@5&C{`ck8=TGm`*@z?<;9IQ^DKJBNqu*rC;)y3vqu6QE8=ts*k$k_U zPRqx3U>c0L7y&4UI5aV!qA6jTnUt1EZKaSu0Bf7oln%_&NLXeBIv913UKhj%Jsza_ zBY!;V%`^ZkPQgsDrnB&HN4mmV3O5+{0 zwAKF3p6%~>-<X zB8C~=H%{Uvxi>T45~ymt-_HZK=pP?`vZH^Mi-0lIQ2#OC)pJ>2 z8@NmmyOAO6MS#JaFmgBP);hl=Mj1~R2QO0J28Ya97Z90p1qp3AkDwcF_!<3FTQhv#tnT5J0T0@(#bH4%_iEy+V`eYS{060o=;$mw zjq6vD1{3OY(+%0jQ^Nf=4_aBdFrCLU9$ZjwDPB;|x{~Y`EQIoT?(#OZP9nE)NO!d0 z6t4nx__R(C1;FJWC6f+BdI=wq-wj6m6E?c7c&Y5q|CBe|CvM?Z8Ig>DkX?xrw^F1Y z#g33*ov8(69L@aqnlQg`%<_?Is-dHt(VFFmc4_Rpn!~`&uSw1T ziQIZn8t2YTBto7xXw@xc=Y#W_4%}as{4BBU5b%|tiduO-Cf~Qa``xYg<+q}@yLe6v zGBTch1@pss_&@#93_07<4440KaaY+o6sE?oB1H%jyX;rL$6m{O#aJsyA!I`X!Oi|A zGs|8Zu{d_~^IihhoZI7C-IzS!iEh*a@aAWKU{w0zqOyOlm0@XO(|vhu5*{p=qrDIH z=KIDLyHNm~kDCT|rd4T^&c$&eM?8A7Uf5W`O)}nm zrvQl|m9wbEJJShXutr(aiN3|cI5aX$ajt_>m5!g)9lYo93*cQmE|g=>n5c#wU3~;8 zJ?P}REdwjilG?s1G4jF>SLbWcd&}p)uMUAe#rvA*KIG_id+ei5ZS~lViZW~j6{MfA zQvG;v6TI&)$uwpqoC9O44261w(jQ)ZPRg{jQA|J4-Rr@Vj+xro&S0U1a-%FQWIc@l zd%YEk9X={2d(XT|%1i@uACj2iJ07_$+LR&#?oIu#JZ2p)0s@B?x=Ojh9ftgJQMZvL z)Os?JJzn;D=$OahVgsR3w0*bfb6mGOz#Fr}zYQ^*crUK4f8Rz3&6k;bo&oP#@krkqhN-smb3xWzYj4+IN(GIe7q0_H}9*2d%ouiBl!S z>fXeM-;PRcmrE62)H42%mxOFPq?F(g@Fl6IC{!0L(o*1EE_;7o-In|Vd|=!xZnEAt z^H~4l&c{@@oN5H$X8P+gX3vWJ+bO+H1*Bdu0gOPMNo^mMt7( zO3oT^{JDVAv(AJOpD++t5lZbu& zb$AZ)|L@BFQL-HkYuP1-XPGFz?#rHzN3`RwQPZ14_aiJ!r90_rFD@{CWb>)g`My16TcDY)hE&Vh)!zA@@(p|`G_=-6A=QOCaBV;Zj|ty zcc^P`<;RqVXg>8w5M2zBn9?-^cGvF(I(}ch&Gxhmk#7W$asa!7zE9)gS?%#e6Q6s`5|HkSt*3}9A{R7$=7$dTMB);b6S|+TY z*(3-rPhk+PQX3NO0e$nXgO#~#GfaKi@Q{b`@Ve2{1ZTmSnu9FWgpBxeQ+qu+IR`IN zkur2-H2X8LaGm5xWXc=r!(!hx8=}mzR;56IH81h#oe-$5mqOJ6zzF}gr@_`g3F}L< z{EO+|tXLTi?39DH#*qs&?}c^j9O5I;T{>1!`<`MYK-jg9cP1(`j!(%C$B}LJ{>J}! zNkkb-G{$VQ(IKdjmA+FO#f$*SnKo#KyHsbIT3z+14lrnHwle=Tqzx1YbzeChoCNx z4zDo`JWBFMG!70q`05+5WHNGGp$Q>FMPWVaQ!)FZgx?Q7A$Vz5CvMN{(JDgyhyl4e zps=gWhXnZJVqXkq^1s)5OPs`zxt+MbXF=}d81`gih1ji(5IGaZWLe8tjA z5WR$*?fC}02eh4>SF)zW{9z3Ewa`^F)mya_qCZNPC$z1Tm>^sVk_cHtQ z`@exfY_Z>|gnn?C|5WZ0ir+j1E<>$lB)5TGD}Y8B0=7!_1;Yxr4kKnX!ghX@c6gT{ zVU&8v3O~#uw|EKOC`Iik7!_jG!ogw;WkAEeR=}gxp$CkDVsmgf!6(LJF8;F4^aoS4Mr3cPySvu~GcB@uIr|C{x(Aa{z`P z1ux?U%zjaC8ldgqC`$BS*5Nw^%%|EH_UMiX^jg)ScbL?ar(2huS2ANAUpfi_g_>8( zcF1Dky4gtFO6m~L(1H@L`@w?;zNxP3(U_QIWI{r2ZqII-u8WhEXs&V}l%$w3?ytye zauz?=kkzaA9(+V&Ip3!=LH#GHHYjg9O{}ypR{xs21hxt~CSy3(bOx`26|Gbd{kpE5 zv>OYygp|<-*SJosBDTkvRf7Z;Uh#GlcmZoO;V4Ex?>_z34TBK^Mr8OkPoTC=biVz zn#hO*7;IbaYy-~nE>TN7i^CkKDMB{MbRMm@3T!h*ir^nbw)E#_G*^%qW61yj4O6@^5f+)XI)tX~-bN;%7`J?p77=r2STcoDpZ7UCm^XCRpRJTIHA7XwE;@LP~L+#=07 z{sLWyR(ikLpx!Cl7aDx$tfPa@tBlFlBmR4TV=gAtoRIW;@W)74d;;YjyY5f|(U#{^ z$abISRqVJmo>^q;lKcGt*#qAo=r@I<=Ia9Fo=BQrjXPPIu`OZZT6cScqCEk9@jGtT zvVLloOCUfk+!tNUC|NR}k7S(Ftj-drSc63FHG?1OqsRe2r-^2rcf}?{Oao@l>j?9< zd4LLXgEVfWH&paAMdzg<48AH}OalERG|<SuEuEWJxwzmlV&jlwVM zA8N5y!4o;U#C?#CSi^fxnp2~>E#5v*0iK2J*n(^$`uWQn;=d=A@ z5;#d1y|-F}`_e-w-g%dcB!zU}@ZC8Kr923^Ir&RvrIQ`wVs>vW%A{PHH?DOFH@u!D zx8Evi(hfwL0{D~yc4v>I!0nPpd*A1-b&XMxA$;bE>I`J-L)Xsot_3CdKZK){KBpOj z{U>q`6H>o~cRk%sui%LAxU;NMJDU+<%0Ag#cwAESE}orA{Qbktd!mU#NsPv!!@O0B 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