From 0fd39c9b2c5fe711ed86aafb2f7407beec4f16b6 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher <471693+Kerry350@users.noreply.github.com> Date: Mon, 20 Sep 2021 15:56:13 +0100 Subject: [PATCH 01/69] [RAC] [Observability] Functional tests for cell actions (#111963) * Add tests for cell actions --- .../services/observability/alerts.ts | 21 +++++- .../apps/observability/alerts/index.ts | 67 +++++++++++++++---- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/x-pack/test/functional/services/observability/alerts.ts b/x-pack/test/functional/services/observability/alerts.ts index 8926d734d7313..435da8ad94037 100644 --- a/x-pack/test/functional/services/observability/alerts.ts +++ b/x-pack/test/functional/services/observability/alerts.ts @@ -17,6 +17,7 @@ const DATE_WITH_DATA = { }; const ALERTS_FLYOUT_SELECTOR = 'alertsFlyout'; +const COPY_TO_CLIPBOARD_BUTTON_SELECTOR = 'copy-to-clipboard'; const ALERTS_TABLE_CONTAINER_SELECTOR = 'events-viewer-panel'; const ACTION_COLUMN_INDEX = 1; @@ -77,7 +78,7 @@ export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrP }; const clearQueryBar = async () => { - return await (await getQueryBar()).clearValueWithKeyboard({ charByChar: true }); + return await (await getQueryBar()).clearValueWithKeyboard(); }; const typeInQueryBar = async (query: string) => { @@ -132,6 +133,20 @@ export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrP return await testSubjects.findAllDescendant('alertsFlyoutDescriptionListDescription', flyout); }; + // Cell actions + + const copyToClipboardButtonExists = async () => { + return await testSubjects.exists(COPY_TO_CLIPBOARD_BUTTON_SELECTOR); + }; + + const getCopyToClipboardButton = async () => { + return await testSubjects.find(COPY_TO_CLIPBOARD_BUTTON_SELECTOR); + }; + + const getFilterForValueButton = async () => { + return await testSubjects.find('filter-for-value'); + }; + const openActionsMenuForRow = async (rowIndex: number) => { const rows = await getTableCellsInRows(); const actionsOverflowButton = await testSubjects.findDescendant( @@ -163,6 +178,7 @@ export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrP }; return { + getQueryBar, clearQueryBar, closeAlertsFlyout, getAlertsFlyout, @@ -171,6 +187,9 @@ export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrP getAlertsFlyoutOrFail, getAlertsFlyoutTitle, getAlertsFlyoutViewInAppButtonOrFail, + getCopyToClipboardButton, + getFilterForValueButton, + copyToClipboardButtonExists, getNoDataStateOrFail, getTableCells, getTableCellsInRows, diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index 6b130ae0f6264..856d7e60996ec 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -14,11 +14,14 @@ async function asyncForEach(array: T[], callback: (item: T, index: number) => } } +const ACTIVE_ALERTS_CELL_COUNT = 48; +const RECOVERED_ALERTS_CELL_COUNT = 24; +const TOTAL_ALERTS_CELL_COUNT = 72; + export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); - // Failing: See https://github.com/elastic/kibana/issues/111907 - describe.skip('Observability alerts', function () { + describe('Observability alerts', function () { this.tags('includeFirefox'); const pageObjects = getPageObjects(['common']); @@ -41,9 +44,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('Renders the correct number of cells', async () => { - // NOTE: This isn't ideal, but EuiDataGrid doesn't really have the concept of "rows" - const cells = await observability.alerts.getTableCells(); - expect(cells.length).to.be(72); + await retry.try(async () => { + const cells = await observability.alerts.getTableCells(); + expect(cells.length).to.be(TOTAL_ALERTS_CELL_COUNT); + }); }); describe('Filtering', () => { @@ -67,7 +71,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await observability.alerts.submitQuery('kibana.alert.status: recovered'); await retry.try(async () => { const cells = await observability.alerts.getTableCells(); - expect(cells.length).to.be(24); + expect(cells.length).to.be(RECOVERED_ALERTS_CELL_COUNT); }); }); @@ -87,9 +91,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await (await testSubjects.find('superDatePickerToggleQuickMenuButton')).click(); // We shouldn't expect any data for the last 15 minutes await (await testSubjects.find('superDatePickerCommonlyUsed_Last_15 minutes')).click(); + await observability.alerts.getNoDataStateOrFail(); + await pageObjects.common.waitUntilUrlIncludes('rangeFrom=now-15m&rangeTo=now'); }); - await observability.alerts.getNoDataStateOrFail(); - await pageObjects.common.waitUntilUrlIncludes('rangeFrom=now-15m&rangeTo=now'); }); }); @@ -114,10 +118,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('Displays the correct title', async () => { - const titleText = await ( - await observability.alerts.getAlertsFlyoutTitle() - ).getVisibleText(); - expect(titleText).to.contain('Log threshold'); + await retry.try(async () => { + const titleText = await ( + await observability.alerts.getAlertsFlyoutTitle() + ).getVisibleText(); + expect(titleText).to.contain('Log threshold'); + }); }); it('Displays the correct content', async () => { @@ -156,6 +162,43 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); }); + + describe('Cell actions', () => { + beforeEach(async () => { + await retry.try(async () => { + const cells = await observability.alerts.getTableCells(); + const alertStatusCell = cells[2]; + await alertStatusCell.moveMouseTo(); + await retry.waitFor( + 'cell actions visible', + async () => await observability.alerts.copyToClipboardButtonExists() + ); + }); + }); + + afterEach(async () => { + await observability.alerts.clearQueryBar(); + }); + + it('Copy button works', async () => { + // NOTE: We don't have access to the clipboard in a headless environment, + // so we'll just check the button is clickable in the functional tests. + await (await observability.alerts.getCopyToClipboardButton()).click(); + }); + + it('Filter for value works', async () => { + await (await observability.alerts.getFilterForValueButton()).click(); + const queryBarValue = await ( + await observability.alerts.getQueryBar() + ).getAttribute('value'); + expect(queryBarValue).to.be('kibana.alert.status: "active"'); + // Wait for request + await retry.try(async () => { + const cells = await observability.alerts.getTableCells(); + expect(cells.length).to.be(ACTIVE_ALERTS_CELL_COUNT); + }); + }); + }); }); }); }; From 4cda49f5ca339e185ac3d716dedc51c552263ec6 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Mon, 20 Sep 2021 08:58:09 -0700 Subject: [PATCH 02/69] [DOCS] Adds docs for Osquery Manager integration (#109885) * [DOCS] Adds docs for Osquery Manager integration * [DOCS] Fixes headings * [DOCS] Updates osquery doc with info from walkthrough * [DOCS] Add images and updates text * Update docs/osquery/osquery.asciidoc Co-authored-by: nastasha-solomon <79124755+nastasha-solomon@users.noreply.github.com> * [DOCS] Incorporates review comments * [DOCS] Incorporates review comments * address review comments * Page turn edit * made minor final tweaks Co-authored-by: lcawl Co-authored-by: nastasha-solomon <79124755+nastasha-solomon@users.noreply.github.com> Co-authored-by: Melissa Burpo Co-authored-by: KOTungseth --- docs/osquery/images/enter-query.png | Bin 0 -> 208804 bytes docs/osquery/images/live-queries-history.png | Bin 0 -> 252524 bytes .../images/live-query-check-results.png | Bin 0 -> 121095 bytes docs/osquery/images/live-query-history.png | Bin 0 -> 392644 bytes docs/osquery/images/play-icon.png | Bin 0 -> 1468 bytes docs/osquery/images/schedule-query.png | Bin 0 -> 598642 bytes .../images/scheduled-query-groupds.png | Bin 0 -> 323916 bytes docs/osquery/images/table-icon.png | Bin 0 -> 1669 bytes docs/osquery/osquery.asciidoc | 419 ++++++++++++++++++ docs/user/index.asciidoc | 6 +- 10 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 docs/osquery/images/enter-query.png create mode 100644 docs/osquery/images/live-queries-history.png create mode 100644 docs/osquery/images/live-query-check-results.png create mode 100644 docs/osquery/images/live-query-history.png create mode 100644 docs/osquery/images/play-icon.png create mode 100644 docs/osquery/images/schedule-query.png create mode 100644 docs/osquery/images/scheduled-query-groupds.png create mode 100644 docs/osquery/images/table-icon.png create mode 100644 docs/osquery/osquery.asciidoc diff --git a/docs/osquery/images/enter-query.png b/docs/osquery/images/enter-query.png new file mode 100644 index 0000000000000000000000000000000000000000..154d2dcad48575adb05e5fcbf66f7b4a94f939f6 GIT binary patch literal 208804 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

dqZ6`7X%k#np__H9k&4oU9wM_PDAG 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%0D_aJF+s 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 literal 0 HcmV?d00001 diff --git a/docs/osquery/images/live-queries-history.png b/docs/osquery/images/live-queries-history.png new file mode 100644 index 0000000000000000000000000000000000000000..6f49917a685d69409a1fc01e0b9a2351e444ed93 GIT binary patch literal 252524 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 L#JhKEJ^4 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

;9>@d3lMzqKjib6HYuY;5$- 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 literal 0 HcmV?d00001 diff --git a/docs/osquery/images/live-query-check-results.png b/docs/osquery/images/live-query-check-results.png new file mode 100644 index 0000000000000000000000000000000000000000..df292309e08532f8120964693c2653a32509fc95 GIT binary patch literal 121095 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≥;BoFK+uT+N|cx@-nEiLy_4$E`Ax4rh^7N1FC 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�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

^rEDOw>j9diqxSJ-Vfr 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

ji=pjguQRi!1@>0 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 z z5?J+_`%R4!<#(FUMIX9rR)*6!k0F=^;9%$yaUqH4So^eA!0Q^3mv$EswBh9L@b^fQ z8Wys${W4?+%6clNc`Q{)#qe~YT*r-vO`UL}zP;6{x3sd|EV}LG>5l6L3o;O?KE(br zO~3VertuZHAI-a+nT$#yQpO-#T|NDL17HnB$=IXPpD5Z+K#ApLfge6sw-^KPaDJDx zXr3@Jj+c!u5@#7_*rVe7)L^sQ-62Im_9Q~mkT~FA?jZyIU1}x$OE5(gaszjMrNZUK zz_Oba$X1*}@S(TX`{nVY;mAA#sRnPiI7#NLc%N1JWRrt&Z$llYqh-#K%Caw|O~hO% z%>bO00L`(div1zxM%qBqB4hP>Fzf#DA_!f*T$}RJU1D%-go7Lmo)^*uW{fS-W2w9= zI^N!*-LYRrnHypjI2Q%CsRK@Av>m>v1b2XBIVBdXdqbkaTWAunrG%u))fz*sF0Lf@ z$`mg;YX9M~P07CLZm#)w?bKC&l1*bD;%^03@m!6v^>QreT1}{U_4jAWeqy>^W32tM zz7Zt)Lo%9~&1{r8&a68C#&BW$I|TBO*Xe_}#RB6li3&~XxJdh%d(2rP213wHX=O(a zOT^mEVZ_wE|6n(m+WLLuE5Ku}La%ky&5J>--~^MU!TDhNycn2Me5q`mQ$1mttVfj6 zMRJ2i_jbO%Uj6jDi@V@Bf)KwDGmIeb1@V)893;~qJKC5|>LYeo8t>OLFozEqyEo%2 zDYAS&>>OtQkck7h5Ouy|iL4IiNNpzX!*O?!SEKDloD5U_V9#NkSEYV?|C`m>FVD0O z0o)zB(8fpkIM)!lFzDzWae|BDMDA07XE<+pAqXR*00qHVxV~7$zEDU|!!XDF+Zd0G z0z7G5R97z|ZF@#-Ywg?8DqAgEs}8|_SH zz$pHnyn#D0@>XpvO`UMF4{CxCrU>7aG{6eW3esYtage%0jXoVpVOd%O6osXt3)sUMTGaAAj z8_3c+oKQV-iY+)YGO`PNrrP5)w>;-_cPxOV!`s{4wcfE3^jjFFip`m_pjnkpd1Ggi z*bHAH8;pL;P7V-j)G~XgQvYDZX06J#Eo>b!@Kr~F1Bq%Yy zytKswQ{tE>m1VtHMCUe4E}`*d&*-TdVQewJn)$_Sd6>Mi!Q%>vjVnF7m!0e7w!_VC z-M}J@ic@7iwz=a|H;hOZjk(ActFBV>u}|JW3N@IKjFO6pgv>V-<3c8Z!^5~M3x*2M zn02SjUt2``6QJ}o$%tsR;4S9BIslgbI=soBFpHFz1vDL)F&8)AYtLIKujJcag)jOrxchB^?By1w~8ccBcI@tTL?nf)>Hc%p<sfN0c zROiv*H%BKXJCA%K%wx)M!i1@%Qm>P+HxVf+mmpGPzcgX`7ob;V<(m7}>IpsD zZ1I2!3`{MX`$CbAU0oi`yt6X!P+#}`-cFu&R-yFxE+Wngh zdJ_L+A7+Vek$yh~T2y4e-of`5`-s&f(->pi3-CmWgtr0T72VrVh6<@KDcTn7hB^?% y+VKSN{1j*R;6ME&$~&&0HRf{uKJZcI9vO#+q$AgtQ}rGAPfAQ4P$BXq;Qs)U2b#G6 literal 0 HcmV?d00001 diff --git a/docs/osquery/images/play-icon.png b/docs/osquery/images/play-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..724d17b5a381da22b4ae19cd50468450706b1503 GIT binary patch literal 1468 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

r^6Ms)Rz^47yDx9j39* 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%0A?lIE`v?vWsywgf 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 lUG6yk5r(TxH;{rG z?cq*_tfcxwQes8xr9AH!7Og$mXrGGqQ?SNY)nD&3m3GHHFtNe46ng&qkJc3s>AWoK zGAb0IT}h!#I4J4Ia?NW@@Bi!Caj|6OW&Jb0SMu-n8LpPp*Bow1c@~lQy~hV$%0^B| zcgWU@4wsi~%7lI4StH%6sTCUMOE&uv?zG!NL)_}8$*LL)(j8rSfzLnz$*F_^si{g{ z!d%GZ`lbQS_Ppm# zO0d}k8FkTT?p8f2!LF0ayr5k)0uk?Y_GZpWufV@^*s^BPVyEw*`AC`Zq|+Z!?VX^L z;qE~Ww!Gsd1=;UygmHm6p~eI#fO$hKOV00chEKOG_C~r9DP%AEGR>L!BzY(IhT}t7 zq_gS1<$5g`F85fg$7cBBBmQxRs!4EWO>a*kZvmHZ4xp$B9l_k?zTN1D%>Ep-_+~4#A*vgYE=EfgWkLr?8wA|4#?XJ0-ev8GFvg7@HAwTEh}|G z&2Ee8H0&58`;Sl?X{FCYglWe(Y1D0^Gzfx*}^rzaeY^0NU2ysp&E{=HV;gXvog*Si`4Mq-;23xiG~5rkt!ho~ z3c*N;14zrW!g18#3~diVV}Jpu=y6OGC4;W7YE~_<>=rPB^GK)gSp@WfG3qP*Ixi zaqnX_d^B4Rl~m}k1e_Vx67X~LdS;<6cOPsbC>wy4VRzr>;Vt?GD__jY&r+zPb={y> zAZ2aNTC#;OeWO0~Sodo>t~fvcYfP2p6}7AhhD2*xirqEI7bSJ`C$~ZY6lWM5s2i6Dt;@}dw1Qu}u4>KxY8?Kllwkr-Y@pVFC zQL;B8<vv>Gc~Ro)_B)!P3DYR9b<(IwT>!1ixkCDK?+AubTMkqSZ~L zFTm8W);NZAWoJ@>CE)_M*D?)S&*a99lvrBI=U-u)OjF|VhpvE+%5j`NwKClJ-cGV2 zyDbiM2TE$6AHcb9jqq?baPJEWzPCw~eLPprM88m!5zz}E`?-eQAu*RwPk-RtPn3LzE5wuzwSKPk z|FUljGvDIC2gEyXuAw6$(_^sWNTXxhgdhjN_gH3)HVn0E} zY;LX(Vvf@A3n+)jAU^LZ>lg*zLPJ<;g_P5Uo)!|&akiklnrhdM>Ca5b0l z3%0a#T?>S@nEaUo`n8&jV^3W=P&8EGMwdo*=XJ4UL=x!sh?ebJVCEo^hD9&IBVqn7 z)}$l$$J`K1n@Wa-OBreUz1e_zBN2cNZd!Oazg1F^K#KxU^%SrMB6OmXAQJVLANxu3 zLAHFL=cZ^TtuhoPRD#icnbiU9@cnZ}6J|=w`->)pCwPw_(`V zlvb##!I+%K?DE46VnO$J;#z&n+Wg!*=WB_eZis4CHtS z%PSza{QU;E>UsP^e+g#(!1FMnI}XP+*spoxx4%Upr%Hfv*D&bPJIL_~RMrX<+WbPE z;0AiTWGUm~m=k^6cA%Re@wfol_-xNVd=%7811Qw!COj*i33h9w?4RqQh6>RsDZ{))6WPCM<+B*ec%DAd}YCn@S%!fqp$qSa<0#3-GtN zdE+%niT>?@#ywu?Ew*tIJo8S`mSKu(*=0rPtd9#&rjEKcOpYrj^A`X&K>=6xo9MM= zf*8M8H28#&q>-J6(X7bh=7?6HzIT9UD20NZ0d>MVw3=w#;PMiblsG6UGO!$b%+S?E zC#9V`K{1rfG=KSVsNYC;ligoX;_6kDNOsn_d15BA@r_L1A^Pd<@H%nVgFqYJyEQUtMQ#Qq*;HLSU|9<%D|J zx0l|0J6x`fmUjJ-A=z378y6R#B|(@Tt!~D;pO9`!%EwP16kI>yA$fBy#>ieBA7=|3 zO;t{JurKnr8|C!48?#67{Lg8Kp}h1<$Ry zk<4v3#@pNA%DJ)AgJ5$pl75rxJa4xOnvny7(tDGKi&KtWmILZ!V6mu5DcR)^KR=?0r+8kSG`SftS$o zoP6?>8pXfH*TXYp+(c?HseA7+zuQ#u;Z4V^r6JdL9cP`a7}P-~`244y&U4kpaqq`y z1AjYp=TsU!OB4Q*va}KkFIk%wb2w?op2#cID|kD97n^Tx6_no%jM1f4mi7pgmO~xa z^8BHxAsqS0@&5Y)bgVDzuZRaP)*r%#gopRKuZOK?$pT%$jN>C4HZo*&+FfmgkXfyH z13n~L*yK;djmAf`e~sK^# zt0HN8%51S(nx6fCMm7!Xjz$ND+=JqM8MC$=<(Q4us==?=UIc*4{aEmJzshiij?t+e z+LB=fe>oewJ@4+;Vd^HG5Y?9EGoW(};c_Wy#KMzxX?PwuU)P1A9hs zhgo`^B-m;>#TNi)%;|!hr-m6^)PTwUOVV>~^wP)RZ1?ZuU zz%In4z%t8>A8j1B6-7G0iLYiCpLvz&;jvrkle8P#`}65ST(%xkp??qThHkt7-kI*- zen|V_fYYy)^PoCt7ATj7UVG=`kk(*p(-l^DT5fzwqQ@0xU_n{}g=+~n-QWmwtWfNz zbN=4*4s@2_Z)93slhuNGs|0@_1Hc(FX@f|9UW7tOe7){0_GwUrz`sD^vH|zC5iH4N zCgH?DlnOsS67BT-QBU>>(Xwn%ut;YQnXfpZtG?# z-^YX|k|kFF*(~M4JDfz)a!ld-Y{DJB&}~Vm7&yo3vuTx}*>2~i#ODRh*`TItd?w`` zqr2(9tN*mxmh>bmQUA*VAn!<*kF21~QBn=ed=)lak^RGlY55dwi~TOC2-vYk?$Kj` zgTn)2&H{7=V+8Oh`NiW_wqFGAiku)%Ze8-Flu`T&T#aC9EuM5}f8H@6EL9SZ0aB!F z2RdU3+L!2(EV_ zwrTxSbABDS6N;rEH`;fjQ`U`m6{x()BG_7&UtkU}utub8|0d?&jE)xLPYhTaH0F&w zU){`(Rh@ira9(J#4)LtI2c_|wu|H19;VgdQ3Elfyw!K?jM-EEP@iE7M3`|yWT>4jK z^sL(Ua0bJC)N{(525UTI zhz7$<-sz~oS*N2(x;g5Y7KdSG3~d|JuN+PoCq@PaqOo-d&I&+s&@uhRLrpByJHWA1 zxankeNqzDXrXLWWU@}g9{>?j#)<^mhY8{aLak@&q6n}}D3fjY{- z0gyqF*xfe0CRidVMYfs$qhBBdeeV!;tz+B&IH+9)V8A%jr$#<)ZIC^S{zBcI-E?8> zaM)?s{f=Fa*4rt|iU$EqFe0nV4^ay=s` z7pZu(V?rL0kc9?TP1wh&oD{IOBix^7viNSduAJ9i?sD{$ z;`_WG6rvD+3VZSAe(Iww5v5_1_lfgtMaM8QQL)pJTxdQi2Nm9eD*zq3SYtwWzxn%F z`O|>u@gl{%?!b_AP-wvvA6`miF309@TFp3y@cV_O*CI7ZNW)64BZn%zU_^hHlH;(S z{C-pxYnzq3AkXs!8l-@epy2L$N=f4K6g1Wp;T&-q?D5R)H7ejzVM3&HWASkfgVa}x zrI#m>9_eKFN7%=951;k%AQuyzkH|R0 z6me+r-TX6t$98F}X{U3|Mds!Ee-7+WWY$9?Qe>g%w0(;&1wj52R{7VX0)ru>GavFA zEG`EE)huNQ5XOshMgeLW)Emi>k>QpW;$Q(u0hCW3W{APg^Ztp)Z zW^>XZure?IG0OX`Unj4|8QGH#k&r2db-&?KFV9I3nR!9{3)?P0EI1`P&1Qzbx8e14 zokW|@a-|PN0;&|*6guw*y`JS(nY8-YjgJk(Hrk!=e@Sz?(a_S8tSc%q>sYN3Yszam zZPv-y+CT24g5kR*mK&8sKrpo~MWr|GmXtZNuT5iZ*LQRb)AbtBK>xOORrLotWa)T6 zb?$U*5l@X`0Cgv_m!2%Ss*iogh@|C`>rK)>23~(dKxP+Y=DVqwZSAUae;jdkS{><@ozS-i}Q&VZ!0nud!xDJ8ACAss0BX@X>5} z(%Vz}+ms;V+XdNMPOtbpCL2W;$j|kx06C5+@iZ6P1~)PSO|F2G1X7h|9whA??_px7 z+jLw##L5OO>iFe3{y~qemQid@E1R5_ubhTh=xH@1?)Osv#1pjR1a249m}`9A{c$oM zK%2~BMigl{9qY9nCu7EUc)=nm+bu5rTcDpG3I3_uN2FFDwZsi+|ieyJi z^EMd&r}NtT2*;<*cjzaP#~g-LPh=K~E9R;)@+LU|7ic$jyaiU;L*A}l^g4E_zVI)uIRP@nY$(}Mec0#ausy$lfotc3 zb!Ai-%#%z*48mVu7#>;u-i=J{T&EFEW@*XVnYN%+UA^YocLU@&KAZ;0o6Y4?l2{Fv z0iLozb$N2HG}Js=p|V0f!7qi18H_4=5)7d=s@q;V_0CL><7V>UD4Den3n+o4{+xr@ z6pdzE!u@40YIz^`!=y%>XIXIJc`#UYYHj?UEfPf6BNKceLU=e41M)B&gTUVbTQ6Ns z@d+H$?!{kzX>Ja|U@aF0@j+E{uj;eq1~jhA-ZYP0r<-H@g4Z{klmQIRxnII$(6`?G z7Bb>~d_>y{P2#$YHtE6qFhB^!Yn5(J1}A9Csx&6hdUz z`0eTpdn(c-QdTD?aJ_C$x2XFA=t6}%-2@+_o?v4Qse8LvrAu_$F2Pm+3njFF5GA2k z;fNKSM4grJK^=P~3owA#PU&u#2(wmS1Sfin$pb*D1uPFU5G8IDd zp4(!l=}kAw$U^bdk&*cvpZdZh7l{OGRVjhElI~(gIknw^fhvB(G=m`X4{KhS%jJEe z!~Omy^Gl)UI!OL@zn4AUAk|(+KOW$nvU~kax2uYWMK@cD82uSR+vl;d(zPi9ER4~Zd&JS=Z@!!nsyLZG@ z=l8tt-$>e--kMsD*&DQWppJ?WDfPlA$b0CMa_no-90+*llV`)MfmzY?NQi#}&sCia zk|4yH$K>@8sQiYdFnP2Gskj7BYA3Bu9dBhm9C5J2HGOrNfEOS;`InABi03B-wYTEQN#5y)WJjOiW1;#HoqOjmif?G&Gh3q1ttG}t5&0I#uf@Fr#@_T5F= zGj9Vd5rqS`3vs{>x-CeDYX5rl|Ard5h{L?R-z(kLS~S7vpGHqt;J_2YTYN4il%$eu z8_VP%^3ov6dF*?me%RPa(UBX}{<|Gni>!*?krN1Tl5zd=lg!HvS(_ho2n#2ccPV z@tBf;irwcoIDywC)1YDPw0=7~{JjM3O6f@ zRWtJ=l#;z?n?qLDCf@pVX8-DbxbHnW>f|)?a_XwoNlrkGDJtz3%oufim8f8100t*R zjT~b7zc@z}WH7C)TB606Q!FforDghxjl$V~prG_!Q)G8j*IBRmNy09%$SdSOP`kn*HLb7F$J8my2 zF&3?<0k0Om{gWcj@EHNYj2QHaO)wC?RgjEF=y3DO7_<)xWT#86i5>ric^S~_N|<8b za`IggVBSBaBvc+p35E6+$)kqI_?kKNV(!aM?UWR~7Gu*T(zd|?4J|B+2`>$NUbt3X z>zC|;V4J<9yw!H!KOrQTOMg^-dkPVGrf_<70hJk@MMc3pNj4Y&bArawWV`B6RilBL z^J6>WnSOj-^JC~z-8ys3agBRUq92&F#98uPLgj_-^V;bG>~cg9H4;USkZ?*3*4l6u zF^&`ZV)7Ay)A3Ym8(qYXBuvtEOT`J%ZDyyho15x0x4ta*o{#U<&bW1^W-(%_o z>ML*KX`M=a6=)Q~nH)2Izc+a*v?Zjs1&Do*J9hHYyk7v>$FI{5({(|3FQwSs~VUqU0pMFT0Rn(1ETP~kFT z^*i<(C<}X@XTxITg!J=M9XSX~$?sE~ksw|Jm3{8JWD3-H`cuG62Pg&P#9G<m_YzZMbDLIICAO*Z-eg7NmUFwX2P#K1TC%;GG^ixp-q^&?K(XJR za<+Wvdfl~jU8Ir%l};h@B?iIWDkw(YTKH=#U_lNL7|IkCP5e}6zorj*W(9{{hM%U+L8vug z>R8r9;9AKV-7?d>Jnn|9@s|=h_+hwWgTLugG9s9M0F=qSk8uvm`-!pXl{I_)R5I&+ zO9MNSt{&NH8^HY>gHQ7(3dQPlCfJQ^=!qj5+k;l~6-6e-75iNmt@^S-vBTqtxhUqKcfUE;uU+iuHQz*ZD#HWuR zq23Lk(}Jry-^l#@*OQpMygV+m*esf_VcTR^?fso)DB)Ge4aCdhI z65QQwqk-V=?#{c?r=LEjPxl$4zu(_4#u{U@cde?qs%p)e_kAgIosjT)NEkHt?SZDb z4ywl%8%%`lz^y&=>NRx*qf7tu=*1Km>P&;D4g1=dUKj$_c`-LC0@yVkF zGH64Ohho$=HxOcx!`oHaxH1TpH1b+~@N8Xtrc=J8bGB@boCaK3>zNv{spOX2q@3jW zTrMdJRC(uVcc_H6eIh5*t+M!C`~LEPbt6Uj_h`!;-Eb)9FXTVFq3rceE|Rr2=zI-H zNxFwKF$s0PxxG$baWbP%hGfZP;ZtdRjsy>Jz!~YMQs~Kmb4+wKVc2_#HQvBSAJ}k} zB{U{Yzj;IS6Bl#|c!W0V_AlkiCr}TlQ|$umusUBr(^aR5j&QRGwA6(yvQ{dT8P8NB z#wa?n8D=QF@qpmqEqYt54{cOMbO;d>aSI>0MFCGRWiaMXMYh&5N^H%(B>Ms>toF^Ru3>9jh)_lh;m1PO-?cQ> zmh#5-87->(R+8p9vit=kiV0z&d&opQB7QDzwT74s>54fRZtfy`o<$0RM23zhBq#BE zV8|a}eci$4`8sFMw5NRq8#~uS#??hK0RT2VL z&er`!b1mbDL#7Uei?cpR1nAh%eir4UZkn6pwVPFcYDWO`Tuv%2#96}HBN_fEH#(a# z)}=IP2o!pZU)pLLgb>P`K_L<-HcVyE76bc}Q(o;k@{k&qxst(?ct6>?^zrwfHqP@5 zVzs~3evuy}_TX5ED>c%a9aejm6g!>mc$RPPd+sWhu20C|bDoF*gh6Tp2$I{i{sG34 zlGOQ3s~_1DsuOG2UN(5FIH*7W;HOatcqh|>x7B1ZAbay@-@YKkS zix;33Vzu5dI8wy8?Fv4fK4vG+dqU5DPek3P|e>XCls3e(i)Y}v+ZE8-|TntLcVK?RvLg66G(Pu9UtRSi_2~RcGnLeu|xqhz)Ensh5Nbwc7{wa_kny#!XZsUP1z)5`1@qzf#V$U zRjYo*&V18GCLeWbMAS%1mQ;Q0*IOH$vvja$N5oXgiG4rDe$6M& z*+uzu7ug}81)@ePwA~o#3!@^yJ%HHq2s^LDUU9R7$x%!fp><$K@=E^awqtREtMIg^TT+T<u9Ix+iH6f}Qt$nygH+WRpcIV}e!d6ut>B3m7BvUGax?$J9OS|ysZ>*Gxr7L4Qq`b)#|^ydN}p? zKOa^m6)RsFY<(kH7mu>{7+x6uC$6+?=a=TIz_X-(<~qf12fay~Q`F;ud1c+6Bh zWcy|$e{F=`jEt32dn-be>w$G}KtSi6fJ}cBX>e#6DbhhCMCRjklb>t$!_8@VwrBep zgT7FaMXkNXw<3iM`gI$MkchC^n^S*O;b_g6s4x^{DD+pr?+@Jc$3?Lv}Szj2SkFHidka|*xNDia$=+{a4JzLd%8u^4q2aK zGs(#4+Wp84<90Bb9DWrH&Mn{sj9THn+o2Gv5IzYE9UR+{)}tXF@%0}ek^br&r=ZQI zXVgm+N{dG;%~~!t-k?_Y$^Y({DNib*zYUFiO!_H@`LUi#^y`WEHeQOj_0wI>M)0}z zitWs?bS#DmF=Y?BBUK3BG6IGxRc>+Ww+=oVY3FEsNOlvrhaqCTA3Q3OFg z`E4<)TZXL(Ac2=AxpzDdY@oRxZ^Tb}hA`3hgF{w6>PecET_19n-aaUYgH7M0@!AJP z#^F6n%|ghIE;}t+(ywa z$cZt*(WABYZ_-3oF4gg^+>Lv9R0LDd;J&`>89Js@@QM_bvKbQ7ft^>pNHfEvW-x8$ ztJVQ)iUQa3WbQB(a*JtNLAfFZj0mbX$@xTi&~z-X1QhXfvs{h~s!ceP(J#J2HuXFq z@IO$wAbqCNf!gzf_AoNAOki*@NxSZ0Cd`mLM(zg;Ap-UR=T&#^E#b?*IeqPL80U8y zCyjlAqM(v_5+`_$4uu z_k9#O_>uTW66rRq zpmAIJ_-Gx_VA<`9D{PG)KNBb(Hz`p zkO?@I?B7E-pu~E6^XTe$USs1qNs14zxv^G;e0}SH2p*wJbU$e)w74W6n-fo=CeWr` z?rmiu!aK(cN5V(Qld_SHCb(ryv@6s@A~X#`U|MnX2{K?)m?H8_i&p%ONh{f`ToJMz zTb!pH5I_j0Cj9&xtV$nr@hPioO$@{Ou1a46=kf!aQ9SRJ1{p4*U`=HU3a>*hKl!O5 zjvZthLeHi-F%~GQwZ|R=vSS#z>XxyL}M-Zop_JKi54d3i@g2t z?zhl%BT1M-{3IV|w0xG=Pg%n{NoIEDbE()mVuKfjSZ^Ybk}NYZriR<^XzA9?n?*GO zW=21x0Ob6_1aF8(o5nLT6p|M_I zv+P_J&fPXiqlY9u6@n4X3>=o;I&GaTBht+ZvEv8me14R*?$luG;XQ#-9{f3Sfz6Q5 zO--zdr}wg+(c;uu@~uOGouu*AYpiL|J;qfSS9IC;7(W5MqGP?8zK=tZ1RH(FKb>ll z%k08v3-1W^vVhQREN$5K8*>Hzspw8^{zn*Nk!SKG&7wyQxw<5K2;bTnPO4zn z2wcyf@JUfdTF)$bH7uvHqu#ZQgLrVzx!U;oF@2-FE75Vf<1MVqQ-~ahr~5@&pJavs zgYl+=;j%67_1^vA!*oPzp;OKe(N8;yY5Hv;iO6s6FiNmJKcH@;7%h28G z$PEvXeI+H|fKAz3A446s13MRn5(z_?UK9s_b#{lW>c8z-Vh(e?Dj<`dz@&d*^7kBbJg#7rb;? zBTq!L^BIt%HRyvdY|LZ8Qhvl}nx; z`+&@-KHhP=f0rRwK9`vQqTiDuolntE-{vjeXzF#c-Jb(qR80O*TV;K#o#GFaE;y*a zLRIFNGy1qYMJFRB3}FdAVIMa0lB&L`!Qz-e(l zaRzhFKh#B{hXzrwceUow=Di0vm4ZqJ%#68QB(Ztb&@pxbMHYDMt`?bZ+lr-O*@Y!u zU!c$sIPe2VticFCpa*6RX{9Pul{TmTDWFV3d&{TOlk zSbCII5r2_`j|8b7WRI7dAaix!O?|}tc++dHKzpE3L?I)vXdnp}+o{*W1-&IMvG|T4 zW59DB_N(#saJ8|V{)}#gC&c!vwA(~6H-V(N?9=-^|6elp^8U!TYd0%Z@HX+ifv24r z=-TeLz2tV^0wups*CYo(Q%1TdvIgs{wzuW;_PgBghqN!%DN>Qf#$u>`oyN`V9nF!- zbskNgoT`J9eitn}(ymi71d!AcR}85JZ~LU7QJ?)(9hrO#GlboN zF>Kr0|8UB_1E$Qug%m?A%_LLYFq%$`0Jhw@1GnNGW9`XCB#$@D8F{@~^=K;}_XwQH zG#s#rB51irs-d8uR1@QZQiPz~coy}i32;#*nLjln|H8)3rQi_}$!38C8xwf;Nz-v( zl=W9HB{09tp4^Ytib+&5C8qzdpt1#wzC`xAphANwa)JxUT0Y1)>`(~#{rpwlutX6v zX_-N~tR4^kM&Q0jYu97hTjTrzQ$QE zP!iy~5>0s?JgIQxPot-Zml`BH^2^a4O;-FwKGIWq&z^N0^vjJ`)`m15>)WF3W(2oL zzkTj6x7+dM1qCHZm+oGK=;`$J%!qbeTpR|ok@O+b)>q%16~jkyV<_2(=@ILL+7iAd zGwdnEDdP7FJB^!MxOTA}ARK;&z2aMj%XjP#QIhqQto?>B5cQUzD_V7SNFMo2&M(G1 zm-b;c3j7IY`A`z*k#b8%edXw{GRGoR|^$3@IShe7j-iqvcKcYZD~>{iCP>axR9< zoAXv4D`R*0gfJOQnti;uYZa>;#P2uTT9Q7uJQ)NpHx26gv|hw&+nAdxP5E02*5qHd zPPQ?m7=7*8>xxEYEy0ey7KJy3eMnG$A-frJ_I6#uWa--oi~XgM}1Wq2g6NK;2Z=>$LyP`m>$d^V5#85Z&wbjfWrxAgBMHxo(zi0rQ9@7B_H4m{!9zy#8F+N8 z-YXu&4NCOkkr5Dhy5}i7TEWQgB1+SDmAXPNHshC<5FCR)jJDt{E2d#BLbclq6MUo* zy|L%Pg7DMiACFP{X7HCyKeH+{-g>_t1;!WE2K+Er%8!fr210{A`PJ1b87+1{2xbHZ zZBKr{Fzyr2+h{XXFH< z6&?k$MZaFk{ZB}gIiyzU`aQvBpq2G`=r&qTq-okoL=_Dol1(+MPdQ7jbJ`&wGZgVL zX=6RJ_}t0RAR+V0*qLvbzr<8NR#|014?P&~wuL(Q z4W%4jTklhb?VVwZbhTd)@37c=Nhg&&u24Rz9n&ZScb^Vm(>?t*h;MN;Mv{(MlHuob zgb#NV2O9i@)3l|yUa%CjZf9{N5DsW6DB*>A<7gO_z9G4QCvk=Y^gTKTQSrUm z?*?)_WT;`oy_U%J)SbQhaIDjitx@7=N;+CBNn{I5cUGMt(2XbP!bLm=Gzw-B5hneC z!t`N}=odN&{Q!eBE%Jp+CL%9|H99tZ=QO-KB*c``i=?hzWhu-yILd5Q)&zg2-1y1_ zD?~t6?k>iDqix(?yone8JptA5ZlH2Oyf~tNq(@Rk$lWP4`=~!eeZ`_5JMUoc{$4w% zQXf@lvjFX)=t^VLC;hYC(x#LNM|>skl?(J*7V^H9`3eWUJk_vf6~!#h(Z~2*(@j!A zg37sgaRGuDKD{-fKmptKPC6!Dbs4so{$dWDESD{0l)f5bBm+hoZYfF@UaL^kFjGnJ z{Evl6g*y!mtw^^WT2VwN*wDQVjL zX`k&8j{*_y5=5E~a`K9M9CWdt+pG6#lXLIEfowCRD2NA2-TCz7=xCI?riJ5j%ozpS z+5xlzRceBflEIg!xD(vSO8Qm)hEz!bqQ{9LD93CNZA4t4vxXx@DAqRx9A@R+(a%<^iZfAj{O&U0@;BA24njpdY6lgcM94>G`6 zs|H=H97GCz!(h!GsyqvMc(+Hdv>4@038_`gVKRPsQIqxhLxb7lW5=x1T%$i`ufRZz zt^kaD8-W&Bn_M$8_qgpUee2xNpZg{X4!16~&H3GWzhb#E_- z7G2Lakly|6-|J8SzgCFIBs0-{w4Ik`!xjIxw?iyv8Dc9Gsv{n%*D-h?{6Smp*?nK3 z8-XOjNBMDKSR}c(oye9DIa1t&=}CLjEm6%}oBR{jdtw9dUCgO)>`}vQ@`UT(h&HX5 z_qJ*FTKGRh;@-Chkze`wV&zkC5%5WWP7fc#0?QCB-t}{90HBA03p7}(A8Tp)d{z%v zDHwqW%nEzF*jfDi*PnyPP3oi^=SIiwDXwbYb( z8!8~v*+2#j(4!@Fh`}?zhHK2xap_|Y8{1TrccMVhh8e(X<-+d~VP%T0>`G{FK}FfW z=TlQpfB*yUCbO~{jk}qnBF z%S7}l}qbwwoa zkYsL5JIX*riwc~W6_s8@=($MoalCCP8cA3w5sDD_=L^f!C<+S zD5b_tFfd(y;XBVblJk+_d1@?DgT!)EcgJ$G-x=h$UsZlh6*zfYHy1AlmfyG}qP%*V z_?<)!@ZQ$!3nNWFe5CunJ^f$iJlSSsB5#?0+aOE6R%&s|j!1Ay{f2m3by$lLaEpVR zJ;8=H&0 zN}a!AR@lzfVK&(D)6#gB4lZD2ZZAMHpU4)iYBnu=r=7qhkb|2Kx!gzJ(T08E2 zKCHgGSqmYTwymzkhEgUC^-0n74{1Z_lhViX{!TS@KC)@-#O-E-*s`Xwg7pgR=?0VR zed=!qi!t`lISSu7z|5Gh3s_&z%Y+Yv~5;N0kWvMrQ@SG7*(pSAq7fD((HXT}Dv)RNVw5hYV zB7Si@CTT+8&4Kmxi$QCz)~&Lz;N^tIrn@C)W(c!y?%mwKRPU#Dukn_+?iMXMIx0@^ z;CyA@(R=UobgXW?58ECPeqno}jw&~xj`nIu4nUEPhQ+0Zjx|XA9DU^|{JMr$&QO&F zBzRKbtK8Li5r zLd=|Z2Zn9kcDxdg)-WYLK@K|k2Q1L#mD1e7gPdX8C#y<$@`QQlcTT+5Ah;IoncHcK zLp%Xb+HDpIFT2FOednSNsux6wg zBoZgm`54~i1T%AMl8O6SX&fcswe3gPcQh$^LP zFsoW)CQ2sj{{h-&uVG&=@~YEN{~i+F3)$~bha)kERa?*DFt1dXJO~+m#_xGhtAV`; zH$7`sr67LIFaKq_T7G*;bghnga`hFcobes|ehcdzOoLlkK;nL-oZz!!^Bi&D49ZdS z$Xf5Jd$5}ncPaoSJ zWRQ>LVKZ@EP`odDT)HkW@H*-}V8cdQ8?V|1+7{~yT*;%FF5EA1LbAeoJX|+jFZVYZ ztxwskT1Cc?2naaM(|)^M0tKcNl2ypx-d7R zOXV4sCC3BHQ6>ENTXFk(PB%#xDA+ch%9n|aPcHV#<{{CnGcS1B;qFiq%S=?UYP!d{ zA6D-`)Xg-!s#ZnjBlQYo?Yn&Sx+J_dlbP-ys?|k_%f1pGPiXnJE^LPqZ<`L<6$5Kp zx5zIenJ$``qeSu{N{F4Nn0O&zx|CqRd}J$7f?!9!DGAxak4q8KN@11fpEfy*Qxqx* zSb6L1)|EH08HhhO`=-6k^_<53>%wXQ!@gA({<;XZ5=|PQbq9 zVFR|Q!Jm2~0+-Y8nDZ}m*+K9d&$=n59_G&j*;Gi)y4g|Y%`4x-!ql-z$JS^%si4a> zC$_)PFC9%Rqb%=C6b$+JLd|#+KmM}HG4*_$x*p;h3p@!btJ!1~Tt>GN+B*o|DJW^r zV?f_w!K_f`AU~eru#V7H12a0^L{Cqf*zoh= zCvSdIExH^{@0FQ4J|zEVfwh|^1cuv1&z9$e(DZHC>6*xeFMcLP_&}KQQtE-T?Sm#T z&M4O`34<4X_Z#n+b*e5QZ($12U6u;44;r}Jl)z1&H()lvcC)zP zOW&*!^$^!}bBR=h91Lm(4!P!TU0aV!pFZubgHdg`0gnwrU3~{M8=+xrMpWcFZ~_(H zSAl*-@oQ^BdT^pb8+vxlZA99;?xP)4n$8C{LrKgYs;Er@N%^%-TH@7Q&n)7~6YQ4VEym)axEsK(^+$ViF_*`p~ zgW<@T!H*1D<^T&&`UfGEwra$z{y4eV9@!>+bA8kJsO5rgRGj27MiX8eQ zBiMSrT@6N^gt!3Yx$+g6j$AWiN+cV+qOX-X0-HMmuQXft!n3&Fj5i!DK((fHHDAYK zSv>dUn%(aDaCJKI5~#Tq_fvkrlp~CZ%@QVj*I$)CI5X==q*t~#?sSV*{!;v73Kfcgr%D|QPDizvUU zk?NS`)XqwdhwgCHF2fBvttefdJFpd75--R`M2#lF-60u(Y~I?!vPgng3shYw*(P9o z8i6R0@?IPo8rq{*zk8mtdxF%h8MJqCfo2S=jm_7Q!N4W$z7y^h(6N{#K+V||P4uq; zq^xv@MA*a;*3qt9@}JZ38SYi|3~Nym;N%zoJLE+=+qktktp_UFiQ5*Y?DiLL@5Qr! zZk%3hTWUJyblRWg+#_?5VlWDAHgYa=`Ssa9i{o;CPVcV=C(wM({Ko%lepvs$1RDay zr%C0yZcZmoUB~l1fTN@CqY|PRmx9UQeg0rSfIiseur`|3yGnAG=0!<|oyQ*8z9&r? zLCP(f37aBy8B?y&AP~3q#p-yzUfk<(adlXV)SJ=U{aOwGokw!*=|sD%xf#v$R)gw3 zQ!FEboUfoZ{2V>qBAnjVK0}mQIHWi(c2WE;z?G;9iAH>9`0& z?qbDupdhZ(xnHEw_%8NCI%|&AD!G~+B|DH#;(}?$rS@q2>{BTE_Tue~vAC^m3>I#Z zo)`qFUjI*8b?)hSbT;>ck|HXMuYN%Mw2pdOGiwRneaNf!0?h9Oa5i{hj6ue3>FG+V zfkI-xW-+sjkF~QwSA&NTv6WJ6~E7 z&<0G}29?LGvsOH_1Ked#+IC6IAmT)?a_)zUvWEMS3A01^%fALv%cq0liasE;4>v}} zrt^5IsB@WgB}oB179#^kjxn4`^#Q;pX9g_wuDzzF#=!tYhzSB66^dxz4fdcO=K29J z4&@ckTkT$67|isT=GpGvD7lOYe3HYXfEkSe>VwYBrN-;FY7;UDIU| zCJ0`V((X;H6>q|BDfATL!sZ>iH03GEqg869yUPfK6l1 zwyu=jerQS(f+&V^o2ozME8Cd6A$B)-1QWR8$!^$>L^YSlqDu|vEoK>!iP*1uwt-!Mm*Aei&6`mVg@7m# z`iB*_OQsZV@62=oSB!gWSOXSK#Gf7TFEWVA?Ws_saH4E74Cx@v(Ai*8FVg^>F`wV) ze;M)p4N&TfhDHn5waev6+D(VEO(BwP3!Di;mP zmB^%BT1Xcr_g#cozm91c%?BwgB2*1V3t9^!zBAN={8Q%+?p7IlB|NpWG~WC75;8zi zG@OVRqy65CEUrjF$RDv^6_nUnkl{G!-yCVQhfW@?S)7a^{RQC%tN=+wx<*Q27FV#mqyrEce!jP4cFW`KBM)K+%JG>tHy zh)IOKw8r2I@(cAG9V#%~LAQ+rDckGjTUvT58XBefdan5Ku5U6L<64n|4`K_Yi@?b` zRB|^xe>uwjJ>H5@AA)-HXzT&~YepBs!FZteY8ZvMD zQ?o&~7Hom$=g*DwZHRBb&!`h_+9PQPhBu2yB>RPgC3O?gUgF^6e<{*MEyeV75=q=TOGlfri*;7a9gf{;lhOJqJLOecdPkml;8b zn!SG$_?%AAvU0dUhTwm>GftF0 zFkPZ+y{5l+%m4h7?F4O38CL9Ie;{a4+QC!p+9h4LP-!%e*rR*f9<>f<0scJL^|QyZ5qVtSHNXQZrUh*+ zZ|NB2elhLSAPtO!z?=Xy$VB-6!%Zqf4{CF9(a)EcR|EPoX;5j{^OEKMaFjERAzCCbRQ2k$D{;$1dIt33+bw&S_uTxV+ zt^IDK^8$N0i_{yq^+Y0GvC#NpPU{UI)=<#g~CZmNGi-EB;Kn^elsVW zMG$0l4h(#+qN)mNYT|v+hP8{z&wRUN-W)(8r!4orMQowrKfaYt7^%*IRo}s$gtR~y zG`FQg@;kq;kpUzq0puiLRMe>Gvns&tsv<)|QVLeE;OFTN$vriZ7Z9fYym@_n1%}#Yb_(@iNTO~z@IE1Ua2Y@tp%l>5i zPvd0`%_cuX_G)R|&Fk^Kj_m7fuj4GokhjUzK>&%pcy$G{+vRG?x0d?;L(q|Nn;@l85m9ewjSVQCr!AGWo8Mmt3!LkBxdSgl z5^zU4*;@>|hB5HCKNBsQz@tH&3cBW?m6RU`rNVvN%%Lzo|F>fgY>9vsMw;Q*_dkb+ ze`|^s8uXHcAkAXiVi)sCBpkmJ`3)DfSf~E_#g@SXB#dhxE;tK8DMjFiCCYSO=!{_8e zqnHiaD@$HSDzThs;wPv`0=E`b$R7qC7q+@1dfYm(~|zz z<8fv!zGgu^Ek0yH8@Jhd)1)OF|BX{sM_guCYx7OmcLU)`4D@KcAw;RqE+g_{lz5RV zJPj${>Q3GK=)@Jnfm_I;ve$}a->fhnKJ_1%g#$p#-5}G)jhU~mMXa2yZwH@fD8?mo?sbY!#dR#!J`O#a)@~;%sTe zFhu3ub%5cnDdWI}=$K5Cb-nz9wk&$7Rf|cTBc4zoT|z=a+QC6piRd6fhz;4Vps>qu zWs|F~Y}+qozbGFlaP97yd#m&uLx%;nMfMxqsI`9lXu+MBEW1~Jztd2H!+P7q zjU&6hQvLg9Zn2uGuyXWFVn(OLze3<2`=l9EU`KKgoYeP^7l~AA$adG2(zt`Vgx)?A zBCD3Q9kTneXJ(T2C}Qb2L#eoeswyQ}5m0Zh#7MTNf4=;piy6*ll$^o$T{rLmczA89 za>+MB?-4S}{UQg15f#^XmU*~*lSw-KZrVhT;6$T&ljsHQZ8Q!|ti93giD0ZdpA=0K zB}LCx^~iv98p_yvis${7Z?Ri8t_UDr7e*V3EZckANBklop2o(LRGv1#9>VGA4uRM9 zsWCn&Np8+cyFh#ZB^-eZ;9mm$LxOO(IfaT-RBs3|1pPd8>0dy|d6AMa>i+3d;?Zh{ zZgovft<~pbW^F!zSmY1NZ9iz`ka;`^xEE`SPe>$p9|JLVtHp9t)E~xq_cAfjC0$)P zd}(niCla=WbW~z4*QgWAggbb~L&x6YYYlpP-8Jk^0=`YxRDhw6ENfEKi5<}Lz>Vry zhD#&RA_9``k2U4D+;^(T7T}dmhc>gz-BjHWxYShE!N%=*9aQt7>>0Q@_k}4j54c={D?6+%c-5y^AUD+$* zijaS%MnD+zH1gvv?`NHqXN-xu@IccqDKefel&>}%PzXhe3TS3RIcUX8VKXds*jrFZ zjcL?-A%0;jdaPoMtlbjo*VnS*I9pub96la5)`E3jx*bUh3L|tGEf{%T@p%S;8Z1^A zUGH7l&9`Xr*&S%(^+y$G7>J(!(1YKj@HL5iD6*Qrm-mgq}3-@ z>-kFK+lEuWbUR+RqYxrFp+aMPcDw+3T!hB8sqK5KMPftVWiMyJm-cu+hT8(wuMB1X zW~_0^fq70lAi?}S`(HbsQu4KotsBMUGK2wh^q)O3L@P4ZC!1T%+b>ztGC$MbTb?lYlT zXIJw>vq4ApyBvs)O$OjP)5a(%A>;EYVJwJbN(JgzZkuSJuq2M?hm8$)ZZdLD>nGl) z0f&``Q&$EfwfKq3j)fXH!RV0Tg&V2%S zC-V*amlW)`Qapjl5RKqpA$xR>y`)Hwd+3yAPQ-s5#?w*$Qlid6Ia{cDlmZ? z87iaqT!eY9yY?7auebBhU#QMgva%{mN0*j}Lc_yjfh1L~CpBm4hiL~(JK66>Mn=*? zCuKlQw)Po1>8e(<=w#XiMb6j2gFt$iMRuqKW8MqRCbF)E1|4rRx8@%|RA&$R-uJ1z znROEt^?ey1$PJ!~RRIn%8f&efAXokVP2^nvc^`K3F;@O-2jEK`_J*cfd#$(Nm78XK zMAieYTL#|u%uI4>AV&N}X8Zy&`9fUl;cM;)B`o}~$N3%M{v@iFj!sH`K~_NNJ_j0k zn$gxEI=kh9Dy@Ua9-FqpJcoQ?l%;~?+bO02p=PY9ZyY4k4j`->^-hm09n!@sh(fPU zEfV}BGYT(Bw@83^rh0R<%+Ro6_m;YzU*>X$t=-|-xCD?i5^0J1I}`HX9P^U>A)sXn zy2-vmJm+gazIzF|z7tEO3S8TdzWs5s-yNoIevizkef5oy$H;!^oQZbERx#n6U#Vb`qe`E3Cd95 zu+Tp0n=(@7!hJ}V;kC2#=P3`%fMJ`J0n(mTt<2Hp1A2UJFCzFh__4elb%y2?2?;g9 zrrJg=n&$na$d_A+ytCsr>I&sgkgMTj0$<4b>7@LwCh_p`=_Y?#FoyWQlbDVj{lJ0s zy?xX4A;GIu8pkxVOh@BVzQa3R%Vg3ZH&1j5OouYeq)-t5Lkct;Wl zU6-F4pBxY4qsl2 z^mh_)!$aT);9UCOD1xs&WBi|}^qyE*h3db+wMvCPpU+^%I+I`vkvd6?=fehWrWdBpdL^pQQto96!oN9N?G2z##gUA zZh@}ssUPdSsqAeO9@9AW7R*C`(O-Gqo?LSE!rUe1cN*Nys!XCJ%UtxwYTr31GoDFS zj>sh|pfXm9Ac>AT_m~IV{{ViK*6Dp?qsx9mJcy$*m5wso?f6$)OTe6wQ+B0xnWZ$v z#YKfJyGjADoL5JRCnNGAiLh?|_jwGk?vnmlcfE|6?0Rv~D zxdt(A0&gpu=-ypVYD$Qn6o%P+sqDtp4o9%Lewc@#R6LOdqXMiE^5pGJ37XKjL?%sH zV2gCNPlk>q?3=s+a?;NfS&j(@v!S6CiI@h+EWWd(Dp6-KTg8_kuSm(XBXAXf#|)Lq z-ujK8>m?;h9U0)-UN39v5TMv_BG@eV?V*4_Ohm=(`e2riPSAXY_K#~|zB{-gr)X;+ z3)J7`PqV#s&dD3&rc!UYOnu+fGzA6HFN)L_5pfEl8^GJ2t}N<(<9|W%V9VfBO+|dr zV1cGHsS>RVe;HNec#(z&$n|9q!eX^zW;XFV*YL)Ne%-(RVy&CUlr67V_RRnE$t|>$ zrX-)CJKv56uM04P$;d8jE+RsT?GH9DbcOV}j{U|c9zA4T51hMt0x+7={pliqX#yH$ z<(O(`u+VRqf=`b($NFxLKDFkXACZ-h2XAVfPf5@^_h~;u9F%e`jC|JWfEzSe4ZN^_ z%f_4ey>Uo(3&^RGN2jDv0g)GvfJrS~#C%*(oGl@AzlEvAA(ktmqfF&?(JQ0(TMJ+z zF}C?&PSbP^bRs-1yX#Rq0GtA6jC|dqT!x+Eqym#xHGgbQ~R=l;tNu!z3S)T)MmFg8;Vvp}AIi3R=(evu7e z28CX$ZD)U}>Nw0Z3U&U2ql)RQ$LFgGJjJV#MzeuJEW010{d>Go&-$45&RR9NCu*87 zk=P4|uO&o)#Y!wQ?r~OtVT08&h>Z>1h$LO^qe{#q(A7eNHI2HK7w?l@MC`j`wi*QrR3mMU52?6@?M_3nT-7c^6!=0O$saNW zWccEgHUgSoA6$z@)XpWaNojaT$a_Kq4`_jTRx9-ImH<8$aSIor>X;wJhxI|r>13e< z`b}sqU~(~dcfJ`D%wVGfn({W81dXph;s-tfsLV+Y>j9 zZ98e4iETUIx!1ei^~3WY%qKIsuJb&Oecv``L70Z0O7TG27j(;vHHuov2oy*}UY)kb zLz|M~9xb|1G(0OR$c&^n3nIR;|9-Vf8_s42E zV>URQQ6UjVEz6A%2VRViYU?WhcE%u=eY768$*WEbg-2uD)`l9C8`oj6o&n1N`A>k~ zn_*VE&g-hc%g51a%!G1kdAU-x^WV@C@nF7B9hk?Yc-du4Gf|NbHI8-Vw+lyBkd~Oi zz`O3;i4wY+@Np;-0(I@S_0{7i6cthD+042>=w$roSt8nx*=)ioaIF`7))PR1Irl` zM0RZnQ^P1mO%ML#Wy=^6qe+``W5<;IAp?$pt4M0UYxN42=#Hi5TyFLl6y!WFLLLqg z%l@d0YV2+2eZEDypUtRuJ-%NSeXRLdgmuYRnRsujCZQxHjgclnfB|@PTG~tW>UfYE zNd*uveenTrROK6Sp|+R5{Mcj>1u=g!3yw~JHA6b1%a5QyGNe%-g(P)?0q*wTY#Z`x zS(+K|^ez%^Tk@=|7g(b@vtWni7WF7N%m{zq$HXYC!~>os2)fOxg2<{E;+1;;GKaM; z<0xt1erAF27 zoDTcL^OqHPCi2l$O!*~FQ?Rggn2WEpAo1;DSvlEAd`Fn z|FT6q2p^5&Fujeizc6#}Dq_O!h-MZV*KJ{CaC*4bl{fvr6MWkj1f!rXz&!l%nF4r} zxw8Gu=Klsx%E*wOrB?;VV-t5q37SOv^QyQK1plD-7Ra?n&cntczrDUl%6rhfZ_*6P z>$p~Q+?+6_mrwLsx#-@TN^^z#JznVGEI2lAP^85ZF-zBU>VBwta}MW7J?8S6LNy^i;eMiPs}PL$I4=ojYV4oAz7MR71a zwDi<_HIZ~2Zhy11n1rjbcn#v{+8Y=5$YRM;C&Ato@H(z?^O&#H7!%Ql8z@GH@sWuS z`e;XB(B6Rp+*sxQOs8;Dd~s!dPESG$S5uzF)3DG;dO@J>EjwYe*a}euo%i!9BajNh zK~?7C1KXMPVN3p1oN02_eL@_s!XowQIEtysC?3|3P}i`Gs6U~tby=2G={LX?AIOxa z?R=@tau18IYI))---O#qhS31Zfx{BNe2v-OUr~c78RSn>4umSwPd3V$*8|pK)tpGw z<}P8h5;qEd78*-nHfqTxjB=h)t_{$lBMj93mlDDM)T8xoCsAy}N|#HPG&<5isIiV8 zs}F*`@ub(3E?w5+IzI3B-Jvj`s$xEEAFy!ypAg6UX-&IIsY({{-23(wg}So#QPeJ- zOD!p!zQ4r7<-YKm&VTkZ0`5PNY>c>ARR(R^n*oQ8G>UMj#LSLsjoKjl(UxWlv&;K& z(*UEwnTsc4es@0oJT9$rQUQS69~JBE3{D824xP+qsB1)6yB{)$7thn;Z#5Ezs`Jt? z9c*$ZPM|JSDcjtbAvX(dxyi7HfaIbMx{dc^q}i~pTesWPGr)04cfNAhwk9p*XIgA0 zvDK!e@2C5@4m#|$551svonf;hSe9)U_x64nsGF2gj7!QN)Roc23fR*P$Luz3xkPpa z;dKSs*sBWBO;1J|mk(03=AnhNkGhJ`RooohI=?oZzpx^PhGd){)PRxp7Qu2p&--cC z7lN1cn_63mFvYEAo4Q6~;!w{@ksfInNBt!O716#=O|HO8-I zj3^>NG2gq>CG;*}q18s4<4r9un2}X3x*&*EKz@QYSBwBN&zR9{ma7yh5yM#j&+)C4 zmt$B((`P)5U&1M0DN{(l?Eykib`Bluv%QHd*^9kk8gXWPC~ojNO3ivJyM)MUADB9a z#>mas=KFX`{ax2db0<=PDT$X#1_j_W!oVv82yG?)5=Bh&U2awA zV|kZdKE1q@X>o!y9BZ>_|EKU-4Fj`jn+BpVkezQ^G34jKHzsausL%P8xTUl4>d ztEdZ_QTGNGD(0;rM!I2)L{YwJ81{%7@zu23Jc=(IyC#Ioay$dj<+@DRE%niM+_+Ah zbCQU(&aa*#P*26zaYO2ekmp?n)pA+1U7he3QraKSpj=$lWa0>;UP#$kR$mq@YUFtQ zlV`vv=f}qP1ZvBTKPF&!T-rwhzh#Alp0^FZzIQNIu)SI=88eJ8VqCCxpn zF|`p{u&kIyDr*DonK+TDH>l7>tM-)h?w+g&oir_Yn<=d1!(F#ImvW~~e-#Zs#R@|e$eZjKO+Rl%Ke3jw7@I>7N^V$% z=9~_(&+oToNp%|c|JrHZo1DgczuasvGop#s^S`7Kp?K-y|Td7?UG`{2zts zsYH`N?+;#Td#D;TWnAW&sV;ZRh#ho;jiqX97YPi>WsLGG zKy9d1>t9WhFCkg}mktCX77!;m$2c#5Gixg=v641%?Y5{WVQtve4jyL-+*{5ut%Bx& zci;5FNp;G=h4NX2iyFNyiAkZPuufcRv%`U}UBrHun!0e0=bcU)U4ggFn)|H|XJLdl z_kkU(PC-ZC{LE};g~Y#2=d#3$+#p;L)kr0a*sjZO+T^Wyk#>VqSz%-9NoI=T5t`33 zP%-v<;BNf!4#ovlh7p{^nJ>mD2;9xv|HZ^6%GSEt@I8J8n`P2mBgY6yd((wJ#}29) zg4S3up&9T5Q34Xg?DtVSQs+?_yh`hOlGZi{qwC#Tx;Mkog}9Xh$@vLnMyXd(m5-7Z zsA`8{$?qQJ|HK1@oPI{dsozd&)_M2-zmq#PIw-srHdC7bi6}MA-1W){T!+m;* zd-A6kWm(Pk(byMG%5F9;AdZZb)2Ej^pbzR`(+a9e+BHky7~ggCn27~yBP61%2Ab7g zl(DtyH`K@oR2LH<3SNnZliz^{4n>RCN%*s&KnBCw>Wk=% zwfL?~wp`R$b_y;yKYPo%?ywO?YUWTxTDj}qHk)uHh7)MX9)kvo0vQ?a%TbDb#;$j3 zv_56;CKpTG@a)z|T?OErUb=+4x@*vcmL?+p9GQLsG!Z-PG)pBbzUmcHSQY1qCvkBL z9w7*uWPPBK1)d=u3%5jH3UuHeBsR=5=Ks@Y%HnlQa#s2sxZU+yBB`nc_p_Jk)y)cw z=Oc&p8uM4{nK7jp%|c>_xpR}fjX@SMCYJ}pU2$pUk2M{KrySKYL&+_M$_9h5)M<<&%^$0S$)^vI%_C>{|tord5wRMe4JjN04 zxu496R?B&nL`B}NB+2(_%lRk5k@3bVYrBA9H}8+45P?BC*kmf{ZaWrh-tV134Y zhLd#bnAn@}%TbZS+eW}E34K4z6HH9xj6NzScU<&u?rj(Q`Dc`t05 zZbMKK!;#ar8!!&cYlA?O40HA0t1_>wU&lq%8NZ7dRu34XV+=K~0~!XJ?-S{t32@Ym zf2iFSz_Q2sy^?sz)r#ko&=c8%nYQA#wQ2P04b7XL(RW8nv%18af+#$xwWaS}4O5q< zn@+6Wx4rKlAmoEU%I=TL&j|fpnkzqjxX-K{E2s-R#&vJi!J%N(dr>kY4ZO3pylIVc zkV&uqxStnOZWdr@ewbEX_ywgzAT63pZeY%Re=jO|zfAP~*hjOv^Yh~MX6LgD!Q)w^ zv#ea^@cwqYy8&e{S7KarB#?uq3;~hFpzz&T+VA@X(J{n&A+GQH2)|8>CRS!!CQ}Zb3v}n|1YCqyMVCVo`0PJQ@&|FLfk}0}?bj zr*oao)E6bWW)n|Jm!;%|g~7CEyk0gLlO+Qq`}c#&X~e_tN-&bQeX>V809TUpGdGvE(?|-qI3zE z#q)+U>Ut!#+uTfx z@4zBgz-7^-Sq4k+Do&EAWYJDDcEyeox1ema@DFAYYx0<=tdnGU6T2(@flDumiTdA|$V93C*3+rFs&_)CsFq zbGh!xJ*BiGgQym;cfqvTe5n_@w>4#*`3x3A$@8fEfYl(nHKUu^UG~K%?i6{dJ+uVV z(1@p>Rg&;=5{>S)FIj$D!SDq$r}S*0RfWe&nQ6sN%$TTJhkGGEi%;s=l)_BhCKova zc2ZNHAHMiT`_u9kn7vLlce6OI=Hy7!a;iJbu+5mIj<*j7o+-= zw6jhQB2wPjrdSLId6P+_bk#~7>uoRV&`t& zU?W4^|6Pf&Lcz4?#Ua}?{Au`$u1v{{jBJI>{!vgF%wEO*J=h2Pq1CWYLwWpn*Ja@0 zve+}4#dHN`hol|JPoN-glRm8Omr?TjU)vucIzmUBRFE}-m}c^kI+g~7%mTEX2q(1# z(KF6rxkQ!up^};s=07Yd3*6S#ju-zCRG#Ze8iTUpykO76yP9~(Y#Po_at{A3v`P~j zOJC-l3T2Hi^MgOL8VRH^LEJ{;=t5=Q{tDK~E!_12D$2z*`{v=MUu%<1(5KLZkGANr zFx&d`MKhgW(@GEm#$k{uq!Rzdnx7aVR%XMvlubg{`c77JPuPpvy||8^9&$-87Hg9QJW;iW)zEx7)nS zvnf5z@=CEnXc(HHb2fR2C}pPd*=A>?`{jy+$xC-2g%g3=^}TLig+F>8W>gyEWN+h5 z^sGy)#%&4!J#nT;AgUCl`)-h}B5KCDZPJ3sjVyPf2h<2_rjuY=Ov_Y3JT|n)g%eI) zz1rkR1+MRf0%Ng`C@=(Ts@qSuSM`s>6^^V+t&wniCB+K|Uk-QV+mdFO!0tP`6q)Z@ ziT49orb!}_0{5^`iC#u#3N95dcrdRCgkfQQ+DZeOkZMX4AQ$HT(nx8i9ZujxS4j$jyW1-NZ>?Ik(?sr>fFYZ-5LM0K;P}47KU8bz3pOXuyo&OBy!>U1w{`vNBSDPq+j`a6EHR3dsqp>^ zGQF+nIBAmw7RQm<9&#tQx;_hRL$JUhb$-uxzQOb*n|{?xRaX^xra4}K=A}bep=5^A zYjLI#$vY9|33&59&ixsQbOD)pESw>c=kne9^dC!r#O1fA8Oehlx5Sw>1dkKi^pz6C z;n$0Hbfm=tg(JuH7Sw6ByeNI(OA;Ipi$pCryr!X{xhQ4LlwRR(evC;rs(hR1aLe^= zB0-(Jk6-ND#tr`};q;QiZS}b>)SJOir9hh#B1~cU39b}HEHFK-K&^q!~CRT)1Bh~ z{h@@*{5kAk&rZ6W=WU`PPxcn-9x3;#@-Za$ep9xp=?>j!5f0;Yvik<^B zs!pE56l-c|(Ux(D%aQ3)J7aF`F>w+m;xFwpkvmPuPE`PmJybows|8;)bUxRhLr(80 z7DoD66+}ULtw|}_-4fCSGLZa3vk3ym_nkN=#_k(e1!|t>up}Buv6jCMcnpz`r4`R# zU%rDu*6l`K8>n*?nnX{H2FQbJ@W4Ms5q~RR8_hnB(y_N@q!a_? z7z4^@iVPCEP*Z29Ty$hVO%a9Z`r6;l~cg>EMmS5w`vQwuD^|{nXGP4ql__ze)LNJ%Il{h5Kj9(uj?$W|Ym49G1WQp_5(ph?k zp*yf9_z{nhGveuAT$6M2r*E5GWY~OQeX|ii@_#kFlH`@i?BAnBoa*!wKkEDpo`5cp zy(w_|+|<5tL7?RYg$X1MfO>+V_mCe!=veL5+m-CbB5tQP7t^y z?5(YU6#@NDd(_-r^1GTY8MwG3Mx19TQ=hU_C1Gdh?sdOu2LkSv$T0JXj5OM-?@x&A zCd>^rZ3;C~YYs~Uv03_Jt(+RscOvV5?TXw_H2^_mY1Df70qRF{Xu=LloxHp zH~ZzVF4ky7%|L_p`}KNp#u#`3Zf|(B$Rw|eeAqkijdNl9&U_eW5D-WAprqxok9YAI za(8}6R~V%Uy*TI=2r$Wg-e;q^mtv&l%RiBsBhF*zlTcxcgf{vqmuhmVQ{DJ3@;pfA zv9tGl=#SKyy*@7o9cI$=oT3)ZRwAg`iGSi;!)~0Pc{7juI|=iZ zzY;9p?(FA}YBwJ50B0A($rl|nlbF&oWn&B!ZEzs}f5P?P0o8|>0xk2_*=nn%M*U3u zCCq1e~&PpW$x`9DXzK)M;W> znVK9;oiY~4J3i%VScN={Kscn2ry;|dvYQcxkLn`T(^A2sBcA88&SQHhd2jAT=$Q26Ecre3^U>dIQwB4jcpg6hNze0RF zpG@;wb0fLDuwRRbRJFcSqn#z-XK#6XestRSgNmLAI7SR4IKPHKw9_^;%Y0bFXc_pj zCH9Ay%}(WP?onGtrWEkOuLU12J%`KqJMJn^vM;n0i3esq(&TA9-<@g!WtL-hj`U+r zU~99u+R>!nFaCEhd%iv|CgMujpL(DWAuWnu#R(%4NmQNLZ6`i?SuydFje(4>04>q3 zJLMnQ$BQ{&tZ3kb+J65~TD#^->mMR3Ol>-j5j;9_Kd};N8Z9eq+LrGO$<2T!5*WzG zsv$4^;GXG~FWS?@Le2=(eJtcjFXB16FgrF;RlzS+Bm(};o4)WTZ8;9h&SLU07v-Xv zZm08QysPH;Cr{}wzlpvA=-;%T7j$vti2m`-LNX=YUl}$!JaZ<%L5oW%1?_-qQqJZ$ ztHbq0{N(5&NlJggR}b0H@>V5_t5rb&xgi>fQ2DRU?f!a9oQm#Zyz4 zeZ2%J{u83;16aEz$CtmyJ1Ks5{EwS{WPR@7kc=21k1zJBmta(BiOLl;Az$S919p|N z&l*irbhH8uKMid1p=|vsi;7G<38JVqJo}fNLMguY$7|9mhiRqx?Q>;N7He(1mxGQ> zaL09g;Etf;H>Ip#UwlUO4r%XfYQh`CbYrAEPqm|J1D023vVimNpoCn1if&8ap3LKf zH%0#6s33QyYojNlA1xm;1Ss8M+{m(vn>rZZ)n{kbm_;B1!(O)<>~k>hlD8Uw7|ANP zhttWMYJm(BhoL60?9||=XoBPF~wG{ua~4NJS$2g z{v;x2s?stUBynC!4BK4l{_qeg z5-1EzJ|yDvIW?#~d+w(;dZ!}AbJ-6~*zWnxU-R4!dRG|=O3CtBR1z|TKjPw#sKjuY zl0+9SuEeS}zPhh*);pA8dwWXS7=Qd5 z@&LR_37)nfjXns?A3Crj9kD0TEM(TcN4@YRZneL`r{dMW&|_WTY1z%**Yx5u;o_`X z;*Mc_dth_s!|Rklb2sFtGsy2C4m;G8s`{ZNQx3!7s{`I zL|^^>4*Rrx)~2jHZdPI;tu;GA%Zuvbn!CV6$uVw}Lv_Di_$^ei;$d^Ur{AsG zTHwOXF!)8#Q~%$KKqd%zuA#tHPB4S^SimGoI~Cv{G@%S1W;Eg9@&$Dg#w)zy>WA8m zrt-9GxYu>OAj`fU(o5VZQ0^kgMF;fpA&tZu;{SgYFbZ<9G$dm5s$u+}$UC+&$+w); zcP%|(0euW;JG{fcyTS`5xql!{3I4pMtgfA*oC9}7>YUq&=6dvA0C65rW7--RhYV(r7oX$JCC{cr>f|;oIyWNUm55UOtSnx<7UP3T7m7W+*VIO~06=6$R zZfd*Ru*y@KegqkFx{ zpBX1i>a+{t&=11(`{{#KzGf7x^KANMNDxnL=R;;DZYHM>lFkp^OO%&dz4K_MzjlOn zoKw4DsC4!Hly3xmJY%!lR@yE+^va6whM{C@YPVN?*?8`h4r2=poVec#cG#|vw$*O! zu2F(zVj(UBGiwE6MqRs5jun4m3|g=zhj z*EKd2+m6{aAvWd>wa7RwKEfQ#7@pk09W}g`m;5W#92ke?w&CY_pB+598ls`ZI;ETJ z3ov)<%Rbl_K@@OMdCwB>3rgH znMYE4D?(+JM(3Cxy<`26d#c}j;~ez5)e(h~w5qr1%B!k~9e1e(f)hEx&59?hOxEi5 zF!`56jpwaUbTQg1;uw@qazt=A&vCr=azrM{IR`jA|uvnSeZa_DFz!6a-ffKVF@<%_Rb)a zHP_FIogGT;B5d=JtbpU=kN-al;DFGV=3Xbs>1rz&Q#lU)JhXUcN4hfAyMO=*cyja_ zpVM6+87Vg>rgVgb9I?8Z%!>woX^d&9<6_}yw<SZ|=`hsWY2i1ZLE-)zMP z(@3e&2vkEWNTqdkM@4O+9P7ivUDXiJoiIe#=P>g>KJO-5tq2|rYd$O8`TY#gx>3lfe$>R02<*%Z}A&AGh?R1VfYadFiZ3akd&uN zlHDCLjRW9_$KG>@-2M%e5q#!G)d&)?;<|JD1}1vEw>R1np!2M{fI}^5PF>HXz-7{~ zRo3*&wxY;naTVd3Ok-+2J$Nm}dVtY{7+0i&E|qL-!kSY%IGdlnT;!9$Sv6AD zi1e^##G6H{N;ylaZw`KJrFn@3lomRTL9TihE8i9xr6(dJ({t~R2~pF~+Ux5jz>@}z zVIZyOrLPOQgR6o5q6?#gVA(gFE9_5=GDdLn6@8)hGvP7+H{kp-aJZ+eduQt-A)osA zWp-sfK12Fe)wx0JQC_BvnE{4 ziO;^to#%u+5$afz&z_24&|n=y7?rByO>9^{fPq0=w#oC?5eXyM%i#KE?|0Ct5`Kn+ zvF8A-MLtLOeHbmLE@4ln{O)4jMjanF&ekl;KIkR=_APG@kG=I+^T=E%+%L&77da!n z!+_z+v^*PH0Y>)7r5@t;cHM27prCAInM^YOn5qY?yB=n`iUT$J;o;Wb2U9-UABVyU zn|Q(D{Pen8RzEjYhYh4zxG~b3#VidTpY^H%==hN9w|}b^*>#<%#TNmztOHIbbBmh( zm)vNE5EjvMtu?XXxSWq{-kKkOz-1SaguEqrOs07~G}sAF?r1i{8WE4UggF@z8jw!W z=zKA1aTt2TsD@39DK?BIrI+Nx9c96BjgFtROuf5`7Nz$n6agIi*y7n{@E@mc*ZNVJ4F48yCG|Vx$MdO(d%v zOe?TpiezF_mc6+FN^NE}B?{|qqs}j*O6k*JUlq)y|mf(Z9^f?c^0Yn=-BOs zPOaK>jD1u@W_Jeju&3>!FlXNtZVW+hziaTj5lf40SAmjcz+!Rb!<7JV!npibk9s+0 zdHxgj0N!W^m^#c8EHB%l<#)9ouyMcCueIah-2^Kn`u4~^=~HdeV2~U5;ggep@ayT+ zsl`N_)97jomFahNL~TVHHOYsgty>o6qelABjupG^ACV|T)=Zhx<&3E|+o5_f(|@%c zFThObElMPn6t2Lx1g&&%W>7_`{m`9Yq{LW_Vj|}??sqlMj;6R|@>d21ZaRz#AZeHG z%ia0@WUk{99%K{bBlED4wZVybfy(@eM04o;h0!hvlWCC!oH2VIFoZUl?6%9MvJCeZk&{p1oa{-!Bloqb3jWA zw+ebp{Z5PI%o;1%*h@#u|1NJfiNe!j79Sjx`N;PToT?n@Mnj5xc$wUK|wrB|Nm%QLrhNmQ$0a>12OZ?WIK3*6%Q z{~F_Cy~rrit{rVT>OaV3)Ub#utvLSt^{Av25;&OgDPYa+m>HA0xz>Ey6Q;rWvnH$9 z4Lv=3&jtL?ut~IbJbbIBx`xA)q(hx*TNn>>d^Ag?eceJP#W<>uX#Mh`Ot=M=@gsHF(f|iEc4mhZpK{n*u$G} zFj^qCOVUh2rU$6A1H;`fPENlU=ZqwPz&?gHgkU655CoK70jLQ7t`o+kb zozT%9EcLKuGuGypqV$2xZOi17a9aBb&4i}8sDxBI>G!KxhiRRzNXCVtU#c4Y<(*PoF1es8zWj)O zPuTbawjm=!KjY=>z+bwN2kf%5WA|z`n$!$oBly_*FqjIC9t@m zWOHV1sm+3qv&&@DQ$g`*)MP7sQxBCs2o!MGa_%r)Hu)kaaw+PJe{LPygonvUjms~0 zIuAen)4X#L#_6962*UYtQ@Cv=^u&GB0HAV}Az?IZ@>S;L@w9dT_w;9QX)xC6up9>| z6TtL*qg@q`SS6j^MBpKeL3c9{rT0P;l(-t-%7=QFgCs3D8Q%4B!t^4v^_ru=3LI(a z2|oX6ZL{@p0?82!duhkYIWri@SBp| zylp7BvU8J6)(5e&Uwg^hrB=cqlkn3?CeP;eB4NGjc-ex)jSJn6ji($Z65Zrk6WZ$4&)e&2zlX)J#ywG7`+~T_(!sN?G3HJ>z=QU;A`{SXU`cVT@*J< z>8Q|_Bu+LPdpF;i;){~dXCpy(T|Rznjnri0)v6Z0v&PAPKIBC$6y0DfPj-WVr{l<*zV-B&)1S+NqkL z4Cc(!6SRHy(Jz@fe$V^eJwl*V?@iNq#1@XL_!Xr|j1Mr?R9iDY6b|(_Slyy&y2=eL zgXP6dF_iiYdUF%RV<8;GyU`IvEc>T0r0*?a{iH4Oi)ls5BE}*=Gf$YVhxLL=<5UI z(0}VxqrfDJf8^n(&&~>L!?x3~UPLL*ichzIPae>~uK|~$pyz|GjHQ+=f0`owVLx<- zoMT;l#>Yx8Y!v6X<|TT#|4M1vn-yo5AyR#db@MJpbrgPWZvU(+@0o zaq1^MW5nw!D!2G$AB-oo)A9impXX-g%c^`pp?5WOeTY*hmgA-1kD*C{OLRzOomnr( zDhx=&)_Qu^``z0?r!+GM0da~!X0bh{JReq4UfCz{#4Qs(s|0)c`yN{|x#*`MR7hP* zIspc~yMIpU8vulKtC!BlGEp1UT@RITeC5AFSvYrQZu}3jWlk6w2J037`g&4Ze&{>c zmx!pWt?r`;GIii}AOm;M`ud=%Rhxt#Xv*gt(1^f#p>$H8FHynVr z#x<(`&@V@w2(Tn?qE6j0&x}UK^SsEUTtwrItv7TqeI|1oizX z!i<_SA8EvcaRj(h68Sz!Ndma}umXvaA}!0cu~WJ~VTZg~l3)ZLRs5}0?D#1v&kDRN zv=BFW&O;Zf@IhWDKVsz)mA>-V(^D&c2Z3Fyueriq2bb+m_Dr_QkFJh~3y=6?Ew1~F z&!^*TmG()r-xT!m!AZa9l;;_!g6Umy_KzqP%p@-#N z@@mNs z#^iXwZWd@EDq3IzPb;!;NZ4)b^CH7kgle>6^d)y2|5xy|+Rot>`GHEoQkOA&PDdan zZRm4Ap_BYWtgEZr9f_>A70m;WNe13EiB>d|x8CdkKVG&<&ez7K2Jm|UOaNntT6MC> z^iI1G9&05sfG`tH{m1y!wX@>@9Bi!ZZSkgcz8)9dlJsIi=&Te!+HiR#nQN<_UScbP zfn&AZ7FLn&<)eQg^?RG`p$AW~{}-%O9tZI{d&ax-I#bXOtg$3>fB9>NKkYYU8#BCh zY3Z8$ujLm<(~h19-QnM8ES9Zo{rDYO~sIy{LI-Gsy2YvtQ~6Kjga z%d^AFjRsX%7~(AVP#jEF?|q5QM7=X1B;+IlzFP5A(1-u);v@_n6CEwh%?u3*Va!aC z558vv-_gO?0pA2t;=45wRj|ybot0(l%U2;xVfrOGm$$uZ_xtNVULX;YWv+ljl^pAt zEphy#XF*M3z$=mc50!$N4pHwb3|jp^>^(?JPtfEWd(060mlB5iv?OZ z&Y0mo^d8F*ANV1rWzwlw=^f z5s!%P9eq?>>_C`dR1Ni7vsZ16Na?8R3mA=DhOXoO_)mqN4&YzXF2T78HS~P zY}TmQ6XXStduOK|BcF6m4)v3aF5_W`Rz9>s2qsmn8s?%L^mIB7fN=la8)TzXykU!qsIfvkko*3c(dhfWkc9v4`*6sNvE{e)YL00{wG-HUW7D|hqpK_E$tozJ zoOX?GKSuQm=gpfuG5g0Q%Vi_LK?kwsEyk;cVu91Uc9$()g}$|;ZMUXsLh!VtbgO3E z$tx~;{11HpFT0z5%f^z}{!~5JEFbr!ic_H>bc(CzzWic?x{en#-soOZ!9Lv`<~$gUSH(4wVwFogae?^CK{}HhF}4@vBVq_x_V8g zFu&tGrak3U{?ei>Z`un)NZuoL;Lam=EUkB{=~|$s#v9c1OS?Pu^o_Cwd?53?KP9mK z*s_*4;8cS=(DH194lHM#hXv3RMd{&1I>tt$=-bjjT`NQ701p{7%gXKafTsLYg_mVx zGYRQb>NU&<=|^tmjh_fusT0$kdO=e2c}DDVJGr>9M@-2~QFsMTw`Z4mso~MM7F8NR z%beHCK(ULqQxyz=V}gk`s4Mlk=5XkLHjX|AX`iBgAV@t~7Y~19>PO1QAMzKlk3TGl zc%2I#Ees$cfA1-hzXfBEhb8S%g+tJW#Zd&arGHc6`qA>Z8NE)k@~8Tn{^ZAW>*eJ9 z$+5g|lC|ZEm-wcCdjnU4jhBth!Ma~fTm_|>ddfMSO7xhF@SWL=J2=+N@T?NA?|YXp z!_;y_(j$B&%GZIoyzmv`wI!=v4rP$4NXP=yrtL? zJf91utgdI$jhhLkhG(Jfz<;{J)QkLTi{Jx=X3t&+f{UWfZHK#&l&?!FL}RWlXE1h$ zMBr0k8ysqa#WI#3ZI+CTD}Njgfkj@#T(=3%txR4{(Jvex34(lg-|?9oL%pG~Vz-&j zE!m?a(^F9~9Er;&u9m_ybMgnbNsVJ|r4)z1{sd#4LHytd)iw zb7_onWYH1SrA_n8R(Z15ow@_&XtoLeS`b{K6^VsZryILOA{yG|Iel#fpL~UDI4o;| zxM@xmqTq&NpMMOYL_xKB!dm=735vlsouKjV34Agqr9OV35K$4RS~qu%_nmq1?+JoT z$0vic8-GcHsQ_kh+4Ks2t1)QSja=kS6G+h6@wk@Wf|;PpQ_?g-&YH$ZCYIfXT2nV_>2(~SeK&pI?*m?|*68-_#o#1_V_^&02cSOpUPfEih25g-V> z9!v}Jz8f_=7JvN@xZWobL`qZPM@!b>#LQ358lSNmEM)2 z(4g#Ib9MJ;H!=eb>dc5cXjqL^03k*eEH)2}REO_`cY=EU$AEFmwcR7$*kcOgYla#zlbK2)J3*z1Mn}3%>$8i2NaY0FOM3*PH7jG?g}-CsjLIf>^`) zs5zG6(&^Ow?wPt&idV|o+X9n!rl<`JjPJ$txPuSvZ<6>wN3whfDDWmX(IefftWVwD9VO<{5Kpe3%*wZ?#t}N?QSWF0Jv`w0M4t z`zWWken<7SH+|^hUwJO?0gt70Vw8n0WSz2Xm~Ze=*Pix4kmTUxc_2=aGaCW(uHd9h!1Ntv!?2eWgdMOTtOpN>Kg_ah3z3^ly z-&hDkCKP(Z?$B=r@$yck_-Ib#EdG;^I~dJ*FGi9SUNuD3Io^1ZeH&$ zLcN3Vs6=E1*#~8}!=D0wI&l!en6eB!+d@w_+K&azYqE%ln9$S*Tt}Ps6zvmYM2Orl zXGc0bRFUn?g5G3=Uxz5Bi0duLeSGn5g1;$MDQ<1ZNVpEiou}h}LS28=397zU^b_gBUjGPUk{2E-Scw`6eolbtc>Yav zH<$OcfyHMNebq+)IFFEHX$T(QJC>!_ZNw~B(|K;$Nyy+kJK)FGO zchn62))KlXibS_~7Ze!Qf~Y_4w8&WY<^SX9Ef}Ki!tHGd32Bg&Zt3m@=}u{IKtQCs zyQGJf?(Xge3F#CVy1QeD|L-~Hyze(4X7Abez1F%`#qD{=wQ?HAuCR@<>{7zdSexTx zYHDKH|9TS6iPkM)g$`x+)Hp&NlIf47F$_F~fXF`~(JGoMhO7mt{6fy z1bQKFYB_?pgXDtjp1~BF#0yb}om=heTFu zl)}hV%NuTMgf)Y#{kJhU>(mq|qplL{BQD>%hIRmGFsZjKV<=?(n&!dw?f7*0|s>c}FVf?e<73Jzzx zgTE;Vpr(;)jmOfy-mH}3?lj?x8Sg=!A8l2;QXJS=r3_5}`}}R7Qud@^Rnya9%1vsR8wG)B+5^$4=dB~WI6*2dtn+C z<^4CExBuRPZI0z?C-*bA(z$b}Z+J{ha@ATJf`#t)?$3>_-%p8xyOvm~WMcfWq9)6Q zxoNbzJGovvHlIEURDTXL=ke_wTnn04!V8#+1r{@4EYE-HY7}jy~oc zGed=d#kkO5oL0My{gQTe`oCDUdR*_9TCC6O7*c^)2q~cVq=`V{EA~iDTD?ZJW1c=& z?(t7!TvWkFlHFBOpBwh1dk0eYbBPHXks}uxgwTAw=C*>(YhY;T@< z-WJ>aS-bO20sLyghett-tagI|TcKaBS)pVG2XYgsNm}I+q*rsTseRet86kz<`{J5a zm1`db|Be+p4bk{}6N7q_P1q<6cJqzx{2ud8iD|DyPB#O=q`gkLG{3FlTIi zoXv(r640JrV<%pCdei;u22A^yJg{e-1MNKLVIB;*BTE}xKd2p4SUc`T-Hqw+{qE|s zM=(p$*t#%`O-A1|qJM$YqsV-@qlzQ1r?$PZ_nl=AUIvglYwQwe{*T0rIbLlSSE!KE z!&im%O~sg;S-*K4QNh-=>Wa*H%Cy2I6eVB&18BJ3j_A{;@}Vop(g9Sws?w-Yk3aT~ z&Dkpdk(RX1Xc&MFI1ET7a@v2*xnjte>%@Uj8nPS^yoD*aowD4ZTtBZaNIYq^aQN6o^d78LaS@qw~&>#%ViU(17f|+vaA|;ef zU$DVVUpv76;GS~G^yJ3WXVKVQKVa_*DIlS-`eW&|^*d-fjk6muH@ToL-EG~c*)u-Q z^5=9O|B)Bs(us1w6+ZJl5{jFPcOwiHOL+O;dgc z$q2V+8<*8yE#GYeU^YZWr2V)W z`c!@F-$EvIKNOe)f|D$r`xqDW>yr?@bzlEQ7xX;D&6vysHW)(d95~!N7Izpuh0z>823m1JX*&hH@O*49^$Qif&WexqgC!r`}pM>ll7y+hgw{}|xA>1XUOWeG@V9N07BgALbwfp5IHtyEds>8hHxdS>eZ{zl2an0Q>;CUXfg70nD zX*Js4)Q+yRSpO)}B9Q$3w2JfLsRcM=a##0zV>b8c9>Jr$b6t6(*LymeZQwn9O(TjJ zo6z0UW1z(A`F{HiX!<43mGS&M$;c{&_ue&LZ;Ss%tUQdMl}b7)4)_gVFLMMa=fvYu zDg@Kr6Q>)WsHKb-Yw^*Lv;AIU9dUrt8}-$su-gb?_hv2Wai_V6V8~_xmB60+?fgiM zv{B{fa!Ld$(1Utj`b#3ie3`o={o{8^R5G{of0aKF2llC-)Xv&SX!gChp5MSfgAq+x zR%bwYSbp%*Szc_f0`QXbL>#c+4HJ9%#iBs$-~Vt!E#Bi1^AVEY{3!V*PmX|}kTNYy zuUZ8(VokWgcT`hBIEhG~@j=OxB2dL|-}2@^Rih=Q(CZBGdEE4wuZ|<9wwEaBDrIzA zZPOZ~3cD*zNyf6@+y0s z-;-K5Gls{c%BL^*at5}xbQ^=(@|pimm+%AQ)Z|Tei_5ikT(E-6Sr)|v0tVsE-#W!+ zfLK7x;Ok)DmUkpwDbN*XRvP!Om~!}R&C6_yx!Lj{Q(pC_^8nQ_Qq+6d>V6R6@Z3fA zju|ylClvuJ6U>O*Ol;QeK2$w47=rELHwq;Ri(y|Sb>9$wN198L13FA2`uU*ohiE+g zK1k?sM??O}=jB$1i0lkDPT6hMA#9Sff3`I3`AYQi-K6Am6Utn^**c^Xsp=K%ITW9f zUs0q#Lov+%$;j~yhJ8LqT=JHfkgcSdnwSq2_6Y4ZhssJ8qw@*Py4-u;>!K;Y8Oe|@ zy&qJ`PMh-iO`Xt)zM<%wkl3gndT9DlLH4Vrk+{h{iJ)7_{knOIF+*j(e#@5mSdk3_ znE;xRCvcSTNvo`>%ZxVLjskFMdy$=$okee^B!AS75cnpp5`VzxIc7J7ymu&Bt+N?J zf4t6q!&iJpM4i~(xDGv6nf7wM90Xm=K#jP-W$Qk?0PX!)2_W~iD7NjBCZ%}b_vUx^76_Sp z)2rpe>F)fA@I!ZQ4hpd7tm-_|ll3!-mpPc0v||DtaJ!~}tbu8s9tJiQQC&rU)Tpj9 z&y8)ncPcuQbYijRs!;#_+4lOswRr>WZdf|EHSJB-@oaTU0c~-41#L8<=gq?2lks3xkmjLHza>C-CyJ4+#qy2cm6@q zl36NBI9l9+UX4YHDKni8Vt~m$>6X7CzJh}XyJM4&!+Vd#>U}E`?P;=4%^kSuPf?lHkN-l7e4On z7H-o#k4rp8*~q?6mX3Y_>C&QT4kVP?5CKz58dVLhV|pUg7>2u`icQMGG6AR}>#e6y z=&x0EFq6oRC?@VeJE1>e6z7JLCyet7Z(4;M7|jfXq1MIt~}7>{f(8I z2}{<@y=Dq|F8)u|W+$lfK(RjUh$@ZQQf9!Hn`sqXi=@?HEL8r)=^fLYi$M{;q1tU= z_lOhYgMdmvKc-6Nh@9EOtXU0%+nfKH2ox*-%jI}3DRM(xg(G$hW`MLt&bfNP>*_qi*F{b#h)N!s%A9taM9+spy3dDa@+UE`*6=A<( z(;S5PDV)!9B;4&H*;{nms}(Molv(Q&S6R@=$+rBBk0>Y<-TSk+T;Q)DPV_V79wJWM z-cS4{`4!ZYP~fmv1n;W4M?}46pJ(_dS|dgG6WYZ0h`M+UOrdy62Ofeyy(wQZyZFO% zWh7$NnUXNQ3K+i4vQLoEkW{tpeJGj>8K5|>^%+LD3fp!T-W5y0%jQqr*e?(;)G8Go zBL@@vgQwCXN1m5bzq&)$kR}Hgy2e#geBws(?@>Tj|3Omzc|`kH(9&()pU3KeZ?yj8 z#SrtW`+iVQxTKUlP}8fYxcJr~?CPkpvn6dp0rMLra&hl(dT;vo5>$dY<1Fv3&$3@D z7ALO0x<93bWnuV27cuFXVH=(yK)K?@ngKvj1>64TYW2lr?6X{Dw)|lB+VL0;x-M!$ zdoH=oCj{WJ1EQ6x_owTuHYS%GK$yp+wOXScJ^a<@%57jOKS`)^`>=JT!`$d(Oo~AM z88Cee4to?gp(P%h>C0hs#uoclkox183aN&uTmRg?*!+$vOr*-ja^N5fBZM3a<9R<3 z7`EvLU|WvFNFV-lIMF%Oi?#nbPQ5&38kmB9U{{$9wmFZs?ElFQ{}IMliz0`u>ewx_3L=Rh-OZ4=98=C3xFA!qLAdiGd%AqrbW4#_7ee*B+yAxdRGr& ziUxEgPSRtA!`O$l>etVxx#XKnabbok@yg>y{VlQVdpM5rD{%{pc$ti_(1?8u7hf0N z@iEt3P037%zACpa`Q`fp#fyn1VFzT@*}xuPB%8mW$jIL4PzZXuozD?Oq!!&yB2yG9 z*+i-etk|O;517JrIPSyJS&l`dO zc95dRbDk)8Q?F{sX06R_k`uN+LME12EA8NP#qDA5O+1;Vm>MLgqt{1J<+MLRL1G0) zr?oN=DYI>~nU(guJ=8MbmiNi1eO~B^vt4_-RmDDr^UX(ln$hAGy+YKqTZIRu0j<~NVi!@jDd6*BVF?6T!8C!EV~ zye=OB2)W)SgP+J?m+`$$f1{h8 zo(|`nHi`)%Sc!z8+jW#x1~?-!bb~-S1&bdXOBAlG0RrklH8_3Fuh?4=9U%f&%u7Qu zC1E!TSa0}7nG)dOP&F%Nga^2ki#^I8h+my`-a`O?!<_@6@OLgG0pcO1=j84eMBW+OQY^hcb5S^bdxYr8hHJF@G$LmgvMnXcnS zJ5}Bdpu&MFwb+$ zB55U$2BPohHRxW%+Hny$BvV-Xep^;*6HZa$+&GS}>!J_E1)G(psnK))68m%OqsTu4 z<={qf=v%q5Fg(e{cL`7^Ff-~17cN(6D3epp*j2?|7rowdM&}MU{e%_y2O54X6iO>) zHV+fKXpZ2Ap1UpMs!J)0x;}ov!cgl zzI>+JIfHNg?Bw1&7pgvdJt+ zda(*kARh`M12E8nM#m?rc;bP zb@{TCLSSg{x%I9L;$EeBJTW?!J*v{SX=0WMpgub#@rA7)1^<%~m0<$#-(K^|sJ=)^ zeW1fUBluUFv{P*REw}i~!1X1Xe z03j?x+{Ye4DV--I*oUSx#Fu0L*b&?5bRAcE0e~t&#QrCB;LBIRn#n+a`_RYqP62z-2Rv=2I9~@X(L12eo zUqde6c_B{BT!HB3XfY{6F}_+g8=whG!AUsQxT2YHJleB|k{YAdEhJs4U+ix5Bsx0QQ{d|q+mhTm zA0sv|j;6XRD~$N3j4*vI=qkk6g~Je`*XfC@u@%mCHaC&@Yr1a;z1%Li1H}p#1!;N0 zfpb8ag#Kv*Bs6lMe+^*a_Pa5&ft0WBt259^$jc)psD+W_w&KTE0`X~aSYbN%3CLI0a$us7<%{$8$OBhsyUV~K^-B7>X#JgW~B^uHH z{Lkw3OPXAe5z#SKKS|ZXf0>Be8ot2gSbF|%IyDFy)kMSo8rRkDBz8asRO(QQuWHeP zVnNHg%STvo0e68Amxig_rdx9zF$SFdw_8@$-t;s5Xno0%DqC`ND|&mT7lLnvRq<~x z>u*VVpq;H)b;&zMofl;N%$Ru=dAsES^gye6t@yOwn}?j|KR;b z%l=~th}-GWEyMW;h5#r{J-JzF#qvERyNI08%YHfA$yeIMv$nf7>zzPb{Euj05@tf6 z1f6ExZS<5;SGiE(Z`}pjKBv0Lr61Y#DMll!aUkc#w&Iq?zcJ+En9TyyN5%bQ)&0bs ztYCrlU6L~aJ`B4TdG!?gv3loyQdo#ZQAxSr-^p3XTDTUPdHiQ2m>S3V58l7Wio|3N z*lBr(Km5m_CK`Ze;eH_bKuq5&9WK=M@}%!UDD9tNl0(Cy1y3+ZwMs)88Z06N>F2XZ zGTZ-tTsmC|+QAwiB#Z5mo&Bz~DYqR@iNxzv;PqIjvxCt?DZ(ytQvYMIEdK*ll(@1) zEGSQf0%yYEUn%e>yUvUv;yluqBBk$MMiM_)d@11m(ow6Qx@a=;f41=nIQXf_q91r# ztsJ?wgXd~M`OR%#tYn=d)!y7fwSL%xlU3Z}Pqd_RZTS;+XZ-yWMwtsZ4g2K=Q=9@8 z9Iev4FOkVJ6&|yJ5u=tEpv`!VP3|d*h@*gvv3v;t=IMV#J|Z+UobNdp@PbH93Qty@ zOVtu8!1g;L{N~ep*jTM{?Xe~seHV1F>2j2g7(Oh?2lNs7E_k#ZJZ6K6845N`CP}ikp%d++B+Gp@$j2*f=v*Uj3c9M^0R0=TG#zVCCQ%Xvc0i0>y6X} z%R=RC>#lEcg>-@84^iEUOK7ak%H%ql98GS9og4%>@a6T(#hnZo8q{pL{OZ%JTT6Nktr2*lEG*5AT94oan$TNtZagfJsn-r3R@ynx(XpGZA+ z7LOi>M;t^3BJo+(wxTYJs)8%vI?V`rGD%qyI|>9(FE;xF`q`3N%*I7MCrE%C6k2meoVG`@a;;}5T1 zY0smKA`%!ziHQX{&PRag$So_x?Du(Cw^5H0bI%<)KABPz$qjv# z)+ZzP;d*W(5_4{=5J@YeAJ+BDa!N|`4{5+NUm7|MYt-{%j=8ln*z^VJ-}Gb1t^QWH zIErIMwCmW#bZ|8G>H~c9%U0y1)}-gjk^^5enR6xMNT`ecTulJ>81?gG*K8dv%-PK| z0+T_5tS|zguknWKPJdbX{qPcTbvdlD8|EQeQcS**&>Ivci(qhy&umbzP?t=JnidU$5DVuNQVHBJO{$ABV4M zt1u0^6v%9SS$uS>P#Dv1Sx&y{Ci|6nJRXlg$402+<7a!yG*PY{Bk!Nqe7Dqy6p15_ z&z)K*KUAc}SgDel2Wx%l(a$PJRJl3Zx6=noBne=K0v=F(?6Ezs@y8{V#zJSPa)&eM zX@>1#xjuA|j?x&Kt|_Sa=^bcabTL9s?JphANfuw-9y;A18dl>ih7OL0!3w)}es6+9 z4r-fw^w7H#4P;v)ud1)L*l@1~1|xE4gcsM@F9zGXjX&n==7J$RDRg7Ae(ppIJ%}3- z{Rq=;Sm-Y6tm``+g4(QQSwt-CC+~(e6X?40NIf&ekS!cMjyvB^WIR|vu#%|QcnZTH z;r4a4T)@ntihx9{AV|yQXht8X$89}w>b4s5u!@iHL{NoMi^hJJB~x+1L?Aj7XR>s$ ziFEF$2b{=#oKdf^D6|B!{as84R!U9RYbHW>#cNdvp<+< z)~*jzrX@-zk`=9H+)H*ac8&io{L6Jw81TM$%Uil%^ng^v4}fIXMS)GI zeUp91kv*$>J{uyVyRZK}7!vr1vip}8qB_}zfcB~6ga$D_s?-(J$TchhtO-7S`c#$D z<+ITm^5A(`dU!z!#m@SKqP5@;wsFoZIH4glP*%Jb*FzAGf8oI4BGymb*ZdFtgDPm9OdmQ&3F^CoOrS7$i7?k@C4wKpF zl=v(Fjz8&$9|zSqE6c|QF3NWMb~^Yn&}NB-O7&wL#Y@lpg2!KzxIbB@ilX~sT^&Z| z`xUuG;6h|%BoLm4z*4#3c))z)eLp6~N_>0`$DrrJcxzk^w9vEC0KhJkv5rN!0+F(s zN%0+X@~3b_nk(5U_EZ{SBwdTPs?&mpUF@E7y0EwqDxe{PlbiH!poeq*9@rBl z-gPs_AL==M6pg=7uA|b5Kq?STQT=#eN2;YFiJe5-s=H8Kv+LGH-h)9HAte8aRln)u zxq;2C=k7-QsTj}d9KXYQiy98il0d&KMVO;i2LQC!vfcL`^|weo=9~V% zcJDw7Q?SX785su7b-$O>=*Vem5JEq!kAe#oZUl3+1uXq|#q0zT;dTQqhPnVIDLL}g zt9R>K@xFl1b+~+i`L;(MDr~n3gWD-7Zqa7nq`yM7pwOFr_c`b0vTmSKMs~V~dN=eL zeWA>7OyA2X zpxtZKak0bqHKfe0#HZKg>bVFTF8TvQph3wetzipUOIxDb|;w#588RsWDPzu=Gys0kEukyH2Ib=NZLSAZ``JSNe81+TOw9lR3pJ653 zKErFz6%9jwCrF&G78M+$PW-Auh zd?|&xZ4xU_W(m((=ITZND89X7cKW~T$}#v|mu^ONuEUF-tT37M`V*{sVWZ8Wa?)dr z1+k1@3)qo%^X8baDQvTU0puI)bic#@ni%Ffg7O`NA=_+&b|F0Z zN9PJ9^tXHy;LB0z9Cf7<2K`m5^T&{WdxcXSGNHjaobE*?Q^rfk$mhn3Xn!GJJ^1X3 zC}5wMWoAGp-yc_M)NIRa6k~vT;W)4N%g9%8rBjA?-3*7J(|p!78j%mfY(Yd()rsvZ zTEg7T36&`i&P+OvU0R8ebrPcv>~u4J<}iH5;inUaDFq+b>=ecKEp6HfZE+U`1N&re z4>FlKWFhV@BmxbKPj>5V8qDHyCuj)bGLegSrz-)av9ogi=1AZ)Bn*-4jtMz5B7VmK zq}$-1NLS2{mcNYdIpX{hOE*orzA$z_qg@m?qk&G?-~JQ2g+UB3f-I=1{3rtemCZN! zs}J0cvSOdiBeTu@`C4^vK?;*@UNAFjEc{Q#7U)b;GDPH5uwmBw^=4SqdYc_j-LpT; zcn7p`8~y?U#`R4~d!%zJWMJ5}S!p7{x#8XjMSnDDDR#mf={2SK>OG5-a@8bh;-FWq zd@E63TniWBO(dy00mc)J{>|0%6Gz$9DWp3H`9_4+Lli^$WBVpaLeO1VRg+t%=dHA2 zIE-bsevckxv8%Qw>V`?T!;GD_tV{FryDZ5dcp~eI7cu~@B+^sUcgaHUwAL1Vbld!6 zo`Jh&gPM957NOrd&dBTMJ%dc1T63W@QckCv0?(gBNkoj?Q;}dh>n|_gwJTwMuGu(S zXKiF5x^>;Bt_(Y`?$vJi2<_p$wbE^xyL?0k?cERghruK(MKZ=$G}Aa=rj;ZgFH4ZIuzUJ9VWs!5CDy7Fy5@S} zTf{sP*j6{&@g7Gm44p(1rNb!v?UN&NRdu;!$pDLunw34N93PjQ$d(8_^Zv-axO$Ka zXr-r1J6bRG4+FK`kXBLc6wm}T?HAs6{SQ5>i`%Ebs6^nOh-6L%xft@X`B?JUO8D)| zf}maQLWsJ!sUgM56!*ps~eo#XA7pzh^AZ*-DTDJhYCHY21X}qFgfPD7id?<9z zJ<`D+cV}_ZPKZ?7BX`ZBYUOdT88%0sz}~sG)R#%x0cmik_YPfh=K5CfPv)uiMhr|K zYb)}!l>ftBiY8@7SxD6}A=n5OsZ)Nc%_%s`4s~$L`$6!zrI_VOH$_bE83L6(_%d3a zK!<`x(-~|;8rQ`sx%R~_Yn+!O@0WMTh%ePmcqnsd&!Nl)^$rgq-p(Mtu_h~jnY3D3 z_{#@FgHK^i(KbXYoD)t{?&(gP?Bo|BV&UL<%WQQM`oX)PbAc6%nEs_KN0DFB^FKNH zboxCKxyu)GL|tXkU>O7mshhItV{yt*t;KLlH~0>U$*>rE|JR(nQSqhcO)2ks>ddfx zGqXt>StPVQk6l)4xb&kN%!82mcXJ6I&t@i=b8$E*&&KzE8|m8hJVH-jV~SOb4P0r2 zeX)nOZ@3BqP={YLKAF7;Iv?bi_ObnpXV5DD_$PVa+`pk9p1eA{EMzB*4`Zsq3WuAP z$$rM8C4U@ASe~G!9{j!8~gdu_l>@=rr zj#$dVM0qG6AHjPHbti3Tm_uEz_rf5JGGmTaAs;EARU*odq9r)kY`r1WW)OEf`5KBH zHJ76#K{6r*7-8k;;!;@%F$iw;_)Q0%$_M#qD|OcaH{q7KgQp2Kk`p%)2zFJ{BRWH| zTDwx9Lp2V4hxpoRx$ZsFxAH^DG5>w)S-zgVK4caWqvIht<{*cB0%s52s)!w$Aiaf> zh!N%tM_8O}A|~ty)rA0~h(_y0G2xK~GEG86y$y@+q{9Q2+*SzHy?YPk3e8%~mo}auO z0iU4~k_e+s7cUx*cpZ~!aiKq!>yp6t+*XH&QA?K;7)(EZT?{wb;r}BZS~dIh*R@z) zizlmVTQMQsN4?YQZd+LNLy9jEwPr*VY6FehMTLX;PW?}%F{$(}<4P+_@?NFHrSN7QV`aN5OT3q+-Pkq~+t0vA2sF9hL zQe2`CBvx|QyX2WyEIVd8_yrO@vcDL62#q9-*zD~CmsAx~%0yu=#oGIxjAkL%q@S0@ zi_ab?H_*QmtjzuTjkam5WQ!_W%FgZfvC1Wbe0#0e_M>jn?4ogqsFO)j8_?{aJ2@zv zi}eq$evTzk1r=fRkuqEcG9$vvAGX)ScP_UMlX5v+8E!~3lurBVBozAJRvN^|C!0JR ze!(Hz^kdTdA*s4vH~Mwb`aSe>UdYN68|%L_lA+ftIXIr>TuO$=Q{>t1yo>kLnwn@z zgkkgT*!ySAb|_@f34)W0T)Ak{Qat`_F&+5jYD@GL#>$Ss;hPX%SZWKgp!3O+T2%<0 z==L>_oo>VGYjL)B5r;c9LIQhNdH7q7Ltf*LnU+$vg{OHPV`dU|7|hKf%Gai62`nEf3;-HoF)nMFFM(r5eNk z6W7@2CuZ18QSg|0+=^%uc?HU!LglqW`-W#li=uXu|LSw`Frl3txX(6P<9olc(GBy* z&33TGkPg2uXZHh_uZuIBn;ja^@_!Vdk{TY^;e4iQmhufpH{Q{4C#_DKki0!!&et0I@Zy`HvJt7q5 zUZI}!w;p)yHh;M9US#7Rs~pUK-fdue_<-*4xqK1f+yNc7MW@I@ zO_$}54!)h2zQJn;&!Y549g-Aa|8(kZ2`}_eNIIcf9Y>>(+#A$YwCcXCq~2{6$4hr! zEQq)+{}L1WBy~xEU*t0R&W2w?7*QyuJ0T7BFkI>C74f|PU16(dqU#AsWRN(@wX4ep z#eGyuqr!xDE30N2jdwbF4e(a-Fg_VPc7n}b@=a}M1h9qVXOp?dTNo7TV)BZ zX-O3|xrb5tjHWR|Ietemj@6<>8_(pLfHP1*1SNFZrP!Yq+*gI zvNSH@EqFAE;aE~}lE4oyz}j@Q53d8`H(Ir%oMKi~8^aLwIBXhjvLqICjoVRuHkMI? z({5qKusnHlrp-mQi5jmn=su_aZuURLM6#z--`3FJX)A`4?9~HuDxvZ@PqE42QOF}T zS;2m#-<4=MahSD2(T;!B)q}lr1$`pLd|6!x7G1O|g5nj7VcsMEBbv#$OTM#nn2HC9 zPVsg-9K*~-mQOQ;C>>j+15pTLznCs7uHlA@I;exuRzxte@spJNdta4Dn_IsW$NKl| zTJ>6x0BPbAHgyQv=Hwj8Idx36ey5U14>eEc1|m8EJz$wr(4yYhags8F?}|(+q|_u& zG;mt<&wk8ey;rP>p%;XvPCotim@5hJ?3L3})z(JXP1wK-f6>}pR!f75mrZM6<}Pn& zIV#5Vk7fGcj#`4v%7%_Dx*o>FyFPy&=JI6*$<|?rx9WSArMXqwN%waW+y*oQMXR&@5|49UMcy(T#02JW2x3zq9sePC#wSyK{MM7Ai(;i1>Iu0wc|7 zRb3Mg{3sSIzv`-q_r*wpv6h_8(8yi9Ld*|=`N+#%NpRfGN8vr&5S^drkk0;;V`Q}u zDfjCCd^Jgn>+Xi@!=%EVeaChsH-~F7!=f4&zM{8t8ekyPeKSxC6{Cy^YA~2B3U3{2 z;a!2UYJ6eD_J}6pRtHD@h=^((W_L!5YF$ncj;a;8uQ&eNQXM6%(}_S! zwc0$YZ{?f*OHU#bCj-(?rg|F7k*42dJKY*oW!PRO<4KJqKy)*$Z;zN5i=6}h!;d5H zP#3E>FB(cm6)!_m3~0$TtO|_HWCnvyr}tKLH++P{7F5O-(rzDN%`a*<5F@+!F$aDZ zB+KKQOcmpZjL(p`tr)Zq3I{80H+e*Lx7ue54u!=lgo_uS$p|F-1tuiXuY37GY?MBo ziT2)ZcvRpV_cb<8JP>|;cA@ns!l^W)KvDR__c;nJEJWW+#lgh|dXdhWgKpKC>S6RF zWp3fW<+eQhYWttY71#4NYwI)@wbPL1gROsfc6zq;isxISdtDo#)#2^jqfroK(2J$% zQDa4|=rVazc(|!wvIjV?uTA{7_D$HHfRGc!t@B|k(^a*!K~WZK`o}Nu_caPS-EMY+ z$>#x$tuyB|w9gHO?OV=u3v`>S5+Gt8J9Mj`qvl&ineW6Koh*dDkyi#N!crUdrp7;5 z!=*`iMhrFuNYR9M-i~eW09i9F!aZQtWx$epq>y)=-KwCw+0=LT|A>98k{1_wue-tX zwtA1SvkF<0i@RF=AomZInXyd+N7!$82(Hd*Tlx=`xdNiInc!{s^otC3ED+I3-~7_8 zJHm|ZS%Jd6_8C{FK8y8wUZJ^6in_2#ZpwkO$tNskCf|$KYN4Y`^G+dzmQ+@QUwNo# zm-}3LbtGz=Fcq_^(h(cq`IxqHh5JTygR)ePpQzn!dk-zH`MZ$hP)2za`cK?b9Rt^8 zcq>#&W&Ew4kVS*JK<5$iIf%zT$?c!x_jUmOL?Hj#=x;IG#<2DLFK!MX9o3nV*tr_i zHiHyF;xkmS(^;k!RzZt^LB0X7-AjyIPzy4DCl`@i*~VQ0ePwaZC#%Y{l}4@T9A)c& zVn_t%^uxZY$^W|AO#58^okvL}FvPy{Ya3ufM&1Zv1Ng0K*b8E7e8S z^}UxHy%)2EOXNc-!@nfkwYD+I?x!TrFN^%|&xMi)1l~3={HFLc{2q^u%`pGL4vAXm z_nVJw6<+nBu=)qTsdqE#7&sI@Kkth!w(R`wxi%^$hum{cicr;Wo(}g4u8`|(aUcjH zzoJ=oe|GVBu*d$>7`dCAZWq7QRQp>=?$zV5Fy)2)2D?v$`2z(x3#k+Pw}Z8?CQ54Z6!&v5Ej!{>!sG*Wg)sm+L!a3G}j?2hD~zM$&E>b6Sa)cwLun_XOx zh3&N(aR*~{t%tmZd$9hXQlIp{aX#RPyo1>%{-Vw*A|ie#NfH#=K!~J2WY2A&1B=n~ zh-%!DuE&O6#oWktW*f!s!dXH&ZND5{itcE9FQAhCNQp!1~= z*~Q2KYS5*){}(z;U1N~har^m9ppV~(cnBgS4_@%fvH`#j0E2D=;8fi1tFF@85;42 zfm01LQlIAKJ!zLeuit3$LAS8)#aV8#r5i<%Lyidb^?{D~N4AxVkB<(f*ZM_x9!~4q z^jz37K=~~eu$PhYev!z#a`8uG{Yq^^9amI8-x_0?b6jLurzqE{UWjlVnh&v=J7A$2+!q4Q5 z3*_rs1bJF_8za(O|*YTmLRpIEg%p!dz3;wt)bF5UKs2i=fr z#n_8ID!<$v6JAaFbs^aG5q~{t3~*3q5m*ZkLWOMt4J#iF&fW&HNIZ7WmoJ*+`6Pp# zne`e}KVyJBJKj3I?!lMabpGMiIrrX>4l?_f6?4KY7<4AR2Fp^1cD|g0)z(OWx`4DV zLx0I{KI&Bm+E;czzflFpI+7w;h2c?RvI$bN{ zL03c6^Q7Jv;(v6SCdY~r^#RQls3tyCxmw=O9x2ZDMF4sb9uZc<&`H5*>ywH2uUGid z!k*J#`gbqaMOfB6olz3qZ)EF;xYicrhFaH#x>4Ti1Wxoxqazvo3_2Fb4D6-m`Q7A>D-C^v~2f1#>Vve)>xhde^ zdB|(R4%xcd?_n`K&SS?oS+%qVs$Z@YsBVZkfH%&B9Q>8hW5*cEJl76MBlG_%6F}7b z9c-&AgL5utiO@i;B*(XgBLDGxPxQ>Fk|9Yrs|mrm3SEUlrqTyVke=sI?)IlPFTI=w zjdgKYj+d`(L~a@)*-pYRk|%utrb!X1j2EgJ@Uojd#b_^Qu^Gepu$2*_W2eROpKUg} z=g<-~F^4#xq(LBkC(eqRmUoY%!`ifZcTH*)_5!G=gN{g^Cjh2i2YdHVeE&t+*Suu) zwf6fs(~FF<^^Q8?rK~C`+|G)FR_#hX7k5(|#M(OIWK$UzaLf4y`VkBdwB0-|R{vdW zlri7s21%4EY`l{?>z7rfE-@t5KBL^(=bJ#JR zyG;#urP_LCx0sik?Z{vy-LzuGTz!G+b!tHh5SA9Jylwk6(O;+~CDS-LN_=7>wLXPY zt@op&>{lL*>gcS_m7YrUUC?stXE~AKojX@dy&INKF~}!F(L`gv8?A%0YzAU43;#9S zF+=aZ)p;%4Pc^K=RSyyzE|muDWc*2v4)1Koo%x9CP!06v(b3S}9F&qFf=_?~V-2~^ z-ngQl3l&xY6VQ~|E`;2Bl}67`!rxJako3!$h5+pJT>n{?T*3~vd!MSEAA42=4Y`z+ zm5A*LU#U)Y)|Oms-qTVS00|QR>H2ZTr5aXA=e&mCgG3w>X*HaP~iV2CJ|lQtiqo zGGXx=`}AO3#CvbMLv}@~2tcY)>AY5^am~W>_gknmWc7ottnZ}TbtMDJ-Y0hcHcDC< zsw7lbD{OS?KfqVDoBNq>q`QyH?(g#IfD{wnhC^JFmbju*)^7az?-QDi-G=Q=j{BKz zqf!Ga)(ahJL}({6LQ~P7c>dtec1W`ZKD{vFQ27ve88xEy-Ppefol*1 z+NT$qq6?_HQEm!~WZ2*Mrc!c zw{VJ1N35+VpO?`jMk})9Y`W01jgHV)k9la{32Tbddwvm9Wr73~p>Jb{;yp;6vq9y% z|Kl4K`+szOby!u~`ZXoph@`OT5>UEZy1PLE>F!Pm>F$#5F6oez?(S~bboedrJs!`! zzx(~g!?X8ZYpyllcg`{27~@W~14VUo>Si_`Cu|{^mcMc7;yKlYOM1CpFC|Jbl1{bM z4`&)t`qR#!dK61KcU=N`&rB+k(C3S|DT4wvA?ZZ77~7~7Lvz8cDK!ZZNwTP0O~e?h z-H6wcdtog-yE5lbv!O=oU7sb_kOeGSNQA$29vxEm)!NvKefa>`Zn028JIkZj(IIztMqUws!yu-7T)= z=<}_74i)l*ed6fq8>xgh69Ws7PjHf}+36JQGFt$Tu3FOB}Tlw!Cg9+BT(U_aA{WU zw(pe8>>SZMnfGS7!*|(m{cGJtcMInM3A@AI=V1F;f_HF{WIwU1E$$Sh&xm8SC|8h$ zD&lp88-L1IWMuG>PwnjkU}1K;NUOPHU(fT7o^pLPhRt$u5M_21Wce;yR&jk&jxQQW zFGCwHID*V7ASkZ{xK5N}7%;0)QvLc3ms)_@x;FwbSv zqi4@c6&Eg>ec`hYgm?%En7Uv4(2*jEJjlT&3NAOx&d{;GNo#UJrMuennYJ}nt&XHy zJiZmi5*#+@qZ_nQ`HSfAJoq>IjZ=l&j+C-MaL6bf8DH3kV}Dz;qtxXTgp4pZ5N3a1t!HViVl<7 zwT{a>V=|~Tatpg6n0XJBhW>Plw(!}7OU#!^rPV09czKpPjMJC5OD&|2;>_z_oWYk)e!Lb8xwr;E5 zT;03>e4-hbUPzT>qkZV+yV=QsPgz4Y*Zm!|nBHKCWK`^PWyN zjMD7(AyYRr1Bv&j`%F;%fkpgG_?It!H%_d$ULrK}q9I1TV_}PRQY)S}uf^@XPHwo! zGj|!jL51-bn=lVKG2@f-wo_TiQJZoCZ6!UUiy-oL@eLiO863S>%r<$fEBc;squo{P zlP?n@VXK0XdTxaU0&T%D!`hSXkJPzg;^e)(y*^U}#pGLk#BwT;LSE4Od|u!^4A#Ib zK@rN9hw5tPZdyJd$?U}_8rDKx3Qp+%cjEUia;ks-3GxBL&Io__@^%rSB91n$8bb{D zvlE?Q0*E#m#-${`uE9kEjrfLapd6qz_u3d z%DB2>1LmUTd$!I8kUPn*Rg>8~QW%OA`G9|b3sdCe>pg*kS(A<3tf{-J&cpJdN2rAdDUh?Bmd}k>s zzSk*i%}Tid&~SR2hZSXa2TFveOuIJSC0MuJqXrx_gTVJ4SSPAQt8XPE8TINVpHcC6 z2csfzITaT={TVUra`ZgTIX`N9mQD~2uj2_bw%*>lT=g~qrDoGwRaU%uEzRJm&5NU& zQ4^kCDaKai-f)5f!EX|`cD^aEPtKuStq0v{5ia`4BB^a9b`vbMocjSiz?U0u>L=VB zkC3~jYRG%3iW)|M4#qg}W{pxE7w$&@y4l0~zDL3t($jSjZ1Yt%;_A2W?FJ#J;$?JW zoW&V6OMYy{3}zTSHtb%c*PN(4Da#$JqGg{rj&9*38&SH^7uY+GJn&fqH+91yUNb-*RxfJ43 zZ86Hs6j5SvhJo%q0D{7Mh%(fN?dKg#R|F=T#2|j>{uJvJrxi@=7ySrJ#Jhc;1>;;Vzh(P zAaF|GOu~`@C=SqOe0+9K4{iZZ4CP8S%v77rtKNYp<#H`k>nC!IM9T|nfPgnq(`OQdBv0g+0V~hPMb#$bq~dBP@xW=# zcMTt5G=b6RVZ?Hx^u2w*3Ti_AaVlb*H0#PrOkQK)78wX^_2yl+GZbV^189WUh&lOm z3@J!b*^^iUrxD%4j|Jt(?VY?&n9~Qa02BDD9tLLfQw7YCz2GxO4xB? zvcqg$`d&Mmcy#jyd-mM|+c}5L8U!Aj2ge5LeJ(Gv&+eq$Pr+?zMm%?f;B{L?;MC%i zNW=?%MX&jW`0)W++Dt7fh{y{!Ci7i!P5Xx@N>^Azee*Suim&2@g8&iT6rqttC)gL?Yl9DuS~ZTc%Ziz;EqGs-)o@2-~s-QZ0fojT3d8iXM+TGbI{`mcNL0)#a=xXJ5NU8l)0gjf94; zKTv0<#yP-Ox0`wqAIBueCAMChZA{K1jiJ?;eC zf`UXd>-3*E>0hq0_Pzt2ml40-^0()t(ZS#DNO@uSDTVnNPabY8A8M9e86M zW1>NiACIq>ijjSHaU1_Mga}I*R*xDQy_>zfCA+kak%~0SinV!mFq^XU-k4_%mCHy-UgzcJO6v7@qYzD~ zk#^X=o?9mai<<%M=Wjp2P?nKceo^<;2u@|S+k)R`t@I!C8A+)@ue#bHZO5`Zw4}Hh ztN8UX>@c&e0j29!ia}>~Ym<*(+oy7wJplGd&xjI2S9?5t0 z^xSJIO^Om>vLLoS&zB0{f9jj>Uq_PISb+q(j4u=D&#bhv6&rwFAC#U-3;61Cjhau2 zI82G)5d&l{x2|Y`kJ^pOE>{W()$R>e6Aj+08<4iEh_f>@Vi@U(DKep8+Veec+^F^cSB4vM(m6V?dmp^Kh3}5zSag(;mCOWSc`>u)J|YGURwp z1feyaZlEZ5Br-O`Df zaMqwFT5S(v<4e&{Qi^H{a=k-{{Q*c%CKeWcvm@c@U1*4mmPxHQ*Ur07N+NO}P;bgg z9i6nC{yLn(hFNU~u0CW(UUBF|qEhpxQYQ&bwqsFC=%5S8APFbAn`vK#2*2t@IOlL# zoBo`(bM^se0Mt{z*k;5JKX9syTK2j{CxzX-dUX-18w9c3SW3;{ zy7j!Fq*N_$b1TiwPm981ZjY@yoYB_{b2ypMFkrNtYa}9aN+pTaKna)`maKiD=cUlT zF9@j1gUG?uI)PE_F^) zR1O6L8M?1TK&vOg=d&8LVzB(kcd}K)fdWnMeRM5=MoQY*{(`j8Ckb)3i5z<3cY)4S z?CdzFsuWj`gPm-XB5OMKfrOBwEH^UxfhW_ptMEZ_i>F>V4ZU~Zu}gZYXv-HbofYjs z*%BcVGbLZ$s<1QOwLaMiZlwz>Cu1AEr(4ITRi=F;RNFAs$&pb>Ynis0Jy=!~re=hb@8WH%*_&OWY4qGjnGf()#UA zxVth_Ty<)l`Sf^*QjB8Q=4_>cFdmCdCHQDncLH^%3(GpXRIfpoYG@zB1u7|0cBKzz#+g3-cuR3V!?;LMvpeDkC&eW`aZo5$o|{W4#ni%X zHeJlnAl-tYOIvnHxojKqouSa0=_7|Hcs)om!Lc=~Br*WyOAh;Hvj)>$yxJPQoi+mh zjynsd27=GZ8AFT9kME6CmgGpf?ox>ilN=i0uo8`$gtsFgUK3pAFS-KcjC~-_&3qK7 z=~Qm_49sqD4|zG5+zjP3O=^8uVt*PX>O(r$6;}FQ`{~L;l#C$lrtt;+7$Z!k=!<@D>leYuCzXHFla*nb9g<+l$1U%wA)-tqdrh)ljHt<7{WH~y@|zo?Y=#=mZaFl0{37Lyt2#QYfd-y zfrhZnKAYp_>BA~3W%{P`-ov$~nF*Z_bB9MiEGXX3mg_)|c2a_tU?n#xiRNXtz*k^v zfJ$~%bwD8|T00_V1>pMl9A+dZ?q45x6MTA)g@xLgV^>q^vugyqbqQE2igojXM?&_jdQxu(ni0_RYtAi>dzlwyC8nqc&!6V7o=ek!`t`^# z0iQ+#t$tWEbBaOQpTUg5B~tI^vi4D4i$(0(uU|c?OC`;o8K{g7{@< zh7pAyNxjE?O$bpJ(&Ax>2du?Kghq-JB;h3umVK(Giwvs05v}SeY@=kgHcoR=7tw~{ z?nx+&61`Y05E*Wo#NM9#KOIs4gy8ClVQjro*5G$GbXiU(4?o(e$M#WJfh~f(=<*90 zLL$;=4}27?O%Iq?PJBoSJl|DE%%4&A4sZW+;Vl*p`5JTY`^gW)mxDr{68Lr`kOUgj zylJFiiw(A$qUa$Q6E((i@uv>E2ME9L86R0k;z*s6F%oJiR^r6Wp+#eUO*Sgu%VWLY z8Hj&`gkAzuaU}S+kB?BnAf_6ku-ZyMg(3}5pVkB2x!ne7wG;!`Ujhsi69X4l5Ol4Z z%B{5BhqgGiTXyfRZ?9cPH`xw5z8~LY?25C`^vNljY2KhoaA*f%dP)zqXnQfp6tWK4 zZNGFX<3U^Tr4f_sInEdsP`vu9}{kYgqxYNEj`%6tV@@;jWj?GRiewALbL0QN(a&$wk zRy_W6TS4eE&|`XDb8oEG>Gg(q^`FNClI+D zHwe6!6Onn@Zd7@q!}t@`DoEm9^}f5hDp8*C7x3k4OkeIiIrf7s=BwYM63fN_Asne2 zqGf2~QuQ{?wx=Be@z3?K7-VANtxECb;Ee&x)2n8Ka);A*4Wl)vE{gY)+n<Bddcjjv@y5j9i3>sY<_ zop_3B+(NL7T7}o^BEKjU#KHo0WDVjma>^X6?U0{J&@C zuSo*_WG#`d?GVdeK@FbQ`eC>gxLqE5{C*mM=r0o<#u-M=IA}!blS0Z={ELr_KrKrc z(80A|In(Iwy1pqHZt3)?-q0UCG%O4TA)$aEB|Vu=Lqh`}tpbspKQ!(FslXwKtYW}D zeLanU7Ud!ULchAk9$kx3n3R+>PqU7;KzCK%2hu{4fVFx-#GltD2T7jk% zJC%L$-6Q-iLG}%qkJWpC!0xZ?MTcvk&!I#01oc8u|egQq-n-00%&8%4eHrjvQ^j|K&R!b)D1<%V~Wxqf| zlLIBmG_Sl;UKR%8+O44(V73|Y|A3=T4mAV&5@8gN-{kZJFX*v_dh8*?K6NztM&pu* z3bbPI=-Eb(iiU=I(B$&vAZwQ(%&GPFu>X;J`Ik4@oPhKLR+{V?k0pN(#kX3xLpV21 zCx6#fHX_&VExN%e->G%?YIa-$rD~uQ5mCL-?*POfhx{vohy5#pr`E8@F!LK3!(%~` z$#%gMzZ~$B;IV#nlC*yM{(FOEfUcmlMdbf-?O#_)V~39m8?%p<)^!<~3Q#JnZoR6s z{4-b7+%O|kH?2oz+Fo|FVM!O)j>td#Zff2xA?5>OHvf3UI7XP3r;ZC1`;xMM->PFi z&}Wz`&C)qn`VfmRTTOj;Lz)-#IbJW{M*Y*DUuMCl6`elZ!8>z!rr5vm)-|`C9Wv)t zE5GsZ$T&_Jg7w{4iM5fca;DMFeUlDcIZg~_u(<3^Juk?RY@)38yiwRNLzjTCe}hy?ig>e5(mhKsyFZ9Y&m-Cx1PwDbcSsSA~4@z;{PmBx|%;5&MR{)(<#PpekKu7C?(G^eA zql;xhN&$O?Qs;s4p)IY^V$+`s{hh&sA(K}%NMG|98b2KiAcC|0?(5G&b*=`&b3Ti7 zJ*y~9fO-+H(rYZpXB0B2VZ&+ch&{5syH^UkLaX-2jE_a;z1l}_QF#1cJmZc^+8QOO zxGSpO1=w@D$b8pP8_D0jv?vbcCHdEQ&>?44@$vr*&BlvV z-_sXU(h8QJ@-xZ>yv2t;zf1U=hUHS5=wHA8%Myx6I;>qZy!+(JM2%yTo%fF`sDPQP?T80)}0qsUZJ9;an)k6(@!4aulo$xLVRNGD4%h3;fEgWqZ$MChp zbDfrY&v%$XSHhR}$$^fdBq3nW$%ecrnEHPmSbUj7Aler#VoPVb-C`NM%-*`}w&AvT?>X|o;9yMJ z5h17TMgZy?Xn&d9am!PVSNee=#w4Rc!I#kKY%44)5Z1n*4eeo_-HDMu_PmE%=U6K# z`I${qyVAn&6S~NFk-q>9^5&@VYM*Yvjt!E;gwnM4`0SS8Oaf&%vU@-7E@B45UW0a z!w$iYdFiTM!&Brbt;ZjW4gM%!`Pg+0qbGHC+BH)16zGHF+p=2+9{tMB-%uvU+J|l> zW;)~cOtZblSeJBD&*E5P|1WFfpn&o4{Gv42CHrSN`BmAo3whD2f7j66jEhUytw2x5 ztyQdALTS)K z7yO>MdHX>rrEr!6%n{!hxkw^KXPrky<-7Th8=y!-{MNU z)r>54N@uj3Wjt98^Airfn9L*`1BH*wULT&_Qsb8~37}ZvZ%-iUi9^dl^pM1fDk>_F ztLO~xFYA40)i3R4^HIOw=vbQ88~@q}1*p;247I5TtC;?=QVa@p3=`buo=g?FiV(J2 z76z*T#lisB>y7&U4817VXV)E)a*zN%Iher|S~m^69TNNQ_KxM6F!)NQMO#rQ2P196 zmeKglswcU5nyma?ez?bZ_^au1s`=wI!iy)a!>uT8)RzJ0ul{jMT0x&vQHw9$b_4CE z&AqH&F$&`ER}(eKAXb%^9&m@w>nmiNWLhTnOubW5a#^|SDBc~!%#w-d#(-mj7no97 zaR#eXbefDzu%TWTox0ctND{^6t#V{HhMYC$s(>DtYoelFzC`>``U`qC?qIAvjoU|p z+e)-A=WrqZ_Uh>fh&HJMj}(}p-6m%^i@Otfl-SgCeLF034`{@eY6#?vG4oL;&Ptv1 z41>{fZg@zi^jw6MQowOL~e*s z)T;UIDdxlow8C0bhk?vOE>8v&!6J$ZXZW;jU7GpVP(~@{vY!k{N2*1CZt!P=5$F>D z!aot?Xq2D-e5{QM(~|q*y7e*L`KMj*ObnNQ%>3fcJuSjJQ$-6Hlgk2PDF3Hg5P zFHB}I-v*si@Tn4W?q<9bp&d(pb!+{xslU*1mOlUzb>a5mvAX|!ynyE`(6vDe0{aQQ ztz5n`GBe)D6~r7R(<;z^t>=EBUA;JR{4MvHj)pYJc4FnY>e<}7r|f?MZeP@2hq>2< z%+fFa%&LhP3={$q8Q84T+IRzyqtM)|_}1ESSjviGv1>bzuRve%=ZXKk5J3CF3?^(0 ziS?^$#-Gm}EMV|Sal^(+jvJgPWf#K743HL^EbFos{_&lEUgOtRPA>+$*#rY)&OZjZ zCMl9dA~ag}xrgW`RU9X-olEVL*aF))19n}yL`4>St9*YClaPX}*nwQ^|A-9#`y5n` z0F1Ah^4mPV=^qr0LMo5enP;fOdybNTv4LeN`HF42d`Qscg+c)(0;!I$&N#?)?h6w^ z@zBPf;r#c}1)dcE-=Fan=tlocQu!O01CqvaGogxBH?ffFP#GF7SPGQTgDDUhjxt8S zp4_+ljRT2K3cvmL|DqXft6$??;?=+UlacJ$1~QSYW9zF$3`vfWnrbq;Glp=f#sRwK z5VbA;zf0LPVF12jcjGf{{pZ0GaUdBFw^nDv*;nXDYQHI04cV_8Gp!uE*RVLZM-0fk z!p)M%-+H+rte|FZWd7C@C0UJb`aL2M$ld>rfKx%qfy@-2`~7hn*T=IgSrLP#;7eMr`5vFD)bb4n@^TnTQ{Wk#Q0A8HE_}t$op|>!aK@gm; z`R{UHM87>Ja_7v95X~2;51sC0&CJzM!Nzh%)$gE5RtgPqcwXb0Nq=Lds|D9TPV?Ws%Y_J-gkxOBTh8A;?OTEk zDV7B@*@liG(5b9Kg<@zCJVs{fG(MX4`SZgI3xSbP6-PdoGz8n$FK%=S zCALU48&UEJE0BGs&(g#gmahnBP5QDk2JKt|p>sr;!h1AvS=%cugSE{O0F3-wN*_&2ed}|KuY?!jW*Rguo9JV!;(0zGlQ{?#o)Gm{ z<4ercvOdc{?{%O2poex|fVBmP@|v19ZEbD!pKtbUFE!au)R~8YIfoJyC z-V&TZ+&IdW9C6OjD5*mjDfmV$K191QiUJQ0Pf=T&)W|4rB2Nluf2Fk<8IMQg>qky; zXg|6kEiqvx7(oA=k5YzzT=PE-yG*p>A$;|qYoP*y47`A5UNZ3e_3h2Ub8LL& zqmk%x)pHUTY9!t~Uu=CUB;;@rr=_FI{rVNTfP0`HgUskGizP%V7!%8w% z6@8=*B13Tp7;P*!+J|8E%zK!qG(hZ!LYC6h+=G@O5Bq*OhAZKqQZB zJ73dVyJ_6{PVfmunCS=E^SN<*MRjFzgI?70=-}XTgmN!Y7S-}tv;mWkmjL%AKE?%ts>WC9oJaGu!b zQO?6Yg07;(euI}ZGSF4VewJ2}dG5R0YMQ!WEwhq8SbzDShWh)zj=BJ!hlKX71&e?c z?3oXg<-ccSj1+)Jev8hA?}cA5y&k}X&8H~pT%(p(+I@E}yQWwgi5ZaAX`_eS*HoK zMKYlAf}_a6zMnvsjcbGJ?=D>q?M$KS^S-N8doHrM9v(Ls4yUkQq@O;PNvE(Ve9g%r zXJS&^*x1O`axOqsPi?{{k5k16PHd3n&zB{Lg30*gmnQEJXS1fPs!4wcux++KU(vhn zN$JIyPUcIA)K)}=dVMX4CA-zWvY_&BV9*OgV-7-?d>6j>jRnQhU#9Qzxprxaz((l1 zf!{r56UWCcC#EXm3jB+RK5=IZgRI!h;F8XOyITYNS<&3#(c&mD(tb>0B2|H0W{g2! zWD)ERz{O5w(1(^N8~x0~K#W?4JlJigPIr(a)RpI~k)0855lM&tj0BmJB!qAE(%4+yW)F~%~8&+?Wx14`nOE-;AX zot<`1?FT~9@g-~n3w7o|#Zo8J1HfUcCy7Q}BnZ{RC}(RGZ|M6-M^EP~F(4Td_9c!8 z)VJZ*BWWB%D;>1@-A-`%y<|sRV!Q;aVK_uIo4$Z|$X)~ssO_Sfj+}~743LnaBbc#{ z!lIHLeC@nM0<_g7SXC{PziK~OHlW~w{ivu5OI34Pc+Gf_>!j_fk*DobQD@K>F#vF= z#VZf>_s0=GuECw9vO~CZis^r}HaWQJ@G@o{)lgI{e!c>(4n3I@|8{#$DJC{1Pptwb zlUUH3emf6m+QHb(?c3JwZV}CJ#@h$+@OC1EWfm1x(YD-7en#W<3hUvN>5CaH>mg_~ z9*|Ojtjf{;O?W+o+9s~9O=j3m=jIZx zuoLKtzTX1n2$Jt=r;MHP{WW5<{B1iF zzQ^Z&V*ht9Nzw)JRmu3;v!v?;T!+E0eFVh|qY9{TW)WF_A|fG;|5WLqmE3Uel@j`> zwt8!v%6WpT{Q!M)WytHT-R34+C!y>7FD&Kt|?du1T0RSsVfd z%9O=?M;edKqERHu`(qRNVOE)PGUHgxV(=;=9;aRI5{yuVwPaf4SQgvst!&Zh-a2l> zp&1#wvok?-CL9Wyq6(n3Ml@eKnbZ?jC?^s!cWA(1zuNWk(|oHtdw+}t1yO8FjN!>r z6O-45_;lbxi&12$PJ?KKp9fu_ck{&sB1OiapaTUN{nL_629pFVs$}8xg?VtLkH=I5 zxA#fQ8{HOlKAn1L)+So{R}$a&K-G=>hW((Xs~Ih$-Y~ogVsdq(_Y}eHjD`cvQiiX^ zzfMdr;qkhK)qCHA@t9op1oTqRZuZudE}kDwhjuf)8Tv@+_&1?acFI8 zy@o&C0lIodyX88dVy3BGEr8vboR$`m3H&)>ja;Kr-#6^r2^Xi=MnKEMN%H~0iJsRr zV+>1HgK{1hZhejUrfG)s(`Lz~kNWzu zhR$fU&9mD3Ctp%`Seyp?NPstY7y^0>j+Dqu>B1WVc8jTVCi9s$A+meq`+XO+DBR8` zBF)tf(iG(6{f}BZL)5oiNAb9alN=^YBRA;{sA zjBCv294xnhE*U9vzFf2o@@7rYg<*FuirvMO1Rj$^;Zl=Ly;<6hUSWbxq5I;sH^hnr| zi5K7IQqznSCIB2pg&JK+KKYtAf zO!&%R*V(15GCXS{$8sb@a%l!$&7*!bq=3&I`H)UwDW8I=Ss)MnM1s;D(RXz%6MvrY+jVuT`Yp|Gpw#}{XSs3i7TEM&U?8ZB~e84v(egsE^RtI zHCW6u)hGw{RgW68WuRb0Ii##it6?zWH6S&ZP9ZEv>L}b?=qc{)b%YgT1qejfTP#cpBHNmK@pj1Uu(VXy!R8Q;h6)Wn`pO^t_3{eyiJa4*WYC zd_tzaS;S}q0DXF#wCC1Ptc~>XPV!i$y zd-HUP*Qb0m70>5rLSKy+P$u=wn6UXSs*Qm1u4*iN_l8v`IF>Q1C>yi=Dk5}>JP>%M zT&Ezn58Z@qxXCpFECCrSs%HRFkR={Y_$8V;lEF(ymaW4E4TCw%VdYc5hNEYZ_=`9o zjnkgo(u9~30;L!zx-FjXK8$jj+Ee(}?t1wuiXF#LegQP?b3lYcL6J8-CNkvLJ8fr@ zb{x&nr)LGIIx(31xqW1u}2XOBr;@)a~s=Z>l2yd!%7E#hUZIUzIlLK*8mZr*{ANkrBP%9AwFyC3uSmKV3KRVryy{35V9#Wp9kc z^=IpivXm{L&Lwo*t=qZ@je&fU#L-M_e``yXo#53b&^HEX1-yy$8%yQ0tWNP>Za9s~ zv(tGHx!3!lzDS7EfvCJHs^_WVZGkvzLl@RF2V9rE?tiskPzJ3 zxdUGc#imN`y(dKpKIK7oqZ8ayV2;NRX&A|*QfM_p;EgFGT)pA^DsNE+cT{&XV1Y$4iBG&Wv<&00dNGO0)5HvmGwX4 zKQ{Eg66TwAii|!iH&$WTgYD3ZYJO>;N=T;9FHn zW{aG#5lT4icO*|C^&bWH#r(>nxgt6qM`nyY{^py8hH6xkD#Io3b&CTcj9EnB8!dUbgf&@Q3-AR3!s;&;Eb zSmoL9csJ_pCpv>?;wKXYNQe3F9ilR4yBlI%OgwJXnoe$?_q9W|`@bb~drZ6E$vJH{ zscp#05*W^$C^ga4O!2r3p)%YHW{EE-&D9(>ecoYjk3RK2b!Q^LkL1N%Lmnd*Uy25r zwy$OdYpr6`biFhb;kvac`zK{m1@;8(PdQi)bY0fpN7eGZbvoq$F}B=7z2%|0S^epg z@WJt^F_iSekoJnl*A~jzZo7+Ai7>59uH%ky^$uqALMlb_@o43Od z5fv=I_HxiGXX>>={@Peez0&8etHJuzeM;1Z2PPK|B4N^$q+<^7+v*=J+1gTjd0ygH zCCFdvh{Y9|lGA@C0g3L-gZ>5|3DYGr;uyjCh5h0oLHlAU zHqv-WQ1mU3Z}+L~c9f&d*pF9QrDTK^4{3N>LV@E}XOB@^8m}{~rqbk^lw}(^Wp@T8 z>zTY5MQxsHncsj+6cGy=Cr*IwM{jg_al0yY!`=Gsi@{Sp)ppOR$I$&5nW4NLVu`PF zC(9~GmMbl$Z*v;O83eGb_r^MzEbmz)2!rj;HgwYL6}})MO~ZWh7`4I_T{Q$rAl5Jn zDIJoT#Fs@|^FFbkU9k(07N+-IGoR(9b2QR#;v;&IvamOcOEksPsfrWz|M0#I@`mt@ z*QT27z0%rZZ@;UPH$UV;=h89wn2VIe9h2$m>U!pmQ}(=*;dVf?4~6pfzEZ2fs*KP_ zRLxPP4pW6nFm4;dAEW;#Im`J0x|>u&e}&f*-QDwzch*8EZac7@^VLStp-#a&r^0#u3u9_Sp>zLPJi_ zC?Y~r<;VwI^U3UF+yYOjaM74H1JOv;GR}x5|DXsOTUW>(W{E5&Wnp&(R*4UG_SZ5s zf_so20kXJsI71?+N0-sD`#2NZ<=m$+;|6ed@Vq8_+n-d|@HJi#2XZTn;wGb^w3-0I zyS@X%b$iV@eGw*l%T9|t=nZgN%qGk+pgfI*OQ^iKGDq}!|7^W^o{ZLLe zIK>i^&;^9CM|=N{R@h9Fp#ux+k ze4$86qD^6AjjwwU^(%S`ib7>)DZ87GiMNZ9&{U5w8SXYx33J1`fm>Qb|=n z>hEUZiCvtK4npZ|l3j|=t*b?;an(!v+#nV|%r1C@6*J>fw;iR3OkWI4@hq=miGfn%wGiB_O+)Bk2&r==O z>ty4tX73xFX_v6@V^}6EOEGMt?dbzypo2MqUh5a3M2w&?tT+XH3H@8_dq7%epemUPSFA5zTy;${KU_0p^hfgUKfLQk-$fQCFnId zYv=;gclCb%1#|wCh>X;0>UXyK^_(o)XZ4wLO`8IQ(2+XNuPjWR;(Xg+2#`riD>}K3 z0stHlK;-LoQb1o!9(Mx~OVh{z|3WtVh+T3S!w3;|*qmF1`aT>!(2nP`P<{lZoLEo4 zAt{thkbDHi%%{y}(;vdV80H8@tBxYcDWVgKxIpS>9{116>;}^kKy)2?DX8j`OWV(w za5DDo%NNu{;W4YPt}-b~xvI;4TGsxYLsr!vzayHEm0_gNwX6pctQpWFe?ocDwsi*m zVZ6pTPGrJ6Bl8DYLOiwi-R@a7oMJd%@6HO3+I_yzHX0A?K%?iP?5}R;FUa^T@Xwc@ z{J>IJjY(K3l>uEzUTE({Z^aYehw>*d?~eF1N{BuwQMkf;)RD+wdl`=!Rde0m`0s#> z1gK$?#eaH`V-b5lBB09(dpuuyENL!Bmi6+%p4sWWpf7vdubp&C22OxRG7B8v`h?H& zQ(4{{Pm9em);PmcRXmEm<-w}Sc1N1xYEUpvwJXe&s?r(*)G6-g^>|M8cC=B1br}$t zsh=3uh7PAD-hfkpKtWl7qHX5xwStgJyuHl4GG4}|lk@de+jhVIq1T@XH5 zRtUQmr8FTlv45MW@<;uk?yXPC%Ek!YjWhS-Y}djRwalDsU0qyv{j3p69|gLG8L6&4 zNY3WIvpWxc(}06w#WwfjFW=!%zM6h%!VScR$E@ICbVuonIO1-6q@JvRuq51e7WJC`y?shtfR>(TlfHO zOQ-)#E%15V(htRX3_y)8bCVgw{|egU+}w!2{3Kg^6T52GAEe{LcB>W^I)JzX9e zWglrv2&z<8u27@By5iYg_c-_a`OD@pdxc&Slin9dcgdodjzs0TB|Uy!vj5D(r6^>v zsT=7;pr1)5xqCCgtMXwr`=iCKKa9zifu+b46PW_dQfRpIok2sK(AI-!hKk(Ey4fsVDv3ja3LeVdDw;GXqLamxEeR?6dsRoo z6gBndNSzCWA+}_>R5<-I4cTh4_sz;pJ#_kwIU=XREEPq$V%{g-ms94U(cxI{=K*i< zEH=57?b=7@vqirjZ@;9i!wFa=-tTQXsDr)S&DVDZB1-Bp>u#f?UxszwUz^QQ`rTwM>P>h?8!zoo?+X%s95$~Ps$qh_&`^L*N3DL-6ku7k4r*t>BhfO z&QZKwmEIcPt`j<2)%U)CXT7ID;_fO7QGVPO)p0~x|B(U@4i*DVyI%SlD6 z2=j_6<=8Y39mM+s=J%pLkmqTYsK6_duT8u8Bg1M*a*U+{;j4a&laiHqw9mn~wj3IB zy$8*xV}A&hj=3c6)6QF>{mc7(oW3RR!>EJ(;U+OHJvlj4`(gf|KU@y^-}l2vI=(hd z(3MWdZ(~i8!4@bc$62uN!$r#5XJc#|kZ|r?Qa$S=a6^NeM-P5FsSrm^>RuaDj)#|a zL%f>ALZD}nxn7O?B_{Hki{_gj!N+h+j&G=@s-SO8ZX8Tr{fKI=6Pl(f#@#H3FUJdZ zG_QgHTBNe=Au3_5%pqk>AW!J-2c0Kw$&JkutMv7l9 zJLhCRY-F!Kk$~@84dpezW2qm`5G}8u^(H@SliTBSNR5bY~nam5?C8o6o z<8tesk);e-!gC^>s9IjBCJZ!o_+s;AR8^j|*n?#5BeNBz+GcOKE}8Pv(aXaH3Q~3Y zbc-y>b-uVh+Mp;~6#g!my|#=`sYZu(yjL?K?>p133ExXnZZ6W~v!)tQH)Vc)9g~*< znABU0rgOysOY65ja`NMX@G}a4;@XuPZ1~9=I5Y+UgW~(`D-8q;;+JRJl@6PX$&Zyv zy`EW>^+HC{T4Xd^$pjG9zZ+d?Bx2Xx+#G(e%MizD?HdlG{Atg4Rxl!gQ24r{lqfV-DrHjLU+fn zxuDmE_I57GD=ZXy@t_#>>=$BYAVJdvYvz3r1WFrdKdnPRBSxOvt&y`)Vu9|E1wHIK zR_$KWCjB+RG~LCWm8&8FvV-?4N}}|`;+oKtV2DdNyGLn1wDJCO+I?d9XRI)#;muXh&tzuGBElsN_3h{4?ROD2N z%>3l*K3?u%NdNFV6b9)L+!FPJvFaV=&{v@h+J}qRW_W22;2TF8C?VJ!qKw=boYa%) zhDn*hR#KCtdxc#6(o!pKYW;dAioKVl#ScXga8*NLl&(vUoXHj~ePn z6P1|BVr8$JQ#DsNm(+tz9FoR^_%H9+sTA=$VN%GTM6ciWqg_FQCdxG{NzB|lY88lx z?D$K_N01LGDuK_QjAkl&w&tN)Z?loV&pt9d%-Df!o`xBS?* zbPf+_VIvNyuRn)6V|^I<*wxDM_!{pacn1?MyM57?>Kqa*R~tVWN{1)6o$@gZsUVMB zdc%I%#S%!ds%xz=jY4x!VyF`b|I<6ql1QD#W|Mw@bo*uaf;2S6xq+y+@ZX zhWE(Rm?LNJiZUIn3P$Z0NU2X`2{@lu+vJm~tA+b?Ek2n!8C`CZ$*ImgERW_VWLzDK zNN2qdh8yo*@~U!~y^1K0Jfjm63-;dkHQrGKJO)#2b41a|gLspgB|kU#e43{>Ehg*y z7|i+C_VVH+6x#ucNy4}4eA)+f|D7EO(1<(|7E+Gw9;F*)jZpA(;D=Hp8M)b1>`wq#Vl>$6zT$aJJ zxKl9LgKXAUS1?HDByDQ^0(=FsRSKz8B8P*u%pH2tw$J%eE`u_!)zvlpKYkv%N=qD5 z=494!pqlM?2%LS%!rgJ4JFdG#c?=j9=JS7p=|mx3K|oZznL$kkbo@Row8-BUP~EI8 z{Ix@;PO)*;?xrUnZ4a89%vS4pG&allGS@?u*n>}E7wp6SellA=twe2FE*-!BmUsp) z0^l!A>PGP7KTm7qHXUgPzl2Rd1RjO{R{Te zBgMbvAR;f{qV`K5K(qgV{x8<93Smu2EB+$Ts2%aofT@b=_3Nk^?r`vXmbNB6vjUaT$A8g)AOPG~** zRANYo)BFOX$4!SaR$4I6#Iu#`9y;k@2g!@jg@|5q@&}rc8bgFQpN$RY8Xt<9p4NQmmt6ada1am?Gz#0X|!ezs@&vdsG z-s^RM7#fmdA#Xy`;!3k9){~e`1ioR+MpmhO*RgKQ<4JIS-+KY0ef!hCdxv+o%2|%tA?2xGpAYaGe)6%`Y1FUZX zqD%?NCRKL4*+tNJETWX(ehX-8e;S;PG4&3QRf&~ke-3Ux{$p~n?iIUgWS?JAS{R+6 z^PZN<T;=*zh^!&Fmz4JQeaKm)e4;AQIuU>P`2P@cd1N?+*=9u( z3SIU|I*?HGi|(yO3ozvy*buoz@*<%?xiy>D@;96HeT9z%s@6-MLO4v}=Q?+sFtD(+ zc52F7Z%L#aL0~m?Tywq0VVC4f^l*rrD}OU_-QT^!F3&B55#BT1!Ur9iln@xc7`1>~ zt(?PdVitT|fdgT)+G2aWd^unp?rSQecY1ltOD#>|+sMm*0g6Iv@0JyQL?BurgP&Y4 z47~ik$B0-dZY#Gcvr_%G$DHJQ688TKmDKf55BuLd=w`8KXIr6rgC{BlTwiCfGTV{_ zx+fCU?!j(VyO76>k0SA}%w4dXY8t+-Jdq}MoB^+BhS3Wz925KrMkMcY#8db`(aEj^ z%wN6f!l;AnR$T_pxwF;LYRhDdF*k>u?ns9vBYz7`f8K}vxuLh#0YsFzUzMZ?M$CHa z5dHjgB=#&v5z^QEwMdoJgx|eS3jO8!$gDoV%F$=yXt3;Arb73o-ytnwsqgchy$1z>of+1kiR_T1++HWrDyiV|;#QKJ-i*^hMsoNz% zJBwOe#AO_h-68NHy9lP`1YGW4Kw{#pSmKDOxzl9Vz2fM3z+Ng5-Eh+Au*0)hwT_67 zPzjq2jzE8XbM523_}_W$Bh#`rru4_P5- zB>xk%`VWoMtb%PElC)lHC#6$g`6lXFi7yil%_XqB?4GnCYd-gxB`dj+;%8VLgq1S( zRgf?RTy(KSM!tBAe>$IR&e%eCN@Ug)I4;N1VlT^yQH$1Fd18O%WiBu+2|0UKK^O~j zm55K~zE2V$Qn(IJ?w0dnU4*w8Rej~2aL77#v3UxcJCnvb$PdEJX+XQPD$kV`p~23jB@E3SLj{D32Iya-ek- z)27U&o@NU8?M{gj#-tpKT7#0TB1wT_`TY>KZv0EMrFy57REVcSe*%_hptFwxV65nY zgmizk1su}WgZti@T;(o@4(aGJ<7(<~5L0T>v23>TVUH~lQcUZ0Yu zr6i@#N=q&0Y$qn)XjngK!a0oEbllaiGJM^4r~Tg=m;Vk*z%Gsm2$wCORpV#>D=KI- z|GeqX3o0)1TN*MSz2pj`PTReZ%J>QD_=&{2%vBY^B!*^+BUX{DL8g`%Sd+27Id%|c zW4RHxRlm-piGNcgLr~-O-t1O*eYu!|5`+Xj_Qf{dZr$~iYxFpFgymiCz_ZecxuStU zMOCsvhCrp`;}K1MK%<>4qC*EKKyRyuA(Lh-q_G+;G1WglJy|y3qy5h}LW|Ro8|AHg zt}8=b-{)Z$vsA=&1C>OVlX2$o7dpZQ?)&rsJ!xAfhY`CkQk=%lPmLRL1pTC1B2OZT znmjqihCwKJ$O?t;qpJ+kx*(e+_O;{9VVlC$z-!HsKAz{g>7cm*Wx!&O7(xn7rKMktqkmyaF(h<%SgJuuzjYs+P8(hNa6VMm&QSfo z%qc$9SpWF*)pq8g|H_h3BL0pVK74A#lZisD!4w1;?%VJ}JtFQaRGp-fA)&YOQSP?qz>Jr}!mgVPyq zbJkkVZc46 zYFarAKw3i$9dBQoF4jHrL}edyEi1q*i}u94lk+BPM*=El5pZx8_xVT8!9CLhCFMKF zk{I-S^F2(aNYvV29TLa4pN`3RTGg(>qpX7T*o`z{|J}#` z_ub|Cxv?IO=fUUnQ|{E-$fLk&p{Dcnf6tTYckuAaP3H2p42SgthWoEiIrfbXG87F) zTyEh5tIdv!ZNeZMf^-z8%lWhWHYfK7H5V_lR4MKO!kf#3Y5fKYmM;^KK2CFn=36ql zZ{_E53p0XObVHcmDkL{|od=K&2s|Fvo}TNw;>TK%300hd&v;(A^*3<>f0FCEh55Q3 z1;V-nKuE2$!b-x_?U0|;Qhqiz>dZ=BQQrJ5t;drHXhD>K7Rv>aGv~tzZ4BBBdI-JW zHJd#jw}P&(N9aeEfx%rs>4K4=W@L_N#1K$McFE=cMv68?E(jzpP|EVW#R>}x4+G2>(M9W9_?Isj zEU;uNhs?uAl4!_yqgY+rDn3mv2iZBriWt|mH1r$UyzCytfrU0HV~ zi)~F@m7gA#T5Idg;CrwXVQd{P8E#sj_!FxN$%FBm#XP%%#yvN*quFmpu(1ub5rW*7pBa#P3C@-(L4N;q zN7n?SR>sCg<_IvzVo?>KJL+p#DV6Y6YybYK4Gqe=n3Z>;FFp8s+kK&XaVj8sydR}1 zBw^S7qK;99rfuZplqJU7;b}TzN+KAZ?`t$4k9;4$jK|XV(CgEk8)`AgavH3csDMDF zI0&NC;VcX_NT!S3A_aN5$O_yVEIWt1e6qKfDPaoMbFtPavbUCw7cM|-t7vbJy4(wR zb(7^eXQWu)tMK251rEm3lxEgM53jNxSgEL(;Pd4-*pPVW|J46qcA3#Rr0#VZ(Tfe<};jP}8(BkFvH?11aPk zgFsJIZ98L5|KJrjr)0zfqL8ge1WQr1r#RSJK<#Z3EH&Gc)|V_ri!ay(s(NpKgxKwS3=`&3>eZPMcD-Q@<*> zoXkuAQ{+}?w>b(V#~+Vvh_Rk|nV;;JnV+X`uxpk|J$+~Ez(aGVm~AFI%)D-3(_VjG zj+i8UM3j~$@Xw9`mW24D@u%FPIG9m-S*4wv@O<4_8#$rtA9K(u^;Z9R;tmgCZRbK~ z(6UO`UyLfN*&KC{estuuJfp%IYq||Yd5q6vVbQ2}&Ei<9h2`B0A4(rAZ-yZlcQ7^q zwmOY+$)0BoDu@w>J=YQIbCAWcjkT7~QQE83cZd_y!q@)sU-S9nF!cj_MfALhjr<%b z8s56kojbaQl`LVTlVJH6Mi|rN)sktggrLi;R{#(PY$M1cm0LY~^()pf0SFiIv9qz9 zM|1}QcE8|8eL-MIZ7PO;0rlo=GUgAq~Gq=-oZ1N61v-{uI%b*V} z0{27WU|3U|Z_8J!H4`Hp?G zoQ>CshB@e_=^!JfFBDS}qzC#c7{La6Eq#)jqs z%$}g2a77iaQBhH+dFW)CT`rL2>3=p4I47jT!A%n?Gjcvjzv+}KPp$FW*)jeZkoPJG z4+~ZL1u)UyOvY1D)n^XI<5U>!T9CRflB4^V~f|N_W8FaVTXaor|oK2ED)` z{1!(-MV43o9y6E^VOOG!Mat%`;gL*!ebJBvIkwpkEpb zJ5~)PGk92ygnzlm`Y0>cb&p|VN5cICfA2fq;z1h|0>L`Fy+bk0@~j-+;Xn`FWbpZf z4OT038D95EU-)}07c2Fc+;2_A&o^{J_!Esxj7X(p|2ZHO9cvxWmX$gJj%n86fYK&% zDtg)kpopv3Y!b+1AEfvA!aF$yotV1Irt%!%csHU7ryr*7@sH~4W#w)CZx#Ue{i4CN zH~+`HlD6#m#`|-_^-RW_A`=s{XDFMU-{yjjiRX=-0Of@n*;BC{5UJ(P<8kZXzv=ne zUA6?J4J+MS8%Y1n^(W{+ZF5YE=)KXxHb=%`vfb{3vRM}U>`n+s`waZk{RZj-JB@^X z$NXgp9XZK3{`6cnlgJ{#&1a^a$ZMiTSLb0-n~z7mF#dmJpkNa??SjWIWZJZ5$$VJC z>rXcad#_D5#bKi1q-gJHPi-j>_7&im3}S~)psW6_k)70j#{4289NI+J zEV71%xj^eiE3(q~{Tu59 z)39P_IvS+EMmwaUVD_0yx~>B0_yD2JA`#8wJ!uE`*D55_ae38`&E)O40$xu|VyT0B zt@U`IFr&Wq??~E2Xg~uG-8!BNhoO^w>v#wj!!(@FG=G0SyDnG zUOa|EqNDvy@qR3YZ74jA%iKI0r4vxdDz-nzRSd#*pmZk35HCKR*`!Bhyx&qv98Mji z(d%A8ygv>8=-mbl>zzrN$qU)=dLDky3VMh-&~eq4ww@L?wQ5)2^2|;DSP%8Z4?bv7 zT!kV{G-VX5z+_!vhNL3|pHt=k_1lJ|V2PjEsF-+kWbVe&fD<&sko+F1-`vmgtR)YQ zWF*rbzk?Toi*^Nfl~56-T=PF`Mkhun7zf(*mmm~EWx>D67A)d8goG8VP*~?zIQ@OU zQC(HYrv_}ed26aK(;@>}HH8-%baZf7Oh50xyB}^hC!3^t@Z53V2qI%HS2`TXa0FhY z*wV=0v$#h7#pkFVcbK0`LfDSj5Y*HawQ2_(PGS}Q+R;D{U~=FrZ|mxEpv?S8o0W&) z1X6fsd~?@!io#{vXJT4RMm;DK-EE?9wFqkTmlo&qDWxpUB~GK^FG+0PGC2XU@v%eV zh_QD-^UohtQZgjJJRncLk*E`_#isqIWM#~MhKX8QaHB@qz1{u600gYv(Ie)>My`UA zm8ai$ET#;~O^98>YSF?!U&NU&yrzN!8%O_Ia=0Ew;&)eYQ9!X?Y<5-3em#>vs&6br z+;2r5DpDv=L`u)EVizC8`tz>3$Lu_c9US^lG~Du61d81LbkU9jC=~tT%dm?8$qL_? zZM-;{6&)Q-vgYA<69x6|;Vhz7$6NTY(=`@%Gl~_+T}t_hZ~yD0_wU|bUq?g{B09Rp zgFvo&tHIAi#G^_RdMp>4eH^wMD}+dM=VLQ+xU!z)b&HimFz^pn=&N-Y-?&an=;^S( zYbJH&ir~t`*Mw=h?iL%~c|MUx6-c94c04o|J!@uZyB&w##2-nY+^29}Ob3{+py8wx zrx;xi)Vr?i9AEddD$N`5PFO@gsD`6PBfWkrp2^qUyD{iyie+#2Wq_>@K8qgb3zU$Q zAjg_m#GqC#++PDc3xiWR1G@5*3VpCaGwPs96nu6$pp~S2<43CNKFmc(9Q7_)?c?;! zOhBkU_TdICkd4f@IuQPqG=@SZ`lO;moAoI*7*BdJ(3>hNts3+O{ZHs+cvO%vOxrN( zcfY3M7uthRnOj{s5ehZe1($cEw-FFr4V?K{fkRA-1 ziv^Mn0^F0pc)Mk1Fe+!#oxLC?71$8v&y3e$ZkGbxi0BG;!H4}|P>#3ZKnaNbE()37 z4Qv|1U|dUeazXlM7;f1F5gqUM zfo%^oPfzWsWnt`6-B~!-_^+KcrW5CDmnvFB==~83eIQNKY3<~lT5h0(&}^yIjT!Sb z;m~ulGOH(7wu9G6*a-&b2*>_$5Y_Vr5zK4}eRW)Bsm-JTwD7l+L}c8p-+90@8_Eng zNj|3^y5tH{fokF{&+U0)mr?vX@qoqk)o3Qwa zJ-O`VaVKIgq2n3Dlg(2c(2&&2kcj@&BQJsa&u_*F2JYsXc(K-^qO5;y4Fse%qZNE< zHx&bs-$}-+)mZyhBtQYeB=w%@?e%7l2W_>#a7AOP9E3pu2sI`X%7lzVZ!hyh32wxq z+8$@DQiEkGTx*Vsl4nHoY+(ND3bH)pz||1@=i5gp#4xYZacS#&KVCIrj7ywS+$8j$ z*&#+5+=u~$-d498!|S`p!o9auZ|{lm(U?5Rgkl_Ob3EJWzhp!wxL`A2HP+4I`>UQc zw}gp`qV1T3jV5pim%Ik*nwv`TQ(H6mmXSdS_Us(p3F65(Iro%xU(Ns2b@nmRWbmoz zC@?@1qYRY-PPaihB-$6C-KDfhKgDaEx?;tpy2+{r0f|=#8%--yfSHD6Mli#|YO7*YVK1-6QU!L|-mFVS7qRji71cbiTB~c7 zCs2#z`tw45!~QgUB{!9)u0)K)Im@YA) zZ+Z*o+C2djJgeLf`;iEDqc%lZ4+>3@sA7k949%fo!ff=;I)8$Or0=pGZf7pnrbJBb zZ-Am(sHfymjjWrt19=D47nax6Fm~HSyeyT_xHBB^1iVi&(bm5CmjxMge)~PdQUv*f z2?U;U6f()XGsgM9-$Nfe-yy02Utlp0kIT7c6Gpyk3oBDrquv_yljqYxu>g6KD1%r!r@xgV#2<}u5FfyyY*{OjCXPri5f-KMJ74!RiT%uPbb36_L=Wqj2Srgb z6whL+tC+?&p%5^|@c&c;*0Uj0?oIY37LWU}YgVa1Jx1@gtj0=3-rpPXA1@<1 zf(rCPW43D!6K(bzUJnx2zd@{S)yy|{T`vh8uOi}2jFbH*NZ#DDM2mG62|&82n#%Hq z&rk!Z>$f0*q^Hv&1l52JMWfuLaU5prSq%1riGq=KFAt@&j9h}?PJe%Pa_A6*k*m&k zaKa%;xqZwzqRe=fF>uSZW+j<)PGX@-`1|vgi$LXWmZN1_NOgDX_3*Mj$sSg`-w-Na zXSgAP8K5&z)Jm`ILt9=|i|)DMIOw(6MH_@PGkXF!XG2Q-Cx9ql(xopL0Rld9$kI&D zUOCb(;pj+%1Gml3ON4vc+m9p#ca6<*^=Qi6&bgU7bcA+)&>tu2V}Eb%ld)5_=I~Rf zWLhPc)kJ9Cs?41tt*Go6z~#p;{+t6RGlK*BBM`RgllNj&=9k77X3ltwKx21X*OQ_( zcA8Cx7Mt35U|t6(c4dL0ouG%<6*9E;T6Pqzph<&ImbGl`IG+zL%x@)|Vn5L*`;mzA zPxF+9p167&*J?oAv@j*+MqS6<8c^hCS+*mETjmUUv>D|^8!l5;%M{`0AFo7) z@?LB@dg=kzkduIq+IE>{TxP1~r+xGqCi}}@wHPtcl!^#j6f}u{A|h^_=xvKD&Md(O zj?aPV!k3AaTnR9Ce&_MHyS!t(jv76<8zii2a?{!J4NpugXPEf=v-%A26ah;!m8%`P zp|!!5Bnf?(zWvb`)ElpRMG0c>M-v;*NHUK{{cFX?UB~Xv z+q8S}7^6D6Ppr4=o;%V{pWknQ{^ugx_gavkbdPG{mW;S`To=RJO~%Ka4kyJE5Vz5= z@to2T)d7$h>MCwLO&M=zHGpm#^nwdSPt^Ib(EX6PsPl$MY6O4gKy$wk6r%3{A+-gL zlzH^C0C13=Xdu4_F~1db!=F5c4moX)H&(a1Ih8s3sn4a)AJcw1;7|9Jj$uVK zLO2Z{}WUP0^%?&H60gyC~Ly%*@4?~99lWlIy_DjUk@$o4A!J#M8<}d7?y9_k0 zmWqNl*RezDTo`zTfB7wdxro*JmEH8nZP+aDuZsD7B)WTEUtTmExcg&_>i-xteep^C8~&(U%%dxwVvJX1^9v+Z!v=ImB8A_P5V(de{;L#}~6O?}*uil5HRuu<+kG31(!d+b|frTJ^%m3=Z$x-lhBFmqGIVsl9xEs z@J8e*5MLgg;5GT0IdleCjJJ7mPRK=SMIhl&0y7ybicvg8w60wi#@8-I?^4U9W-QQz zrP4a>pGv$$iaEx3m6pu)g~RbViwy-K|6oP?^R)45YpSiHUAPa=HoK1J87R|Zde#z+lXRkC5_)0TfI%>8L{uf-dN)5K!NL zRCNi-`_c6%q@Jhqc?iS<5RawLQVbBu^7OpW5$?e?YV8g_U{5(Hre7VnI`DJ`bc$ts zyqd7C42odhIt*`0$D}9_QD4$z4;eoH6ca;Cyd**HLcG?BJ9ouWaPeqCo3N+zRF@g$@8d;xN0j zq<0V?kfqy!%#vs$wpxb%fso`7p+OWA}-e|ELanH;+x;mmOl;}UeDHh@<4 z=EflPAkA;LJ%sNrxk-OLln3LosQ)UCgqg%QP*2;fXbR3{N8r-r>ueJ&_$zJ53G2TzC_)O*A$AXaPo* zK>ikzNYJ})>ti>9>bE;y*j8+(Iwm;k5AVyji=Cx7Icfwt2fFm#n~405^LCFc?#s@% zFf{iP&T4Te;eP!Gum*p{5}0VMyrvRVPN_P#Fne5(&b6XOzRt+5A~ zU6Yqa_9O~{qZ4{GPt>;ng(F>R%#3hCHtl|CY!%W)0#zI?TU|aHhJX`pLygCk@}g7t zB{SBn0XUMnIrtxO`5Vr_tpGZd(cQDbQT`&T7sowEHAE4C$A($Ll#_YxCZSxRURELp z7}~Q4WdP*p*BMf=oPFQEqp7}aoT8&tu3~)X=}L>NG)ZswJhc~pZa(<};yP)NjK0xH zL9@L(?1Tk%8;7UfIBbsr|045|ZqsPIm<-yZZd{(t4 z!>g9=ptp#RrvWA+sQvE{6i49%ygqSz7lJod2!l24&E}MdC;;9`dE`IWsko|wN;OPJ zW!8zZDo%8AuGT2m|MzQX0Opp&ZEk_dk)bUmO(j5fQk^eFMd>B>YSDfK#NDS3dYx7c zF#K{-2;*q>YC##wxFnWxdIGGmgKQa(`MnRv6dulBV-s{hYX3alz(ny+ID3W`CW3<~ z0%Zh9HtD-}4>-L{HxT6Qy2(C-cz}_Gc#&`P#P@)FP(+Jb0q#qQu_pQn}r1SlUD=U%PLx3{H246jU=~ zGI^r_h=-qx6iuBNwK%S~y2!1!NLD7@9!;@MymEqZi5$EX=0t$goJq^SGPcN}v7_g+ z>=>WSXXan7kkYk+?zGh7%4NrX898Ld{u7J6sO;PAumz`Ze9<>T^wPEq;XW^@x zg{}a^I91dBD>MQh3kV}dIcDpX`?F>$Mp{-3<1L$R!CF_-wrs6d$Yj*2RVXyw2(tKzSDv3MqMCVgOsE~_{eBfuMZ_eO@-sX5 z!Qj0|YhnXP=e>;Z@ov3%I`_h_Xn!HNSVLrS8YM1RZWBALz_=S8#acH}~@=Se>F(EhM7oB9=@0Gd?=;pUmx%!|PVCJ$x4)z;U}brp zV$sSbcpgg8+&!TgoFpMG9sny=@Mw~UgJfu>=RX=29}5D*!63&JJ&8U4Z|**>?kle& znoml)$OquDCF~Uf^5L4YoQ>EZ!rZ*P?^z9>aNTbo4(|9o&PO>^-f3rm6kWCPnfsID5#_OBG%|TkPIlfGCwu*K zaH+~v4zHi>PrGbJ@3cg>)AEdZd%U??l7O8An3VZnNIQdwFVYm54|>882=!l+MGN>- z0a!vl(mey7GUFY`Uwb20I3w~snDe=4M=&rVb5A88=r|{Wmny^iy(#%897M1_ zZmt4^@i4g@fOT|G?Sy7IR~#1&R(9z9xP>wmNcQwvGuONw=Rf$s;KpWXo9yUpjAJ*U zUA^ly_>?r?Cw}{cuwO^ud4Z3w6IebKnGjRJi{sMme+;laj=rv!kzKCm+74hHh1FGXnI2Hj2(sW6bQGgllnu3yY1njR~`y>#4@~ySe zU0I85{^8vsNL(g^6UJoPL)R-)Oj>dB`yHdy>!mw3jGL&?PM(WS2JIu@brqN~Z27Dr zGyDMI#{1>Hq`b#Nddw-9X+(lbku<P63$zHSQAY){#BKM!Boy1X{qmJp#E&7)~3j41s%B<_#$)PcBNI|5fEqV$+JXA z!m8~xcY7d7S#67^MmS@^%!ix~%}&U*f*kTXW7P*+elGyx>e@HWT9J=ze3&Xbtt#Ct z+yfWttyKS)Mccoj?52b_U#~*XJTj#n1-wG-_ejQy>`|dq;LCfu!NX@DR}GpXeaNt1 z;tXiMH_?~!8g8v!SAsHQbp<1hDJQbdT6KUu7Gq&nP7?+-1Zw80S$|ETKzf#U*Oyu5 zOFFxqAZuBHOq%bNmv#jsCu#wC2#F>&i-;f}A6c~MPI7keV_`XgRf**9B0K>ko@bi8 z@8}?1q=7o2n-VhACsYw>s%JfNnJ<5L5{;v3m}+i!qIO~?5VZ@|JiypC)8UxYgLHq` zvrXGuUKxbGtHEqHpUhDU(Ar!QbU~5S&I?TY=Vk?3JQ9rF-}F590cK~N_l*w(^e$b; zw=U7eCrs`~I{S1Y)pmrS-E=l~lHsjlVZv$W&|oE!3k_Ls$#NIM_EYr-x0yxOy|PT4 z7jhBuYWq1VxAP^FyXTAulZS?m8!Jspq?fL@KJQm>1XoZ&C~Z;JpvBIG_a#J9P^!;03$ybNXBxUxpq=s&ySr_`du6TV;dd)? zOp0g!S|CR;4A4t2kK`EPX`vi7`69OgcgVCEOxdx;VbStmg$Vx+tep5UQSCzZXXLi;oJ3u!Lj&4%2@D=!&nAf=V8ct8c= z84TJK(&>t%+8mgt1YO)HSP?E=4c%5QtI7`N>T7T~S)$5u)Y~qd1L!dLl*!RC&j$Q^ z`qLMg?W4BfyCphrxXLmKgDW;2NG;nJM}`Z9zjCs=2E-f4hWv}kDNz!G2!G$6&6cQ; zP&g(~nvM4Cs{CHijL{O=p>Vnk*R;?eH>nB zr#U)cQd;pV35tn+_R&qvrWWRK1L7f_mwd2TWCZO*K3<<1#Bi1WIK!8`ba|E`f3au) z_7X=hNQSHYWzsKwYsr;bHSS@ak6ogiQ4>LXfV|Nvk^q!4-QL8f*5K@y9Lo_Vf?&}9 zcfJpWzV*@<2<#_}Agn0zg9Z`_jg8CAm&ZZ6xa2&xJENsTxa zD4x!0Z^UTia>6Ls(i|F``$F>_8z*3PL83-*DYit?UhcBh%E0 zb?EZdxZ^S5`Llqb`nwOyy?oo??bK$uzMxg-<9T7r!!=hhHZ6`)qd~XSb2lZCM2|lP zTQkxLV%ZU}^7`fuP&u_`9(1j413rC^$9C>Kt1JrKv67fgTrl5LfyaE}CmhrO55EUa z$23$|o@N$00;^dR?EV(#mx;JIMSUh~^m81G07Gly{x5a>;<3~8l;XotIf37Q-K~3E zP;-b+>)xNH=mnCA*CA}U?$F+KjJ`}6-ZIzy0b;A*l$p}yeE+UnSP3x1SwK#`=>5 z-&AN0FV8MtG`Kh{I%Af3;p0B+Mh%U3bEY>sWL%ZKnE{ zW)AMwyzs>Iql?Ibn>PIxD7;y!*zL9_;VNK87W}T>1q%KT?0DT@^=@x5^>rgf) zZNWW#osal1SM_+p?@Y#iCwrng7_`qHi6IM26yh<=q1>?QI@=bA0U~ zLl+B9t9)0z=;rFxlKvWHewdr;%1{>GB^w?OUT#NySVnykt6T`zvjgk2H`Y-F=^zQ~ zJ}>GodQ&T*v(KGoQbdpCQ_C)2be=$#c6`0wNVm-P5am}v9j^@O2B_EbIv>Skd|Hxn z2rGLC_`}P5?@WzTH;+9QAq#jH4`eWu(nV>7msrMlzzl9G1nG(-zvFU|?)5KA^yCFC zqshf*atEARohi#H7})(aDP(XjT3qc=xdbC_L}r)0E`4CC_{R#a^OSK~=gQ&BFL_l{lX z3gg?S%a!PjMNdAN)3iTOT>+~2%_`574?)=rVrd_Lf{*eykPgPv{ieGpvI2_edGLMR zx*{8zy$}#_vKs84B<{RB=T2_ZdgCN#zYq%y41Ft4Jm_5q0^am+Yc$_OP)N$vNd#Q* zv_se#SFx5|o}ivo)7*sLC%j(?S-;l-iN(#}Ny1wIBuAMhoJG1_q)-vWK|-iHJNW{^ z8(3r}Drf^wp~wEI3LBl2?Dh~P0(~@vVe00TdEyX$$K#?!g^%e7Z|n8RF)>7={2HIn zPE)$8XlSff(QC_MgRW}%Khzm4!KdWn?STVKsm6z+H5F?JMwgwxcmnxzGO0#JyTd28 z&ocTHv{s)}n>LEne|9|fc^A*a)Ma6d3RV;cg#8(wsj`$u%0l^Ss#iA@xt3sgM<_4@Xd$8GC9Jf2t z&(L^~;}6jvLxz*{WDKpHW8984n>8&qn6=9d-9^@eR8lH^K{C-Hw?Q{uSaeIcF}GXzp$rDC;HoT^i(;928W868 ztW}V%pLBz+WOdPHvN+)7eV6)vfMx%d6rMsNd~dJP{lpEnZASz=pK}y#47T2{JA^Iy z(#cc|<|QKKpX6zkV)1^j8F6Ba6nQ2{dq9b)UNn40P1)e zv+2DoreOvD#9%K0q*Kp;f4U!T31<=%g;E8ygu4F^O=lSuM;k5c8Qfii26qiI!Gk-2 z;O_3hZ3qdjgS&)42=4AK!Gk-4yF1+Z&N+AWS}?!*U-#R)_ES~I;$1x}6o~v@X0Zqw zRdKs^${8Q{cS#d%0bS@x=Cv@42<~76Bwhl`5+8^~za_ql{X(Hg$W8h%U#jiA_+A>Q zn}9xH9LL6T=yxq#Eo!7H3Ib9-QpBlADO^?$Qq?j^r^OCEqhfQzAOZ@3VWTl-X6JVL z2c>qCI-4Oz)q1`HF}#{GUsTZ*di? ziw3mVwT+cT2&hb6xK#}s9GZit0jXeaz*pUjWeDf-*Wg^PvILrEHFuNUVy|Z?(a{X7 zbb58PvhCO^0@G{BHemH{KW00RDbLq(r>}w;2d~KC;SyrI)A!&`dNk+yK^U)50#236Ln2SlyVlW)Rc8EZ%RrQFsQ)h7jnCXO#=7ycGFSnY4k?RdC z)i14QZT9Xc-v|RQ6LXi^52ZK{@K1dGm8P@n=xrGS?zW;h4+4{m)>2(P4(Fx<&aFm` z=QmKQu8D?%Rr@%BbO>>ISiJ$MpR}V_hm_4byPN0Jb{BBDC>T+I6yT$$Y~p%+dzRb& zxd+RO?8#TkReN+awBx|=ua60W9FJ5ARpM3R2W(aPZYxy)nZWA^KYe###jE>-$lz`^ zdOwLbY%v%kq3S}{ZTb4pVzOp-7rFM!>L^`>dPe$^{huaS&((5l-}N>t3>2Ju^hj30 zTw$j}$%gZP*Et{hSQfWN@E?4olkZ*oyX+Zf+SaMlxj22%J&MZ>Cm3Syo%xO%BQM*} zIYwHwKY+2;-viTr?|(g>ow~spKCnqPd}xKrK2HDD^sO&ViY?a4Uf@mq3g5QOp&il|<{sF{yPz-*=pL+7^ z@tZK_BXy1gKpl(kb`c*=GC2Q$w4c2-Lf?NlNa7234NYs(8<(L;NsxI|8y7WVL&l`E zfJjw|uO*pRDKF2RHt0JuD`Cj93M#<}9gisSK~f4pdzxeKx=FA}u=zZk?s9GPdp_M* zkE}lHY@Y^hNMa)EV7o?jn>LsXK@jJ~Ehp{Ex`v7{Wjy!;83AU9DBmdHI-bX%9LuT+Mb!st6yp&^nUP7l0S>E^DpIe+|D1CC_V=y|Lx0!)rG%G$da@ z3(o*5v|8mF^0d7E>QqMmc-xJ9)|!+=0HO%ezbCJXxp_|GvaMMNqs8z(7Bc}an56nu zE(V@t3OK#d_rFE@tePFsV)btc++&q(rfmi14D1q_UtCYt;Y6qZ9#8Q5}{ z=`^+SKOF}fippbs$)1-_EN%)yS7Z1cdf@?k;-ha6CxMfIJh=NGQoAsQ&Fb?^Rnd4d zo8(>v1vy@Pm5`Xm`odrpYp5;F@_@?hNOI{{Lt$IMC-m2lSsEwClSBKurlZD&`sJ6(4QB!eX}Cr{pr-Y|%eFW<;v1Tc!2Q$NcG{E6G_ zy5M5|;dYo7S*^zd{#-!)iot?3Ec^$L&7^o{um9`Ao|$uisRFXV>es|zhpMjtx2A!n zxwnV7Ad=-{7!|b6`O)7x)=edn`(eR6u4UBYlw)!wQk=O^BI0zEI39M~%*<2?%g&4kywBZLyHCfC?oNJg9Y?g5Y7{p4d^jVGlaR384sBm!&l zrKr6C&WpuV07HZ17uUb|GYbbdX9D^0fq>U>5j3Mr>tiXX3KBLY+xY_<{57EQb5)+G zgf+lzMJ)YikND$3cH?q~cDar@7zw~t25V#Q$j8x66SmEGQl}=JE<0JSpC@@|XCjmb z$Il)@(Pmgye^1rPW;Uoy#)~l-GCYKiH}3%x#g2cR^BQW{!rh{5G;PPeO+{&YB(Boy zVA~Q^C=+l4R)C|KMIT~sxYq)$tYU$I;E{IUo9So2D&QKSh1jO3KQbY74*6`fQyu1UbVn_Y)X_U@Y-77?K zwwy{%E0O8cjnK?lb-BT!MGUen&Zz`;51_|SPN?p0hk5S!xq{9^sD?gMi*xScyUY}i zYf0T}>^d~b;Ax7`5r5DPKMRXATW3M7kO)CyhHK7M?tyVlrA#Fn^rb!_g+FfFNt`sM z<&1vkdT+UUwd*hfC$Fs8{E^TR(8+m;x>)hO6 zR2yf1DW;5?-p6x)H!%fDeLY)SwK;)F0-Xjer|a4EEurkurDjyQcp%2ae_zCf|z=_~&Rcf$ppcIb;m-4{8r;upIxj9G$?Krry8v zoW~o%9g`iL9hh%Vt&gZUc*tTYocgAh3GRDie3?0%8^b~h>CkQ|+FUlg#jIu3Zv}bLuo+>sKMxxdvUps0=}|>z2M&{dN&^oAeX~Wi$TBczgc0>Yjq2R} zbPnNbOPY7I;y0gh;Ul(DPUK1c$EfPlUTjgW+up9=5DW*?j;=!NIYN)tuO~KO$~=rb zi`5nndLcn2N0iAKZYPy>8r}NAKCs`!ix^Oti}jCIqSaG(&e6T#3aw`S6r2W%6x>47 zNyBn9P`l&PO5O!`2wSmrcz2(fdWHeL?qx`+QY_AJ9|zn-f~&70kLp66>o$WMNp|J; z$#UgOR|vp?&WiWLnsf7mmHug%h02@?{B__%l(F2mQzamWmyKI1Clxj5K{Wm($M4n0 zT@;q5#neU6jGRvXlW+Zx2m0{tX)?h&DjW_v!lb|5UBaQ6I^3Lxh;7hfOkMxz2g;nr z4lGKZzCunD#~yL1wGeVw<`QN&98UN0r8<}NWj6;(#PdaoveC0BTrcs6nf;r6>|5#y z&4FF^qPla~5rRI{)&=IxHUIjugn3Hb`TXMxt)0DawynA2mJ~PhU|eFhUPhb6Q>^1k z#dG`N6TVYImjbyz{3CXJZ~M=I0^W$by_|s0)W>q8@F-k158GQWtI21uVcJ@p2aj6Y z$m7?Mz{VH-R@zo^2|F*+8HhbvD1!+TmgJ-RpsG^?49qAa)HotE|H@W&=MTUUCS!S5{zl8_aXz%7IQ=ahmr z@kc+wZAFp4%OD2B;#g^`(BSL-rP#Y=PN5u8(`b?3PaFEMz9y8=cIkM)Mm%R2+J5p3 zD(Lf`S9ry8?#pqIA!D$tZ0q^--vt`&)$7Wl<@%e%w};_C)Dv`U_P{mNG@Mu~%iFO< z-??YWE+aNsiVeb(nkrKX+piD49`TzlvpNW@;BM@A~F> zk{bq#cww5LJZN&184US~4O(1Omsa?9#eUiuC2Xz5W^8+wQHb>Zrf*mzHs@LhLu5K1 zR+yx4HUVGAz@S#qX5^r$RcUZChgN4l)z&QHl}_Dgw>t7Ip^481VWh4so};Zhx{P#E3koDE02o ztx<1ORGc1EB+MWFXx^Di+_`Q)6?za3^o>!jh@f4|>iGAviQOq+sBO_1Zuf2Ht}W|} zAS8Z5_@vILnphn6y@+3w2_HdRdsNY`5>gW;ow&{RK8z}1S`MxKcBvFXOy(RxSlRIy z<~vnxjc0t#8lcfa7cZ)2_Q+U<2@s*HeC`4rTGJf=@5z^kfPPGfV#Z_HvL~UzI028r zNI4)?k(l{MS7GKwk2;s|gBSUwB~Jxm7TfDzgm5qA1%i%@~8{yVX zlZ>-gJ8m{=ic=3V)i=hf$LxG*+|f|cI7>3FH%rcD1?bbEJPjv&HUx42aHSMr=U_QgUO7N{TB0tO_7dUdaeiq0yH+2Jd?Xs`o;_Ff|%q&xdrIcH! zpm#8lF3PB!bx0X_{~pMY?db25@_Jpu%Pxq*{|}_!&3>v^z8nGb(M7emiTPk>t%OyjJ9AmijsG7nX z)t<3#4njqc7$BIF)}3lpiLYG{iaA2{Mks{&tEo_%eEvZ(K~5yT${BohZD7GDpc8*& zzwgtD*AVJ@hf9{V)2LQ#QwfqkZMk15H17~?Qj%dFo<+`f>$mR(y}VeYtu$7rR=K{S z*3B35Y3|CBv_c!Em?yUCk_F<2hjN{sljNDz+;~cT*vizLb z1Au9A^b}%@_2V&$6ld4u0o5?96Xn2m-9I`8znDG`1~UIS)20uQY}w7v)!86+m%99+ z`A2>Duq^pIR~c0rHQ+ku&q&xOobi*o=Zj}SevQCZuEJ%-rEByU zeL8c~i`7>#u?8#QyukoM{>vD+>u0`R4{p08fC%un0q> zzyfMNApg51{mZop_3}RmodZ?OL+e>bu}!EEv{_9(6WM^cQ@bK5af6Thk8w&gQ$lSU zw$XVAIvTxa8O5RcmU+_A+G#?I7^iNQ6^1!UISgs7wj)<3{Zlu4bgWd+};?=yyw{oAW+ydGIyA4&zo1wiGn!5^pCnvLCa(9DpF_e8BElh}U{a zJ29UiUE1g{(7Dbwo@-FMciS1&-LQQFzMz+W=j?{-hFgzD%$fI_I=@>>rHLTV_hpnI z%4z=V6fl9mF zFI4vy=D8HMU5zhSeD0OmO>$gpvCMb8y7JWj!tJJL(`4l^&Lxh0KuJDEZ1^?7=z~xu zZEQ?U!= z>&RgV%OPEUorFE>(M8^Auo!gbKLxBQG=}V5UYNyxjpgX7kaOqB&_DN4b^)@^2vpk$3uJi^m*g5Le3qMIHg{QpsGX9DWjJrR_9lFjaVZ0Q}jd=@sXXZXRO#wMLa3cH2WOb$-H`(xQ} z^UYtu1~y>%db@vfijtAR4OO(J&70ATj8vbGx$dz3^qmB9Ept_p-#jq24pMhY7$c1>@wWZkCM(R5NFgLVYvZH3>WG7bmgC9yc z-9Qy{Kje1UJ!$@w9BD*%?$99bbc($XBI}>VPsd_uIi9rep>-RqPTv82qh-Y! z{=z-tQ~i8Kc*5>a`2Se|(bliZvGQ(;Mt?f^ZCna+kkb{^ME`bqCkasE=uCKoK&fN4 z_rC+t{K$6Uz?ANRBudA*7jV6qUCpvz9pQIz<7Khhp->hi&%On=>FdK7d+dP4($XL zBqOoUKj6#uw)?7f_+$IqD{@k<$W#qace~Nb9kR&JWi%-_i@sQ^`C!_tSFX{|ghROc%f!c&tx&U| zmrID~D62L*+ZC*@Ao&Y>89MBpDhMIk!29>Ir|tDJqI1KpUsuzN#)4Qzoy&E_x!W~l zd?hM*YO~87MZ{M9gdd5!62;LW*PMxXO_<`n{CbzFo<)*x(9D$Ib>4n4C~&X2$@_m| z2uuzE6X{fZ5JV^9sCp3oBNUT45MpuDCIRz9Qx!5_fMEU(y-4(L#|dk7Nf?$p}0q3bQ!u}L_{zc`*=9iqxm~RV@LL@K(Oe|Qi7C^d+gsVZsvz0sc?f6e zACiAk1vK^^kE9_da=AgbgrgTYri4AJ0C{a(WPy9oEXWksHza;%` zuQP*RLp?Wcsb1z)xFY6|FOfn$eM}mK^4pG9rI8`%`}!Oo_u8YP(T0Si*#`E*RXlnz zN1gyibx*-7Ec{`3^=8yKgZBWo>TnyJ_;-Au4VK0pOa>z&kc^C*Cn+pA=sie zck89}=y4FlE)w@82{VVan@pkdwND^)W8C;^i($OXz1RJsBQ$Kcf$K#xjX*1R(@T{2 zyBaRy{98;p^)DA)$R@vHTn&-roO<$Nb}W9IM^9OmC@Z<)qiUU=@m=%W!|1k=?obx0 zW>3*4D7x2K`&%qGO3>GBQR||my;lh9;=HT*V9OiY0s|GT(zFw!$J#vKO`=y=d&{{= z))mEr#HAulB??!7FrizEE0uJx#gg_69@c8FJ8Bg6d1ARRj#j<_;`|rRHdwT};ecK_ zQ;|i&{QP?Z_aCd14d4!Fz4zXUm!m)R;Hdw%E;MLX1d7$E>Ac5RWB+w8`?-Y`i(>(c zkYZ*ocVgmPg_!|mcAP8ZmbY^!t+cp_xs=z5khg$IKzlShDZK;K`BOE^zds7GI$+9R z<{gh*gh>WD7G9y+D$JlF!7QD{JNDb<`L5?`4}h=WQ&^U0pEH4I$bVu+5#rZ_9fb%Q z!)BYevaa7*SrlmF&2ne7#kL(u789Q@+b+jzyr;}Ef6gWG%xdxzpE_bqY8&}1u9bwc z=o|7QodA#3C6C+V?p#Pl#+DuH z5P!*cFJ`xy1oxaT{0KvQshrsVdVcD9?(2^F0R^#|f5JvSJI-I|ggOeb!fw3+3@;fu zod`)z@fqfb+&6l4HYG{S)f)znsg)i2Ya)o4y<4@yvwY^Dhghq(xSIxS%*pNFADGQ} z@Par$C(Rh#b&wNC@#2<8U8SzjVGtF>Xrp=4e#|152Z2Dl)Nn4edX25|dTt+q6^`*^ z1e~t6wu;HEC6&X$n;(oyrQ!q&hupPmt(2qEdYA{?ei!XmHaO1At-AEy%*`b+Yp^53 zhiqYhk}94z37t+3JnuDR*6%29qj8Kc$e%o9}S_C|44lf(hqj`oE)v8~3efb5RA6Y2mDyyTlu^PW@>> zg8^jLEWw0ex0NdsS>RHFV51)9Fn#G*2Dj>X5%$SzBz&&Fj-XF-@kD|XAGRsoBf#gQ zr-Sox^p2u7dY#{L3@PsbiI^M%o@=ejowd#k7@b|e0pfm<>WjmAmkLU4%y=g2f(w9~ z4f!0;7&-@|sg>qxKmU{&@P=i|=YlZf;%WoF$MKxH6l;E%FILXBV#}}3K*{;l_Ek8f zwzf9nqd!fR{fo; zSlcieSDyM_EnY?mE*VK?VO1}y5$Q~)JzwcC#-JXc^?4WZZ7m;R7-7GQ{L@D=05O+M z=z=`-2448nW0}{P5t0Mfo{19>A7gQX-%+2;-MsYjT$Ek6&a3}7?Vvn%{XaGHrpT@) z*qxgd9+C5qhk`>$sUn+oPO#lG7u*ijF5SJzwxNcIFM$0Qn6DF`mmRjR#;KHSCmhvQ z?6GZ%^{a`kO}P{?vTJ^yFkw*vX9CnmZAOLl61LyvRCia23`z$R3i(`Gz`vTz)F;1q zosRK};~ZQxU+uor`Q|@k2~qg2tFMV={aEjrot6rxGaQU0S)N?M2k_e!0Q(YLdOWoG zfjKqmICr&4YB2+AUlrz^3U-1tlk~zr_Gx1S?#6IZ^&Xs&c8UXK6?6*_vbs(;<5Xp*9@wc9|z?r`{QpO@R-FCswHdw$Ssrbx0p;&beOo&a#U-_SI60N z3#3te+L0AMw|I7+t86xd=Y2j_JE7y$xW9QC0xnQgyju;C*;>rV;xKu}Mr6RAerdJ4 zS>WyX%AQ=2!hQWgi}LYI^7Iv%vdTSjb~j+;LccV&&|~ZFi#>Z_F1rIrl;a`0f$r}& z3X?x>addF0F&a0*kFag%2P>BO^?V+o;S<@{HRa5if2SQ3PtI2EU|A6aVJ5wwzLL9$N}`v9~cg>Pwh=DSL+ouFX8hAl^6+{Hu73 zIl|!ME33cuSUQTr)>7!rTGMB9L@ItY@QM_AG+ig|ncY~J_vwy3hxDqOOi=|Iv9U(f6CbAC7-zzCVUFgp0;&7}_aBA`TT77iT(SBUZfY zkwwAb)_pXKb&7*>qCyxQu{TH~xyLFK>sF_7!tj(tJy&%6&iaVuMzGepQN5kmsXp~9 zpb5^_I>&u82LOCzwEngJI@7N3e-GW%r_ow>5^-LxbJ||2v)33)pfrxcrkLzid1taV zls8xz5ar4A0zukV1R zWC^CY)KJ??(SaNwsYZdr1+T-38v3z71{EpwU_Ak+mA--$)sH2aAiVWNbH}Ixu7s<1 zo;#M1^Cfz#QRH0=V-~4Q(Jd1u&OI6)7H|-H|75EX?w1rv)ejSBgvs^{N&j~ynxjK4 zcADancriS$o_(&Qe^oj)r@8FIO@0oy+UyoK=>h~hP#7lv(TqIdo6hb`yz*<$Dk;vM zoA1W^Q;d?`ewhnYhYl~Q=7hr>lA{(+CK_E^n++5CoGN_eEiiX`je!c=uBdY1-(t6< zT6Q016FN&Pt8_MMJcDaYTd|21Vf?XaC;Kym|=X_bEd>zGPFwLzBm zg?4rrp3=C9C&>1f)f=@$V;V zEZyNFNLy*?tWjtQaLAbH!3f_%Q1}lQKq3+9H^y@$MrDZ`KpOOaap77phzxG?xUho5ffBMHT*CL&9Fy0hqG1qmhs49Y)ZBW z9K-?R0kEzp3DBBS5-crNXt|?42vQIFFGhY}klNx7f>R4iUQjr}-T40e9&F-ts)AnT zd$xLq0qrgl>`SNRme{>NYuff)B{nM)EuMtPBFreKUu>%ruGw?gHJ$go?Z*u+0kApTdZZH&U3Q_Hhu(gg+^F?=f?1k zzTVyHS9IVIlm{~)YZ!Zm){jMD<(&;P-f@K*E#F43>X3>#L6|`%adu`=cwb~xLY!7x z^a23fyA5MMssj=klnnzVI{xY=+#WAC=IX@48}`cFtge92S4JbRkx43xe7HcAq^!>j zOH@0NM?qi9*Xv12^kb4SO-4P2_>$SI_~x;?o#h(MKEMe8WSdidBI2~NOu(lTi8~h# ztja>3Mj^AVv>+-_F;v~ek0e@d!1HjWyJ#1dwNOuuDZZ}k>jR~PZBM@#4%UBoH8H~_ zP%rnb35G^9lZ4Yr#L3sp)!hOshC>dMIWMo>Lfe=j+(SPhWCgB?15-{xd!5$SR%8pW z*SxB&jmZrFm!S}eFu48xhB!k}lDJXiywnR6`&Za^NcBqLEySO$yp$Pb`|=g})QhwI z9)kFKUe@_^+R83h?63atawjv$WUze!sb=mCC^4a4aBfq?13(=UGNxBvZW}xQH3=^R zppuB!ls@~oiRkNT9o@GXHk^R_`=j4E6xH@9R)XXHv-&VBC%@PvVXTZK581|UFyH+0 z<(F&6nNZkck$KFCs!4vJ(yD*_t^I+=>XGCsS>AqN9zY21n$Gycc7PP0@L*yYdK5nmAF}cZ9E-nRaE?Jl)aH znL=JUJ1n`sKtRYpSg95N#OOS4&LhBgEZ+ph^I%#x68*`d->e-a=)WoAf7YU2=p^F> z)QzyTwSCYR)w8Ts%)yQ3Ph4VWt^C+A?;`hfEn@%vxsExwHCSRZCPU*pS!%>jDG*m! z3BRz;Evg99!n;jgKsob=Rt~00Q1hHsV-nu0=YN9uW73N~dF~$9c}3U#vfZ7q`Mu|G zwOhi#n$eWIeQ~!5^hG6FPH68XcY?_n=~?OrM2>M+tkUwN=^wIJwk_P_6P`j*jA@cu zJ%om7x+9EwBy=ROFmd#C08rEt!&WU*qnzWhY{-|4eFoM?io$nXRi%Yt04(j%YB@aV zOAm1hLpDa>F@W5X5mrldLxwZjTBpkyU2FYU{WWKpp&1O1Jm@V8%86H(_;!3;JHf-k zq#}LhEhjj8{=drB23fs$3V9Ih>G+Ud7emJX4j*PQL2ho2J>IFxvM*$sg!vLPL+V?) z(t42HdqPsewqf+0lgdiN$a&o2* zp$v#o#GJ{s{?VDRUZde<9QN-@TFyZ1m0DKK&va^qfM{ycE)l=OSzEZ9>78d5T2wGT zdxEe->PHX4om>};1csh@$tu7@q={1i{Vfg%Bk@BCtfkpignkd8Un^6d79H##ckA%o zCv=9$hfh3QA^A_ZX6KYe7!uYyx?77f{MO4m7HhedOg6(iZu5>0lUS3axAbMd_|0ZG zAz6p*cWd?=(&c*f=sz=$U>x|uH{sEv91_J0rBeI>)wxd8rMGwHFPl1uvot-?qKW8L z9A&DJ3_oEhD^(A0m%+`xi>R@7gBgV4#^ISW${*}S7A~kMmK=m_cGuB-LhPiLY~Le#md^i zz3RXU{nn|=j`xY_<;^3fhH&c$qTHMk>d%8>uhqes2D0SHAl_j| z$s4>zTiel-G!zE#FzWr#sQ7%vYMIgYKumOga$>Umbo>|)7`j+2wpMT|4b>-SANI7U z0N}XV(Q7KXye`YfyfGh8=3O<}{j)O=xKtTXV$BQAX*}0__Lle%ZN4M0$Etycu|>Vt%O?#2 z!rWqT`4rTUnvnzY$W4g0ePIO@kae-CQ=x@DZx*T1@%fv#>lsy6>rHr16|X@{235iD zjrOfn(v(Rnslu?h+c1QgEOwVj5S)1t#>PW8(ZCnz$M4yV67G=3HQ&+GJGu@=wcjcy zAI)%$t-49Ueb5886^?w&7A}vG52KZ_81!e~!J8dj-pyCzx%%TK-vkowgp*(k_P7kT zv(T6$M|bS1hgjUP?v>u-0cCZ>VdXSF*o~tGI{xF9O@jP}mB$nLH8xepswXOKOvS3r z=dGW!z7XVvMY{v83sTV5Ll9#N5WlXQjw74rTGL9%id-91{C$%Cw5VA%xI2ijPp2(E zclBg}_&VFTk%EZEwO#-q`R43!I*2L$mPWiOL__guBBwZQcK54DWjcO=(Fd&1m)LHo=mJYV>!$U8DEj|s2R6Xnk;lQ>P=m6o!fDAD*{^$CK_nEdjgM0%B886^0uI{ed>zFYh~U(zC)VIsW@ct06yL(9dq>B! za5!I*_ZlDr@9JelUR6#XOFo}qzMcpg30f@1%L?I26vjfoNsd9+U^aqSGnxa}Hdpo0 z1uL@5Vky2X$aaacWe`64A5PIu_t8y@5xEQ-d!~qhajmGPK%5|Bw|17$K>etqLDa>I?b&=E$^kLOG#Hn|-XNio5k7^;zZrdl-z zil_NgTRU*JwhY33a_NlS<+;jqG3J;YWoLQ80lnlHJ3~6)2+F;1 zYeopNAdSh*MpY#pKmLRxnPfES%Xaf9Z!Onb^7sVmJ9~gkv;dsl^Fue)$ubhtchs;I zhCDbd^f~x7h;~^y!>^ae$a;qOIP1>p#Sbcf80;~wj;-I`Rr)l;Enlw`D19&8 zJ=-s`2)u|UwcTO6Ex(P{H9KBDU}k#Sq{I7;?3>Ii(UsbO02YYmxS3e%x0+TNbn!HT zF_$aC{8Fs9CWLR8#1z9V=b5iY@lJEeOlgW}FVP)}omKtqJM-N@Pbyo$!~}7a)&?gh zhyW^S8s&BjTR1MbX;czb$!1Q$iAP`InbhjvjU&yTOrz)QRLwM-MZ28sSTd77s0>@{ zO^NaqepR`_nDj*({9E6nV=l-u$WUB}smx+SYK|midE`^?u}m~v3}w2L_{d{@f0NY5c*pO& zbX)p^9o2O;<0rPox2^(fg|l#G$K72bSrB#;|Gydk;H(&NdOR( z>hdL38~sQzluxs@A_Y4uJ0Dy84iAd^1Jl1SSsy$Nyx^ejmsYfltdYKu)P%4Vuy-16 z?frzihS|6%8$Dl^yajH#X>`GVn~}SYZ!tu2CC^e&^6p~k1ZubD^RgK+1IgZa_~r)# z=^qmn>dN0(4g;TG&jI@<)}`mPIvZqFfJnysK$HIE_QEtjxl^patshd&)&_)ALDS{9=~ML{q08zT-Y9E@G=*4%&Tj&HqI<3q>Mw8IUf_sld9Mp2G$`+>Q#bMg{>+sGiZyFc*8p{S> z`OEYSFWWmpBxD}LwnE@qGUFDcI#tcVt+$3|j{Zx1uHcLV2C?RwiHdI>$9e#01$efM zn)`pUN*&^Jds*R=&h?tM!CPdhM&#{D>EmSk!WyvC+!w^W(XX1^dPXZ8_x_Xxejw z>7HH0J3;U>f?PVx)&TSsyns#A!lO?*;~^-=ye)4b2k;(IEQg7^VQz&0Z|=`yALJVy zM^bm`?RTuFS`4+2<iz7gc2@9eA?D*};;;FejrHWcC zp+#1P0tB~N86pCol<-vPRk>DVqGqNaA=K^kygXn3VsDep1+QGB_(I&%V$--gKo$BZIf7MLxV9@aJdNl4ByKumB8#*i8j`&A9TYg=lVd z!HS>yb`Qt8S1YiUo>kY;&WN+1_kNcRCj6emlQ;2+sLqn;@w$3KCx6Ad1*e!?83R!) zF(2B(zxtyTj^HL2l||m_UwDC|Nc}MhBCQB!za`xLQ<60_fQWLr0q2`x49klp?u>r& zwI)e;o}Ue#=pEh4Dp_&zR787WsD`TVrXj=PHu;eo2Gg|m;*CA^XKv)3BbX)PV_ZF7 zr!F$9!_Z{@sjR@thKZ(Z;$Pw!i|VLG=6^kz&TX4UpVx*jeZ#gpydMuFS8=KVsCYiN zi`L1iSET6sZR*{qVl5Xk^C}M?g(YjNHt(B!?(@S|VU{=x&X;O%IJpM3O%B8kQYSd$ z+@08A;wpag!4 zVi9CCqeps7=goCb_%iI4gA{7?Sqpvi|34}J|Nj_VhQjX7ZaN$q%Lk_SxGpIW$X13N zZWsYMQ%QVK?1l({3m__9ZRy)c9Zt2w`pf|>3zO5luQ(|#IsTp2d<>lU=G^OCxsDKH z#T+v<{mWLNzxtCz=1sC^L4Ik0xSD#~#$DcoKi>6U;au>SHCI8%W`(3si|VZhah8#HkXInGx#`4k9l*&gaUL>PyN<^kayyZD6D5!b zXbn{1!8a__aa9d8MDY#OQi|K=K$+J4uap!0_WvCbr2dhsWr+mGiiD7k{xR-**nJp3 zW&Uw87@V6ef%Q2TLi&UKq80lE68FAniw?Y z@o4*Fr{ni&3m=(s2C-VAutP`X2Xn`VTbkwj%BO%ot7(q684mOTh2;yNZ~tuB2bL@( z50Bd5M6W`8k+P=6UO$VtuIqIub|qChFS|rvY#TKD*Y7Myih4}uNsN#ML~0G}{>egj z&}*X7A77XyejE;aG}-=6Mert1f!28=f`;HW4)|?)M}cuZ`V8{S1GipKJiI)p{<+8N zM7obKEMLFy2xMED*l*z0=zsisx$nBaHy2&*lmnHIn3IyhrH|{mB5;1Pr<%I$Y`D~K z5%+vQ&$KbWttxCYi;#C>*qs!T5?4L(LH#m|9NNhkngjJd`Lejy zllz~tY`zBfs#~Pmv1HDC>0p*)AedT&8>*iFuY zGK4DisugVUcNI4+T1&{fFJ;QZzY;SwB5FILjg>;_T z;r~kcDj&e1tXP{w18YP{qEU_cB=gbdCVqzQMw)F;(hS27o<)awc<$9-Dn{mI|8mILkKyUoyJ%E3Bd23AVGmPR z(+$L@-bdtffQEiU>qx+HXS|^ORpdkl{pcp{f`I*p`N$n(OU>`Iz$b5ge$o2!x9i=S zm%b-_#9kd^9RX9(#C&19{l!ZCMkAic#f+p;BIS3ioEeZW^W~&Rm``^B=Py@-@2&&} zk2mJ~pALc^UuM1|?>x7Bdg@>rCT21aQ5J%&Fa4M(#7&sd*Rk{o{DWle#iR2{9P_vTwt-X@w3~@`1p!xvs=8( z57)&)+g=^^b$*YB(}~>m+_&g|u`j=z3Qu}Eq>t{;9CCHmD_IX}f4|5qU#>q~d%Ofb ze_Rjz+HO2hygo;T-nl&(2Q41%MTwT0#@H*WY&cV_;||~~i?4dtzvuNemh$ylxpzH< zVd%}=vo5a#08p9-8f7?@V6NX5BfLi67-i|`C>QmYG1ue9Je4vn7!sp2M`Ww2PSKYs zY%yA{Upu~fmD-8&-EUOnX{;8hMpL6h0i8{~)HZ5n1qK3b<@g>lH`-n?1h0f3p1;f$ z20p8p+U+vunNAh_I1Svvp*w6!Oq(K(%J)~OG(viQKgjF4MRyk$^xUhQEBbd6r%5u; z;w|_kfcMv{|JDWL>T1wyt$w?wN5pc{zrT)N!acy&?#b2%tDVWj8CbL8aQlu`2J>26dpE18ODE@oG`k}uuQH< zwW8o$Q3r9wt;e(V*UBHs1?atZ|GVb-@2|QZ_%5(r5;r<#dQxjg$vQAQr(>9o&F9Tx zJmQ(5gc3!rv^)tN`gJeSU~b4gu$(+*FL8-ZbHr2L+RA!(@O=>W201f>*((xEF!%+l zzx%-R4{E?zmHvgEsbyhC(}RVDZ#ZybqmrWQqJ3i@&KCnKQWRD0@`}uDR{U+HqKy~t~&&De=>F#y3_1B+|$;< zwZ*%=6nFfWQ$M{~o^o2o)4B5HF;hh>5QBY=l;s|1DaX0Aa3X|)?0WR>Z{yMOd2YlJ zeo(DpVMX~I6~k|&$o^&a=hI;FPO*U96E-doTEX5(gr>G(S<=X`yzyD)dT92&r$bnv z^mC%+^>AfWU~%M`^R{sST|GS%e3if{X zxVZg2dtEqMu#XJz8IWsL#M8dP@T*Y0&2)+&eRRW>R+L{=wuUF@i;w=SA4+yEe^7Gry zu-n-!Jc?UlF4aCw;xq8>SoA(>u^U=fip%V0XE0kLbgyx}I z($GEpu46;scUp9lB`_1S5II`nJ{Zj0D6hi}K=somI^|A8U0Vb>pPsm58Ug#%4AEeVI|?(G>A zPCSy*01X9PjlFvM&ga4ncX*S23FQ^wIpr@O#&Rux#Ik2bA7UAphr07dh#=#>b4kvG zPG#WmjnqJ>MlIj{wc918`B_fYxz?|og@AHo!gJU7P#80 zY@i{oU5o1&m!gDa!yMz~h8hc^UkUob9WY_rpykoC)Ot}TH7rULZ3AX7a%Lh&!{;d6 zDVj$!BAUjE8IZ64L+j__?dgP?7W=;Gi?+|V5ffnot|kd+mB)9-S*5vt=kBGqt!+O; z?y334E}_9^ETmr_I(;RtR_E#WxEl2>!3@P@%WWRsy!iJg`08Sf%ehnjaRa$M@@@A= z_KnegZKQYKAwrF1;KlDX*o&>)^KbU|n_7A;+q%&T?y_%~qcM*wr0o;UC?5G+O|v#W z^x|PfFb@GObR-TJn@M>rw^QPR-U8ud&y-7OXS-$DpK4}oXy>s98+CPNzPt#XjeUpB zMap(&p6Qv`bXccLvpYYoE?w?3qmjz%+6c6Q+%1@A`RzU4Ek89zfqvS~ROZ0bToat9 zACPW#dCzVnNINXAZ;;bo!5-H;o0rqHge36!&bcI2@pM27E;$X9!H|bE2)bGcC4c^A zHteQKtLO%(F~>7I7D(e5I_=UJpGxOW4|FA-T5Jua?6=-KMd&sd2M_%0cL8#`-~4Qn zY_Nx7$OI89^KX8t#=J>9RmK?=e&4Hfv5z0e_F{Tj=%~f|!lbFI|CWvO?I7fKkgliy z=6Ji)z1C;;)UGoWyWf(3W2M4yS?c)@QM9?;@_{YYUTiBI>aJ#1l6Xs3Ae!*^_>K z(F&Z>Dgr|<<(u$cs4x**tgk z5`fR;it<)JM$?FSuDMzG8w<0eQ&PEZ+mdZxZ_N8q{5}F(xA{7|+6VLZHdUc(kj18^ z&uon|cD2KzcM)W$Dw3v?PtDDGoA)z^Zpd;pjDl8oAh{GZ(Y{kO4B8L_`rlO~e;#{2 z!+Sg#VGI{OWqKSSa`Y%JoO|5(9zc1ecdqrv3dj11NmdW8-I66D{>zE|t8f|<|9E(+ z(}ZPLdw};oPeJ+uXI>bm&DTG=)6Z8o>>=0I-zRJzW6NFHwtYn?W-)vYB7PTX*KyUs zBKe~=WIn{+%iA8jeljClT?>|N#t4Y2O4ahC)Frd7f z{8YO{H7Ql?5n+C*iVpRi5Ld2L(j>_7V`xG*I!RL#q;Ww1`IPRU4v#UatnnEtK{zMikBfF-HBFpiDa$RtoUWo>ayy!VVFz;y69mSC{AOmyWpES)8 z+AXOdm>gWW&IwnLp;eJ7bQZz2*hiOjz-za#L;E=e(Hl2qP*w5pP(iS$X;V+Wn0}X1 z#RXcr)-2Z^%Dp1Q7Xl~=E+=YWS;<27`0E8hm48X+3U)7xxm^HiY z`XtR724NEG!nG6gk2ai2lhsCfEEhV_fqwpmeTdVb?iX_H{hdx0b-SjM^`0g?m@BAG zUYi+uCHs;<*V_Gws#dem6NKyF2rD4^0?-EA6Wjgv$KU;*M0N_Sn8iYe@1N1Fwbnd?YI-=R8hz{Ia|amFxNgvbp1h_ ze{CIog}-lFb39#gryg3Xg5Mzvqe8L$E9Cp@Z~PF7OQVJO_>bi^ci}+FDw0(w{wTi|_D)Di zf4LBLlBp=G72U80tUmGRt8dt2k6T9a0GSRMuF!3t=X8{}JV>&5>f~)jZ8JqQVc8R0 zq8UMqvDxU2UKBcNwgtsi&I-C5tTejqGCGt8%Su~rSxpz)mR17A0CuCfU3QRkfv6(^ z0U^DQw-<};fv}741O_`gbvCOYMp`muCTLL@15>8qg-nsANFJ>^8-YL_ZAb}`VS{pB zs{<+lD|^g7wX_CMpP0g$ug#@dr9uPSU{sO7q`6cF??Wo&R(fZ#A!)oR?|5V7z7hZp z8OmDTodX{kUBsHQ=t88HH)QN_oX8-trDlOzo={E=w~|Az{Kmn;&>lB8@CX{TH0oF* z;64_E>dqN&BG8#$t51q|Cvwx zODroiGnE~&Zkb8l?}q(w$sSuNPBw#aeEkIGNq5)o_QWj3stm*Kgq8g7FIx?24m;~` z{FBUd_l}Q@d=^{vFSqZyt}Mc)7_2-U0sL{=!hLbyilGLsxxl$|$X@olzq@Q_33 zU51Ly$jPaCCb$;PErIX2r1U-gfV5kj^=BWSP51+#N3u)Hj(rgY&SL7T@*u? zM4f1O)nL(o-siu!N%c|T#E*97_-g*}YdP5HKxMsGj&e#=4c8&ib#{5YeR!)IeL3o1 zX!z(|1BtyRKm53?XxtR$s%02omV4ook(&rC(Ol1els|baHT0{&jkRFzg&z(TeuZpm7j9kx@`<1 zT3fRU=+IDR6s&*aP5mv2Va2$xM2BZqz0AP%iT6iEg)XHRq_wqfgC@kQT-NTQUP~5( z9~XR1Su3`;bHlH9H-`U&bG3Sf_f%u85I`8g8^GR_WFdq)MQ^1$C%n)~bG{nmKSmIK zq3OTV8r9KXHP)kL|KnfOS^s4->fvFsugJu{uO9zi5&i%lUK8L0@@(*=eW*1X)QMA& zm6ryn)KEx&tcJ&(ibu&DZM;VebJgCSCYZzz2wz4sFZc6^3HCk1l&p9{+=tWVk^%V8 z%UGA?X}KMjOX=mqz!c77-Itapt+#tjn?-Z11(KPk^6F}KMN#D@g_n1*sx@R!ArJJyJ*HykJ=3e;*0b@S zJ7oK#7fY?9>t?=|06ko2BL`xe7F(VBDzs^T=a6}gT@8hwC-9rHN96H6@YeTMac;%? zRTk>0`#|En96lydOLru(c4qYt=Z*QTad1vW+8DT=yKmQav94=!@?tD+yRRy>)wgYV zX6%>%)nlHtnCF8$=AcuoW0N85=-G+frwF%!6mEr1VUW?3?8*0wk`aaknf1|XIP80Lbbn<1HxSm@B8g{qm z>MWU3+CV}r03#VtE6Yz3T{Nh=u&Jr{h<{gBMn%k=T-$V0Pox{KXF2F#Kph`Ud z(_ZLNExs@+)k;C=HQV3B?X)2U=J3rE^4-}jS4~p5D|O^okUScR;AcnGwyJEuKlSx? z_J&`%fssWqBYs1esmtyk8%V!F-*{duZZ*Tk*J>@4eUVh8z?w0DknV?B_ zQwVq~YOIQc64s)gy~z@WWwldLaZFmur^*U**jwMqg>RVJi5?jIW{*`16=!X=I{{rs zd{r-O$>fB><|%dn)cGx9Et=}mx~)yhoYkOtCO<#FK|YPQ!Jx&PL|mu*Lm@-47q%^% zPGu)ha!Y4-0(w&}ad!#n;d`p@d^iA)uU~|SQG__()1(h{SY8TMm9^Ei;Ub=~@Q1#D zpe}zf2x0vbB>gsl!14|vGZs|KQM)u+$Zw88EM_Gu)L!hACN52;n8vMSLH{nLpwW(&yW;%)8+y^c*NZcU8p#Zq>aneQo>y53W7 zDk&_-uR6hD!8C57FfTMXhnzO6|H564lf~-Jcua}lI%hYFC*8WnFJDls=w{Uw6sk8< z=BsTDZ46u9x@wl&2!I{e-&&i`)mTEl4U2&v4ZQkj$^lLxjl#w`_z6Z8+NxuzHA9^A z%2{n8{l>ZBp&{k00jd6Psni74d&!JKhmBZY7<%Uq>+Ig7I;IT_54(HECl$LL&S`*E z5(s(hG#?H2_sjL+VP>rMJmSg}ZyK89UU0Fo4cjOKY^@KQ9JqIcH!WOE?S~@`{R^3! zTR&6QMW1})t(TgEhg8j{ne&Up2m8yN^7~l@8a@POs;s(brwF-5xi!ipFiL-LiXND) z>cIOt4Ae)upp#6w(FJ^5yC={}&8VHUPjOeKSLQ zcjU4;&@-g3q5@*lEKl!#6<9ZGeJ8D;;I>&42^$0N!hq7=aY;@$?mnT)1vYO4+QIA! zZ?#%qdCs;Kml!_1*32<3HC|Gn<^ZQI&iebk%nTfn#*5>=+^g5&vF@5vGu$0+blpGT zFX5b;80Hi#guj8e>*`OaR>sRnA{IoHCVui1%&KWxbTn`~G}b$MCN(#ANnsKKnM@9S zE+rt)VE3&&_Amt`IO!K7n!)2;2P7>31YGp7>7<(kwI@7^69Hz9-~pL39{l=WQTrb# z^FO!v<$gI~c635s%)i5jYhGOQb{mD99jGBwKwzl2u=_W!Dx1ud%NaV_AI!6Lgv(P|3DK23j;D}TQdVHA! z>H28j)jMsXXo3wy_}(8I{bFdHt~N!5{g%k8y()UW0z07TcG@SR*W_l4cAv^)5|t+E z+X!u#A>{5>G}nVj;g>D?(@8doHKB9W3YHM~z9s>43@u&_5W0+wUjdeEIYO@5EzplS zET6(T>kFiF?P9fbHsM~>5zH6cxB0>R6l|Pxwbn856hdA20FOFoz-2xZ^Lp1!cWHY< zb#Wv|LO-p}7XEF5qqRmEf%U!#?Bg8Re*5v|>0QU$>E<`i?f^AnlT1S1 zZF1L@=jexgyR8UqR@+@p@VgEK*!E)0isvK6pg?;d2B|rl%y$;jd@Em2G3de-Z(@J3 z$5U>0p@X0TEePFPz-_(tN#8R;w%qx{e2I_G{gob zUAHYrO!M|DK8c=Yt4SG6`D}(2Eg;M}xZm2?AT*k)ej__wYptnS5qkiiH}=u(u`)M6 zgLGm(mguckz7&>>MItL53`AYiF&+;!p9O{9-QOTW&tjQ|iFF$;G)^Yo2mm zT?(K{bpI0?(b^tgslB&OlUJGd?QWs|xS9N-rLTf>(O_DMKV3cgK3`+o;{{teU?J^B zvcH#WkDC$84lI0CHmq-O-ku{Y{0vl|L5!ih^qbr`943hIl#-J)jZEI(2O(GYynvO4 z{?>N|pnlN)Y+gtcF zGVj!k7kX2pt@5d|5dn1~&kjHV3qaMxuL20Wh*GX$;qi9S(?s9LT>>EUNLBi%ZJxOa zjOjYCFqaN3`>iV!Mk$bNO4`Wq#%UA2y%ra0Tt2{PUgg_TvxuPwgoe*`Pfd~{u5+#* z*VN{!e6c(QDrX5~K+63-K%MMv6OQkdFAU+wRdxCHu~XB5h+w20kKO#F#sY@O#ZRtj za*fZ9k$Gno{cqb4UisdiNDcTRBDo9+Y}YTrz%XfqS1tM&hTrLdX&1}1s`|_HziDJK zQKL6Z@K{YqtyoKJ4wt;MX-pi{t&g8Tpsi?lH|aLJFUx0giw3PYq)u=6JwesbNE&9w zz-yMK93*86=*8m3@V?pht%DlN*fU4xIK4z#7{rFHY#{NvRkGF8V6I8A1BrtpSkg?_ zFKHE#W8j6V!nGpUuZjm~&C?w}SuYhh_5~O$jJ6fFH{@GdFG^BLBHd>cwHEmsTWxJ_ zHj=?0D;_!*?>k#jOxIYd{z^ab`CNNLBKZv5%jRp`=uk+_(#w`BhbBin3E7PT0*fQl zc-;VjJty}C#}KHkC=3Mi?edJl(t#va_i26i4A}dYbCB$$0twiuOD3>Ebx{U7tnwC+ zvD+l(w%93exzvg5GDwub?B2xSK%6esUjHSGWMT+o#)(iDs$MTM-;O5oa6S&dqYoC# z)?7eFANXC0{|BM}a|@#bz(Yc4>%sDW_G^vAr^nhyY8X71-=#&ofe0PdCxB?0^#h~; zYMz4_qf=bkIurApIrfP95chX6+-t$PW12 z4z#ALRO$FERwy;wUi$J$iZ1}PFtnCG6c9>X4i=UM@2#S|f$A`{pMj>6ZT|j!%!$s1 z5yb0neXeu*XfeozJJAVerfrkZ=!`HQ2szdyUkCph zlIM*^na-gfz_Kg#qpCf_9pYIISYuW$rg8Z+L3x|z8)JlmMqoMzR}G5Z9^*p$t9Hti z>@{Xg4Dx`;zMk!sRHUk{mw}he9^#FQoQhc-D_U+-1C2D%NX%4{D%+txK_RnYkEj)e zZ@iK1I8e&)k@nr%*RxI9*pjPyCZ@hr2XwxC#&ZjApKPR6)mApr!r6E|x!nA&5NcA{ zZZF;vW}&zde^8fLT)Rxi^_%5DBKiU{2!~9}tYAY(j86$iL$4&UOs?8#PE!?KSmdhN zc_9qcs#hmGNa*G5;$V*C;rsM(MdO*mgs;bC(paVso|;KD&vLD^p3e6i4Is}$W@p3L zv6UX?8#&2~ek&#Fnk2Nn@GX+4Ps>Exz^{CNyf8Yds&LcYXFCJDun%+2+I$QXn9~B^ z2yrqTOt$;k^1iRuz`x_7&S$cJ65{{3jX{!Cc~59x37^EKyITIZiU}`xg?H_hV$JlH zMtoyykf@tqpZ;<)C=55!5s`mhBBJS38k96R&%|r=X?AzfeHpKPrZdC$Qoxvl*Nw~U zCp$0gA(Wb-`$H}C$hvUIU(`Z>faO289Mo6< zJ%{6dXA5qE{)j-x4`&iBY_i@H>3k)293XU8`Yj?daZI?&R$=)-^yZw4BZ<7$bow)N z0-HcLB6qTcExQ7q1cNzquOEdQ=lQCnl-atV@r{<<~mrk%djC zDV=ZS8E`rgxj*hGFQTUPr>{iRAjSa!L-1?>LS=&SxXI?P{#1gzqelgF%7NZ7TMt0T zS0~V6r9ofoy_lMg$&u((lXVkvKBcJ;nA&(ZzGrs;NX+42{Okf7iEi>atRGaDcg5`|!qvF3FlJb59QpA_|c z_H-5b&Sew+{;=1@SU-x_?S;`&iaCaB!oEck=zF?|Z;|)<;51yXTfDGEF~`{!Te>}( zV0-y?mj94egzbE-GeGa+fZCO;_Z(S6%PON0tns2r%fHP{PY}USgLQEa&^poQvwswx zZd7OL(=*seEtql>Nb#0~ah*NQhk22P>S0aiIc^Js9ny5wHD-c_PRu>BxpnCB8K2eV z7j+2XXG=%{#go=01#{mx4`fT%j>F-S^8$}y2A8^_f!Q{t={D7RA>a}AKriLBRYSEI z?y0Z>p6ZcmUpa{4gcI6NL{Z?)3TFV2o}UywP&?(nJe;4-$hXeU+%PpCOtoHjNuV|o z08bmZ%R`;tBV+u#TJoO@Y=5>T2Y(qzs{(ZNKdaHC^Z32b&Y1L#x&T64OQ&$~qFdS* zT14%_E6D6VZ@#?~PzsS0zXc0HsXG3o?-*R7QX4+sK&5HZ$hPpSE|8;;l`~Pz&mgnu zs`ZXpY#AWNf*>HrFfn0;ofe8M*tWl8tD1m%TW2@B2Q^WwUcxATWCQK>fHrb>zOD>1 zzVF>2MZ_Te%8T&qgMn(HkLij`pqA?slYp#K=JWBJS{NjaSF_EQpWl;)knq>*t{5S1 z4#J9mSsaAfi5a&k7Ph`xa0dJSAWbV2c0TK@(QWhlC|=_PqA0Tx-$sUE0K~M7IS4{@ z$X~iO0fG(8T^x|dyCc5#bG1H61BdUJ3k)#RjC7wfG*fXz5Xjc-piSdw6F#2yMM8UCq26|vKH6mu(O{cd%k*Cr&^+q&>VKJJ<$0d?g~bhzNz+BFc`pPc?fdhKz`M3u zB}nrP%qCO|lws4kg?07{vg!KWY1ysnvnM@v^D<~JAhYNy;(hsSM4(f>tSD-izs`PO zoL`!6CEmGOhqI6>UgG2<=Cb=Q!c+Zd)kf_SU9LKk`cTq?1Wkio*4FbE(sn4nN<9WT zfM)-=_*Q1^c(aD*RU|7+Xd+^EM%I8ttuoY+Ij<;N(}7b)p=zgxntx|~I)`()_wjc` zefcW!;b+Oyik{d^YJ%Tc4C;T*1O=?nx$e^zDL{LXy8ka3t3Nl~cNodhU$p$a|N zlw7GA9+VICu()gpfrthNhWR|@oq)RiJWc_aw9me7qi+bL&1wRZZN?YA4oe~*>uo?C zr_zIUMK+zNG|4C_OIZo6c&s(QiL{SNM1BZ&ujs{u zYC*=OZJO>HA>s7+8c?aK=L_xCYYroPW3td}S4hI=mY$aF>;@6oZWzCHaW@5-B2*2Q zA{FYiamqTyQZ0rjFfm{0A3Zje{- zbEuc-Be$x&>A+Y2fgGAPS)s5oN$4mJXA?Jk6u8*4@{R|=rh!t%m z2D&6&!&yik{c-fwhTjcpGqyxQKldD}&L1Bt{2MY`6svgw&OR)OQ|+sh9tsjEKARal zyV@Bi@UmZ1K(m5zq``{<134$>*|7u&W27XMnbZFFd!P@QZ2YP67^<{QX-QJc18Nk? z3yQte4FlbN>jX^%3ST_xWSuTKe#SbkY`sT}@g8Dr4E{W+A zW7NHeH5`UN{imhlt7T{Zhy7)}Ht3eJB|yyueIbL>w7N;==XX@r+e|_)>#l|SBIue8 z#)HvsO=M@e!Im)T_LU|r_ooA-ptQ|3&ax(IEbM0xHtorhc+6_e3IpTgK2ja9 z(pYA+@M-17`!jjYiwLeEmq}|4$Itj*EYTvd(wvK+TI^_diJ(M;cvcnvMsWXQDfVBl zwky{FYuQn`juPX4Y+z3@{3T>*zfR_oUB5b2QS6~+lcwn*Y95{todK?Ms$zbH7KQSa z+c9V0^IkhzStl;ZfTbjp?nZwUK(fGXkY~C+L&$m2pL!4(=|jX8pdYloH&M1bmdNbh zcK2{3SBE8DshG6{gE_vSswi^kbIH*ztG`P?gJz?e$&X07F zG~_e+h7NFe-{%}COg8d}y@ulKT0@-W>Obcz})8kcJxpZ7N zBm|}yNKRV)Y>qUh2H}?5%vM9*Z@wKE9K_o{HW)aZI{XqxE##SCJy{&ATWeKDwttGp z)H$MxAtJ4*QKm83uIzLS1juyduGSn2H6jhOR+Ghtw^v7WN;@u^Q`;>}#m-&K^Hkz? z_t$+89%(xm@*gACN@74~`s};3srUOn@-A_73T}m%BI6ej-_yr{%mdY!^@Z)MXvbeF zEkGUIz#|=R@t)JSchCwhNB-=3;(Jh4&(fNB_NVHBVq|F;u37zjy^o5>z=$Gc1Cb_I zVA9!Q_2xkQyj|t~QpraSXdrVs;pkuoM_+E3Vd|7%=qE78V>2_hd#|E!almxv0fRce3ysI zJ!OVz5WlneU#-Us!=G6jXKs1Dr@V&yOG5Lr2G?T4-a|g#mnH+?CW=btqjqqiyytd`fV0_F z$`x6Rqx+I89rvgXQx2c^rQS`Sq94rJCvd;-)iPZdrKHpji$Sy2Ck(P;o%a#uoBaut z>Y}lLL+{h43`RiPLmezwx*ZOeh zs#qIKxew&pY3?o$yX>>hcV=;Q^sgut^Jff?8B#D+zQX?es4`FvEWIJMktGi*4VE<> zH6U)WRA>yg22=JGC}x!thenHBN?0xl!#^&?0gL1Gnbh3U=cCOQ`U{R0!@kukT%KEo z!$xdiCZk*zm>QIqI~>GTJ2yc~t0J?U_7ES7430=?&VO;XP^gtciq_`*u)SkC9ry%ZUJISBEStwKW4$7amhmiRJX;!`(+EpsZR=<1L0<&!f7V#N9U` zq*Z{w<0WoIN~wMm{;HuSXN=CLQr(`Y%_7=epeU+F)HYyO3G}h^BHj6uG|(}AydQpQ z&03;SHt|4iX%&Abrcwgg~6A&(vIe|g|%KqAa>S23!U zLWllKVLY%qOkBe((I!Z{7g#6lm!$Bg-jy5|fxt7$Rw?NVM)@yL*%JS>!02%t&3$tU zaOKHd5j3IS?bHVD_W2y-@KF$Z0qS~en+0ih9|XFaURosdA3Yr-CJ){#UA0ri#wDZ_ zIRYa~>YecrA{P+W6OSJTFwzzlFl6~cv=3D4P|P?@5_+X&6c8Mz46Z6kFwvPHgZSHN zlEOah`eEORD8~{5xWDXUya-R#TIgZr;lZ18m~9j@v@Ggj<`A10%k6s&iJLA`m9&H( zPoCJh+gTSl#puo597rmeqCifYVuuz)pe3kiJe9G?&_}UtBs`D=6Pk&e!-ND^Q@|O0 zPGm_4n8RFS6QCiwTVI5Jac+f`i?|7o6GbU4`O@2AuI1{(vo!;6RF~n_kBAt>Q_B*V zBB3I4K7+3YgP%eIMq|-X-DBK$K?x&9KKf_g&`2Y!`bMo>5hFs?NkL1(+O92-Ti zd0wwqdKv|kyzyU)B2+Q36epQ_6Rd&snXK+&-0&pjwpb$ipFP$9wrm09IY*9%~O(W&_j-$5>wm2staZ0i_)1vi^ zZRWE*RZT{BM1#_mQJ!bF6IL)FbAq@!rCU31ydH~zL~AAO$R=VXM20x<`V|8QgER;S zw@Ok!*9EdbRKV9nkNjp9eH$- zj_XGMn2PFT`pyoj{(GFYzEkmuV50$%5# zAOQ}euCGK=XzOWRvQ|fGG+|Pd%DOCEOtQC^yHqV+S1N};4u0z8SSqBVR=D5wdQObLh?9724V3az&m(^Eb(4vFa= zFSixKDs!AUf>e?Y45+5))XCv!P-!Wsi?NDqMO^K)S1=;?ohm90!>{ch<*pU@m<(4tfGEsvLLz0E~IA~RXh zt_hwrH$^8uI;}`KN_` z#g8MCsPV{TXXR1KC+P(uo*S4~Q4-`1x_fLG@90rE927b_*w(M&&et|XuHo-*BR1PP zX+#Hc)|Aa0Uwx>iY?Hcj^|@MfPnfv7Ve?~6lFRKjV@48DOpCu^?J%|K!zXe|fcP?7HheUhM-E5z?cS%EXpb<2AKZZyQA>BF$SuL;>vj4#_cfi6fvw z&Q)BP<<&gb}RN`t%v4MHzi9r$F}zaySXpCVxT{^<2oCH2I7?2}|n*}TiqmcDi0 zi0zk8ab$}}++d2S9SPnVj@}NcfjQxlr%A$M@T6Om#KcbA`uNobI-2rI4I>H3&T`>c z1|)B4()vN`_MP6W28hHn{G?BGOCVmdd)1`ffiah@aEriFIml=(RrEe)-S&_Yqw_~!b;dPH4CIQ z!_I$*uu*tmQ5Q~_Z;DZWz0O1J{(gLGLLN)WQ<*+RS=;y(c}I%1ANO<^7pbql+1o(t zXwJL4MlU^;mhHjE(!l^xuQ%F1x{80Hg}d3w`K2*-fEYUBuxVFocPhfUFNerBX+fa% zAjz5d8~Gp;PfivqWf1@Sg_kN^^xy)LP(#H>vWTwko-+@bwLF9(sRm#7s8K~J#Lo&;m-az zH}M)Q_E&5=QaZxF9b0?K2Pi3YNJ8P9OnFK*M;^FqP!_SrBIZ^WKj~SXGOu5K7zN}*L)9L?(3jQ9(NGNEkbK;lJ zlac-&#iF<34ZQ4(&Y)F@W!j+O2gZw-lL@Q?>3@#S(9e;d8vQwkeT=84s zewIV4t>1_w{wfBJ(i9UUlY>OOA2v`bBq-2PvG^brJ^2pL(&P3sHO6gNC&d|Vm{@@> zg?sgSMYJ4#_wNJ#r#(OmgaTYAe0!zpZ=11F`YM|iCyzAQ@vB^ zLR){MSn$){(NvJ+MUKK#oP*qt3dO`AbOy=(jUP=W@=lot)dI68)A4-XX8-@Q1`e?3 z1kh4jU4Kr=>ScSZRBQ}a?Q7S53ZYs3ALcsbKQ=Mj+-{18Pu`n20^dO~lO^K0gCJJu zlsCzsd;k0JQrg$BbWGshO5St@7KPET^YJtNhZBh;s@BD(=B|BvDki1N@)y2TQS~jZlp}jTmPm;e;s(9QhN*&mKO8O}Q%-na|pW6FgQtBMpaW8Ae z6nje*onzk~XfnTdPrSw8plx0h>M<{E6|6H?wRZiJUzAV=hxN`FU25KD8wi%xNpUNQzEO8 z^g<%r%}^uBswY%5l~JLvu`I&OD>h+4`)C08I;|*#Q2%v={|pTNm;j<*Cct?&+2;TL zf6yf;^|;Q^CfK=ORlHMgfA!~T{v-7G`w0L1Ty8VCAIKtf`$Vq~h;p`32BZG@=KuO1 zf4?Y?2`#zPU=q{2F5-R|6^ZrhwBO|JDipY@uJgM;JR({Z0i(bKmZ z+iUYhzNx8-jt-+Ke>lGvj-=T5PZBPJJc({->3ArMPWmaN4xIE|L zTM6;;QXGE4@bD^ndJz%iA@>}qtCV5e8Pj?e?*&Lwz2D|^8s!E24El#9I+|aS^5)YJ z{?tws40Sigb5%I}^&lPohn3~3q5xvh=JlJL>klTSpTu|2_43AfB^3*HdONPDiM4PI z?IY%}zO+=5(n)S!M6P-8d2JR*@sLt?j$FUD@^NLbx)4frWstdOa;%dbEnb$dXN7)Y z&17v4Q|31v729e{t`zH8KB^hNBmlhndR8RD;2}z-;bo;ot*rb_lPnISjIrTu$EZ!87`s~^$`wknzV{aZf94QWkxvkcS#T%t0)GwAu|*46K?O@T?NYX zsJIc>Fin`l=G@#D7P|=^D;BKRFd9%f+jvQSv@Pk4_lzEp+)OMBnsc34uWqC7zx@QX z1Z5-A$~yU*!}y(kvnnDOb1azRE`kH4te4eU`MFLO9XRWNRL66SXkt#aleKUKvXz;V z=hgKuOfx^Mi@LXo_PnLTMTseK%v;sF;p1ivUzZo;=BACdaEl{B4`b2cdPq1L0&K27t8EPWQoq7XTt0fS8dJ2> z_lyQqg$z!(U7U>4>#$a>>1UxwCeYM4_e>ZodHr4kqRYjHxBlhY>;x2_Oj$*Fa@~?= znAy|SwXfA5M}V1*n%<~ec&3#yG7dULgpLnyuY7~tK$#PA=P^V%RHS&l%>kF{e|SG} zvjcMP5tKB7{>P{kBf$YYB9SvMxZsA2;YusK@d;W@7ytN?KfA8PQ?>`ENQHhjihnZB z%{k!@YeP9z+(Z@`FByL<luH!9DQ_>`Hh{ z<&cRNe^0U4vY&3Ad$@o`pP2n+ZF#0_)Y=McNrcOclzKlPbVHHpHY~w zZRml(L=`pw&)?M(iqwLVAYmwI)(hRG8_EB%wbSh%6YTc~u1rv3kuqhZK4VE34-}_f zH!49b%zMu)54>*?f%JOJY>L*Mdw!yu7W2%Gb5aAgc{>ulu$wmemlKJ-5%k7h5h~GO zSd`2^l`Rz-QE#36T3IZVv(34FI&e_$9m!}-8}Yv&y+Y`~6)gtP#y(eOu{CjQFZQ`A zMaqZOMNjTc8yij@UeC~mz6~pbLCKCHGoR}T_F^)s4hGH$Vk!lVHYz5ejv}}8++b|g zaE&Ym9nLqh|JBX*zY$0~ zgJgC&?Cxz;)rVd7s1;WUE`7t4ALEf~1yUPgDV*@@pX@!cFy+=EC4R# z(^HG(Mms~3C1DFK`k$eDma<|8_Uf51HCgmaz~w4u<+7;0r*Yo%0rr-ePj*1rU~tgM z59JYc_eAlsvhgg-FPI1$5hCyXI)@|D!KsUie>wA?r%C~HKzBy4kGS1H!h8)ra7cWy z(yg@Wn*02sqpOeK0n%+5>Dv~;f{t9|{&Z4P?Prw`MTGIs`(h&-q!NZe?F-!2v-@t)F9v z+KQ)dOay}8h4hsi#(Jf_oF@`n4}G7Mt&%I@q>3yXuTE+X`uFl2*VgBjeB8|M**0Hll&9Ma1{s*P zEirZ-dqWx-a@SAIB>#PWz1R+h(-!^ⅆ!2Dz(EF0{~Nr`O)IB6G~e-Cg&*YH;c|5 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 literal 0 HcmV?d00001 diff --git a/docs/osquery/images/scheduled-query-groupds.png b/docs/osquery/images/scheduled-query-groupds.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7fd8ae875637691130bd500160609ba8443a0a GIT binary patch literal 323916 zcmbTe1yr2Pk}!+~2DjkO;10pvVQ?q72MaEPyStO%9-IVsf&~i$!QI{6<>$S-dw0L{ z-T&-;zd2{-v99he>#DA*XChUUWKfX^k)WWUP(iYiYEV!BX(%XI8UXy;or`naw*wr3 zwS@p&G*7>E|x$li27&FP}fos zRBF^@EzbPM4_X6df`OOLB%MgV3L)&w{vXqJs`$6XWzOJSKa=L5uMd5nZ zR3z$!DRMgD`nzD2z37s|uL2*wTDSgKu{iO$%s$QWK54N$eYe+A{xL1_Jv44cH8UQ^ zhD&&Sgb-swNO&F|e7?3S38O)o0t~k@GPpr+Riek+XnuP6CAX?cngoa3iOjTarxZCN z9{TK?=7>AA*Fz$i$bl~Djp<$!yJ#P)SM$2rY_E&3U##_~EvAjhhDX1Xos{ct=rdC9fwwhFzp>-{$NHCH&~)I|T0k&AkFUA0fX zJSkWD39w;n=i5lUwwRWgh$#Y}u(XmP~MqaRMikg;kO&YeaW=J95KeT zJFMP4S5cnapVjT&*TkiJaUlTrGqP1L<9xvR(M~<*Vt4z1D`UjlQ}XGd-Bb3)*EyH( zc;9F5G9--|;i))1B^akKIKn?)TI^ybF4{SuMhCt^H6~Vhfw|@2@Lshag6EU#6Uto?=--K2hxm@}3tM;x zlHx~LOmjL>_*L9FtFmA;c#ii~?+K!e@;~7b?fw+J;IIEW+TGcc@-jGR6nx(dsb??` z7Y@R$C47pj#GSM_uL#vQ!xr&l;(-yNAt1+^4<0k*0No@yaX?dgMhPn3Boo(So1Z?+ z$$geG6y%wCZjbq(R+1^T%vax*XZ-qsK|x=Awvq|0F$_3D|Mwjgm&6<-*gNq+l7)KMI; z{6w-TVGTD!TE@X%L@9pQphBFLdia8!F44Qwm8^|GO@OubR{-Xu|+xcI&St-f?(ZtnO zi1MSN3b}-XvjsU93nvR3r7#jXIk}*-`6qrgN$LLtf4dT*v~qQIj^763mfY{`+frz{Oc~iinWKu=Z})sb{6(7 zZ(|5^u(R_D{sZ9udi38z{tr-{{|4n{IrQ0a-gDTSkr0IfNs4KBKp$lz z_-GHLHlj+3B<@WU;{3L$qoa3CY5I zUbsq(?rL1)aF4=dgOUCTz=sAJ!^H&rTNlE3Fjcw@Nm>y^EPD8VH29ybkVRImFduFH z-46aaPT*LmXmlv1B1{Cb6b9Z)8+EZ}6QaOBAOG8PIx_4%zK(-Ifwp# zH2$|P&gQY2Kfce`el_ojxZL(KqC1~Yo2>ry|3I^lpB9KfmiA3c`lvgm8aESy6O1Fly9BfSz=lxvTgKaX14W) zavuGCqNFP1_4h;|t6P8J-H@|aW6b=!ax*TuhK&f!5dLiRH&K66KSye8s~>C|a@8TJ zqkk83HkeUNHWNt~jU(^BEeTv=KSMBQB|qp={}&(r-L)Wa2k?chfREuu=x>UQ^jZeF zaZiM80YMek-<_|h3!DP}A7Y%xoWIK)M@0-alRmCLFD#6@{(aQnC{FXX3f0$5Py9WO|4dJMqjzV@sC_E;-<$B+Hu*TEN#A^( zB}wd?r1;wdgtT8+8$reEU!5BgP%^^XOtDj;>_?-A`U zGm%a?ZfX+3C#{c^@4NbU!JE|(-?S{h03cKyra`DX^T84#tErjF$(@rnQ>7PO;~;Q; zc4pOxngL;DVn{0=W;nmNNcs6w^!)bL_QvONM})@lK%*=rU0YVdEVv#WDe2}oSYP02 zQ`xZ1GHV^vW~=7lVNz|(OYv{qmZOF>af?Q*+TO4UXro z(v8g3COWs7%&@#k);~}7FIm6Rp|7xW-L{r^hM@}|pk?v)y82N7l$( z%m1!})t>)Q8pZcYxx&Yime!mtScbaY!&bPQUm5Q1^-LZ36Z+Oh2F{s_wlDfrOZoLl*ENZvm zGt>aKSh!(LRaZ$Oc*@^KxMLBl?MuStQdxbYj8?1e_)sV=XG@q(SKv?UzyE&kKC&M- zwaxH#+L&Z{A9{RcGG$!R3-#szHUV_n6Tm0zmHOir3%V&_Cna?PCw zdE8yhH0eOJ%{JgzO@Eo3#5%lliFUlL$^)o-ALDQh`o3Tp^V3o(FVIODc7LAMg^HF| z83_r=fUQGF?7Flx4hsv5>BX-z0=VF_bNgax^!@yam|TE%c6wxFQm?1C7k?4hhwRkX zz>T)Oy*<);O|;z?iK2>lm@4Zl@RMRHmM9_GU@Y> zE7%j^(o=LEJ=s6bw@Nb!t57){52qJQ@bAYaZ0_WfP_fME1z8bsk1m?PRTIE45Hm3| zrxt>SYQd|}e4ZS7a#8X{wY6B`(CD;rXXt0?osqsp%DILURkrCB5MR-OtD~&X^X(RG z#Ny-x_!xB057fhhm=e?htp>wF2R5J0NgUMG)VA;Oj1tX2^b;9)Gwq8GiGcut!UFzz z;vJjM8jN-Sn@9093D9@xi8#n0V19u@Kw;-r>-ZWyxeAJ zbTrxWy3qU@ytQ;oqa)^CSSLK!;pcmDa!Y&K z?%<@C`purFUFu`zC3y1Q4-0ecyoKMMb9%9pFwh?t8V8kF&{bBe@*{JFR!zTVSrAyQ zP`=GE>^8APq`aakx%2s9c#gf$;@6cm7A7Xkv8;^SeGcnurLNDD8zTb)h97ZqDDp}o zvtAR3&0ZDzSA1%wL#wK|ghc=1;^8K%BhItNfL_(%2Y-JtwIp?X?SXd#O#fQpe(ox~c_sh@pV>{DkH%g4CVkcu7^Ne9iG`qow5e7- zZebR!0)Dr4hJ#1G+jQLJ6MX25%F54bDN`UzrjF?uJ@CGvE$$9;U}BOHvq)wxrFVSl zcl9&x!rMhte8Uy`QX<#M-n8j z|3Hi_t)CJ5ytP27qM^a$?|<`d>3!qH%1{EEnu4T^3~2Wb!tzd2OAD92811s!9=O~J zPDUVWSN6I-I_bl-t4`98HKH*Ixq&$MF%QJ&lDkdQXBUCeLm(_Ta&7Ly+~F;aq(&97 z|C>nr$o;k6?+IxTJ24{x3AtwZS+H%|d?|&34Nd|lf&*08_-1ZTmRYB#xgdVtd*fnQ z!;ncD_a-ixfQXk@V_}Ylv`^wH8nT3x9PjP zkEv@V5F#UEo$hzsWSqprpm!~XBI{iv_b^W2&~b25)zqAaiw25;WT4 zEse-W|7DyCi^DngiRU*j!bKn_b74na7&~`nl%H88m6{EFi z=_Uzd9azj2TPxC`&rM2~!l6&bzo~XMKE=B~`A3@)wq2h;cB{t*L|>YirQV>{e}BNS z9$hH8d*(g?>m>ChH#}uJCoOoMj91zt(SGCRoPDAYdn3Bm>jILCBO;8Aw*xfjT>uS; zDyvCm$;&u}?Y5SzR}#~{li{F@gP8zLZ5Zc^px)djz@1FSYv2p>eylHk^5R_cO4Ku? zeN@ttW7#hMi6b#DljJ8pX(-sR*SzJ@v39cpVK1;usVQ~&m3Sm4M~OARGBW)Gwv2m0 zEe=?;ssas?Hi?xC^>9%p+ZX2}GyK9(Qv-R5FbhGC!@Mp!=8a*L@=~qZvi|B+F@Et* z<#vVSnJVrugq%ec99NQkLa#5THPHDc!4^o{5b(9&=0aXaBkJsle6IDb z=Lx@Vbrr9>xl0ahjL+SRbqizHfPu7hdv$lxP2iXGd5G>iL@C0$H8X^pMFy zS(g8!N@so%I5NDLXSVHbyp}$E^J)xb_#=qOJF%5If!`|Xi;Y_Qd*DkX>ZLm|f3Mf! zIR~W1eKx)@_PUioYI;La2k3-P@E&{Zq2mUx%7w*(wZg{H4rdM`!4KyJsu^^jp!k`b z!cbKl#sAIOUs2N~E$rkcm$W-tis-^7N=H?~?g7TL#{8knWQT-+w$U+ihP(6#9`?cU zgN2DyN6MsojQfe_ezyz0^4DII<(qao(MJO;U(H8b=&>|!t!4-B17g^3kSY3@#>w~G zReI&0Tk(%X_FvslhPH$r+E!md?x!okdXga!F)OQx z-#IRYS0=>!_yp@aPz5LBo!XLG?QkA|Bw)r%y(@KT-_|+ulyKSJ;Di z5MoUjKKb}AdlZ`K&I~2C(t zvmlz$&^QN*-z2T>AP@h~leX5+(^ciA&G@r%lhua9`U6}zpKOHO{POMc&GMpzxo{$o z_)?RRC|X?=9XCCty-GH#eeK8dpeG`2_gcJC-se&_$g*4}`Fu%pQJi4@gPXz4#GV1&i|)8_`ceK3_SV+JAo)ntb)_+-mCaBZ08tvedPD z6CXH=-eZO?#LCFXwDHI9>N3<@9sa@nF(|Cl=3ZE-*90vlt$luTo+cjjk#KJ!0CL;) zN}^NusiTgKmG&ox_YKomo?9*Lo3ID?pbzhAy+--@9M2NW$rHxcGLU2_fk2C^FRC07 zOM3=wuI}4f1Ca#9!)R3b6$rZ;m&adNsE&Mb8e@1iMP@%eoCv z=U&D}WT>CbL+dZk6Ucra)2m}i0M@YPsM-A4zR9f!eWcrm19OG`qS6OqAr6KC3CBfm z3t**ccN`Omr9@S=wB%O>Bn&z%9OE%sI*srHZT+pK&P3>Ieicv1Qly30TXk}Zq%Zd?w)W=m+P^;SMYZkP`n_$dlEk=mJQYoxU+4WOX zvY*{4vGZb;2kZv@#QhV(#5c=ryANxf@&hR`fI*1gu?ABpXnTfa48Nj%->;Cya_ za~_y&SyF|8aF(K=Nr1ePA!xmVUF~ zI3f+BGlcxJr=xhi!jAB93+eLq^JW%r2`O4}fyH9FbCjqZGd-Rl74bl_B%K!*YtYbluz1! z#$|CrE5F{ZiO&D)yEcj+F94r*s&szVZ30g(&(^GbpN=nQFKZ7Tv@zC_lJz3r)C^aX z4(AY5OZI)-IeZ^T9u~RPmWqHdpd&2FpL$zx;+du=#WEPjyrkUNUSWf0cW*%xt;9%K zG*iHHW#ewD76o*P5b2B+Njh84Y}=wV%*<#{O$B z-rKG8$@ti&*@KF~*KO`6saL-hZqfE8+tOY4vEm2mBW{?J4FC+TD7-oDJ(24gdnMv+ zeZn0USEZN}724<^DgE=A#nV=HvEb7WpY%^9y;Nh+4~UC4d&3mOh&cDL z;RPtulGkiHA_c+Vj5Sc-RnP84;hS9TVNTS^fNAH(!I?;~4OyAoc5alvuxTfjuNwq^ zL~u^rzoPh&27WZz5W$eP5}LrEv|p%Kyg6QF{hA^ng{Sm;aJK^U9tRhv)zV=nh6p3c z)#Y?-cJp3;+lcVV{d9i! zmjQEOob8G*fN*mY1M4PT6&R*|sR ze@8qr_B}=|+K=PO*|FBm6cw|vCGA%`)#Ip0lG~S-E;k0rL9((5n80v()-UB4z2QIf zPt(O8tBkrLJ!Lq0V)bcLK0MQM zd!g8TiNq|+N5fS2%9j0yB75W7UI2cevT|O|Ze@(3uPqwRaQU-m?WI+|9^TCL*+K?9 zqSY=vV&V%2_;VIY9C{Ml29x_gBF`*TZvno(h6DV3u`oe*HR1EV*P%4##U%*D^Af<6 zVD|iHfD{ZJ<1T!>b+~$$5knb~M~R3k$3*Ub|6xqb%sexsRcy)noH7T(^tarlXqouV=K7xGDb=h1i~fsBAr zs1U+M{RHVy3PO~L3tK)2R>)D{_CuHdtC7X}Vzl}40xFy_gZatQ;#P%GhjI|0vb0w^ z*-9J?$CEU+GBP>x?&O+~5NdLHAx+?st0p+! z`?LcYA*TAo63_jF^Er?LR(LVhCs?-=wmIr%1#^KG>Pdv^ZJ8bw5D4FEu&09ly3C-C`4?Ud-jM2B?S| z&|i^05V-k9(BeO@_2CmJLg~QvDOZmqS$HE+5&V(j3VeXig4K}y6E_sz%3Vq)UW2K0 z_LAuRFmAax7?y&iBORa{JiQFy^mh|aS)8x2NAREj!YuE;J>qw;&?X>u1#Q9smJJa+ zH*EdwAdUZnByq6K8`%P3cefB_daeGYL`>Ut8;x>MI3n+HwmRA=4lv!!SxE)xOyVFD zA6JRv6@5qZpx_Cl{9~kh;JD1&J8Kfb(8<0z(F1Th{TQ9F-f&Gz-S+0;t*liSQ^KR8 z>vI9Z7||_wd_;Kz?*In`izxNKdDXAhn=5AZ}B% zk#F%g*Zz6(u6D7Uk{54G=dCSm%)uAO#d_ND8atQMJ1bD%bYW1VpQ}mKrdo|fKqS8_ zr^@R~hXkg0*5Y<2(YXYY9Uk!p^<=o@a94~at?&sUkz=NZiA&?rH@}B@VYYTNNd$sW zW3gj;s?2}{G!Uc|n4#Q=Jx%jmq;f_l5fUy({FhXgr+-4RYDJ*H^K50gCV z=Ff)i1;7i{`~-e4tH;pE^vCh8o$(WIx&i4vG*4boDKPlv@k8vD3FJDT)vZwQ&8iOx z;-^j6k6^Y5KHFzZVLG0Otv;M)w(-p-!app;#UOdwNi-fwx^BvMec@tKD*?885~z-$ zEda6CL-Bl72AcTqxO)`ix$Dg^APmn=w|5I$A43?LYsP2B93Y7XSH+e3OIXdhGun$U zoVm6#MhAT#o>>Kx3o#MPn)k&&o%9&388PgQbmy<3`+KOlAIuwF|I=lx5)2#I`d-TIfa*=1ml3D9 zDShT7){OdHfnn&$=bHm-f`5<`MFl)>?$gcpYjKpC*?i{MT25L)5N0cZjiv_MSs*7b zDA&$$Gad==`t1D1iINw`s8mIHpvP<=iGXY+u`njFg_m+_N<;nAu|MfXtcOy62!nV; zWMm?JGOghRyZEBvK#5{z6UBnwp?+eyqxtxQx5tLfB>XMr+&(@I2L9Tw`p(FX<+)qp zAUTp6H~r4X6M<}ANfNfCIq)pAYt2sk#WcjbK zv>=AWU2UM%M`i9ZH}upWm38~NMI>*#9Up!ehA7@&y7v!7rl4UK@Q;+7>?a-c8|oQV zYU@7Us7PfL3)r1;h!{r#Z^T~!_<_j(NCE}Iz9oU)osGD~jMrllGChns;x={pkH zM4ZQ=@Sv$|ie!?KgQC1q4&>c%Z@}Q5($cJ9=IL;ez$b@hlZ>au+pgoznw#CnTe76L zXmO>B!QKS5*vZQald%FVPffA6%6}n8y1+TB_nhh#N^;IdrD}2f?+eW11cYBe>=8>Bv z0tUe#k%>A>^oe-5HD1RkgI@y%HnF@}^e6eBBjIv5*RI-DpZ~nu;KbmLF;3J?t(fjO z`7|vWd!cP2t1L-iGwoMQl0mb6JMzK&1JbY0?lq5hUUD*7_^rt>Vv|Irz2f-LCx*Q5 zB$G|Wr7$(&taz|cUY|FxmEQl6b!;h(!QtUrmmbmZ8p)Htwl#s% z7sF)am@HIG`X-)3FGN^LQcbgXO!x5Omlrc1>QiBPg`V7W!mt5WZQubycFE6%v zU;Ivkdj+AMh^h!HjlNp!a0If2gI|f+=AT~KXdP94NaFSYNM@%R`v$C56 z6@`Y`bhGV|KRD9-iuP6go*_=4P+y@jC2*E9f6v@NT~(2KBhve!6~XXZrI<8!gGKoi z;hLvTZ|dh^y(nXc^&Y8OlTDf|xFr1r1UDL$@FKu3e4Lkk0WB%42hB-BE=r{FdZp9$ zx-S}vLx5Zc>a=r3124|JuH^G-z-)12jM(k6H;CMMZ`1XX;}R}o(X!e@g5M4fr_(}$vPDyXkL%G<*E4P42y(@Hwjpv#a<;Mc62gf8)z zgKy$@cG&Pb8UKF@z^Z9rwm+{8S$42xqY8LdvZrnGS7hd8pc&{*HZ+C+MD|JdWga#O z=Uoak+>6r^AdNFs(c5WjH-lW|HbmyG{@nX0Cv;8dlbi*)pHm}>VbZleE{8_bE5EbQ z_=aROlh2u5Sv_0M7>YpTQC%dVW219jQEh;rqP6S_rVos;=cqInVu?l#rgd@~wKPlR zx08fR>P%}8RunrLdG%ijznHy39X2{F+z)lD(wJ{kG^$*-{ch;SdTfPYhptHGTju3Q zx?NKolY0GDv{zewU}J=6YOoxAy;-rXvTJ>e-T&En3e}dnvg7cRq?;8;0zWY;7?$&u zv{!nn+er*-fd3eA1A8$d{z#6|$(J^8)FXH!5`A2Hfd+bolqI{acKo$#Nuot-T;>fW z;I^%d2!LXxkUe`R$Kjagf#8_zfiImVTbOt-Ow3655;W}CV$qoanYFCMz&nVc(YEAu zD@5fY8OFO8G+;(?)901?%pfzsvZ6W6O!0fRSg0!_=Xor+>8*T`BIe6*PuGc!LF4CQ zsyZ{5y>W^N)DkRF{IOeY%7BmSOW5$}q(_y54qeZm?$7;Snvkol&P?AnOxrVhyRr%z z?|Y*4m*^Ukt2aIIBxR}I^)+?fC%6bS&qe75=QwnAeDpgXk#UMDP1-B9s&=bbj3gYi zpEdvxaN^hdKB6@}UQPmlJ&3LCR~xoRXv>@KAxd94<|73kZVx4(nhbL!vd#}GY6PHr z>aAyo(EZMY<{Qk?vHeTULQY&YZI4g=PZ!|~ObvcY8L6PZKB*pjALNsMs~CNAr@DWY zO6T~$STN13It6AOHqXwId5ss(@up?Tv3)|V#yRxpZulf~#`~4qjY9y8+%m)Z!;#gk zyYbZTb`g?fa6An@UI}mT$lv5M-y{F?bjeWLJkcj$(H_v4Q<~5OAg@_Y-3{7_MVaeM zU$Xb}pMaJGdNoIi-b?CJwXzS5PQ30kj7PI1}s>+fx99GExBfMg1Nd#~Sm!4TOY{ zVMTRQi$1fkh)AU9%Nux$2 zkU^9ZWO+%4D$;mLca`SKc&QRo5HF=&Q|jQR$^5oUUDC&mpUi_cKfs42*7hlLld`&q z2aeXq@^hf<6TSTCi3kV1`+;w$)9ewlNy+vla&{KPvp!N}xGd)Psug99p)qSc{S8xk z>s%h<1@ls|$MgHuBmQoL$B`00!cnT9I({wTdp0+n6!`~lwaS=$y8?`br;{oTNvP1c z*NAQs^Adil1PCz2t1S|U{bQA~+0$Bzf6TH&0Y8cj#!nRhi%YKLX-X03K;xdVvq7t5 zytb*Y;F{#We%^P$t9gcB4 zY2vBUf9bqyp7Mh+=>zrAuDtcNCVFm{gZSKUArO@JEv-0tIDeuAgd&c?iIA2f2<-Zm zN{t|!e!6f7v&G;{BzA!8CQV0u-9z?z zhbM{Ton^eyxai=vpD<-1-tS8fpkG*P&J%A>1cvj`M&TLygu1G?WnuSL>P#9CqPj7t zq!%U6&%<3KtgCS%g_mIZG{#oyHfZJepIc;QU?^Q540&9VXxrmlm&w*BE^-&S?`^<2 zXJ8e@yeDD+s3Zm>Ar|5j(I{t(SDL}~GM{ctUJMp?xrv7n6Q@eY;Zr2jDvPtR{X)dh z{CGwa6iA#!%DLQ^ohy#-v^S2qtI!Z_doU+V%pO#7uL2!{#P@PK^lf{sTSAb>jh9&{ zpiTpi{^$9Awp6rJzO0ynDx=HJZ)cap&Ism|OU%Ca%7W)b#KO*feAcCG1w8bMHW-<2 z?)Yj}sbnY+yuzODN(BT-;6z6EY3VfC15YrB3>eUGvXnbmEhn2MhD-SDIsw8vR#mF4qug`t9T?w z+xYbP%G?K6RD0V1;p|$HZ%TyCsF%0S(0a?}#o~l+Y~$ojH{pKSFc^+&EWf>eWQaYN;4`k%4eS~UNN>l+R366-we+}?=`-Q6dm z?u9cFGt*+mb8H+!E4j?$N>GiEKKa0$@op>zW#A26U^reuDjjbO)wU}bqMZ-fu3luA zt?wb6PuJXmBW#`j?(dqn2vr7|!xQl)EM+mjTw@>hgZYtK0=?hI?*k6e{8PE@k13wZ z*z$aRzbe&kf^({DQ3g}***i!6D@3dwn|r@aHTO@2f${o5zfo*^ zvW8%hn@#yFeNH+M5XQY$lBA=t%{>p1{_^eAFcy9O96Ase5h%?2Jp4LqxsC5MsmRkR z+852*aR5V;Jx9W4JA?8y8fs&|OZf1pLn8VJsSbL>{x|K>X~=N`*~-}56M z9xR!=%gpp_@*>fpdytsTIvE)mt;k~yS&QHr9z4!l60pq)Z$tV#bM*-#)a;A>IOzX! zhhX3Itt4@LEpjKq|Fol+h4o$mMm~3ny=Qe*Ip=OOIVmk;rw?5Sk>7e;^0;BTM9+YV zGhuREb8`Bc3hrmai|(h--L1)xp&o3D?dQpjZKVkpSHM}q zvQBCd7hTeQ1g@N;2*AYgl_?Yx{pSu`7Zj5;)Au-$hYNHG*$HGBcn2Ep2jp%L&BD9; zX?wLv%-hfwGyX{Q;2ko@7CLEqytt2>k4>)0s?DEWjW>$0lfP+M3^4-U+1x#?^~K{! zKdoJx>%?42v15NViQxEz5}R`P7*fz`yGg?v-|nlARw!3xK`D}Qh4}&ApJO0G;wl~o zAjsJfaYJDQ|H^WJ->t0}>zv}JF-WE0&rSpy_oM2G5Q{K=3HV|$_J=YZ*K}pd;~wey zR7n?2lfA-9R^0 z$}pg2U2{DLg!ZjZ^}fcZ-NY za?XL53NKaFFw;eaXIE!^LX`RsH;O5lJHK~?R)>mH2Kzl_gEupf#8(FxO6i#Bl3)?g z_!Bd^<}=+%^$-DXLOdhp5b^(C^YI5=vTcuP+}h}oN4 zvG>RZ!&EqJ{S>v)Q3qQ~0j>q*yQG4ybIZyD^EJXAWb`UZ9NZ+*Sy~0J2p4+1 z5s-d}v2A5^`#gT*$n!ZVHj{gk#(9d5&FyCkkedZwCe?WN z;ypI@_Av1@ebN>E@zQe*C;JB8Q^fr7zj#??=~|CF(AKr>I``SPa0eVZj= zNf~83UZs}&b&HmK_z)5apO7hlhBat7z<<4QOsgYM<4Ux;VFqJi6|d5g?~j1@TWdCA z>~Y-91;_4lFOInjmUYb~BXwNB@fSmrje(3E_MjW0(u!8SD;im0Z&@m$(RTg3mrEAg z$!9e6lGuSL^Zri3nTLhJHI{DY&7ZQgjE^8S zGS-;SB=JljtjxP7xstd+8oVX~WC{A~W)XNSn0rIRTlJ45jjvZu^yei!+Z*E7jM=M| zn_~(TU745T&;dugB;PY`{Jd?`TsHSeh^TJk;$2|aVpZ#}O21`u9p6z%!YqI_K9=|v z({7Mk_&EtQT(ufqdAd5#zJR10OJD$1dJZf13~`6zm6VyN#Sb~UwY1vK_d0-$79yGN zH$-Gq=Mj@fcL8tjL;wmn)8))a#Uha^t8(ubU5;=K2DiR7)IHA__hb7%)gi-MJMJy6 zdPO`f6)sl~z53o^&eytC)WJl&RTI+FXK-d@oazUQ+sKoGzON66Jb?GZT|(fwGtf|V z_aVPSw-o(1B1KHl<48o5aN?gIcCjSOk%YASvC&^QWUv+Hy4Q`moD`DquNK)rcl+5w+qmwFwg-{V ze3~rB-E~DoyrXkV$;NlAj<6roeUQS#_4&H(!8~7g^hIdw?8a6lCfPaJ0!n=!L}9ki z+l{%d`-kn?KVp)@B`~Z}xF4?uLII%$LR_<`XGob7MbuX`DaaC86A37HE{21`uDNzr}Ao1O3{m z^%zuOR@u0vcVdsJBsO96pW`QUL-TsDa z!CntTLYx}5*7gd00-z1tQ`eF_@9Vj=4)+;SY=vJYHn!8Ik>h&#nZ*$o`E?c%s-}l` z^U9bfB5fC2&}QIBf97*yNezo4&X6xol(~O!jxQ?``dTrDd@J#%0L3%QvZfP0c z40J$2%T`H1rjlP70gf%#{vWWXB3KO!~ zi4=uGnOO3UUN;t;h?4EOTDLYPomTyuQ-J^vh8XWyTP^(jU0jN#J0bea#tQ-xvrgyP zZ0AE}mn?vCXmp127Eip$dLInG;j+`f5-tFkVZy7Alwd=dFqUmX zI~yCn-l8IF?J42m(cJbe3iI%gNK!o{6(~2wQ98c)wtLuE(x&Kg@;S6kT$-SkGQA9sW<}4azfQj*%)LfVrT5U`9iiWfz^f? zwQHBcu8Lh>t{=Wq?BaF}EFDjre;Uql@Tg>6T55;4jPQT>2*aHRC zTg6uh*44l>W}88azOpDq5N6u>%BELy0(;VH-s-F!Ph)kQtQ_3``Y!8VI~bacyBG z<*deLn+$OB2oZH#wPK#8t7t_dXihK{$cy{IZASO66CP&t&mKe@9c};!8plq|5f00y z6Bk+Vxb!=684M}Abpu^VCZ>eB4SYCj27u}}2La)~US|wpDL-`LVhZrrio*m0xySQ* zFX6z3e)pP|%tBubQV+(@w1aqCZM9%wMIPe))AJ?ZfnIb8MlT2K^O1}p3t9txeSYLI z2;*Izhxlqi&Q$4NM zjr4dvlMwirPZ*99wFho*NsmsZpBHUH?t2+Go2#9#B)895S%ULTuK`cbr(?jNK~E$6 z_4R#7;L}Wp7D)(Aj~Ib$lO3A~0yHti*M z%br(08|7C#U%k_r3!_V~q0wNstyQB^ngy^j=I%`GDVArFn^~K3MPvl>sh! z#{b9HSvAEKwQV{`a3=)UZrmXd+}(q_YjAgWhsGU(dywD`!7aGETjPy0yfZcR&B0r9 zvTFZ;U28wjE!QsFNeke{Hrb=cE;NHzK&&hu6HxX!3KbFm|s-c98g65yi8=|)bWY(M<9o- zVCZ@HoVEfK@CxFfzVBE%;P%1D^Y2W(&W-G9$SQ1oQ(`cq(GI^wrVihd48`1R{nNh2 zL!r#GtN)|04z3u{!`_oPrWy z<=htmZvmJN%5%Cj6=}o9>_^AUEh3-3+)axD@0_s)y3ky4u^!zt#LQ7J+fAmv-s;$H z_C~md{R?;UP}kSaeZw>oBi`anq7%jF5`fSe+ce{st*uE9CSd>- zBP7*hvXp;1B6->*N`JVzaUGJWyQK%^=&I=P{wxD)oyR2qi+}TYkC|li6~aSf?vO2C z6xTcIySQzAd(Ivc7;ST7R{YK2a_U&z;sb9391jjOG6R#E7vFN!T8EIZlW66D81 zxyk3eZWM79pxT7{-P9}#UTO*)013C39Lf7SX?8HE;@<%Weat23`J!ENFqSy6roMT^-bzZ;cg}!hE zNt^2_boYpzf{Ol+;{(={d&h^%AJkP!)R;MWWm&^g%Gs7)wHuw@GBjo_N^8diBX4Z~ zt%mN9f;Ek*T5HdJ93=k-_P`z=^IR8CpoXV7f?{Sldi> z9rs0wS{D#T7S|HH^=B^rE@4+PGGudIp zca5%S@Vu%y1Tny1T1;N>XOld0&D79`*xFL6e|^^a<`C)K=|4MlMfyZkawT_8Ce(Fe z7#Lj+q+rVYUIie05MVH#ZgRcz?9TfsoOM`T&d~Jx*p_}0)l1HB!V4n~*75dFDhVzU zpkB(lkk;=R;Lu_uMeS*+{1Of;o#`g(e#g9$2q3&akdaU?$5y_j?wtInWuz|b_S?GG z`0HxE`_I`k@j-NRvMh=PpSSc61zfiu5B>aVz`AR-%n(mr_jPCgQ>^x=iHecZnjmIh zi=QZm!4F0%w2eNbj2bFhx1HJIRlAI97WbEE6NTrsgkQx`0w5oIbnVSmtMlEKJ=2}z zBCq?RvwwSg+s)?t?IUTPNx7i=)cK=@%zJm_$RqY0??oPNlg2DJDbvv7hR>V!6BAuu zxtN}6>ZRR!@F(Lhzp@E2tR@QVyi8nG$`5?CVMO8Xd*ZxY)mAeN2AuWSL&w=X>XW9QatY2PZe-LI~LR1jN2VoeB6J?=L>w zR3O*uemARkE}qsa{|VJ)^GK)FWlU*BKRP_LF&{|_4gI>W=zf;Kgx(t4Q6)7s#;4QN_Q4}zHCZR;fC9G$>g4eLX)E4W^;ByN%6cLP8m z?C4?nIoM;>qhn zkZE&LXK`OdCzo1B_;a6rPzMQYrb%65ri@IWW=$#fIRo@v+p~eQv(+?zfN&W!>(CfI zyOg`qV9%!ZTd3YU1k-1cNL+_z7Au=*Ln}@EGDVf32v2DU!N0qyp5sr<7b2}1{c}vV z7YDRJt{K5HiW_R=__6DpM)nK*&c_W?SmKX;odlR7LA9tCl5T<;2XArA;M)#Ac3#9i z9pIW&opG?LqL4Xj`{|haTjwC4mQors*oe#Zc`>VPkPDNiQ<=-z= zg7+(lsE&3HohVE{6rlLP9Rim*fNc83W(0QmJzAAeQdu?!&x9t>MZTC7?#D+pT9As@ zfw8Q@GJ!kE#EdhKP*mxi=7L-n6QiT$78Z>IwFdR0#*oZbE5=e6-t+@Lo(2|s@!xl2 zOw2MRVcWz;TUDRJSBj{uPK74_XY~r_ss}EkkP^4$q#sP(z&jZ#5d@sSNBk$P3s!LB z?e<3u>{c*lrH~!9%f#J(#m5Q`3E{q)d{O6Dw^^$Wx=JOj#dp`v0|MR{{ilO`_Mf70a;+-GlSj@})wUu58oJ+Y_;Ml7h}_EQ;{M!%;c$FkG^!H{3!)< zf{>`w-VOfL3Mup{>)}(Of@v0K?-%cox{7}90fYp@?aQ2%X6j6F)0t@)zL46J4we&U z>jc2fa9nog7(O^pb&DAaj&w>&qL5x}ymD}~d}YwN7%W4>#DpE1@--N$YToeE0C5=* zBV>kP!oc7DJ>P=`d%MbO9udL27+2K5+hPy`T`m~V`8;ZG`_6?0A>YNkc`@0Ow>I)c zMCEr2`h-z;($X|QPf*R2GumacBBapQ&-bLs=d13cA2D1EOEI#IBj)CHH}<|rnzXo+ zb@#MUo;5nJqhhTqZOqg51m{4$9nnS(v_3fbHpeDOFy_)xFitP0g@wNBS^8wKcM zmg>#gW&2aakdhu+xbVMOlF{;dC8z@;BCYAEd*o zph1P!`|YPJ=0wbnjj5E7`5g5>)EFaIHy|-Z;qAiU#RC9TH=9~***IR02ku=Y`ybm zE$5Yy3q2h15KI)(2zfu!5RBF;#TsCQesv!L=g)P1&U*mi?>EdEUtRCNy*{26&Lwm8 zc8jqA8?g}_aK_O!$NX$9helP0?#4Ma){i+NDm-k> znk|fDusSGU-=BG5`pLP|aG_5X7Bor4B~K0-;z=uExHqa+>&{En*q%WRX{cGqd%hQP z!4xG(EQwV;3Crr^Iq-9v0rztk#7vlyRPDMNQu{y=Rwx|3#;5)6E%zKkSYOT=<0&mc z)*708(u?4ek+F72v7=Ai_G8O-!2vAq^Z&ADnH;ys!R`sHV&fL}l^L~OKghGphG}y} zNeHa^Q9)KPQZD@WYv$+{iy0SE@RzQI`lj_bYxf?re8EGAsh$$dZkt`PZH23XM2J(w z4f=0F9X)QBSNboD>rOQ(@He_|F8!Tb#zP45pNY`2Hj1pK+cahcU1pQ77rqe6cVw-3 z-h4!RklOW%w18jGdvw|t^lK#g4$?r#fhflr(F9B3d{A@QM>cI=lD%= zeWl(1S|eqMjeh^Km~hqd^OS&fVw=<6{L3QGcCW$-NOY^cqKyRC7+tsawV2e`^^-A@ zy!%)Kz3><07kktWefMj{WQ<87aOHf1~_ehWht_ zFHfCL|GJ9rkjOWZhi*XPuN>mBB_7AJog9Be@D#phN{E^8W~Kro+emi=s2$tN23&iF z_~8X+;&0bh-VoPB7okt5+P8qt%)T}8my$VFbC{H%r6@sgF5DUmQu&n7Q(0;)A;alI zcH6hId|9w_kY;4+Y+g@CSJqR*V9l@UrFhI=dzUC>>88lWa49`vH?8Hq{Y9VLFMzq5 z!Mgtmv_4?L)9!T^Ocf!~{_<4t+S&OPM?(k4HF?t*=(f?vOR19Qz2Qqt@lsrQPx(i0|-mh!~&TQb9X3gZYLpt#YRC8q61?e>~Ii|>z3?x6@z%W;ZN z_t;YS*f7rPRn{g?BOfD|u{x^kVNIGO>AQRc*l{0+%Xvr!3^<)WFm*3t^k(&rZU zJ;Xs+oa%>X4~i$=p$@`}<)@8K2WLCkymID~4r^_^f@54C#FEV^g-Ixwr<~Q#{!qs( zy2u^U>-oY#Lv=G79iGw7^evFw_G|~C;IpB);skG>25chBX^FpxDQYPnP#2A~XkGV+ z6DEa}-52!oZ|XIgp&kWFB??(5ZiW&@zSXI_nX@|8eQLUi7^I4ZR_VysxiAN{8ldonav%Tv;wSSU^C)^r`hSxa(QLrWIfYMG?c8)l8929kA@q_clPLu@pbJRQ>L^jSvfM_Z+5}hYI$098E&Qk zqF!VlxZVvh=oubQKP22>Ww>C&pb2M3Y9-8&u7KtQXcNKST@igB6U0<>ic6B-Xox#< z5@iaavur5lBZ{iE!*|V&t0HI0h^X6?3{gOFRte{pczIHl4HaGrY&i8TS}K>aXBpb% z_oC6I$v7{0Emp+hgOIKQtU-h1K^N>H-$sNpP)kzAuH9_H8~+6~(qdkreJM{8xi z@qIcCoZFn|%kzHhvJ*=vRL(W9-rJweIOs!_*aX^2!n{K6X|gI(qAQTt0tST;r?hJ3=n zT0{zU9q$<9;bT_P5lsX5d?iXlOX9`GogtOsi=UwARK2)p*7^;=gMb295`d}e@fdx; z^EG6Lj9By>|J~vZ0HaDHmf0A$WGFW22{*(B$Aa=Cx#SSo2(4kmN0Q1Tx#ZH zIcI!a!!a>UXC+;Ph=VJGnqtvX;~C{r*2^jd74UlZ)5X-r6gTA(UmF)`;iUN+^l#>O+0n>#wp9_vnB+#o?h)LM*<1$ObG;e&bJY+;yjbzyR_+`pnQWPB2)acvlm}p0 z68yMKJgAT5NNdbMb#OJbFS>b!LVnvB{Wq=!EIYs?Jo%xSa0^SSN!$~>#F+k3tTk;U zjP8f)@i=m|;bZ}FyP#j?#z^U6rvoap9%Xv^n6LLKcUmBsk35nw(NqEwz)lJU=MO~X zdkp>dbn*{^J^$^{w0#fl%##aKwjF+ALJr$Rq&W}>WJdYxyL{{3;h|){KPZGuf`*As zM7MdS6~^U)NO2{qyht6vnF+48lhq)Ok{Hk_%lIpZaJ}7Qy2nppyeZC6Qv8G8f-`CL zE47Z3jxMUs6oZ!5F0G{n&wb;Ud)jcDE1}eWVwh5X8p=SdrO0gaQ0v~F>UTGB zaaSih#1CT4r1;Ug`=;}*SE)iy3)tgOq$uvIb*nm-+05LRe?R`sIg2!ue>+mQBl3N@ z5+r-j*&WR9DvF%0D?tw$LR!NHWx!MkZ{-_f5qE>ztVz(s`!NAeBL;8RF=rc{@WMm1 zhcc`IPL-NMLnFQsh;hWSeRUMNa4ekVEF$7jt4-$#)jCZ)fu?yIzNQ}>9R*;yNdu4k zk!BTij1hvEs%y;!F_>&pwLnys81Qs^yg#`1fo0%>$hl(M;jmlkoMsn&5)P3q>`Sk- z>i(eRkgG=mVkJPl-7OE<2%@3+nKC9fskREY8Hn!kt;M_NEsiE);$)eAWBU6@Y{}n72mn#|EUhQ- zuE;4rV84XdF)aoLIHR7X;lsi$);(JCLb$~)HDC(pVy01pPJV04lPQFviPqKGe>jaL z>UfZ1&m*k78v%70sAqRpLbkz|cSENoW4P_xo@iok#$ zY680ozNSCagb?cSi$GzZs2(ZmFM-Sx2+E=5Glc4sX90`xTXx%< zI9Ouup(giVDSnq113XO&-snt|D+28fEW?F%^J}uFg$iwnu)NeeXl$8J=v1lsR`~Vh z@o*L2mGVSy+xdcu2tsU;?NP_k$cA|`w~jD^=IQ3NhH9=$R0#myyJ&okNv8n1s3oB& zC>(p(Ee5tw)z2o5j_U2c@i!DfW5=a=cdZ}mhA4kC=BEug9e3&no)&iN%Q<;sKw}Ql zQcN@IkQojoC1!rj0r#g|9cq*k!2$BP>09VXAI?mnT7gw=*co)ptRNP!}%>_)I>A#S*x_GGSD~R1c}13bX3<{ zeuJR1kC*f$KaLHpH@@jq;HlSZ<=f{1adO+WVP4VCa$T^w@Uw3wfpB|$Rc}_-KLZ0Ao&Kpzj zZ|4OfS_f}@VoT6Y`1C}CbiW7G3|5<<`|iWuRh7!=!#=M9R(>RYDvOl;He3`)e7v|_ zw1md9DL7Ux8%VUv|Lb+uH_lao@$l1zj>4XpAZmf`#_ei!JRpK@p+UjV13fR+`Gyn^bdtdQ)>ezChECD>KHk|E7KOkD3b#oJ+_u9q4-_VVT=T%}uUpTQOukvu8(bucy# z5v>3-^REfZ6bB+H9q63&@LJROiF{JXd6Kj8B1z6-cxd76~k2=8rF#0P)a zF#BU@xwtq+A*^;sngw~YU1z}vv^6FHF#(As*#rRzV$Xx~PP(&w2&f1xUoiEsiuier zkKIUlK3eo5J>ArS(MnZ~@8dh~3-Jhmxnp5sinnKfzwPTI(F&bwpzgodgwr#9Cu*s4b#kvZ9Chr^uaXgC}uml#XB zHBPs_uFIWD*t&aJBjEzs2~1L4MZczjl8}KvF!Vm=o^P_qfAa#aN4 zq439hBKGmsk8@Tk*`G{sTZht^4P3Q8+6W|GO3(=Poo|EC&B1EKWOI(9Fs4_qCN4t` zDj2fer6S=4qVVnO4gUFhP6#vQ4Iyx|B8W5Pm0?XOa8YyOJJtr?J)fe}3S24=KI;z|iuM)@o>b>;F01?s?ld)^YCl~5 zm)Ad5kxKk_a)c2x-Lu%tIEkdD+;3Pb674y=^CR{1)wK;EKBgG;%bmO5*Nm3KqlI!5<`DBMWdobks_C8$`+cB( z$fe5(f11u_+3`mB+Lz(d9l)}aVRk%eCQ3~TwZ{9^tW?2f7T@L0Yx{TR_~dKGYah+4 zmM4}*geZbc6b9^3G+97zJu!nK>v5IIr}>k-?rL;^JoHp|bf zG?tHlc>( zBB7T{Sg3afDh3{MlwI5`rHf))=WI86UO}ifZ!Zh{a2oPYp~v<*AI`K_1>rEMlu^B- ze?gIT8=4qZW)mgBkIClJzazlWwIYdy8)ktf0JNrCzSP&M5mRbjY|fRn;6QD{AFBo_ z@jSvak&jznkpM^-d{yGA4TdUBXZgCBvMlLOVYBgcN-sYRs)V-kA^4#VyVTBYY_wNm zKlG{M6RD_R$Q3_NCErVvK51A};i<`?5I%}4iIbkUPRbV+3IJn<92I5|aO*IHrtPi8Mqau{9azfGPCi+@sl zztcKg%LiMZThMi7aXAKkNb$d9hkhs`#lb|tZTeZ%*=TkrLPT?%1CM5kk-@$j?w+18 z-b0=xKDyfI#NZW*fK}0-l&CMb6q?gb%244*r;T8ot4{4fe&YAto!yqjBJ)tpm0Gog z%Y!WYk3c}2{hu|1q0*%#&6UQvV#FUqD4milfrx^L*A17T(d0SRQL#3{ zLGB#uqt`LaU`st0Il`b};ZRIEl4bv`dR2dde>nPf8jhmpJ(ziv^CPt*CWj}-TS-Gf#hLkGU}@OZ(3eK> zeb|ViITAB)U~)PwML}tIXB^l2>Bk@1D~N}j$`S-$apKl|W@GL6)P-X=hW>v8os8C) zEamXqMlPS*%tA}!Fgu*9X)_~Xou+|t>+EM^SANAsURU-cA=7cJewZQrpi&;G!^IK2KAXvbq$_%YuERG($vQ(RVf!m z>}oS=7Zs{8tb^m}!ekf-(qzB_#A&{%8@{!-w84B+iV-8_ts(&kdWM zkk)6^`dtz*oKx~836%L_;e-T9A;94hW#pP?Fc5I=1Q z7T9|x*Bbmur%|OAP@`lSH|IAn5HT|ea)WO<%=Ru_&*&9M6gt#|P7Z$X4oXBuNUj|K zo~Nj&Q4snioR6R!{_Af`dA77-$%(Yr8svq2*Y}9$gafnwiy|{13`%1}s2aK=YiRKX z{btEEjSpvO$@NW>j$vk4QO~8MX$J{8T^w^*;4kL_*0T6UPL0=a!n36NEYtC>UOHk2 z{O_@ww>E%~VQosO(N1s(C=OfbqNj5-(!?vpUf#G5BXFS@_9(Bwc9>8VXC$O%4Sd$q zxx5BY_`K2Y6H7QUKo7!yyJ!?PBYgKu6ALIkv}HopusIa?XLOPBhpzzz1L?Qx%iWBb0!Eml#8XsB4M}`-vLT0-i?O3)iDT^7{z8d{gq`fu~{YtjYKA<&6pX zkI{|WT4I-v4ugn8W-C?He)U-)1*-C`kz!G>%g))<3GWTx7gO$ROfI~eNAaXsGWFb( zxkTe4^#GHf~>aIx8}R_zj8}<0E!$I_W8D8$oE@ zG4Kpx*T;yW1@k&nk=7(D9?W(GTMxaGuY9At4;i)0EkKSX_1RXc(Ya!mZ}LG`%)c3H zI~ocq4su2!9(h#%3dSY%%$3NI2_FXPJpCYVljZma(S@kWdqloI!LFtwhD&cf1>=j3 zNy_#3e1?>6YU)susil@H1z3?R-;)3VYZ3F!cBclu-4%Gf-nMJ?-Pt|eyhB46LGMXY z7X(8BKYJZtQBI%l;TzcxJ!~Yggq!H6y`>bSW~LER-#cklU(FcId~+>`^~W7~{Z00B zi$c_W%nau8z)rbD@;h_rC8#x6iKz`!Z{2C??9#UWINwLC+pe;WVwN4Vk=dY}amH#Z z_P27DjI499!Fn-=Oxf*{AkDkWbC@zWXIHJE7LPdJ)-Y$E$^A%H)PkT1vA9=4sqtk= zUBKaJ%#>>O1Cxoxr=ej1tdW`P<8w+KR&J%girVZXPQEK+_f00G=-Ye`M|#0B+KWYA z<5S$59dB=`*H9x&8thD2NkmD`Y75nI=`xeu0B*2?3E%r$4ot-QA0GEK&J} zk5o#y|BK+ey;^j{Ftz5tU;7y#QB;W7Q=B3dO+vua`>XvMfA9Tf6<(rg5Ny)hWA}qb zGFV^klO=N?g-q?Hv-{)C`xU7=SnuxaAXf0n!`0K`!bc|V9TeErinLWaDk_tFSh3t> zyXu4t_1?H7H5R3i!=sM75O$i}UD`HKZ#IS)#wU7t=<5|?U#XuZ7VdTnn5tS;t(FoG zwdZ>fdW4YyS)`tXllsZIMt>j6QVr1Twu1@GcvUq$(x$`)3L78So2eQ8s|N=*Nc@wj*DM#7db z*qG#>UO%_d1UxPn=pTdER=w^+|5CwqftWY;hC z?GaA(1|j>*9n^zuMYL_0!k3_k!3SF zZXaVlu%orVf?}Vid#amoSK8nChuArCX>Fi)Mrh~V-wh^ zIG=o?T$518?~x`ppN2I2Xvdzuhe_6$l}lU>X4$LFfBg#jl4cs3MBT8d%~rzoz3%vN zpK-*wSuR`Yq8q$%;?axRjqkC$m%4Sx5B@)Pf6Q#JgYmv`nF9GiyS2MFLp1aF>3fZyPWFk7zc0yk);Dn zc!&R#V(!#ccP1XUZg@Li0m;iM>w_?pdeY9{80qxZ9N|-bJwa+~rtE*nlP{LpHa>Ix z{nG^#2`ImH*?G9Cw|n)PocEzARu;tqfG&TV3{6(#NMix(QCStQ)x$>AbiI>MSpC+x zK(BVSsX3;RK3`iB1|n38PK19AgD7RC*F4(CeJ%9=teu_OXb2?jO);t3WOBM9RaaA~ zA|^^F%Nd(DQQI~vWDldD+Ts0@q%+o5azzVXjlrwH1`f5j?3?a@OmjAOjjlX$mk{E2(kp2DumlkMwq zqGM}3$aSK=C@wJ*bx&RImVB=GX=g47Io!}7yf`jF?WKne$OCBR{T4F_GH%8Io{#gl z!@wFGCh7jp=<&MH+w>pfFvEjTs}Q_fjQ@N~z#l9B#2Y2FMFZ_#?si2U*lE^r=ml+C z80NwV*;s3qqh>W>ggE6Hv=nzp{AO{V4bz`3UlZzSK!uzt|09>iCq#H50j7J&(ng}zla-uSj$-qUTH9EIgm|14pUfAy+4qxP>N4x!rmze*MtysjU|Fa!_NK-!&ypY+ zy|0*oJHfEy)L0&!(R+z;YxD|#0>2|bA9UH%A;Cfm!9XpAMPf2z(5%Y{wQ>aZV=l$+ zR?0qqZRFe}oCjVr+%DqDGQh5&lkmUWi_vlC`8v z7$s?rJJIrUA0@-mKvQ46`DTlc_4{n-#=ekWI%viFN>9u+h(A32tiSUDo=R8r?{;Oy zJ*{-iaP{OiY{)Zh^jU5AhQ}l857RO zW+H${bly_>Mu*7ltb&-)((3}^9R!&s?AjyuzPXous|)a*Pn0f5N*90E1IY^PXyy!c z9xQUl&4if)+1J6;UuR=|J;{e`umH-DThgGw83}So9=3}jhA)cnF?d~K9-)G|sCa-K zxHnGDZH&E3p(ZmUVf6iN5zD4K92i2#-8Hje!t~5D4?8pH^Cj?D^{3X45|%??XmE6!==NfQk*MvFP;kf13W;%1q5_e4k0@X+*R8h_=v-Q=+4(k5z(|bu`KK z#%UWJrt}J|CIgk##q8QJUwo(Hj))w=#uE+aK4dV`67&teKk+2K3arxjJ~(akK`t?SO zHXdMB!GE(cDu zrQPVS2oeJ(8`F1f_Ti`y>f@T(?d&XwfR5CWnD$B|7Tk!=Fi-9^HOS4%#1ytX>c7w2 zxpTdoVu$bc!4pL4f6gpX&OIiHHS+GkwR*;4E9y2&Ep>E`Ifl|fOnG-wdAG@D3av+Z z{8zvROI)?UXn4|Wtv|hGwFe2GUUwB=elcjh2rqZ}^0M2sE3+CaKfXb_R3@b4dzj)G ztcej38}z*kPyXdP?ID%`%aQ`5Mh&SA6UoGauDXZVinwHMIDc?kf|3SD+PNHwxzVO7)W%6nhz0C%g!tM@c9`6t~HO@`0fn1 zDo~^So~)=iGt>_5G-y(`<2RdXkVm{g!SA!V<+ZPT>ZHCh#GLYu-V1}RlZYeGqDo*$ z#5zDEnYW)Zczx(rFCK3=N9Pm5INOGQ?3vC*S1YCK#?_ajPbfCSR{K`3m$9GqX#ye0 zkv1>4NA~7mqH-WoKC4N!c!<5w1N)p4OLMFc6{?~UBK2#;14P?~g-~hm)|#7kvnlx~ zl(PIN?c|XobZe(Tz`cCXZ(i@BbKP6RI0C0)De3qe!DPnQd#pPL`jH*8Of6-ZE|1mI zM6AlWwfPv3JHB8I??#i9G4D@3g8BHn)d*5|tx;=7Q^~dcXDfJ$be%oHNlvfgn&b3g zS!aB}KRe^06u+WhbC6W=+(4~ltHDG|{y8$us+=`^zbHyv=IDcCh`h*Bk3=1Nrc71e zk(?tll4CobohO+u5DB;X;E)^O6cEy%CD7vItxW3mla*?jIgfMIA60*9_YcObD-eD!*hzN$!x^7G2MQNq4ZsTM z@;f+BU1O-xFY!Hz5tAjk!sEcB&DewG@SCn|tnW8>8cDZ(;jV>m!Ba$I){m>FTMx2H8GbRmGaIK2ueH>+<*Rb|T%STTLAA z4vP=Ac^6sOwYM_4J|iSVe92`nX)SwocoGVF%4U~>wUnlqwBWy%9s4S<)hVo&_!kJM zk`6)we_-N90yf55mWCm2S5R)ytNzT=6!B}*g)bHh_E4*t${L%U4F(w_Q%?z7iP3Ja zGhbCO&6w244wMIf(%UMbsqI#3xn|!QcJui-gV_RYFZ!3t*$A{Qv{NBi&u*BjU9o0V ztcA#eq!Zzxj6}S&l4kcLu-Xq^{mD`EN$X} z`Nb4slck7$rESwB6%>0NnKu@6lrAT0kvC8BYApT(mj&~X89HN5M4V`=zja}!KU3xg zxw#I{b;q1)6e;r>HCiH@FaZ)pE27aAsDmGHe)~@T=C(M>o{yiA+|qec2;cLSzCC4F zFMiLn~`E0 zc)B~S1e@t~Lteq^kwN|CHH~RvBW7b7sgMmFO%Jnx+(GM7}P+C=B>MK_-J3g3L~&ZN>Y(M`i~1ma7R9{`Q? z{~VXDm|Og+pZ!RP zxW=xaMbaI8!=O-SMDr<1ypELRzUOqx?B#t%gg%R3D>UZVid0f*Uy*_{%I?3iH|G^9 z_sJr2sx`#g7t!^pLKo4-=YEB5kj|94ezQauUByrOq4SQG5Z;#oSC3*S#Cwt;O^jf8SRw621mne(Z zB*+%=C|OmdCBruz;+1K(r*Eo%gTOvTZJb%r2FB3!%~S{2v$peTku74zyxa{$$eX?Q zniQk6_ksZd+`&R-_CaywPglU&tUMW_HQ5|Fw*GH)+9(uUosotQ1Ph@$B8_~OYHf9s z^y&D5@rP72xmO%<`#z9+sC^SGwgbaQ3aH0%<2;GWYe-rC^hb^NBO{x(7mWie25~0! zrC7}*JVFeiVx?e|Y;(vHfEtqPe?@3~`ypFiCkQ|Uzz%a~ z6a*^0an7@Ej0=>ijZKIm0B>Gt&u)-WcNVB6UW_jW>%(8yqodHuFC1`8e({@}uDYWX zoqMLykyAbUVm?zgyv+WjW;9n%AYuuD{e`cZnm*peKL}T~N8*K*x^mSKNHpb*2QCju zG%bq-;+TpYp58S65;YgT^j^mr|&YRc4 zF~l1tjKUCAk7MC2!6AzZB4%Sb3w&?_-?w~&(w{aPe?2sZAqD_EzW~RNU{^TxU8x@T zxo!kLBan5^-lCS7emQP{7KG;rv$~q$_`qf2OgqWFxh@P6*4}d2155wZ4(PAUP68pY z1Uk=?Dv~OH-k6Hj^3ng8LpXAC8ftC_wrj3B$4EEW@m3Ga@OK3cGts%Hif{(MFCQSL z1C6#1%QO31osoyOJDc@IV}7}}CNBNZ2JsDX@0T`;Sw#dY_+Fp3S~dci^|K}lC$BdI$GU!1FbWCelX9JyFTBCtc>kNH7AX_x>KzubU{k$tY5LB zGrUwb*@E|D$>FZ(cAr~6XN|R2?b-sD`Fa*bcfHlljW_z9@cq>D@UC)H3CU~fLT+wN zYHJGWC9>$sPvV#0eOm?87GWnl6`d};!*5pqSR|R6JLGzS1|nLsMFsVnOXUbQ)7*}I zMRyVhsOB-KdT_x)D{N3I!D`J1_(8zCJ{gI84BBO+Q7&nyv9qc?~A+R#d z+ioWNB+9Cdee~g<^cJjLDy~L7Nn+uk5&xq82#9yMk2;rv?@R9%CfVXi_|+*n_xRaQ z*?LRv&k;SaVP&v{%vToRgHO^NFY=b-V8yX)cwExE7^_4_?Mf;C`ak;n>*khFdz+VW z%Ed?p(4D4{Y3RF$fYxj)_RV;8)Y*SFFd~|N5XDQL^i#8dG0E(LZZCN3Hi!fbZ(2hg z1`FR;wMlzMof~{b$&YC! z2XWAi%$XIp6ZVI*@Xrwq=mGrZ<+uK1Dw!}-efI$DXGet~L>KCe^&71bf5yCW^b~UQT*u&F z7^tTvB5%0ZUmG@Tb#k=wfWD^dH!oF^qfq;a$i54Oep-+EY<{P*BRORorT7_su$GTJ zV$S%oAQ&!?^LIPC-Wqw*(Q(^I5wYUGqyw54~b6?Xo9Q?KtR{t|;WZ6XYHck0A5*995BzxV|u|x)C!4GG9#y97(lVPl?rhjZ)(7BwmpS=8IH1}DyEB)df8p8N)P zhwr-7&ivz7xwRdVlbfxBdzlGlYKvd>vrb?<6>8u;_4OAo+Y+5$Gpv6s96Pw8j{V_2 zgzyO(v`Wl%GcAHT{A(y%OMpKp#wIw(4e(7KJ##lNdFCB5P$5$p*54z!7mxUlx?^!{U#)2SdG=N^_V?IDYvfz~I2%XnuNyQE zGPBs}CLr@lILq=za+c zd(-<6p36 z1^6=rwnY2xb!l9flz0uOQ#E!f3SIL<>z0Fl#|9jdLl?Jlb7o$@xc?T|%%|&DrpxJ) zNw8b+mIIme(s~+v9-P{s9cgD{~ zJ?w|-jQT)7VKY&%Qbc}LDMP8t5=$yQa7Ir;!dumZDC8`WEEEz$cDKmioMLL%Ot{r0 zFl}=h;1)^;aP{$b<06=s ziVV1pAHDU?37a7x612i+EYMDwBt(7H%1a!-&l6^0L&S?ltnd0zhgZT-EDZdgo1o12 z8mebcOa&o^d6dh$YBHwb5A$ju6?gT1rR03 zG^wJ$5KpIkCILYjD-`axfd!~BVK2iiW+HY?P%$NqNbyukePt51M^6=RIjI6;&AmI! zd3+tng8EBbf==o~iQx6p+>G0o1j5G{jTbWx5CTK^df5yBP(~htbjFvNg6-}f7<(Dx z0W7NG5&|(guCx5nKBmD{zqqVI;3x{A7zf(mO7)@}6kId3-oCwWiK?_nSjJTLuff=_ns;)P_WJ2 za{iwDsxmBoN;bwL*4ZA^3HN)wyyZ#{`j!~4`*b<3RC+p75%i(_-j&8mRS=*QQ}nk+ zVzLiHm==>XS>h`wrmi!ot#F+{yQP5BOm(92upCX1$$0N&W~*5CxC+Md8aB}4>ps18 z#XSZ0oBw+*xteTY_H$ZN$Sr|1Rs4^@9h$2c)qwI^yu^WsZa*`QEG-SSe-E?X;dSRZ z73uPEi>l-@mD!(x8VtLw)=OdMXYZI&-t7?Qt`YyTo#xgtKz78up6!s^6_F}Ag~d^LZgs?{JH83l*kXmy7H zYJR;Jwhoe(3pzhp8NW!(qEJXo`Xt}_YZ>NS-t;5Oy6GrER436{I7^Uc{^QAdxz@W9 zh6h&XzUpX4F$W#IC=(VyI*crA+N_g4^yYNXu_-^w3Fz@ez|3{Yw879>bvnR{bBfV? zs#y5^N*9&{K=PdB{*l3T@pc?U)r$-2cI7b|GZ1o_Kx&y!o94*bmVO1F=1JtSVXsQSno)WTq%H!)R`X((S#J|o?Rok8 zugZ(dCZ;*NvYS(s`#Ef87K!&Ua>R-(IejXs_>qJKS1fY5I;^?YItd4VAMs&ct98_P zxjHE&fRTN)8fc9A;B@$ri?{mdE3+r1EOiHzOl>$<8Sn>5jz7pYdr+PiRrcomimu?M{pw%}Q=Y-5htHl&rfKJtAcsKgW3)A3?y`^S0C|X}vPh z`J>NV^Yfxx;#O1Mkw&ba4 zVtn~X6M@$B1JujHr|LIX$1LmB{+VY*tut-5c$MBKb4TPiYi+9dYktC#WQf|Xuux1? zIkJBAh2v7Y&dtoP4>AkYArZKQM{}Vtjlt!S-KK`jlGBrmGT6zJS!~^Ukd;-y`9Ifb zZmq}44r8oOx`>yZxvuy<2E5AFPQ~qh>PG*nuPNZ_&=n*38Yp{-Hk{G<)2d(1N7I1x z_SFvnKF-d|dKi%^+C34k_{b}TeXK42BW}n&Zrq6l*&n;q`y-{Gd#yn!#|{`~Px6Ak zDWWC_$dbtxhw%`+*)pQyoJ~Pn3@oov9#-lf`mH=4rbv}4SsQEzcPp3^&N1uaMlF!= z@t|M+>L8jjuzTpPncMeh1Df*r=Fud&y0;%{^(c&JzPg0s$0cLnrQ!q9PUdyTI3o5$ zA8<-sX1J6{T2mq8_BD3`@tZvLZy~S1$U5;J;LxM$yGmTe0Y~*GycZ?QnVL_AskLy5 zP8;lZZIDIn!p{$%Rxg00H@x;RaomyaBJcNOj!L$Uoif0KUK%GQ3RfP^jcV`^S6dt) zl3e+@rWOOn9DlP$t_eZ);kdXdprhLrj~YlZ0zQCMj>zB*Df_&?luSvI)l3=Lh1Cd$ zE#fdL_pn<>r5O0_2NLBoFe5Ubk-Yhae#C7%?A%>M6ykf{4taC#?k?cxwjVFgcI|= zE#C_;LL{t+(V;=+8fB*<#M9A@ra+~2I6hs*tB@1{X&X<7R1!f?1C~ZnmPUv!; zUc2ucXb5Uu_)b;HWwT$~6?VgmDQ*kT+z6w*-xy0FIZ52SM2SL|pw_6$XGIctKz=~B zIM2SFYR=!=ftS7v$*GQZH2_lgKtQpyP?40DGT+!P3r6hlO(wG*Y+%`=4P>82_Xe ztE`clk#gcPPHB!~#9#=?dR_n3r#7AVmm?0Zf9mF*g}bZXf-<#bh#^30ph34@jaPnig zikYNGkP)67xFHgr$s{-A84`hG>lQ~mQAiXgk7=0Po$(qY2G0ggBa&5b?~`(XhDV*5 zsd9p86$zuon127sy)J)nR`LTV$^vMxMn#Rmk4@WeN{+Iutx+0e@*17d`*+@^*xWmd zF_}7{_{Fi*uUL80N2!{DN|8~3CmLu}9rJCRmQGqzWVCu}MY#D+1)mA7AXF8UH*-P>p=Zd)Pig*Re{opvQ44 zgKSvRk1sETyiwQkVGpNb)9^Fh4s8LCZdp>mo5>T@>c*|CIN{dhy~E!DHRBxX+4G3q)>+Isp)t51qeUfPk19~eW#Of7IuZ#d&v=qLNcA~q z%09@k%9*Jq5Oq{ubK^f#*WE;dpT~~A?TQ$7%QR)`C_* zG}wBWA%&Sc!eRS+E7f%F)#hMvvFnCYvp<)gY>F67x~7fpz*i@N^}~FS9x3$7=kffh zT{v!eS>dn>4-Na8J*t?ZdiT0i(T^+e0BfOl{IPG3D5GSKcQ+Z3uFh;&BY>KHO?T?1 zrDm*s)gSnr%xa#Sld{H?DGDSlICQPBlb{Li(|OkOl?k86Kk~J-kSwY|AFpRpPB=&! zRG7!pJL&*ZT9gqVt%ki}iIfA+eB<{$wm0ii0yov%6!bhZH{}WU7DDod7mR@C0?!BA zE_aTGI7c{{{Pa`&M}#)f+m2ht>K$Q4yY=C?N%D(lS}VtB+)k?(#b5OYcQn;xy*|r< zNQOwKzbuH<*_|kKZC<}isqzsxm)aC*ZHyc9w4Ss6P2p_1yP~{_tLG1H%ab?>EJ@r5 zv0x8qUYGT9S&cqAjadAhGG`opfWkVv#fM(IDY1Og+4K?JabK&pfT+(!eLII;ux?+( z=z5PCsB^w)!kHknpYo$=VbDL-+X2eRzUBhU?=jinzmhlEn>*QbyxvK9MPfUD*w=}j zpijGGfIQuU+;e&IRocH)Sdf6HWb~;LNh$hNld~W#pFst{nl;{py@bG)K-rEVvPniD zqTQ%S$XC(Cix%Z)-fjyyZeD7}F=Rm1kUg{FNW^3#Sl>PQcWnG~D%-UUe z(q}f2kik$Sw2lGO^X7U&Ecf|NqJ#0r0m_A#B^j*M!}|iG+$r48Yn;#dIxfNa%+=3! zs^jiKed|o=Z(1`xotY+%P6MU#t2FZvy3;JeLk+0a}xL%-iG%yc`K|4vu=q z=`}tnwI=4o^7t_Vh0KTgyD>G%*Rg&VbC5mD!(WS&lATBelZ{Nrtg*k)XXGo{^t9=1 zxj=ujXZ&W}r|@9~P8(>qxxM5Du3+iyJ!gm!P%v}Y}HpVX-c?QW8 zv!&?#-QT#)DL{xi_*^){r8#G!eH={!I5Q!n$3sCZoB7qRl9WqEg4KlNf=P7B_gy@iV z=M*&QM3$c|S&PlPjBt9r&u6TaA+f9@FF?Wgeb;n1Qy+;}!+bE8vgEFpt760GF$*(r z9w2pNe8mB>U%KcQVWb?FH+b*6%f8w2Rlm@CzO&jgy_7boqds#@1@HHO_|^2|yedOE zQg4?ft0HU~%&mvYU|C|T_xn-nM-?`skSeq855K?78J9h0&o&5HoEiHp(Znb_&O7y^ zR&`|nZ)ODj=NZrQjijJXOG3ip&ozk+iRGq3bFpp;(yY26Nyl-8{vEj#8Ambm!j z=Z|z7W4|u6J?qEyo7c*XAIttSB~#~dQP*jSZ=7~+;uJR2`LSmJy^Y(V`$_K8dCxg( z5d0-gKkM1)w4?Tb#CKzlPkJyR-*#d*B_DaaT9)^6OMU1`S7zI{&X*tjG@)A|F_A^T z?p0}4513eD$~9{&R{>h27n~LOEBES8-GzB9KWu>1CPVg{H2}~3meNfxs|zw=@1TwM zr;{c79RQzs2z_ND1?*wa1v$>d>+YeQ4nQ#ZXKdg7k=$~GE8qfed|QQrIdBFfoiouT z1CVo7VMaL)2mEpka3a)t_*w~!9DQdvCuP6lZz5mXCWj-!S&S$7TqgWtYA97j?%X5g zdvhT76LeXkhL8;~mP4+y2+d(W-omgwe40M~27u4Rq0sWIqZwI*YF4{NalXg2&@d#7a`3i2(Cwora?|1$~N_;2Rz}Un23b?@GI0h=?bXqwB>yergX zB^neiJ2|us$JNj&*EH}7l}raD@^EhHtM{8TCurr3U9QZ7&PyzOj3_BLr-9TB*h0t- zF>uDUrdo2J|M`QF#o;>Nc2HNn=QS&km!CbFm7CoR>4Yxn7{7|+ji0~PaNN}|vnI_6 z=@tw+IwrOr#PG%5oh~F%SvI>Mue@ec>s}E`*E?z!JAbr6sxHHm_+67+w?jujmP6SN zg3RVQWA%Q!w_hwGg{nH&4Pv)o4u8sXh`8R3e6Qn3g=_O1T3lv7r~~xKJE(32<)!;P zyB?GWH)J6AQ@cJISExYJYr*CoT+qgTBI{;ylZ++m!>!8c=~vJwQmpZU<`?f?lZ0cJ z&~Doa^zN(a@u+tH5r!fqYYvJOIAgJ43Ulb6nWF#WHcRQxX0Ud~|QZJL0z&vU8(7fup`--@@V8AKa#9bRzFC!%`)V%HHfzS8@EiF1Dt5@*xA>L(ah?o_h2VFywoCaL3rjPmBoHs9{mk?>- zUif0pAA%fEm+1IR;KLGH0#W-Qz8Y%YrjP(@F9%F%m2VW2gqL_fORTyyoY(zOQ@TnO zv|Fdq+dxV=f(VqLrk3Qd{yZIPg#ZO)QZ+c>Vn44cl)}2ny;PnKH4?K9mX|9gzesL< z9;99gyxhY0{+g+YnyHtNOHUC!>d;I)))=-;Ica;p@wsCc2EN#Z z0*-j_1Vwxgbcn@cMYI6^F;gM}e2G?Sg)78H^$F%XUHuxlUV~d&_Pmoe%D|ct@7i@= zWb;6itU8IvI}^lMh9yC`>~Z1BXgd3J!aA~5_5=}fPOTk_QxW5^-%qsD<2qMSy2E!J?NUN_$k;CtUwDr*)h!_0@9LpL$kvZM z`t{$8J?2=$*oCiLP?N^N_4a47!@aedV9yxmL*`O<|4aT-ZqK$inpq9ehmhC8g{Z$N zcic&iSu<{{eb^kCv=Gq~?tUi`FIPV=ucc5%F-x6O^7)scIpK;kej$}pFW9Lj?dn_< z5kamtMSi=`)VM^+=kv8CrybtW;TEJPCU9$Y-Sr0$DsLy@uRprN!0$Bywhv>hKtdB) z3aQ^N8>9cZ+w0$&zdp4m(Z-5S;{h@WU&MQ2aqhsVG2GCr!9LSOFP3B|9H=|mtTEm? znAbVW1Z^$nZFac&4)pJ$J{Sv{hjbX?i^PbH4a~ z?ubuPA%MOYH(4IX|5?W*NGnJ48qVbLQ3CKy8Sh1F#5*gi>iora2LQIUDt{YGV9QX> z&20K(OM(7Jo$$9MMNg6B`CiWI`f87X4d;<>C?Jds&{EVDR(M}Q2DPcU5E8rRXSaP? zm2ULgjUrE6OaS!_CO-{MKrmx9p6z`X@^C=R*f3ScP-qM&C&vM{qZ#6v`S2+W!u%tR z!dcHY=ehXuaA^_6)^0*mA!=oYJeK~TKjf)0sfaFGS6a2uqMUcKEU-feB&A3;m7B`f zlGtu0ztGlxS-LUu++LpqK`ZP3a{eVaI6?EZXIT5`OOWo5mf%-d=!339mm{Yox@~QY zVp+j4PXL&ByWfZQBqhtu2`<2Cmx;;_X<16(8OYx97&a}@Zbxr7}a z5QI~*0XGb#BSYS6Hptqr?M#RIIr}j_RYeWphr3~|I}yyQW)a2Zq%60V25b+wGg}$o z1Z`wsd&aZ?g6WVg<{VSx8?|yAHjiS=!H2t%y5MjR3{+S?Ne@NO#Uyw+*{Z3W#u*ei z<4kJ2M|}SzW^eaH47*?CVX{1$OpI%K1#GQ8oc+h+_lLXi<<4d3)jX%4g*BOwQOa6#2ohuki#yJWtb++U&b<)@iDUM1Ls%o zHZs6ji7e%erNkn$`4h1Ghdupc??vHULC5b4^(wM{F~zifEkhPu@il(oQ7b#yw|y_U zp~Lt+lC9USYM&SP^w1CT+6d&eZ5h2>e?CAFW2(@j_uQOMPP5mESRB%wK`Icy>)_-j zEK3tTodjcqRTvG*(#i#CFiO6qa~fGSMELMP^N{iSsL8`p1()WQPX1=(k8j{UkfW6s zSBb8#;U(_#Z;Z4Aby(u`c725g?!HIXEqdZi4&c^!)GkkaPy84`2g01ZR&8=82}t|r zTIf_6vHlvF-PyK>KTk*Ba$!YwLiOixVGv73 z8d2OvM)Pu(BDr2hKJmBo<(Nb-aGLe3HLU810C5(Jt~=9GG1{QQn$)Svk^nw6vT|^& zulG$*VJDif!N`F^jZvhHpjd!6aUDSh4Oh~vmF*6LDG_UynIrU_zV|hXjCkrJkG_E9 zH7DDMZ&>(Xk8(qLHY#gEph9D>Y4%KqLDIWrdbWaZdqq#(%8Sgp9}KfidQy8?@yy4b zGKiRo7^;{_!=hR2gwk1ej$b27@tOQQA<(L_Ml9`rI{Wl*y%{KIt;wR_$Q^;I@|ZVL zLla6BwfBu3Fq9`bVy=1R?tQ>Gu+HvWVPc?#xR(yb|owVlMM-C zwx;5oA2*|NAWb~3u6d`U{Ngv!phKW`V%$Ar)ESv%gkU;_xfN&3I#U8MiSU|#&|^KN zrls5xJ3aHaRsua7O+J8~*pw5mVeg;?CJYdpS>(*8NOie@1|sZc7;(I35GFg5!lL zX@zPE&4}UnGQfY|hp?$XUy+3f>L}MWy%gI4SZg!}+=FwoyzmCvgEt?XZo};Z=05LI zU2Fm7<0X@82#GsRyF7`iS3U41hWx!^{1UUUdo596<8gaS9V1Kmj03NM*zPzBWK;Ei zmPy2?w)vwvWb?>Vyw}P>BP4T}!8{2Mk9fuHQuZ}1iTWEwL@DaAITzzsaAGv|yguZF zdHLotiu&kfSuE3DfZ*1T-~y$5klzz)yYR`z@A@xPy2C>N$(%jX91l41=GRkm3N&+O zz6s0gZ-b1%7FiY|Wz>Q6S_Z<|Epj|PqCLt*sk@(F%k|8mYd)Fj_p$b+rKCl<(yZm} zk@i^ORRBr?`J+|iHVlVV5>%sC!L^ko3T1RnUL)ZAbdM?13_Ob>A%k8o0erKu zQ(x>}6?CHjJo8T?R+V#gG3Fxkk>+z&7Cx>^{ca$ZROwazRFPvcA0266Aa_NJZOhyH z(5r@1b3Qn4Jq@lEB{U91y4egtd|a=%Z(htG_*x#^C&v@BD@gvAUeL4@1#T?T2?9Ry zj&eD`ba619+N%WMjReaGduZSd4G;aK1pKB`@3}UzV!@^|$!fnQ^s_kZnlF0wys#|x zo)aBv-X6vfoz1D2S{h*g3EsWIk|W7qMXN^ZTm08CcfNF3oVcIkO_*bye`?}^O#jy zT|QK(@FPaKOwfOl5QE`MXOhomQ;arItVDAM(%=e%P(p6T}#?x{@aib%)S zE>S;Y}YK!*~6q&!}$Vwh(|8YtfTaiqaMtWh0 zuUy!c#+<2FziyK#z)DcNSHA~pN#w~l46$(E@)eW9aQ6nj)2k3ZiAe@wvg|x7CjIbu zB<^C#**{U9B&{-h6ay%P%+Bs9n@KY+3BI)Ut8F264e*5hVC2iigI6cY4697?t}IMN zZ$cN0o{wi8u7T5hp9{D}1gDmGth%+es)(%TP7)Q=C0QO-LEMHe_T;@MJ`y?81U8iZ zg|IkTVuEx&-ZL2pgIT|_e@UJEN#ok1RR(ne(;^olF29{`DQ+dr^N1)DCL|1l>b?iG z1G^MNOBgSPIYtOa26i~U2%>xw1m1&J-T$VWRf04Q2PE;sx;?pA!L&c*=;B_N#}c|+ z2Zvmqm@iiOXC*!Dh$%ipbb$#sE}*W%J4YCA)>{F8rs`vn;MW%0+igSU=O*JxlOD&TvM#^M)OA6HotS?nxz8uv)|C)JW6KX7 z1dnj`qf~CRsgUV@cu&OkX`t`fp!-KYg}MZO|D_KDm~k(vyzeRi5Cyf~kv-}{^Sw#JLYC}|Ne zb<}sn*P;!LX1+N_=E9cQb}}}Kd7)hzEqS5co7-9G%jfXv!>bM}zkOutZ)EfE!(Z_F zk7&t>4-J_M4XvXuXP3C~(_12?OAb&v#`1L8SBymmEqc zo2|r1;47LIQg3T~H0Gl}@Ty2U9h8H154xu$_=v3jWavj?#ZT69fz zAthPBb%IUKh^+dsGQ`P>#x&Tu-lY2pL^@TgRJLmGLxB+_k&vbEn{(&E8%vVOASem5dvOL=n#w^J5t>=^!|9Y4u2D9QMdH^Vroyll@G>IIZej~zV9+6ipgxz;$~lmxmH4IVvQ$J z=r+q2pO2196ZMaF=Png&@h8ro`nyK;p)=UIKWB9l?OXgcA6i{dP{=;Rx|FDyPV+8WwR{OwIAa(0*$ff`PQlr1g zW4ksblFizmcRBuL+QEN&*-8}#l1REUh6Q&bk}p2$OB(5k$w{r9u@c?ta!ivipe<|n|IA-68%n$$eMqhlUwYzOdG>}an$r(2Ni8GBqlk?9s36!Qb zF_ZiHub{ZNc&bXb+A7Jz;f{Jm z?}YNl%G%)ZehpOpe;PhcJz4qFI$MXb3O(7Nqkg3@}<36?08GIJe-du8d*+ z-X3dQ;Ldj4p2_pFoxCbisHa%g{Y%oWOam0b=P#e!d~OfHy6QLfRn7D_H|O);)hu)r za9xG1G2OPBvN|F9-fZ5SFOFJz!s>GEnh1=~zPrKyEIR*q7gC5+U6DsTTfe8iwBush zkD&jdgM5_@v(L8^)lbe?$n7+o#n|Qlg~%HlBjKmD6+XoRj{saR4(<%@<~9vf;^iZ! zE#b3FbR$a+@pweDZ4wtv8s!Xr{Pm1``i6@eQ_J|T{#DHW72H#cM8AjQD!JV^bVOsV zZ7&>m(-IHn!%fngJJbGTi)A1+np<4ZY;l;I*FtXS^B4TF@kV_~Cp{zoWqZ$H`YsY2 z$Yl5_uPY2BwJ|zmTc&;K=?uRMRNAN`wS=p1adUw2&j$9-lG+}EaqCi2I{tLM%eSkF z2KG=P_1%0Huk6F!^nLqQdT$XA>&>mcx1zRwzSD#BPKAJ5jUmX~z(5+7N&f@CJFRd9 zh%u2#*^_o}2b=YIz`5Lp|4xf3mt z#BOO3Pp6WEry0rXR#c|*VvS>$hOqEXPZQFpY9QM^CDVls2N0qr<+& zu}4c-e76l!BRAM(>urO~qy4tElQwu%Rj&3|Cg>jucKf6xgsLb4_bMfnMELI){(8BA zc@(!V?AOR2WN7^*U!7N>RQBag1f^HSoJb^cK6)$XA}MDU{1x}f^E+v%4b?5=r}JlY zVWirmUzCU2rxxFh`Pz-5QQ^35n-klO>N2Ak*EjM!u6MF}$Qb4iJC=IOMOEH!U179U z=7vglS}uJqQd~7^Mho<8DXh9NK1AJ#I9&L*AOU&meDrja>d)Qd|5+52WRaGJwx-%Z z>rN|ccyapf{Oqr6@b{v?{wDNmcwhEM+&U&ry~kyr z>uiZlP;d0tYW~O7z&zSph?uiU7j$bZbV%E|8zgBrAPv3t`8}uOyV!?wek>$lB0I0k zk8zyl|Gv||THX2OZS?MZ`PNX*1rYF&{~9XsSGWJed9rzUizFb1HIT6vG6s0+Uc7TR zdyM-xp^}Sw!Tv4qZe~kK>eg)4jPd$4e$=?KHIvkstu4#H6BZyhrzTT~bk)-fNu2X? zL*L#>8b67e6CzbDC>?`Q8o9X{kG!3~3UL1Moo%eQOx9uvG9q!S8Lu$vD!+gEzq35I zCdQ`uHaJ-FTTf;94{p+*vNY|4x9%kt>ZQwFME3vxw<80nTfzR=_^Yj_>1A2UotQ)a zL-Y@SB>&d{1CY8e^5FA-)$)J2z(xEA_#)3HyfrK=MzV-()Z(;^>7UVp{Nifm@3R2@ z+gjT3Z@JZF3!#FLc93f#1#;~nIp>?!|91aBuR%%rcIBLnJ<0gl>ax3KnLhsE zzOKsN&8^6zzZEAv+rN2SHCB=u3*XDC@3KDayj$GZ^7kmd3vM(}0bV0Jm}|o{qbkh0 z@Yz`xWbYzYtptC=fb7>_)N04h_3Z1P-Hn6V?`|Em{1IAdq<{912=~pMTy5m<^dh}T z$Wt|~s81=O*mqeWGK0$SM{+I)gGrhz#PApYog7cmmOs{~Jed&hyQyZ~9p)`?qj@Js zP@45NTLPhmkeHfDQZIig%e)J>9{N(J;;UCe*O9-{|o}u<7q46YKs8U5$bMk%!H3y_J<-iLFR4x zy{~e4jFAZ@!_*8v5dw|pgiLj$%uU#2Y|rQF==bD;d0nnFb!*&Jwik1ih-d31wntkn zZMz#M+RE6?_W9ms+Y2}~+b?_6ymeeIdbB@UC%-?_adukaKn;IOFC}Rd*r83K}{KV`pTd&7GIj*^@MckhYswt-n8it;|*5>EzX^rqZu-#cd`yUqG zrsxkQ3nN>5+N*C%GxNdyegSROdpVA3+Pms*aag{M;XvyIK{fkv zp#c~9!qn@q@Q}2@W^9jy`=B>h9%ql)cpmQ>@YgzA7ml2z{lkX;=T8TJB+JsN?xmMc zL?A82I3;3#6JMtdZll}m9FS7`R%Uy$P|Is$OW2x8kgM&d{NuDUQkI()He?mCo}AIo zr;aTEw)AaF z<&5vW>)8U{!YUY1@|d*9UoTqU{0>jp-1gx9X!pULg|qjp3}Q3Ku3UDFiqFlF7jQLo zdBkmgNl~hMeWm|pFlW_Jfv`yZPqPjF9WevvZWBv=L+A1N^n}QhztXg^!5K9pec+o} zCig^^rs$8A_xTgZmqy#(+KyM%ybt{@i#ZgB?w8UbZdk6QypX*VyvtOJi3KrJ+0z>aOj!b2|C% zCzkGOYC79`=f#q>B-@5di+?@6I13e$DRS#OgC8Kc z?X{wYjcx|r7^QnonaIX=+X6q%MQk7JVBFd2{IXEcSBIAPJ z`Dnjl4ETh7p#He{xQGcND+d2jd}&JG^enLFc`%MB)T?un*q!8uz`tY)npMq0UB zSt^x_bNA4r6TW~~H3iOE?XE`kLQ^o%CoDaBN(>E_JjOJo3z&Cp0 z54o?j_tuk1Eeu`2xk0i!1Yi$;_upNGMVs=cYD=6uY3mHI6!xq$*kqw)?RT|hw<*36 zgoQz6rk7tG-TG_laJAWh3!~Ej1n&SlNpZ5J$kxn!lr5spdYROERz@ekS^L8;B9U?B zd?}-@0Qi&j{>rK(5fzj~M8+XW+HTA*&sDGgkt1p)y)BxM$}DekV1oKh2udFq8CJEF z=(N@Q-b4p#9eAqN*J;JJ$O9Yt?wJQo1RVA>9t||54W`!m3@+acCm%(nU4FbaoYxe) zm?ht5y*ZkV8EA}x%P+ri7!W~pG>m;VJg#!t={TCMo=)0%aDB7eg6W$8_1f~YT|V=Z z00JSR6qPXeYx8FU!>x*W2oB?Mntq^EKOvImkiw#_c^l zR?E1k4j7a*FnHM)YTGh68Xw04{dX^b298u%(ZgQL&3okN+G%ju3jw5&-9d&1x@=2U==LVO0FW8x?E2z zo&Blj-`nTDHJKJ3I>l$p_{z!tEux7woRryo{_*hlIblhSr9L^#ZfZr}zLn&XpLUPR z)D8Ib7o+g-9SR(ti;^1)B=wUp(LWVzrJwRzVlT@N$E8fSQx|^`*_HVd%CGP2gEF5` zl0g_)>)Od+Q1Nbvsuh2E{YYfz!!jSn zv$${y+Q;C7=5kKrH4K`tqf4W6XF-&Y*op-OP=6BhIE05t%g;nJNlvI%4|JBqxCkaj zh!##`LDKKWTJlR$8kKk?s>|g_Ky@yt^QkGv#SBMuC)x2AQgfHlL5md?HA~@?Jw2SA zr-*s4GS}KgUft2?h#sTguHV%gmX9vxG884l*V7dxJp+MC64N*#!OjC1AGvt*$R6G= zuKJR`7QjCJQ!OvL#Ojy49soLATYWa5qB5aIMI<2}EOHv_Ml~(8;>{T8r_XL{5q$Kn z^e|1TlzzSOQM?QqAx?;P4l;3RjB)lMO?MxyO}S#H%{((+^a;JU7KT zl`AY`*m@h%AG!2>8=mg_ns#nZBJ*r{C(7adus75tKyUzIk{ypJD)_R*?Ync`GG9O~ z-hB&eINJyH$GO+P;md}UotJK269e{PHc_9+r4T%TSYvtjIAOvT}pc#2R;>qNZE#TM-Rc3i38P+&*yv@cmN?#hNW zg~`mww$*?H#4qXH>(e?jt&3e@C`(ZRaUs95NQ=a2Mog!%Hf9#zH0dwhAFRr*WTzux z8t+sa{C0>-5R0AeXfb-JhqBnae{MZG|8U_MVUOu^eJL(yqh$-GDAP>pcec;2nGMo3 zUwq4UrO0XvLfc(r4r2SHYPyZ1?YXE;@Ai@BH?J+`Z_tZjn6^>+Pey_>o`cI`h8M}n zF41c}!V#=d%sJRE9=F>+R=^>G)~2SxE5Htt{)kFuPtK34^wX<+4{>~^Mmo6VpGC%Z zrauKJ&|VB5TlOhpc0O=f7Q0x3!^=!yZtQDuG??~Zfr0h({OOh`h=-P_3Z@k~A*K)S z(~g!T6Nadi$y?zrP0O+I_MPylPpn}){ldkoh)TSfZs>b`4~+;n|;j_~$KpD`dy&qY*IO3Y@}N-K>BnE*ql;2^ZcJ&){o=G2n2 zw=FV8_g6=8$oKPA{RE5^VS^sOL#CZAUMOph#Q}dvc=WaDBURZ75v+4`^xWqnCF^j9 z$7f&huWkZM2Izy{Iy?0CO{P_aMEN`$=WJw(a9xuKdV_pM2naZW1CSjZ9c||&QymC*2qc8n;aNG{XI7e%Z9oZ2> zUHT?bGy%_-R1W!j%f^zy7_?fx_HI?xF#{K#n7J5u!95J*yzq!)p6#E`MgcU?H^5GE zLH%E0!rKNO3K5k;7A(%!na92@6>(<4glF9^`!}I&-7}FK=Z)v;uJG|GX9__rRvAb*oWw76kppBUuLt1;xsUm zfDi_JGG_42DIEa??NAJd6k97i(yZpdPK}x5=8SEFG6YqUW}N)`w3|cnfKSCyvC~@~ z&6PQ1#hZOH2hC3CKwFWM*Y)dTTFRM>>%=Ojz}?H_T^vIEV3DgUs^Q)!o|IS4O=gm= z^^qQj^($;QEf{YIew$~%f02a=U`f~D>`fc;dSh+sONVs;*PZdtKwH&dW|TQ@WPb|C z=F56WOQNIj@oRJSwT7KK>E~5!f6cJdeaiES^Yr24i)soX(a$*%BT7mSyHD}^J=3AJ zi4PnTYF0|ePl&Q5V~1U5?D$h!)x^YT0@1c7kIfJ5(lneS)Vur%Zd8sg#hCef<3D3E zepf8BdHt*Vg;c`i1?G7aT~)?6`25D+Zj;|UDqHw1Vn@xM>8NxB$_MGUVE@*TQNGiykbX0=Tp&sx z36HZNQK(Dg8XZDRDnx&^sPOXwW*U&hWRVs1M9SE`;1L1}Bz3LZ?l0oxNea4Fk==ao z>Fx^CQY63lb^4W8eszsv`V2=Zf_L9spVPQZO3MM;dxoDnWqrAtgZLtSc{x^VHTTt9 z^{%irGL^MCYnRM4Xz!;W27;T*OUoQp4jl>H^+-D?Zz<@BhUYmvX^=GW8NHIAm=|>> z@O`aWW#`gPqWB?{bXs?S`uWv~fy5;<2a;!@5`+Uo=L{vt{JL)MZqsA z3izLY`SRB{Q8T#T|Ef|to>q`;S*0Yp83XN|}ZM#3C5e)9U4q3n1P zUO86V+%RITIaY@m7)%8@`uekH>czctB1omHuFVPOnbB*PjHNdUAdX!5Ka{<7R8;ZT zE-ng^0>cb2bPOp73^}xPBcVvc&`5)X(mk|jg*8i3|#_BcOxxINZsT2uHSp_ zUF%)1{#dhE^T)uP&pA7v{p`IHYK2hcG9pFc4~r>SYPDRVumk%yvyg7h2|9h1hx-9) zNqCz+3qW{fO>k!-WEr30yOJmc7J<{h^enE3QN=vcaWqs;WJaxY$Q?9%Y=9~L^deaqpXIL(&Pwtb zs(sp=C4}&+r}sVcx1gW3{_4TNupvdu!k&5hBNmTEEB>9Oj;dWy!?9GI7jv+tc$__7 z%l`gsx#@%3%#43|kzfaxMuE7An_sZkl1HBh(lNW5;S<#s$=->%xN(1Xm)$4rWK!SC zL_w4>scY3fnVD_K)l8e~`aJu-u}z+6WEA%2n~dC`B~c(`n&$f~LhYePU*g2MF(U zv&y2HyF+?lOP|)ld#o*%z{lOfN-NOsS2ed|Hba~_ zMc_cXpY|i<+g$H&yh{$1!oO-?D`K^9BG!(aFBj)>I!-u(HJ8s$1N7vX3K>= zH<`-rOFH5?l1~znaqgu*3SPSLf4%@{mx{3&;hxWhe0i;@C`35F7mZEjaPAs%(K(aL z-51vxajw0!>$c3GzcjK!zc7fwp1o5IF~6T-aoEF~l8>^8IchfGieswxJs%LMSfU&L zP~S%L`Do2gqE!NPlw#hW4n^WqNze2Y-X*}Ld9goB2CFYxO~~cyQi;aybj*Y2j|k4JA1(MLTc75HRhtX zH$SZ6VQN`x3#B&VS&t5BbFG0v&J}qKqM{Rb%qBZp0~5C)f!&xCxKv}3Z0G8lnIZedx~-wDQD7Xv zxX2kWf-Fl0Y|ef+-_#C|PpvP~IQ*=8j4!u&Rka3FX>`e9JT*f-q`FFNCtv+`&7Ubt ztltGkno8*=FdVeT)sp(65 zq5!35GV*0!8;Ofnh)=LLCbG8uYR9+cu(q>~!x9~YFK6~b0yozn8zVm7s--#IZm6b? z0?kf+dYf;3i~U@ms_Tx!@zM~wlCnqC&8O9eSe|nh z_s<@4B@t`WRzXl&s<;!v`DxyLzI^l+n`791SEK4m{ONWD$W7tk!p!iQKq;LVIma8V zfvsye#YPW@)zpiK7x7Rw!b_f_U@3k{orwr=%AJV^(YfvxVJ0Poc_e{1!%rYjo>zIw zNh|8hpKh4kj2b7*bAgdKKN?8`{jMT6zqEVd*te+Ns2Pn_r+W9#njCEyi!PcWo*I4b zwB9>&x$z+e(azqdK$k115XAI&a-#z;&iR!p@P3|lxJW04^Apeqo?4W4b|MT1?s2i* zB=Nk`m|mvI?eF^ReiiRVr(@s~khI5;Vqwm82fgc-< z#K70c9+BGDcd06i8~K!rT@u%CY(ix27A0iXjiKJ2A&G_$v;0%E$~>deO4!*Rh%zH` zU#N>3q*4X*?62pxng+jKyXGj0h*RfBy{*#eVu|`};T!8=OsO^sY}Dg+pAlAfE`lq= zS7v!l(D3~cO}6`f){lsD3{!*b_Q!4d<#0KE`K9G+kbx;CJOVW+U}JsO>@mL{c{m@` zkG*}1++)`hIA7(O9n1Kt6;|LVIrgZuJHdByrdmBGvivhElcTj!a-itrm}mQez6+Ba zX=T|;22p)fUd9`~OLWorz!2rK@u5n7?G+(5ALIO=O9QEpT+_7Z1h+YDzqX?{i0>Tr zS;en@Ht@O|TUqQqtk-(i_ttp1FW4_qr`HGqd(T09B{nc_FAmw+82E0&x3bQ72&RsR5`$gOKKb58Gn^!!d+ zkC1-v2@98~mqY6j|E9CUguE#m-ga)K^HYxX{+P}Ns2ZO^F$hC-{8fqI_bP~!0r2U0C$ zh%1S2u?aSJFwbNtBhCMnQiVI!mfvaVg1e@z!@|Wxmt;L$J4`D~Kaqxzgn*n*-R_;u z@6ft$RX-!SO#?+@w%JJZ4KZu7&#Y%}5t};qs6k+FW^i8(H;;Nytuu5SKhZKr*s9!; zbG~dC;c+K}?LUr%E#NE4uL2aSxwk z$3$5ttca%Z*Da4^JUe1b#%7*gS+e|~duSie8D@c*odmxAY|o&RaQi1-K($-At~k zd3aNC&Td6>EgY!gU|rUgH}whHa)}*XZK_oV;WHT-Kmt?j?~G1PZUyQjak8noe7fDB z8p|iTluf7s%#EFtO5OR07lmAT$T`_DlXZ!*$Ki}hCW8?_T@8nQQ&z_7swj#&<2D-A zckg~Q&A-vio5Glmb5%FzVm2*BxoGnYZBHOsZ!pQ9VFkB-#aOQdKet-xwPKDWIS=$U z!@18vccz`ws2#gSwML(}cE~;gDTwejN9qYC^UZLQ#UiO@HFq~!Cao34+)3z?yc2l# zWAZ=IU-GsM&!E?Id3;{JxcwtNXjv^|DNy2Rw-;}mo&AobVfStBO;Kky#l!Ltkuqxn zU5Wdg+J5Izw2tnEldk26G&4T+C&VS^RDw8x?f z1%G_z%x~#k!w>9dZ$1wYPVBHmkY!S4uwk(ooEhqsdY*clVCVy2;YEV>4;{N|}fH{iTKN zZ%U1)vp$diuQcjEWK+EchSpA4q-+ zD&TIZT%ue4z@g2J%xg=Km4R7@d{8&;&7_Gx=RTRqKCI;bX3h}P8tLPGaq9q)fXurh zOc=EEC19vegtjjf2-#W7e2X4!7J&~1DXeR>5Y8JatMlBaJZjemKI;}NE3$jlo=Vcg>GIX%!8C7({jKUP>w#B`H`>c77}VV# z7x3}>FVjE0>R!3&;VhjJE&nBlPx#&0(1eY3*Bo@oK=K5+IDMC>%BMBTNzF@uvxAI_ zSl9hUXu{-IFQl6nQNZz$V?Fj!M`j8^K(ECMG3%hBs zVH8al9@q?RV19FY=jrmm&#?8TQT;v&h6&Ar-0fRWry|f3&ydg3$DNlYVh#eY54uv6 z{ouLf6gd{v^M=yK#Z!j`4fnN23KPr;q!%yem?@SE;G%Fe8F7GL4ZVxj83)I+?)_h- zQ|iG;o|tp?#-|Bdv|>YR(ds$g14;7i@D-f>51SeBNX2%30)hnB8~uUTp%AGP`f^3= z)4C!xKTjY=V<2E)pSoQfV-GPxOx{~dA6)T&vJ+Hm)9sCaj?oG_zsY#%#s#-{4jG=i zS)%MhSm)y`g7;GH<~IsYOju~>u^3fgi`R5oZKG#~jFB@KhU*qQp*h=$oGu;mu`_xR zVQi9!BZq`^7^U9@p%>LD50n>{UPzwj z46}^feDvh`!TDPr`^DmR!-H1Me{gK(WWk(KAiZ{~b=*P)b=eZLVeH$ff`=4@NnVsU ztg2);ZVuEFx@5{xcvdgbzm`z;uVBA9;g?4P>pqz{TZRkMPLT?$Hywe!a(8vlJQ;N%EgTot&%H;OKaJ3fLO>;}k=lI-EpVQw@r|+AK$FyvxuxEWlsdzkC z&fco5YW$plGYO(t%gv8{MMf8-SFi6AQHmAE{Uy>(LX+zr#nb@{JpXO|qzbFg#t^}v zwe}10m?0{+V1)ek3TYP#RR7Y)>IwfJ^diUlB1kUlsc7d;0SKu&tzFPacqA(u6quNhXk{X5WXvPE`P`U`hu#Ek3&a7{e<;*HcAwi zyr-y_T@}PlOI&p@n}%3^@SJdc5s?l0U#^F1yPzAV zQVyau5m8u0bCyqj&D+@~2H@j_Wv4)$A+U*B4(E`8Mc2>zJFggvMI4u3z|Nb(SH;k9 zeGAOEae|N|yM=p?8DD8$@D4q6DJGjsmo~3+M#q@LNrs_^ysz$NV)KW7SYRa-xtK{} zvfuuJyX8RAc)jppp>9>i$n#gaX){`iprROirlz?M6A@@&Mg7{vIu5&N9*tWHqkD$? zsOC{geID+w6kLmGFH7+Fy|(VJVU!3x2Tu3k!3|qQBwV_N1B-6$w`tk#^BIHP<=++T z0}NW&rG*%`S`41==c!z##p%E}Iw-^GL*ZGyOA-PG(I_TV;bV{5f@%k7V1G)4 zF0GuAuk?oN-t8urNRD5E-%j%@y?YZ`0tPU6#SehfB2|@_ez5`3pq8RtiR7Sx#GjJ(W>!fH?SJnNv3jv0k-?(w+O9S5|IWDsD1c#qw^i zecQ>5ET;EY@Y%%E%lMfTKf;@sb5v-LIT;_UT?=#ON86LOO3IxDdfCFnS-&PWX=>?L ze~j(vLDqIRJ49PkwzU)hAHAUxFn{A%ALOHYEryNx`` z-&uQa0h6S?Ks)EBNkQZhv-*b;&Rtp*ezTl3+$(e!X8LKdt`r&NtznkjbL)(|!>Re7o2WzqCvA1^00U(hL@2_XLA&nkNUCvA<|=_xh}OFIUQ(6 z1zqX3KEd$!5zd>QpL=>lbQMun=wJ_ZGT6+8l2(WK#pjzD>+Z{j>Mj(epWhkwLXz|r zz4yK|$)-fW0pW{DL;IBaRRm$j4u|38NsePn_X{8qmvu8BY&$ydQs-AT&r6-mczzG8 ztNFxh`QYT~+i;vS5CMyqcK|O#!vSUYRwC=s)NqrKtCRYfPp}i-?MuL}IF;hhl+Pk=O zhpa};^?lr2oC1!vzdJOeIp`ci-I6#7q{XztHQQ_k^KYexSUQ+fX!xozq}G;qPM)l3 z#p20Q#axcKmJ@P7r;asRP$;~$Q#AZ*6)LC4W8XM6jGdyVwd8fTCdI_0j<8Rp3PeeN z@Ut=qGcqvHA>6%_L2DV(kyc;GB#K@{dQKVUXz6iSf);YbyAM7NpBmNB+n^)rbkAEK z@nRxq$Y4HVQ(~FBhc0Z*TE$TFqXnps?%gUcUMD|C8NJwKXMlkUjyY69l(afBjP{us z&8U(lLnG2_kbN}p6jos^zVn%rrY);C9i03EiN!HHBIr&157e{LSaXClKUS(Ah}^CA zBhhE>EeAU8T=-Ma)4G+}_U*x7A-uIVv%&Mr&3Oamk}5Ens|UXMEB7JMemocc?PbPA zIkxD-$A?HY67g-)*^ZyC^q-Hu-sXm#T3(vEZrz15ha8YNeD=t4Qro?HQPxP+Dv>ZW zuVCN1Vc~no8BO5OBNcBsS2)Bs>Mz{1Px$N`CQp{ z2eeDRgX7basf+;lif5}9=SM*Eh+FOX$rGB&i`7n{xu+>|jcF2%69vY*Qp1+`IH^kK zEuMY>5*fdzFP=pHP4NJ=Y&QV|x`$Mt0mI7hfOw)JV+Z>*D5&lmVg&KKA;vIUkAT4} zV+%qNYVgo@@3?}*mn20;6)~`~>9(-vC$qABMb?A<8^K&l zCj=YzEAPs#1N^#dxhW;Kr)QUX#SBEXUv`I9URb@8HX^_{Ij3c?W`)ZNV4B@|$OW%5F(N!hq>idXEK)3PW#) z;NQKzmQr48n^Rt9jX6Hra4=(?e7TP^+nKQ5BRbZp`%kCWaz ztAba3UOq`0{|)QCp{^}|~>9;dB6LpvSg zFI0-LVnw6y^-v@$*(Y8i&6fAoSD$bj*rYGAzch$FJw0-d?#%H@bQtdCt;D>q=_Bkc zC~%DAi!hJx8ROpf$}T%}c98B}Y0DniuxIYQs2E9vk~nlC72LSijHuh=I^yy2+sSDgpqtJIPZ zSgy8?YGdj|0KV>7{EF86S+8)hz^-Xd`|~&sRVmY9YITXgAGYl95Lpd&u(Ud)x7dzU z4&Il#Mv}|h1@BjERUbOzPiB{rVd`gzDaFGHJ&{_A+>fnieM#q+U)pTz=Ah zk9X2V;U9{v&Y?jV)ND{u$*|+?G7~W;p>6~Cy*0X7J-Zgl+wJo@RpQ;{jC-BWw&N5g znk7%mOpem1SkVYm3*PsBef{23UJ8wdLY!->u%~_9ljE}5 zWP-IcG5Oicw7F2%s-Atp$6v}yVs}Qbw_fP1-Qr{1bqSG z-3yI~C`#HdxN$cWPu&}5TG%Eo@WzR+57`?iV7o6*0^r09!VOWIGuJ{*t9oO7L)~5x z_{1YQ72Y%1ANAkkovzr{7g~;t$+Nx3MwGKV*WKOohnEi0> znrvREXDu%T)Srk323Flon|_Azi{f=m4_z-sqRbtL=>A$D1U?`T`Ul`)SIzO(pPjEG zgV}VW?IC~u@K6R@-R6VcUXKTGag-a8IQUGCav;nJza!iNgwN3BCP}<3E6006A);$| zsHiG`ORqSbnYn#Z#nY`2MM6zHg5;!5dvoCy-3O+0`1Ix5f%_Pgf+dnUP!pOug70mZ zpwHZ+#Hj#9=g2gSqf4rNd!r}})TUuVV~Z-~=NzOv)cBlWXx?9~)JWk;s5Rm_}cS~}3a}iLumr3l|D}nC=|9WabhW|J?IXvaqbSA<)K4131`&MWuPi*5n zDjH8lYl-OoPj^3H*T_1(+|3~ekKoVMpWmEmo4-bV>JE7BN!%eM7~-Z%M+T$Mak@wx z`3izbEbZ-tztWP%bYolC+aUCY?sa~Op&t8E?=Pj<$*nPJ|oV)3tBEr>Et9ru0ICL`(za8i!0lW1vNN^Hh?}_48#k z0YC5)Tsn!gBDkk(xFxr%)Ko8*=qLs(`_7&Eg?4}~wIlFCI#%n@k^ zQ-Csx5Iu1TbHkN7fZNXvN6*+#LUBaAn&skAKMKvsn`ow_4#q_KNV1{^BzeCPYWXj^S! zI}rtpXX0czF2>2{T3_2(w!5grKNIRPSwd zF!EW$(PPZqLbAYGf^M_}#b3XxHZ#a!D!e0)iHL}(>QFNJ$c-z&w|!uMP^wJ|5AQHC zx01on&#yw`>+0}^IR8q-fDlo0j)!y3$XNH@r`Fci!(Yb=u3CI(7=4X+t1a*xdQ(vT zo1BWY<#PU(45!`M<*lZyw@_yEs3ZL%M3GzAOLG^P1~5>MdzvPHJ>Zi-Py_YlDmY z3~t58rxq?uT`1Tj24ygyeQns48|!?qpH>WTuhXRk$6k81n8d~>VA=Vbu#TgJq!tg!QAk1DG^G8R@=W=6iH z4fcy5rApIsSpx&A{sArynJ^d(7TO2OX?z?&wK+_|$ize+L&@76PdA&j^Wz6e4-JZ(M-CW}u@TVDZI(ZRFk_cao2N%TBl1@%V0OL1rxo`iizKG&PJ}6Cb%bH#!dO_s zer0JB5fh8n$eTaBMFs{}zc^N0+uVdbko;ATYijJL(cb8~%xsZgkoXxvl9;G%XJ_|N z+YN4EX<2Q4?N?^ftY{(scG2m$m6Dg^u?5C)Uc2P~n+4F?{@!B&b{i;RgY>gcBmt+` zf*TnC`@5O14FwwN*&pS|YHNcwtZgjT`>lJ2hPt4>1tP+ND{G|!jXu?QzZ~n3I+*Tz zJJtR@6SFyUaTb`G^#PwY1;Xlh?+d1o_nc^Uc9`}tD;YgMHzJC}o?ga#5c zy7N^|YD7wk6`KsEJrXUaVqZF5V+pJk>>KM+R@3PGa!C4p=R1guIs_AjZDnPH`0}7U zKXkKM#~|v{C$Xuf^5%#lgTj&$n9u`qZS6!mCwm3DqPUZKFF-+#->4{C?2w2@_{8t; z(;K%LcZ|%_*7Ca7)Mf-EwhPmn3dps;f-aZ6zJ}fQR(lAL>&d?El*pR*TZCm=gwZ`K zf|HlMjQ_h+m@~R9LJ9jp3ZV@HO`^uXuWXI3A_6Y4t?|vcO|uf%=>v)AbLj1y?PXL{ zN{^B5a%O>_$M3=r2yBlH7RzO{aeYs(TUhu2IXS}jBnzgm{&5Ut!4>zieyj>;Dd2GT zpfvCN{mXF~j_U4aQ!1{id@}jCpt2!>QGyf#!FZG)ekiiB5`gKsUO?$-8zKr|o>RCTl@ zyXJp{Ar1}?VX(9W!kMY;w^4sB84TXP&A_7A7-6CwV)s_x719IH*3NLvkCM7H0qVYo zz;L#7Lc?M}=9sMX<<%7jTVY<{TLu#o6HT-ng^@>@elt=c0%zuxCdtdo zdso&HEdXI7%Q&eD+gzw~z^yPXM^Affx-PYg+u7TD`4krxR&@(*^in#hNA{XO!>q8L zr;Cfw6%|<{dOX4+BQ4k8lMkP97v<-aKiqxO6HnLk`Ud+|z97_cWA@Q??kDbORv^0p1;ziBF;1O6O`Vwr(|`Rj<15_ZGc znJmFb0o!4*2SVJ5$ZzjoPt`lcj8k;d^S-4`9ImIzNMWq0-9QG}lX$8pAVzwCpX?K8 zv)A5p9Y$^;i~K(;I3}c4RTWq@k0n$b3y>C=;p|zr6^u%jl`n~Sps_LS*k&XaLZ-*RKvkN2!*LD87ucX zQYbuHFdBR!=3wTw)Q**o2Os|Zy;ltFxPEj3 zu!H}1#`Hh_K1~BG-~p{fnD;_JWQ~lpgd`C2ukL;HshS|SY?&8glIpORtM=TnsUJ}G zyxWrY1O24F14`w#8r9=%IbBnPM~v@fYU?BnjO1HKfh8p+^)>UD85wVx%$oqG*Z&an zBrB7L5jZFNpSTo(Z@y<30-9Rl&x^HOtHuh@m=U033ihNH7dH_>TiXlG|R> z7V{|TTZEONGCt>1=l#vvwadx0V^E!u2}@g}1@S(lWMm;)^^JTP?FnPSS5lm!AS#c| z8WY94e*XTW3)4~lzGg~JP7hi#u#eVq*t3M8Cqi6z>gwv@N6XyS;kQ3Fcr!}#lE`$( zZ0+n^JcKi6?(wTvO*OkP7&#huKzCFVBx(Sz1Xs43kI%y8@NC5IyD2HDIw6QPW$@9_ zQF^jiPt%D({i?E>TGvX~`BcXm0RscW+;@pn>keRWKjm&i3&%RmH3L?#6e{|#C#&(H zk8gFk`YGS{H@`<`1qLbLes#yWMmY**jU<2(n8V#7S!OK(AT6$Y7gfD17Xf+yqzKn! z>2^ihT3SJ2fREa=LhjE!mk$dU-zG>*OuVGUmJXD*eaj}IA9K&aTU%F`koOVq?({Jt zmD^;cP-H5(%(IIM3>(goQ&M85*xY=i659)hTb%Rn&Nq{gr*ZL#iA9?0Bmd1N^ml6m*cUyPNuj5(zpC>0l z?BGXdH<|PsCb6cERvA3aD$5pRyZJ_YL`x{+h)p|9ZGY+e&P`6n9B7l+w#aol$MVlF zww*B216~93B>RQOn?FTlk&GzN<#BW%qhz{EV@87@b>iQ|D+q7y4WL*PC;9K*^{RyN zMVTY_Z?orh3=9mYgjJ1zISF~v2*oT>`m21`NMW~BJw7Nhy$e*0odpE$T%mhhlP^^P5%JRPRdur8GX=oZ66 zu#qRxy?aNR0lX}1tfGAsl9H`HJLL^o$CG?OorbriwKZ$y7>GpaM44Gd&*xvvz3ElD zBu^du^b(g3v4zH)4U<<0W8^q|6ZQj?u;!n}Rkq)?fEJbrEF}IF=YMeVJM<)I&&|c` zQ6Wn>LI*Q8==%I#BYcTEJVsXL(IVda-en5|+&RBpFPu9LIID8o%q(9?;G#wWn@tgXm`X`hB%tYTPiz;m0Pl{70u?-FKD%cMNQ9Amobuo7{L(TE80K24TcT}j zOr++h-)uwvqXM9!Q{8iLLF%BzaY;m@NZFLQ4O*ZvKPW^i8KAWC-@e_i-#`r4-;u;4 zA+i15N!)1g^RwZTU%?h(ba?`~+nFyprRXP@0_B>-lLezmleF?nQSff=fw zR=Sw_#S+W49^IXf3I49dJ59X`Uap@44f_l8&mK@2f1A>XT zLD{|0jf~AS?5Jg%GfzJU9Q0l&>c%R&L@vW_xH3g(#yaXdY6+ z)*Vl8T~QnFhpfsIl4l%`&)^R%2FCPRj^DIOEIQb}(Hc5W7eqSAhD`HdHm1Ic1ud9T z8q4ix##`4*0H9}m+1FkAO&}o`DZ&%Esrvda04RW3&Q(B>1@xW?D}Km47!$3OUyApK zNXDqXd%!)<|L}pv)6qfO(6C#4s^C>KBPC!oll#cB8+#u|6H@a0a6Y$NTi-kyJ|{IU zeTUf=uuYWjLvqf#O?m9iv1?|bW%?;o^^htM;Ilz74+P)5d84Ic7(JD)U!u@s*~}1J zH&k^(ZJpMiT}a?-;^07NMi~=FGhxZz6`QrYo58LCRgVHGE??747U8KyE34uu?$7aY zt6J({j z*AS>wofMX6wG0aHXZKr&!9?C}Hhc=FRv5x`G(7E*(5j;f%^ZVkHX_INg%_l)k(FvW zMf}9fuIs2qQy(|pWWX}9-74Cd&8)@rOUQ?Nv;yP2sXQoIvDxr5e&kAdh@Nrzo{UH` zr_C`F+(Ni=VV9~IgPN|@qy7irr$uc^6KLPV_V)D5yL89V%#{j_p^V|+7DP*~co{y$ z#=gtU!$TaCwq{9X9XS|F#QtDqt|el7=5QRg@9SkFWZZpO(Vr@ z8!%6g)z^Izpw&EBOpd{$V*(+T5I)1H>->aLVsK>L*w(IBUZOp5HeD#xTj#vVz}bCH z9q2E74E}C-m$c6c&!H44QrX~cN)KVmhux{NR^hHe*t!hgbR}zW)WZdb_Pv0Kdd@YX zKfiqZ1CRsh`1r61``;ns;AUp67wh*~!#<>teAlPdd~5eAxNwc-?EHNGcT3qwd|X^! zLBSoNy1IC%wux@2bFsqN*%>2qmecJiG&7e2{n)q;XfT!H0f3Rbf3K*aQJxG*OiCII zak^C|q{v#y(2+`G@8`^u%v5F)*$4V2;q9gkH+#B%xzO1O*7S}2`Mks_GEOLL^TfjF zo?5ltrjKT>SZHV@F+4ASKy=YEVNcQUJ8hU!*y2m6(_=0mjVHN^I-zp327#{@PN;Cy zpV>7CQ_&inWNBe*b8mg8$>W2!w{Aqu{nX-H!TJRu(G(Cx>%fZL)hS?7DsZ%Xu1@`$ z&o4&-YM%-zQ_B^o?aQ!C_psS97DWzISH1xC3cR9}Lr*ctVs_+{v(@4h>w>QqfZ z&I82V>1US0fJ?SneLvI&2Lg+wi+cr^BU`vojarxQ)YX6Y#u9Vb>PfE6^R7*bOMM}= zb)Q`^(e}!AuEUbwyQ>Cp65L39%%mIUw-0E`qzypRU6mr*ZB^JYmOYw$P3SvKO@xPE zlYL2PX^9xjaxx-+fqYA7&Rh2De7GP+&BzOyP0&iwk;{`X?_(ge1? zah>>B52y0fGQ?d{vZTbb>z79+Bl14RcX+5WEWDIZ*?-ok+hq+ef8Gq7@2^wOj- zvg7XD(4bk~+l%v7Ag~P+*pki)#h?)1)ebKtHeBiD zQdo#hzRUA%W@nRzIf;T$*1xeA>+A~k-F~Z*5L48xD$5AW%;FaFXH%Wh0ttz;3Vkn@ zhDNNpPS~%?>d?hi&wr{>wbOyCLVhlNc{LtgfO;S@tQ)(vwf+%-Qn0=b#l~zYn}($pi?&Z3}Vk9Ks$()UGXco6tt|-Cg0uk-{z65 zha~Xxytpw1hs{^IW+n*U+X>~U9xwzZHsxOM-kS8adKsSOcRBv9$SllT2Mg?;pNdGX z_*5^%=5#AR7U$*5#V;HuM{bk~(PysGyZtdT&%V}KB?+j9;pD92zD8{HKq%OodG2u;YGwes?^`t2Zi_S{l8?d4GQg&D~h(Zl*!Bl)3U3A z&)z$b(t+dF2U0Eil3111)q^T?51g`(VSzP(NZ~c%>dK`fR@Q?dZ1U-lYBBbH`((Fx z*@p@mB>5aFYsW|W(Rax2`6DzF+cQTVSr~Rk&vC@+VoEIrVpee;K;4?oFJ?dZv?;~v z=LUu2cc2Jup51vQaIQXayWqV)sqaXGg>>bUZ(k#y5EdLwjUmcLh-`hq^(}~RVc<<8 zQW`L-=(ynU>lzD!Qy z1_lPoD=9HCGvCI-!m6_P87rmFugewZ`&o8Esxx}1q7~b z){af5q}wW#c&=jueMrV?V z9yn8RcPslV(v;3M}cNH%zMzi_`AQkZ;z{*KeND2W>!4M1$r{mry58$IamfX>r-G{?v=neRCeHg`nl?o16YLJ?-2)`0C5f;cosdz43a?F| zd0C|7`w4?<=4UT`^NT!4S;+yW_zHUO<4C$h6HOOti_xs4t1EoQ^rP+=)ck@-JYsr3 zzd3i0pZelU=LpvIg1Qf?$B)hAOy|(�ob2+VgFWPGnjGE?I^|wGEO;s#X}QBdc@B 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℞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 z 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

s-$6Noz-V2PY`T0Nlk699D+$nUy~DrWQT) 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 Ru-yTER28)pYUQjV{~ybHO-ld( literal 0 HcmV?d00001 diff --git a/docs/osquery/images/table-icon.png b/docs/osquery/images/table-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5c4e9e78d9f09297095024de0036cabc01d70247 GIT binary patch literal 1669 zcmV;027394P)Hg1+lHrgWSWcKdPn90sKGrRqvPeo9CG3uKX#J{(IASm?@+di}}l?o-=)F3E6 zwD^Ni=!>T7nL9I?X}YoAW$t|Qo$sD|?zw001?ah|SeB6#0T!CBEf+H4bBB+JJu8re zhoBb*p;u8ID_yBf0ya+zcePvJL&AGs+11_tpRKn>9TgyPA7ZoSs0)aX0r00)%XR^J z`jH<$>RKN5V(7OqK*TS4xZz{h!*f1C3ECFkK$#7nA@pGN!$;%jYv zwjAKwmYb0gKL(K8-kPtb5${A?tlI~wzMrJ6wTdBr=Y%%%EaEMQ&o}4FQ^DA)s*}Z> z!FI&AHCpoWI|RUqx?7s@$8!5^Q=anY%X@i5{QA6kNcMelpE>R6eCYFpmMsVT zrI(b06~u#xf1yS}_UGdMvD``!0~u->P=lA4?YN`hilQ z|3tHka)7T{2CGqwjZfMwx$5irQN_*|e4l)UHmiYuz74Yp1t^#>hrJ3-SOXDcC_o0^ z7T9R1gAN8V6s;5)ieI5-7aQlmJn}lUna#nz!j%5V$X|o`xX!dHWQRV27P1=rj;t2b zW$~+pTw@bIek?ZvKPDL<64`^#UNTAck#RBsB6*5DP4<%UA_FqU$I>2EH_cM;u)Q~SI+rg`Rn{L z_AC5qq~L$#SMj%U$6Cz0vP{G5Y*=%5RT^yu;}-DInZ=349rJPVM6C3K^oO)8y(fJr{l>k`ead~!ea?NsT>_Ci%bnxC;Vy6= zb6>{xYV#Ue-+LB$7`JEXmTRm^AtP)R9u{)KHsMiWGV&)32xCG~*nyU<>-!d;FP=Re z4r3qYr~6#KE>;1F`>_J_P5xC?ROxV(DIHdCO*p$HRQI@7^PwV@Pvuf+ z5K}u-6REM(K@W$srgorh0{i?O)v0c>QtHxU-hBdD(>iYJ4b2sIOVX2K8m~4gmYVA5 zh^QEb$V`rCQ-|7ZS{nuL-t>?3n=-o(6I(7vocj#GzCZEo`!3>+v;dYIfPu#&ZWzzX z2i^rZ^Mu;6+rb@?NPG+6)c5T6zxpzGe*M(x+{AON=PiJ>H#?ob-|uwRK0yDg0B4PV z0id6JRRdfL?*IS-CrLy>R5%f>R9kBkK@|RWH`#=3>yypJR(;f7@(&7uKC3l~Q179l z)+ezDwiN73<6V4_3aQqVA_`W;Us0-{RD@FTv4V|IAx)Ds?(8~e>I}QuloWfG**SAA z-#PO!Gn&f<+bH@rVhN~#1YBJFhOtNEn4e#O5m097-f(u9gXYLij9eeWu6RtuXmZQ_ zmoCFJL+I-}4PCFVamR5mF)@Y4#s=KTjI0+WK$6svhM8ycPFJu%sxP>cHw*)XV07v9h#;of`*5#*6eWM z%h%d~xm<=I^bZ_yxTC2_ z_`MZ;fOJEiznI3W*Kg6;*^axnM+Juu$Fi86eg6-KJ%7dFXb_sxq~G>8+E=mHa}(^?fcPx@f_lRKVPcEe=5HLzDhkJJmRk| P00000NkvXXu0mjfqGLB8 literal 0 HcmV?d00001 diff --git a/docs/osquery/osquery.asciidoc b/docs/osquery/osquery.asciidoc new file mode 100644 index 0000000000000..1e4e6604a7c70 --- /dev/null +++ b/docs/osquery/osquery.asciidoc @@ -0,0 +1,419 @@ +[chapter] +[role="xpack"] +[[osquery]] += Osquery + +https://osquery.io[Osquery] is an open source tool that lets you query operating systems, like a database, providing you with visibility into your infrastructure and operating systems. +Using basic SQL commands, you can ask questions about devices, such as servers, +Docker containers, and computers running Linux, macOS, or Windows. +The https://osquery.io/schema[extensive schema] helps with a variety of use cases, +including vulnerability detection, compliance monitoring, incident investigations, and more. + +With Osquery, you can: + + * Run live queries for one or more agents + * Schedule queries to capture changes to OS state over time + * View a history of past queries and their results + * Save queries and build a library of queries for specific use cases + +Osquery results are stored in {es}, so that you can +search, analyze, and visualize Osquery data in {kib}. + +Osquery is powered by the *Osquery Manager* integration. +For information on how to set up *Osquery Manager*, refer to <>. + +[float] +== Required privileges + +To use *Osquery Manager*, you must be assigned to a role with the following privileges: + +* `Read` privileges for the `logs-osquery_manager.result*` index. +* {kib} privileges for **Osquery Manager**. The `All` privilege +enables you to run, schedule, and save queries. `Read` enables you to +view live and scheduled query results, but you cannot run live queries or edit. + +[float] +[[osquery-run-query]] +== Run live queries + +To inspect a host or test queries you want to schedule, run a query against one or more agents or policies, +and view the results. + +. Open the main menu, and then click *Osquery*. + +. In the *Live queries* view, click **New live query**. + +. Select one or more agents or groups to query. Start typing in the search field, +and you'll get suggestions for agents by name, ID, platform, and policy. + +. Enter a query or select a query from your saved queries. ++ +[role="screenshot"] +image::images/enter-query.png[Select saved query dropdown name showing query name and description] + +. Click **Submit**. + +. Review the results in a table, or navigate to *Discover* to dive deeper into the response, +or to the drag-and-drop *Lens* editor to create visualizations. +. To view more information about the request, such as failures, open the *Status* tab. +. To optionally save the query for future use, click *Save for later* and define the ID, +description, and other +<>. + +To view a history of the past queries you have run, open the *Live queries history*. + +* To replay a query, click image:images/play-icon.png[Right-pointing triangle]. + +* To view the query <> and <>, +click image:images/table-icon.png[Table icon]. ++ +[role="screenshot"] +image::images/live-query-check-results.png[Results of OSquery] + + +[float] +[[osquery-schedule-query]] +== Schedule queries + +Group and schedule queries to run on a specified interval, in seconds. +For example, you might create a group that checks +for IT compliance-type issues, and +another group that monitors for evidence of malware. + +. Open the **Scheduled query groups** tab. + +. Click a group name to view the details. ++ +Details include the last time each query ran, how many results were returned, and the number of agents the query ran against. +If there are errors, expand the row to view the details. ++ +[role="screenshot"] +image::images/scheduled-query-groupds.png[Shows last results last time it ran, how many results returned, number of agents it ran against, if it is actually running and if there are errors] + +. To make changes, click *Edit*. + +.. To add a query to the group, click *Add query*, and then enter the query ID and interval. +Optionally, set the minimum Osquery version and platform, +or <>. + +.. To upload queries from a .conf query pack, drag the pack to the drop zone under the query table. To explore the community packs that Osquery publishes, click *Example packs*. + +. Click *Save query*. The queries run when the policy receives the update. + +. View scheduled history results in < or the drag-and-drop <> editor. + + +[float] +[[osquery-map-fields]] +== Map Osquery fields to ECS fields + +When you schedule queries, you can optionally map query results to fields in +the {ecs-ref}/ecs-reference.html[Elastic Common Schema] (ECS), +which standardizes your Osquery data for use across detections, machine learning, +and any other areas that rely on ECS-compliant data. +The query results include the original `osquery.` +and the mapped ECS field. For example, if you update a query to map `osquery.name` to `user.name`, the query results include both fields. + +. Edit a scheduled query group, and then click the edit icon for the query that you want to map. + +. In **ECS mapping**, select the Osquery result fields you want to map to ECS fields. ++ +The fields available in the **Osquery.results** column are based on the SQL query entered, +and only include fields that the query returns. + +When mapping fields: + +. To add a new row for additional fields to map, click the plus icon. + +. To remove any mapped rows, click the trash icon. + +. To save changes to the query, click *Save*. + +. To save changes to the group, click *Save query*. + + +[float] +[[osquery-manage-query]] +== Edit saved queries + +Add or edit saved queries to the *Saved queries* tab. + +. Go to the saved queries, then click **Add saved query** or the edit icon. +. Provide the following fields: + +* The unique identifier. + +* A brief description. + +* The SQL query. + +* The defaults for the scheduled query group, which is included when you add the query to a scheduled query group. + +** The frequency to run the query. + +** The minimum https://github.com/osquery/osquery/releases)[version of Osquery] required to run the query. + +** The operating system required to run the query. For information about supported platforms per table, click *OSquery schema* in the *Edit query* flyout. + +. Click **Save query**. + +[float] +[[osquery-status]] +== Osquery status + +A query can have the following status: + +[cols="2*<"] +|=== +| Successful | The query successfully completed. +| Failed | The query encountered a problem, such as an issue with the query or the agent was disconnected, and might have failed. +| Not yet responded | The query has not been sent to the agent. +| Expired | The action request timed out. The agent may be offline. +|=== + +NOTE: If an agent is offline, the request status remains **pending** as {kib} retries the request. +By default, a query request times out after five minutes. The time out applies to the time it takes +to deliver the action request to an agent to run a query. If the action completes after the timeout period, +the results are still returned. + + +[float] +[[osquery-results]] +== Osquery results + +For the fields that can be returned in Osquery results, +refer to https://docs.elastic.co/en/integrations/osquery_manager#exported-fields[exported fields]. +Scheduled Osquery +results can also include ECS fields, if the query has a defined ECS mapping. + +Osquery responses include the following information: + +* Everything prefaced with `osquery.` is part of the query response. These fields are not mapped to ECS. + +* By default, the `host.*` and `agent.*` fields are mapped to ECS. + +* The `action_data.query` has the query that was sent. + +* All query results are https://osquery.readthedocs.io/en/stable/deployment/logging/#snapshot-logs[snapshot logs] +that represent a point in time with a set of results, with no differentials. +https://osquery.readthedocs.io/en/stable/deployment/logging/#differential-logs[Differential logs] are unsupported. + +* Osquery data is stored in the `logs-osquery_manager.result-default` datastream, and the result row data is under the `osquery` property in the document. + +The following example shows a successful Osquery result: + + +```ts +{ + "_index": ".ds-logs-osquery_manager.result-default-2021.04.12-2021.04.12-000001", + "_id": "R3ZwxngBKwN-X8eyQbxy", + "_version": 1, + "_score": null, + "fields": { + "osquery.seconds": [ + "7" + ], + "action_data.id": [ + "72d3ec71-7635-461e-a15d-f728819ae27f" + ], + "osquery.seconds.number": [ + 7 + ], + "osquery.hours.number": [ + 6 + ], + "host.hostname": [ + "MacBook-Pro.local" + ], + "type": [ + "MacBook-Pro.local" + ], + "host.mac": [ + "ad:de:48:00:12:22", + "a6:83:e7:cb:91:ee" + ], + "osquery.total_seconds.number": [ + 1060627 + ], + "host.os.build": [ + "20D91" + ], + "host.ip": [ + "192.168.31.171", + "fe80::b5b1:39ff:faa1:3b39" + ], + "agent.type": [ + "osquerybeat" + ], + "action_data.query": [ + "select * from uptime;" + ], + "osquery.minutes": [ + "37" + ], + "action_id": [ + "5099c02d-bd6d-4b88-af90-d80dcdc945df" + ], + "host.os.version": [ + "10.16" + ], + "host.os.kernel": [ + "20.3.0" + ], + "host.os.name": [ + "Mac OS X" + ], + "agent.name": [ + "MacBook-Pro.local" + ], + "host.name": [ + "MacBook-Pro.local" + ], + "osquery.total_seconds": [ + "1060627" + ], + "host.id": [ + "155D977D-8EA8-5BDE-94A2-D78A7B545198" + ], + "osquery.hours": [ + "6" + ], + "osquery.days": [ + "12" + ], + "host.os.type": [ + "macos" + ], + "osquery.days.number": [ + 12 + ], + "host.architecture": [ + "x86_64" + ], + "@timestamp": [ + "2021-04-12T14:15:45.060Z" + ], + "agent.id": [ + "196a0086-a612-48b1-930a-300565b3efaf" + ], + "host.os.platform": [ + "darwin" + ], + "ecs.version": [ + "1.8.0" + ], + "agent.ephemeral_id": [ + "5cb88e34-50fe-4c13-b81c-d2b7187505ea" + ], + "agent.version": [ + "7.13.0" + ], + "host.os.family": [ + "darwin" + ], + "osquery.minutes.number": [ + 37 + ] + } +} +``` + +The following is an example of an **error response** for an undefined action query: + +```ts +{ + "_index": ".ds-.fleet-actions-results-2021.04.10-000001", + "_id": "qm7mvHgBKwN-X8eyYB1x", + "_version": 1, + "_score": null, + "fields": { + "completed_at": [ + "2021-04-10T17:48:32.268Z" + ], + "error.keyword": [ + "action undefined" + ], + "@timestamp": [ + "2021-04-10T17:48:32.000Z" + ], + "action_data.query": [ + "select * from uptime;" + ], + "action_data.id": [ + "2c95bb2c-8ab6-4e8c-ac01-a1abb693ea00" + ], + "agent_id": [ + "c21b4c9c-6f36-49f0-8b60-08490fc619ce" + ], + "action_id": [ + "53454d3b-c8cd-4a50-b5b4-f85da17b4be2" + ], + "started_at": [ + "2021-04-10T17:48:32.267Z" + ], + "error": [ + "action undefined" + ] + } +} +``` +[float] +[[manage-osquery-integration]] +== Manage the integration + +[float] +== System requirements + +* {fleet-guide}/fleet-overview.html[Fleet] is enabled on your cluster, and +one or more {fleet-guide}/elastic-agent-installation-configuration.html[Elastic Agents] is enrolled. +* The https://docs.elastic.co/en/integrations/osquery_manager[*Osquery Manager*] integration +has been added and configured +for an agent policy through Fleet. +This integration supports x64 architecture on Windows, MacOS, and Linux platforms, +and ARM64 architecture on Linux. + +NOTE: The original {filebeat-ref}/filebeat-module-osquery.html[Filebeat Osquery module] +and the https://docs.elastic.co/en/integrations/osquery[Osquery Log Collection] +integration collect logs from self-managed Osquery deployments. +The *Osquery Manager* integration manages Osquery deployments +and supports running and scheduling queries from {kib}. + +[float] +== Customize Osquery sub-feature privileges + +Depending on your https://www.elastic.co/subscriptions[subscription level], +you can further customize the sub-feature privileges +for *Osquery Manager*. These include options to grant specific access for running live queries, +running saved queries, saving queries, and scheduling queries. For example, +you can create roles for users who can only run live or saved queries, but who cannot save or schedule queries. +This is useful for teams who need in-depth and detailed control. + +[float] +== Upgrade Osquery versions + +The https://github.com/osquery/osquery/releases[Osquery version] available on an Elastic Agent +is associated to the version of Osquery Beat on the Agent. +To get the latest version of Osquery Beat, +https://www.elastic.co/guide/en/fleet/master/upgrade-elastic-agent.html[upgrade your Elastic Agent]. + +[float] +== Debug issues +If you encounter issues with *Osquery Manager*, find the relevant logs for the {elastic-agent} +and Osquerybeat in the installed agent directory, then adjust the agent path for your setup. + +The relevant logs look similar to the following example paths: + +```ts +`/data/elastic-agent-054e22/logs/elastic-agent-json.log-*` +`/data/elastic-agent-054e22/logs/default/osquerybeat-json.log` +``` + +To get more details in the logs, change the agent logging level to debug: + +. Open the main menu, and then select **Fleet**. + +. Select the agent that you want to debug. + +. On the **Logs** tab, change the **Agent logging level** to **debug**, and then click **Apply changes**. ++ +`agent.logging.level` is updated in `fleet.yml`, and the logging level is changed to `debug`. diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index 29dd5e49a668b..75d0da1c597b6 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -34,12 +34,14 @@ include::{kib-repo-dir}/siem/index.asciidoc[] include::dev-tools.asciidoc[] +include::{kib-repo-dir}/fleet/fleet.asciidoc[] + +include::{kib-repo-dir}/osquery/osquery.asciidoc[] + include::monitoring/index.asciidoc[] include::management.asciidoc[] -include::{kib-repo-dir}/fleet/fleet.asciidoc[] - include::api.asciidoc[] include::plugins.asciidoc[] From 69dd0eef4a833d3a0443a6e946a66539d47fbc41 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Mon, 20 Sep 2021 12:46:04 -0400 Subject: [PATCH 03/69] [Cases] Fix export flaky test (#112456) * Refactoring flaky test * Forcing error in ci * Removing fast fail * remove .only Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../tests/common/cases/import_export.ts | 184 +++++++++++++----- 1 file changed, 132 insertions(+), 52 deletions(-) diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts index 4a0979c44359f..f149f4b5d13a8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; import { join } from 'path'; +import { SavedObject } from 'kibana/server'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; import { deleteAllCaseItems, @@ -22,6 +23,12 @@ import { CommentsResponse, CASES_URL, CaseType, + CASE_SAVED_OBJECT, + CaseAttributes, + CASE_USER_ACTION_SAVED_OBJECT, + CaseUserActionAttributes, + CASE_COMMENT_SAVED_OBJECT, + CasePostRequest, } from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export @@ -29,8 +36,7 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const es = getService('es'); - // FLAKY: https://github.com/elastic/kibana/issues/112353 - describe.skip('import and export cases', () => { + describe('import and export cases', () => { const actionsRemover = new ActionsRemover(supertest); afterEach(async () => { @@ -60,42 +66,9 @@ export default ({ getService }: FtrProviderContext): void => { expect(objects).to.have.length(4); - // should be the case - expect(objects[0].attributes.title).to.eql(caseRequest.title); - expect(objects[0].attributes.description).to.eql(caseRequest.description); - expect(objects[0].attributes.connector.type).to.eql(caseRequest.connector.type); - expect(objects[0].attributes.connector.name).to.eql(caseRequest.connector.name); - expect(objects[0].attributes.connector.fields).to.eql([]); - expect(objects[0].attributes.settings).to.eql(caseRequest.settings); - - // should be two user actions - expect(objects[1].attributes.action).to.eql('create'); - - const parsedCaseNewValue = JSON.parse(objects[1].attributes.new_value); - const { - connector: { id: ignoreParsedId, ...restParsedConnector }, - ...restParsedCreateCase - } = parsedCaseNewValue; - - const { - connector: { id: ignoreConnectorId, ...restConnector }, - ...restCreateCase - } = caseRequest; - - expect(restParsedCreateCase).to.eql({ ...restCreateCase, type: CaseType.individual }); - expect(restParsedConnector).to.eql(restConnector); - - expect(objects[1].attributes.old_value).to.eql(null); - expect(includesAllRequiredFields(objects[1].attributes.action_field)).to.eql(true); - - // should be the comment - expect(objects[2].attributes.comment).to.eql(postCommentUserReq.comment); - expect(objects[2].attributes.type).to.eql(postCommentUserReq.type); - - expect(objects[3].attributes.action).to.eql('create'); - expect(JSON.parse(objects[3].attributes.new_value)).to.eql(postCommentUserReq); - expect(objects[3].attributes.old_value).to.eql(null); - expect(objects[3].attributes.action_field).to.eql(['comment']); + expectExportToHaveCaseSavedObject(objects, caseRequest); + expectExportToHaveUserActions(objects, caseRequest); + expectExportToHaveAComment(objects); }); it('imports a case with a comment and user actions', async () => { @@ -132,7 +105,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(userActions).to.have.length(2); expect(userActions[0].action).to.eql('create'); - expect(includesAllRequiredFields(userActions[0].action_field)).to.eql(true); + expect(includesAllCreateCaseActionFields(userActions[0].action_field)).to.eql(true); expect(userActions[1].action).to.eql('create'); expect(userActions[1].action_field).to.eql(['comment']); @@ -172,7 +145,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(userActions).to.have.length(3); expect(userActions[0].action).to.eql('create'); - expect(includesAllRequiredFields(userActions[0].action_field)).to.eql(true); + expect(includesAllCreateCaseActionFields(userActions[0].action_field)).to.eql(true); expect(userActions[1].action).to.eql('push-to-service'); expect(userActions[1].action_field).to.eql(['pushed']); @@ -195,16 +168,123 @@ const ndjsonToObject = (input: string) => { return input.split('\n').map((str) => JSON.parse(str)); }; -const includesAllRequiredFields = (actionFields: string[]): boolean => { - const requiredFields = [ - 'description', - 'status', - 'tags', - 'title', - 'connector', - 'settings', - 'owner', - ]; - - return requiredFields.every((field) => actionFields.includes(field)); +const expectExportToHaveCaseSavedObject = ( + objects: SavedObject[], + caseRequest: CasePostRequest +) => { + const caseSOs = findSavedObjectsByType(objects, CASE_SAVED_OBJECT); + expect(caseSOs.length).to.eql(1); + + const createdCaseSO = caseSOs[0]; + + // should be the case + expect(createdCaseSO.attributes.title).to.eql(caseRequest.title); + expect(createdCaseSO.attributes.description).to.eql(caseRequest.description); + expect(createdCaseSO.attributes.connector.type).to.eql(caseRequest.connector.type); + expect(createdCaseSO.attributes.connector.name).to.eql(caseRequest.connector.name); + expect(createdCaseSO.attributes.connector.fields).to.eql([]); + expect(createdCaseSO.attributes.settings).to.eql(caseRequest.settings); +}; + +const expectExportToHaveUserActions = (objects: SavedObject[], caseRequest: CasePostRequest) => { + const userActionSOs = findSavedObjectsByType( + objects, + CASE_USER_ACTION_SAVED_OBJECT + ); + + expect(userActionSOs.length).to.eql(2); + + expectCaseCreateUserAction(userActionSOs, caseRequest); + expectCreateCommentUserAction(userActionSOs); +}; + +const expectCaseCreateUserAction = ( + userActions: Array>, + caseRequest: CasePostRequest +) => { + const userActionForCaseCreate = findUserActionSavedObject( + userActions, + 'create', + createCaseActionFields + ); + + expect(userActionForCaseCreate?.attributes.action).to.eql('create'); + + const parsedCaseNewValue = JSON.parse(userActionForCaseCreate?.attributes.new_value as string); + const { + connector: { id: ignoreParsedId, ...restParsedConnector }, + ...restParsedCreateCase + } = parsedCaseNewValue; + + const { + connector: { id: ignoreConnectorId, ...restConnector }, + ...restCreateCase + } = caseRequest; + + expect(restParsedCreateCase).to.eql({ ...restCreateCase, type: CaseType.individual }); + expect(restParsedConnector).to.eql(restConnector); + + expect(userActionForCaseCreate?.attributes.old_value).to.eql(null); + expect( + includesAllCreateCaseActionFields(userActionForCaseCreate?.attributes.action_field) + ).to.eql(true); +}; + +const expectCreateCommentUserAction = ( + userActions: Array> +) => { + const userActionForComment = findUserActionSavedObject(userActions, 'create', ['comment']); + + expect(userActionForComment?.attributes.action).to.eql('create'); + expect(JSON.parse(userActionForComment!.attributes.new_value!)).to.eql(postCommentUserReq); + expect(userActionForComment?.attributes.old_value).to.eql(null); + expect(userActionForComment?.attributes.action_field).to.eql(['comment']); +}; + +const expectExportToHaveAComment = (objects: SavedObject[]) => { + const commentSOs = findSavedObjectsByType(objects, CASE_COMMENT_SAVED_OBJECT); + + expect(commentSOs.length).to.eql(1); + + const commentSO = commentSOs[0]; + expect(commentSO.attributes.comment).to.eql(postCommentUserReq.comment); + expect(commentSO.attributes.type).to.eql(postCommentUserReq.type); +}; + +const createCaseActionFields = [ + 'description', + 'status', + 'tags', + 'title', + 'connector', + 'settings', + 'owner', +]; + +const includesAllCreateCaseActionFields = (actionFields?: string[]): boolean => { + return createCaseActionFields.every( + (field) => actionFields != null && actionFields.includes(field) + ); +}; + +const findSavedObjectsByType = ( + savedObjects: SavedObject[], + type: string +): Array> => { + return (savedObjects.filter((so) => so.type === type) ?? []) as Array>; +}; + +const findUserActionSavedObject = ( + savedObjects: Array>, + action: string, + actionFields: string[] +): SavedObject | undefined => { + return savedObjects.find( + (so) => + so.attributes.action === action && hasAllStrings(so.attributes.action_field, actionFields) + ); +}; + +const hasAllStrings = (collection: string[], stringsToFind: string[]): boolean => { + return stringsToFind.every((str) => collection.includes(str)); }; From 5c97b1362070bd5f9dc00b4c15ccb43014b1437a Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 20 Sep 2021 09:55:50 -0700 Subject: [PATCH 04/69] Allow Integrations browse page to filter on descriptions (#111649) --- .../public/applications/integrations/hooks/use_local_search.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx index fc2966697418a..06c4bac5c7e8e 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_local_search.tsx @@ -11,7 +11,7 @@ import { useEffect, useRef } from 'react'; import type { PackageList } from '../../../types'; export const searchIdField = 'id'; -export const fieldsToSearch = ['name', 'title']; +export const fieldsToSearch = ['name', 'title', 'description']; export function useLocalSearch(packageList: PackageList) { const localSearchRef = useRef(null); From d3b44c41cd7c5f8ba19f96d1cfb83c3992866a87 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Mon, 20 Sep 2021 13:19:18 -0400 Subject: [PATCH 05/69] [Fleet] Support automatic upgrades of Package Policies when updating Integrations (#108269) * Add initial upgrade policy modal * Fix modal bolding * Fetch dry run data on settings page * Fix request for agent count data * Add conflict CallOut to modal + call upgrade endpoint on submit * Add conflict detection for type mismatched variables * Clean up upgrade logic + attempt to fix modal closing bug * Hoist state for policy upgrade piece of modal logic * Fix update icon flex style * Add unused test package fixture * Fix 0.3.0 test fixture * Reset test 0.3.0 test fixture to master * Remove unused suppressToasts param * Address low hanging PR feedback * First pass at refactoring InstallationButton component per PR review * Finalize refactor of InstallationButton component * Fix lint errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../edit_package_policy_page/index.tsx | 2 +- .../hooks/use_package_install.tsx | 1 + .../sections/epm/screens/detail/index.tsx | 2 +- .../detail/settings/install_button.tsx | 84 +++++ .../detail/settings/installation_button.tsx | 161 --------- .../epm/screens/detail/settings/settings.tsx | 118 +++++-- .../detail/settings/uninstall_button.tsx | 92 ++++++ .../screens/detail/settings/update_button.tsx | 310 ++++++++++++++++++ .../package_policy_actions_menu.tsx | 5 +- .../public/hooks/use_request/agent_policy.ts | 8 + .../hooks/use_request/package_policy.ts | 19 +- 11 files changed, 604 insertions(+), 198 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index d887076568a68..35092cb67f7ef 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -726,7 +726,7 @@ const UpgradeStatusCallout: React.FunctionComponent<{ > {packageInfo.version} {updateAvailable ? ( - + ) : null} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx new file mode 100644 index 0000000000000..f2813058afe5a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/install_button.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton } from '@elastic/eui'; +import React, { Fragment, useCallback, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import type { PackageInfo, UpgradePackagePolicyDryRunResponse } from '../../../../../types'; +import { InstallStatus } from '../../../../../types'; +import { + useCapabilities, + useGetPackageInstallStatus, + useInstallPackage, +} from '../../../../../hooks'; + +import { ConfirmPackageInstall } from './confirm_package_install'; + +type InstallationButtonProps = Pick & { + disabled?: boolean; + dryRunData?: UpgradePackagePolicyDryRunResponse | null; + isUpgradingPackagePolicies?: boolean; + latestVersion?: string; + numOfAssets: number; + packagePolicyIds?: string[]; + setIsUpgradingPackagePolicies?: React.Dispatch>; +}; +export function InstallButton(props: InstallationButtonProps) { + const { name, numOfAssets, title, version } = props; + const hasWriteCapabilites = useCapabilities().write; + const installPackage = useInstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + + const isInstalling = installationStatus === InstallStatus.installing; + const [isInstallModalVisible, setIsInstallModalVisible] = useState(false); + + const toggleInstallModal = useCallback(() => { + setIsInstallModalVisible(!isInstallModalVisible); + }, [isInstallModalVisible]); + + const handleClickInstall = useCallback(() => { + installPackage({ name, version, title }); + toggleInstallModal(); + }, [installPackage, name, title, toggleInstallModal, version]); + + const installModal = ( + + ); + + return hasWriteCapabilites ? ( + + + {isInstalling ? ( + + ) : ( + + )} + + + {isInstallModalVisible && installModal} + + ) : null; +} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx deleted file mode 100644 index eab28a051f061..0000000000000 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/installation_button.tsx +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiButton } from '@elastic/eui'; -import React, { Fragment, useCallback, useMemo, useState } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import type { PackageInfo } from '../../../../../types'; -import { InstallStatus } from '../../../../../types'; -import { - useCapabilities, - useUninstallPackage, - useGetPackageInstallStatus, - useInstallPackage, -} from '../../../../../hooks'; - -import { ConfirmPackageUninstall } from './confirm_package_uninstall'; -import { ConfirmPackageInstall } from './confirm_package_install'; - -type InstallationButtonProps = Pick & { - disabled?: boolean; - isUpdate?: boolean; - latestVersion?: string; -}; -export function InstallationButton(props: InstallationButtonProps) { - const { assets, name, title, version, disabled = true, isUpdate = false, latestVersion } = props; - const hasWriteCapabilites = useCapabilities().write; - const installPackage = useInstallPackage(); - const uninstallPackage = useUninstallPackage(); - const getPackageInstallStatus = useGetPackageInstallStatus(); - const { status: installationStatus } = getPackageInstallStatus(name); - - const isInstalling = installationStatus === InstallStatus.installing; - const isRemoving = installationStatus === InstallStatus.uninstalling; - const isInstalled = installationStatus === InstallStatus.installed; - const showUninstallButton = isInstalled || isRemoving; - const [isModalVisible, setModalVisible] = useState(false); - const toggleModal = useCallback(() => { - setModalVisible(!isModalVisible); - }, [isModalVisible]); - - const handleClickInstall = useCallback(() => { - installPackage({ name, version, title }); - toggleModal(); - }, [installPackage, name, title, toggleModal, version]); - - const handleClickUpdate = useCallback(() => { - installPackage({ name, version, title, fromUpdate: true }); - }, [installPackage, name, title, version]); - - const handleClickUninstall = useCallback(() => { - uninstallPackage({ name, version, title, redirectToVersion: latestVersion ?? version }); - toggleModal(); - }, [uninstallPackage, name, title, toggleModal, version, latestVersion]); - - // counts the number of assets in the package - const numOfAssets = useMemo( - () => - Object.entries(assets).reduce( - (acc, [serviceName, serviceNameValue]) => - acc + - Object.entries(serviceNameValue).reduce( - (acc2, [assetName, assetNameValue]) => acc2 + assetNameValue.length, - 0 - ), - 0 - ), - [assets] - ); - - const installButton = ( - - {isInstalling ? ( - - ) : ( - - )} - - ); - - const updateButton = ( - - - - ); - - const uninstallButton = ( - - {isRemoving ? ( - - ) : ( - - )} - - ); - - const uninstallModal = ( - - ); - - const installModal = ( - - ); - - return hasWriteCapabilites ? ( - - {isUpdate ? updateButton : showUninstallButton ? uninstallButton : installButton} - {isModalVisible && (isInstalled ? uninstallModal : installModal)} - - ) : null; -} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx index 98cc172197d44..07c95e0d77ec7 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/settings.tsx @@ -5,30 +5,42 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import semverLt from 'semver/functions/lt'; -import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer, EuiLink } from '@elastic/eui'; +import { + EuiCallOut, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiSpacer, + EuiLink, +} from '@elastic/eui'; -import type { PackageInfo } from '../../../../../types'; +import { i18n } from '@kbn/i18n'; + +import type { PackageInfo, UpgradePackagePolicyDryRunResponse } from '../../../../../types'; import { InstallStatus } from '../../../../../types'; -import { useGetPackagePolicies, useGetPackageInstallStatus, useLink } from '../../../../../hooks'; +import { + useGetPackagePolicies, + useGetPackageInstallStatus, + useLink, + sendUpgradePackagePolicyDryRun, +} from '../../../../../hooks'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../constants'; -import { UpdateIcon } from '../components'; -import { InstallationButton } from './installation_button'; +import { InstallButton } from './install_button'; +import { UpdateButton } from './update_button'; +import { UninstallButton } from './uninstall_button'; const SettingsTitleCell = styled.td` padding-right: ${(props) => props.theme.eui.spacerSizes.xl}; padding-bottom: ${(props) => props.theme.eui.spacerSizes.m}; `; -const UpdatesAvailableMsgContainer = styled.span` - padding-left: ${(props) => props.theme.eui.spacerSizes.s}; -`; - const NoteLabel = () => ( ( /> ); -const UpdatesAvailableMsg = () => ( - - +const UpdatesAvailableMsg = ({ latestVersion }: { latestVersion: string }) => ( + - + ); const LatestVersionLink = ({ name, version }: { name: string; version: string }) => { @@ -68,14 +86,35 @@ interface Props { export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { const { name, title, removable, latestVersion, version } = packageInfo; + const [dryRunData, setDryRunData] = useState(); + const [isUpgradingPackagePolicies, setIsUpgradingPackagePolicies] = useState(false); const getPackageInstallStatus = useGetPackageInstallStatus(); const { data: packagePoliciesData } = useGetPackagePolicies({ - perPage: 0, + perPage: 1000, page: 1, kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${name}`, }); + const { status: installationStatus, version: installedVersion } = getPackageInstallStatus(name); const packageHasUsages = !!packagePoliciesData?.total; + + const packagePolicyIds = useMemo( + () => packagePoliciesData?.items.map(({ id }) => id), + [packagePoliciesData] + ); + + useEffect(() => { + const fetchDryRunData = async () => { + if (packagePolicyIds && packagePolicyIds.length) { + const { data } = await sendUpgradePackagePolicyDryRun(packagePolicyIds, latestVersion); + + setDryRunData(data); + } + }; + + fetchDryRunData(); + }, [latestVersion, packagePolicyIds]); + const updateAvailable = installedVersion && semverLt(installedVersion, latestVersion) ? true : false; @@ -88,6 +127,20 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { const isUpdating = installationStatus === InstallStatus.installing && installedVersion; + const numOfAssets = useMemo( + () => + Object.entries(packageInfo.assets).reduce( + (acc, [serviceName, serviceNameValue]) => + acc + + Object.entries(serviceNameValue).reduce( + (acc2, [assetName, assetNameValue]) => acc2 + assetNameValue.length, + 0 + ), + 0 + ), + [packageInfo.assets] + ); + return ( @@ -129,7 +182,6 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { {installedVersion} - {updateAvailable && } @@ -147,15 +199,21 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { - {updateAvailable && ( -

- -

+ {(updateAvailable || isUpgradingPackagePolicies) && ( + <> + + +

+ +

+ )} )} @@ -189,8 +247,9 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => {

-

@@ -220,8 +279,9 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => {

- diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx new file mode 100644 index 0000000000000..00b6ccc2f7912 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/uninstall_button.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiButton } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { InstallStatus } from '../../../../../types'; +import type { PackageInfo } from '../../../../../types'; + +import { + useCapabilities, + useGetPackageInstallStatus, + useUninstallPackage, +} from '../../../../../hooks'; + +import { ConfirmPackageUninstall } from './confirm_package_uninstall'; + +interface UninstallButtonProps extends Pick { + disabled?: boolean; + latestVersion?: string; + numOfAssets: number; +} + +export const UninstallButton: React.FunctionComponent = ({ + disabled = false, + latestVersion, + name, + numOfAssets, + title, + version, +}) => { + const hasWriteCapabilites = useCapabilities().write; + const uninstallPackage = useUninstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + const isRemoving = installationStatus === InstallStatus.uninstalling; + + const [isUninstallModalVisible, setIsUninstallModalVisible] = useState(false); + + const handleClickUninstall = useCallback(() => { + uninstallPackage({ name, version, title, redirectToVersion: latestVersion ?? version }); + setIsUninstallModalVisible(false); + }, [uninstallPackage, name, title, version, latestVersion]); + + const uninstallModal = ( + setIsUninstallModalVisible(false)} + onConfirm={handleClickUninstall} + /> + ); + + return hasWriteCapabilites ? ( + <> + setIsUninstallModalVisible(true)} + color="danger" + disabled={disabled || isRemoving ? true : false} + > + {isRemoving ? ( + + ) : ( + + )} + + {isUninstallModalVisible && uninstallModal} + + ) : null; +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx new file mode 100644 index 0000000000000..8cdb3ece30621 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx @@ -0,0 +1,310 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiCheckbox, + EuiCallOut, + EuiConfirmModal, + EuiSpacer, +} from '@elastic/eui'; +import { sumBy } from 'lodash'; + +import type { + GetAgentPoliciesResponse, + PackageInfo, + UpgradePackagePolicyDryRunResponse, +} from '../../../../../types'; +import { InstallStatus } from '../../../../../types'; +import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../../../constants'; +import { + sendGetAgentPolicies, + useInstallPackage, + useGetPackageInstallStatus, + sendUpgradePackagePolicy, + useStartServices, + useCapabilities, + useLink, +} from '../../../../../hooks'; +import { toMountPoint } from '../../../../../../../../../../../src/plugins/kibana_react/public'; + +interface UpdateButtonProps extends Pick { + dryRunData?: UpgradePackagePolicyDryRunResponse | null; + packagePolicyIds?: string[]; + isUpgradingPackagePolicies?: boolean; + setIsUpgradingPackagePolicies?: React.Dispatch>; +} + +/* + + Updating an integration to a new version entails a bit of logic. We allow the user to choose whether they'd like to + simultaneously upgrade any package policies that include the current version of the integration. For example, if + a user is running four agent policies that include the `nginx-0.2.4` package and they update to `nginx-0.7.0`, they + can elect to also deploy the new integration version to any agent running one of those four agent policies. + + If the user does not elect to upgrade their running policies, we simply install the latest version of the package and + navigate to the new version's settings page, e.g. `/detail/nginx-0.7.0/settings`. + + If the user _does_ elect to upgrade their running policies, we display a confirmation modal. In this modal, we'll report the + number of agents and policies that will be affected by the upgrade, and if there are any conflicts. In the case of a conflict + between versions, an upgrade for a given package policy will be skipped and the user will need to manually recreate their policy + to resolve any breaking changes between versions. Once the user confirms, we first install the latest version of the integration, + then we make a call to the "upgrade policies" API endpoint with a list of all package policy ID's that include the current version + of the integration. This API endpoint will complete the upgrade process in bulk for each package policy provided. Upon completion, + we navigate to the new version's settings page, as above. + +*/ + +export const UpdateButton: React.FunctionComponent = ({ + dryRunData, + isUpgradingPackagePolicies = false, + name, + packagePolicyIds = [], + setIsUpgradingPackagePolicies = () => {}, + title, + version, +}) => { + const history = useHistory(); + const { getPath } = useLink(); + + const { notifications } = useStartServices(); + const hasWriteCapabilites = useCapabilities().write; + + const installPackage = useInstallPackage(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const { status: installationStatus } = getPackageInstallStatus(name); + const isInstalling = installationStatus === InstallStatus.installing; + + const [isUpdateModalVisible, setIsUpdateModalVisible] = useState(false); + const [upgradePackagePolicies, setUpgradePackagePolicies] = useState(true); + const [agentPolicyData, setAgentPolicyData] = useState(); + + useEffect(() => { + const fetchAgentPolicyData = async () => { + if (packagePolicyIds && packagePolicyIds.length > 0) { + const { data } = await sendGetAgentPolicies({ + perPage: 1000, + page: 1, + // Fetch all agent policies that include one of the eligible package policies + kuery: `${AGENT_POLICY_SAVED_OBJECT_TYPE}.package_policies:${packagePolicyIds + .map((id) => `"${id}"`) + .join(' or ')}`, + }); + + setAgentPolicyData(data); + } + }; + + fetchAgentPolicyData(); + }, [packagePolicyIds]); + + const packagePolicyCount = useMemo(() => packagePolicyIds.length, [packagePolicyIds]); + const agentCount = useMemo( + () => sumBy(agentPolicyData?.items, ({ agents }) => agents ?? 0), + [agentPolicyData] + ); + const conflictCount = useMemo( + () => dryRunData?.filter((item) => item.hasErrors).length, + [dryRunData] + ); + + const handleUpgradePackagePoliciesChange = useCallback(() => { + setUpgradePackagePolicies((prev) => !prev); + }, []); + + const navigateToNewSettingsPage = useCallback(() => { + const settingsPath = getPath('integration_details_settings', { + pkgkey: `${name}-${version}`, + }); + history.push(settingsPath); + }, [history, getPath, name, version]); + + const handleClickUpdate = useCallback(async () => { + await installPackage({ name, version, title, fromUpdate: true }); + }, [installPackage, name, title, version]); + + const handleClickUpgradePolicies = useCallback(async () => { + if (isUpgradingPackagePolicies) { + return; + } + + setIsUpgradingPackagePolicies(true); + + await installPackage({ name, version, title }); + + await sendUpgradePackagePolicy( + // Only upgrade policies that don't have conflicts + packagePolicyIds.filter( + (id) => !dryRunData?.find((dryRunRecord) => dryRunRecord.diff?.[0].id === id)?.hasErrors + ) + ); + + setIsUpgradingPackagePolicies(false); + setIsUpdateModalVisible(false); + + notifications.toasts.addSuccess({ + title: toMountPoint( + + ), + text: toMountPoint( + + ), + }); + + navigateToNewSettingsPage(); + }, [ + dryRunData, + installPackage, + isUpgradingPackagePolicies, + name, + navigateToNewSettingsPage, + notifications.toasts, + packagePolicyIds, + setIsUpgradingPackagePolicies, + title, + version, + ]); + + const updateModal = ( + { + setIsUpdateModalVisible(false); + }} + cancelButtonText={i18n.translate( + 'xpack.fleet.integrations.settings.confirmUpdateModal.cancel', + { defaultMessage: 'Cancel' } + )} + onConfirm={handleClickUpgradePolicies} + confirmButtonText={i18n.translate( + 'xpack.fleet.integrations.settings.confirmUpdateModal.confirm', + { defaultMessage: 'Upgrade {packageName} and policies', values: { packageName: title } } + )} + title={i18n.translate('xpack.fleet.integrations.settings.confirmUpdateModal.updateTitle', { + defaultMessage: 'Upgrade {packageName} and policies', + values: { packageName: title }, + })} + > + <> + {conflictCount && conflictCount > 0 ? ( + <> + + + + {' '} + + + + + + ) : null} + + + + ), + agentCountText: ( + + + + ), + }} + /> + + + ); + + return hasWriteCapabilites ? ( + <> + + + setIsUpdateModalVisible(true) : handleClickUpdate + } + > + + + + {packagePolicyCount > 0 && ( + + + + )} + + + {isUpdateModalVisible && updateModal} + + ) : null; +}; diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index eefa3c870f283..7dc313970ef20 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -117,7 +117,10 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ disabled={!hasWriteCapabilities} icon="trash" onClick={() => { - deletePackagePoliciesPrompt([packagePolicy.id], refreshAgentPolicy); + deletePackagePoliciesPrompt([packagePolicy.id], () => { + setIsActionsMenuOpen(false); + refreshAgentPolicy(); + }); }} > }); }; +export const sendGetAgentPolicies = (query?: GetAgentPoliciesRequest['query']) => { + return sendRequest({ + path: agentPolicyRouteService.getListPath(), + method: 'get', + query, + }); +}; + export const useGetOneAgentPolicy = (agentPolicyId: string | undefined) => { return useConditionalRequest({ path: agentPolicyId ? agentPolicyRouteService.getInfoPath(agentPolicyId) : undefined, diff --git a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts index d39b15a3b3bfa..f8d14647439b2 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/package_policy.ts @@ -66,14 +66,23 @@ export const sendGetOnePackagePolicy = (packagePolicyId: string) => { }); }; -export function sendUpgradePackagePolicyDryRun(packagePolicyIds: string[]) { +export function sendUpgradePackagePolicyDryRun( + packagePolicyIds: string[], + packageVersion?: string +) { + const body: { packagePolicyIds: string[]; dryRun: boolean; packageVersion?: string } = { + packagePolicyIds, + dryRun: true, + }; + + if (packageVersion) { + body.packageVersion = packageVersion; + } + return sendRequest({ path: packagePolicyRouteService.getUpgradePath(), method: 'post', - body: JSON.stringify({ - packagePolicyIds, - dryRun: true, - }), + body: JSON.stringify(body), }); } From b34c54e9344c2e6b29e684ad60d68ea13824a59d Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Mon, 20 Sep 2021 13:21:33 -0400 Subject: [PATCH 06/69] [App Search] Automated Curations: Split Curations view into "Overview" and "Settings" tabs (#112488) --- .../components/curations_table.test.tsx | 143 ++++++++++++++++++ .../curations/components/curations_table.tsx | 117 ++++++++++++++ .../components/curations/components/index.ts | 1 + .../curations/curations_logic.test.ts | 14 ++ .../components/curations/curations_logic.ts | 11 ++ .../curations/views/curations.test.tsx | 135 +++++------------ .../components/curations/views/curations.tsx | 142 +++++------------ .../views/curations_overview.test.tsx | 47 ++++++ .../curations/views/curations_overview.tsx | 27 ++++ .../views/curations_settings.test.tsx | 27 ++++ .../curations/views/curations_settings.tsx | 12 ++ .../test_helpers/get_page_header.tsx | 21 ++- .../public/applications/test_helpers/index.ts | 1 + 13 files changed, 491 insertions(+), 207 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx new file mode 100644 index 0000000000000..0b647bffb3e26 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.test.tsx @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; +import '../../../../__mocks__/react_router'; +import '../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow, ReactWrapper } from 'enzyme'; + +import { EuiBasicTable } from '@elastic/eui'; + +import { mountWithIntl } from '../../../../test_helpers'; + +import { CurationsTable } from './curations_table'; + +describe('CurationsTable', () => { + const { navigateToUrl } = mockKibanaValues; + + const values = { + dataLoading: false, + curations: [ + { + id: 'cur-id-1', + last_updated: 'January 1, 1970 at 12:00PM', + queries: ['hiking'], + }, + { + id: 'cur-id-2', + last_updated: 'January 2, 1970 at 12:00PM', + queries: ['mountains', 'valleys'], + }, + ], + meta: { + page: { + current: 1, + size: 10, + total_results: 2, + }, + }, + }; + + const actions = { + deleteCuration: jest.fn(), + onPaginate: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(values); + setMockActions(actions); + }); + + it('passes loading prop based on dataLoading', () => { + setMockValues({ ...values, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); + }); + + describe('populated table render', () => { + let wrapper: ReactWrapper; + + beforeAll(() => { + wrapper = mountWithIntl(); + }); + + it('renders queries and last updated columns', () => { + const tableContent = wrapper.find(EuiBasicTable).text(); + + expect(tableContent).toContain('Queries'); + expect(tableContent).toContain('hiking'); + expect(tableContent).toContain('mountains, valleys'); + + expect(tableContent).toContain('Last updated'); + expect(tableContent).toContain('Jan 1, 1970 12:00 PM'); + expect(tableContent).toContain('Jan 2, 1970 12:00 PM'); + }); + + it('renders queries with curation links', () => { + expect( + wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').first().prop('to') + ).toEqual('/engines/some-engine/curations/cur-id-1'); + + expect( + wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').last().prop('to') + ).toEqual('/engines/some-engine/curations/cur-id-2'); + }); + + describe('action column', () => { + it('edit action navigates to curation link', () => { + wrapper.find('[data-test-subj="CurationsTableEditButton"]').first().simulate('click'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-1'); + + wrapper.find('[data-test-subj="CurationsTableEditButton"]').last().simulate('click'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-2'); + }); + + it('delete action calls deleteCuration', () => { + wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').first().simulate('click'); + expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-1'); + + wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').last().simulate('click'); + expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-2'); + }); + }); + }); + + describe('pagination', () => { + it('passes pagination props from meta.page', () => { + setMockValues({ + ...values, + meta: { + page: { + current: 5, + size: 10, + total_results: 50, + }, + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(EuiBasicTable).prop('pagination')).toEqual({ + pageIndex: 4, + pageSize: 10, + totalItemCount: 50, + hidePerPageOptions: true, + }); + }); + + it('calls onPaginate on pagination change', () => { + const wrapper = shallow(); + wrapper.find(EuiBasicTable).simulate('change', { page: { index: 0 } }); + + expect(actions.onPaginate).toHaveBeenCalledWith(1); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx new file mode 100644 index 0000000000000..ad508bea1dbc7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/curations_table.tsx @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues, useActions } from 'kea'; + +import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants'; +import { KibanaLogic } from '../../../../shared/kibana'; +import { EuiLinkTo } from '../../../../shared/react_router_helpers'; +import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; + +import { ENGINE_CURATION_PATH } from '../../../routes'; +import { FormattedDateTime } from '../../../utils/formatted_date_time'; +import { generateEnginePath } from '../../engine'; + +import { CurationsLogic } from '../curations_logic'; +import { Curation } from '../types'; +import { convertToDate } from '../utils'; + +export const CurationsTable: React.FC = () => { + const { dataLoading, curations, meta } = useValues(CurationsLogic); + const { onPaginate, deleteCuration } = useActions(CurationsLogic); + + const columns: Array> = [ + { + field: 'queries', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.queries', + { defaultMessage: 'Queries' } + ), + render: (queries: Curation['queries'], curation: Curation) => ( + + {queries.join(', ')} + + ), + width: '40%', + truncateText: true, + mobileOptions: { + header: true, + // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error + // @ts-ignore + enlarge: true, + width: '100%', + truncateText: false, + }, + }, + { + field: 'last_updated', + name: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.lastUpdated', + { defaultMessage: 'Last updated' } + ), + width: '30%', + dataType: 'string', + render: (dateString: string) => , + }, + { + width: '120px', + actions: [ + { + name: EDIT_BUTTON_LABEL, + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.editTooltip', + { defaultMessage: 'Edit curation' } + ), + type: 'icon', + icon: 'pencil', + color: 'primary', + onClick: (curation: Curation) => { + const { navigateToUrl } = KibanaLogic.values; + const url = generateEnginePath(ENGINE_CURATION_PATH, { curationId: curation.id }); + navigateToUrl(url); + }, + 'data-test-subj': 'CurationsTableEditButton', + }, + { + name: DELETE_BUTTON_LABEL, + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.table.deleteTooltip', + { defaultMessage: 'Delete curation' } + ), + type: 'icon', + icon: 'trash', + color: 'danger', + onClick: (curation: Curation) => deleteCuration(curation.id), + 'data-test-subj': 'CurationsTableDeleteButton', + }, + ], + }, + ]; + + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts index 8c9e58e6ba0f4..473162dcbd91e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/index.ts @@ -5,4 +5,5 @@ * 2.0. */ +export { CurationsTable } from './curations_table'; export { EmptyState } from './empty_state'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts index f4296d4ff2089..0d02fbe413870 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts @@ -50,6 +50,7 @@ describe('CurationsLogic', () => { dataLoading: true, curations: [], meta: DEFAULT_META, + selectedPageTab: 'overview', }; beforeEach(() => { @@ -89,6 +90,19 @@ describe('CurationsLogic', () => { }); }); }); + + describe('onSelectPageTab', () => { + it('should set the selected page tab', () => { + mount(); + + CurationsLogic.actions.onSelectPageTab('settings'); + + expect(CurationsLogic.values).toEqual({ + ...DEFAULT_VALUES, + selectedPageTab: 'settings', + }); + }); + }); }); describe('listeners', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts index a98f83396b3b8..76adfa4b6ed4d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts @@ -23,10 +23,13 @@ import { EngineLogic, generateEnginePath } from '../engine'; import { DELETE_MESSAGE, SUCCESS_MESSAGE } from './constants'; import { Curation, CurationsAPIResponse } from './types'; +type CurationsPageTabs = 'overview' | 'settings'; + interface CurationsValues { dataLoading: boolean; curations: Curation[]; meta: Meta; + selectedPageTab: CurationsPageTabs; } interface CurationsActions { @@ -35,6 +38,7 @@ interface CurationsActions { loadCurations(): void; deleteCuration(id: string): string; createCuration(queries: Curation['queries']): Curation['queries']; + onSelectPageTab(pageTab: CurationsPageTabs): { pageTab: CurationsPageTabs }; } export const CurationsLogic = kea>({ @@ -45,8 +49,15 @@ export const CurationsLogic = kea id, createCuration: (queries) => queries, + onSelectPageTab: (pageTab) => ({ pageTab }), }), reducers: () => ({ + selectedPageTab: [ + 'overview', + { + onSelectPageTab: (_, { pageTab }) => pageTab, + }, + ], dataLoading: [ true, { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx index 85827d5374179..effd03e2e7437 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx @@ -5,23 +5,23 @@ * 2.0. */ -import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; +import { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; import '../../../../__mocks__/react_router'; import '../../../__mocks__/engine_logic.mock'; import React from 'react'; -import { shallow, ReactWrapper } from 'enzyme'; +import { shallow } from 'enzyme'; -import { EuiBasicTable } from '@elastic/eui'; +import { EuiTab } from '@elastic/eui'; -import { mountWithIntl, getPageTitle } from '../../../../test_helpers'; +import { mountWithIntl, getPageHeaderTabs, getPageTitle } from '../../../../test_helpers'; -import { Curations, CurationsTable } from './curations'; +import { Curations } from './curations'; +import { CurationsOverview } from './curations_overview'; +import { CurationsSettings } from './curations_settings'; describe('Curations', () => { - const { navigateToUrl } = mockKibanaValues; - const values = { dataLoading: false, curations: [ @@ -43,12 +43,13 @@ describe('Curations', () => { total_results: 2, }, }, + selectedPageTab: 'overview', }; const actions = { loadCurations: jest.fn(), - deleteCuration: jest.fn(), onPaginate: jest.fn(), + onSelectPageTab: jest.fn(), }; beforeEach(() => { @@ -57,11 +58,38 @@ describe('Curations', () => { setMockActions(actions); }); - it('renders', () => { + it('renders with a set of tabs in the page header', () => { const wrapper = shallow(); expect(getPageTitle(wrapper)).toEqual('Curated results'); - expect(wrapper.find(CurationsTable)).toHaveLength(1); + + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + tabs.at(0).simulate('click'); + expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(1, 'overview'); + + tabs.at(1).simulate('click'); + expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(2, 'settings'); + }); + + it('renders an overview view', () => { + setMockValues({ ...values, selectedPageTab: 'overview' }); + const wrapper = shallow(); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + expect(tabs.at(0).prop('isSelected')).toEqual(true); + + expect(wrapper.find(CurationsOverview)).toHaveLength(1); + }); + + it('renders a settings view', () => { + setMockValues({ ...values, selectedPageTab: 'settings' }); + const wrapper = shallow(); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + expect(tabs.at(1).prop('isSelected')).toEqual(true); + + expect(wrapper.find(CurationsSettings)).toHaveLength(1); }); describe('loading state', () => { @@ -86,91 +114,4 @@ describe('Curations', () => { expect(actions.loadCurations).toHaveBeenCalledTimes(1); }); - - describe('CurationsTable', () => { - it('passes loading prop based on dataLoading', () => { - setMockValues({ ...values, dataLoading: true }); - const wrapper = shallow(); - - expect(wrapper.find(EuiBasicTable).prop('loading')).toEqual(true); - }); - - describe('populated table render', () => { - let wrapper: ReactWrapper; - - beforeAll(() => { - wrapper = mountWithIntl(); - }); - - it('renders queries and last updated columns', () => { - const tableContent = wrapper.find(EuiBasicTable).text(); - - expect(tableContent).toContain('Queries'); - expect(tableContent).toContain('hiking'); - expect(tableContent).toContain('mountains, valleys'); - - expect(tableContent).toContain('Last updated'); - expect(tableContent).toContain('Jan 1, 1970 12:00 PM'); - expect(tableContent).toContain('Jan 2, 1970 12:00 PM'); - }); - - it('renders queries with curation links', () => { - expect( - wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').first().prop('to') - ).toEqual('/engines/some-engine/curations/cur-id-1'); - - expect( - wrapper.find('EuiLinkTo[data-test-subj="CurationsTableQueriesLink"]').last().prop('to') - ).toEqual('/engines/some-engine/curations/cur-id-2'); - }); - - describe('action column', () => { - it('edit action navigates to curation link', () => { - wrapper.find('[data-test-subj="CurationsTableEditButton"]').first().simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-1'); - - wrapper.find('[data-test-subj="CurationsTableEditButton"]').last().simulate('click'); - expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/cur-id-2'); - }); - - it('delete action calls deleteCuration', () => { - wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').first().simulate('click'); - expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-1'); - - wrapper.find('[data-test-subj="CurationsTableDeleteButton"]').last().simulate('click'); - expect(actions.deleteCuration).toHaveBeenCalledWith('cur-id-2'); - }); - }); - }); - - describe('pagination', () => { - it('passes pagination props from meta.page', () => { - setMockValues({ - ...values, - meta: { - page: { - current: 5, - size: 10, - total_results: 50, - }, - }, - }); - const wrapper = shallow(); - - expect(wrapper.find(EuiBasicTable).prop('pagination')).toEqual({ - pageIndex: 4, - pageSize: 10, - totalItemCount: 50, - hidePerPageOptions: true, - }); - }); - - it('calls onPaginate on pagination change', () => { - const wrapper = shallow(); - wrapper.find(EuiBasicTable).simulate('change', { page: { index: 0 } }); - - expect(actions.onPaginate).toHaveBeenCalledWith(1); - }); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 12497ab52baf6..9584b21424fe3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -9,28 +9,47 @@ import React, { useEffect } from 'react'; import { useValues, useActions } from 'kea'; -import { EuiBasicTable, EuiBasicTableColumn, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants'; -import { KibanaLogic } from '../../../../shared/kibana'; -import { EuiButtonTo, EuiLinkTo } from '../../../../shared/react_router_helpers'; -import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination'; +import { EuiButtonTo } from '../../../../shared/react_router_helpers'; -import { ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH } from '../../../routes'; -import { FormattedDateTime } from '../../../utils/formatted_date_time'; +import { ENGINE_CURATIONS_NEW_PATH } from '../../../routes'; import { generateEnginePath } from '../../engine'; import { AppSearchPageTemplate } from '../../layout'; -import { EmptyState } from '../components'; import { CURATIONS_OVERVIEW_TITLE, CREATE_NEW_CURATION_TITLE } from '../constants'; import { CurationsLogic } from '../curations_logic'; -import { Curation } from '../types'; -import { getCurationsBreadcrumbs, convertToDate } from '../utils'; +import { getCurationsBreadcrumbs } from '../utils'; + +import { CurationsOverview } from './curations_overview'; +import { CurationsSettings } from './curations_settings'; export const Curations: React.FC = () => { - const { dataLoading, curations, meta } = useValues(CurationsLogic); - const { loadCurations } = useActions(CurationsLogic); + const { dataLoading, curations, meta, selectedPageTab } = useValues(CurationsLogic); + const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); + + const pageTabs = [ + { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.overviewPageTabLabel', + { + defaultMessage: 'Overview', + } + ), + isSelected: selectedPageTab === 'overview', + onClick: () => onSelectPageTab('overview'), + }, + { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.settingsPageTabLabel', + { + defaultMessage: 'Settings', + } + ), + isSelected: selectedPageTab === 'settings', + onClick: () => onSelectPageTab('settings'), + }, + ]; useEffect(() => { loadCurations(); @@ -50,105 +69,12 @@ export const Curations: React.FC = () => { {CREATE_NEW_CURATION_TITLE} , ], + tabs: pageTabs, }} isLoading={dataLoading && !curations.length} - isEmptyState={!curations.length} - emptyState={} > - - - + {selectedPageTab === 'overview' && } + {selectedPageTab === 'settings' && } ); }; - -export const CurationsTable: React.FC = () => { - const { dataLoading, curations, meta } = useValues(CurationsLogic); - const { onPaginate, deleteCuration } = useActions(CurationsLogic); - - const columns: Array> = [ - { - field: 'queries', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.queries', - { defaultMessage: 'Queries' } - ), - render: (queries: Curation['queries'], curation: Curation) => ( - - {queries.join(', ')} - - ), - width: '40%', - truncateText: true, - mobileOptions: { - header: true, - // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error - // @ts-ignore - enlarge: true, - width: '100%', - truncateText: false, - }, - }, - { - field: 'last_updated', - name: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.column.lastUpdated', - { defaultMessage: 'Last updated' } - ), - width: '30%', - dataType: 'string', - render: (dateString: string) => , - }, - { - width: '120px', - actions: [ - { - name: EDIT_BUTTON_LABEL, - description: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.editTooltip', - { defaultMessage: 'Edit curation' } - ), - type: 'icon', - icon: 'pencil', - color: 'primary', - onClick: (curation: Curation) => { - const { navigateToUrl } = KibanaLogic.values; - const url = generateEnginePath(ENGINE_CURATION_PATH, { curationId: curation.id }); - navigateToUrl(url); - }, - 'data-test-subj': 'CurationsTableEditButton', - }, - { - name: DELETE_BUTTON_LABEL, - description: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.table.deleteTooltip', - { defaultMessage: 'Delete curation' } - ), - type: 'icon', - icon: 'trash', - color: 'danger', - onClick: (curation: Curation) => deleteCuration(curation.id), - 'data-test-subj': 'CurationsTableDeleteButton', - }, - ], - }, - ]; - - return ( - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx new file mode 100644 index 0000000000000..a034c3b2a0689 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../__mocks__/kea_logic'; +import '../../../../__mocks__/react_router'; +import '../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { CurationsTable, EmptyState } from '../components'; + +import { CurationsOverview } from './curations_overview'; + +describe('CurationsOverview', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders an empty message when there are no curations', () => { + setMockValues({ curations: [] }); + const wrapper = shallow(); + + expect(wrapper.is(EmptyState)).toBe(true); + }); + + it('renders a curations table when there are curations present', () => { + setMockValues({ + curations: [ + { + id: 'cur-id-1', + }, + { + id: 'cur-id-2', + }, + ], + }); + const wrapper = shallow(); + + expect(wrapper.find(CurationsTable)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx new file mode 100644 index 0000000000000..ec67d06da4769 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues } from 'kea'; + +import { EuiPanel } from '@elastic/eui'; + +import { CurationsTable, EmptyState } from '../components'; +import { CurationsLogic } from '../curations_logic'; + +export const CurationsOverview: React.FC = () => { + const { curations } = useValues(CurationsLogic); + + return curations.length ? ( + + + + ) : ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.tsx new file mode 100644 index 0000000000000..855570829cce4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.test.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import '../../../../__mocks__/react_router'; +import '../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { CurationsSettings } from './curations_settings'; + +describe('CurationsSettings', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders empty', () => { + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx new file mode 100644 index 0000000000000..4bff7f3b2ef5e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings.tsx @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +export const CurationsSettings: React.FC = () => { + return null; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx b/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx index a251188b5cd90..94440ad4e61a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/get_page_header.tsx @@ -9,7 +9,7 @@ import React, { Fragment } from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { EuiPageHeaderProps } from '@elastic/eui'; +import { EuiPageHeaderProps, EuiTab } from '@elastic/eui'; /* * Given an AppSearchPageTemplate or WorkplaceSearchPageTemplate, these @@ -35,13 +35,30 @@ export const getPageHeaderActions = (wrapper: ShallowWrapper) => { return shallow(

- {actions.map((action: React.ReactNode, i) => ( + {actions.map((action, i) => ( {action} ))}
); }; +export const getPageHeaderTabs = (wrapper: ShallowWrapper) => { + // The tabs prop of EuiPageHeader takes an `Array` + // instead of an array of EuiTab jsx components + // These are then rendered inside of EuiPageHeader as EuiTabs + // See https://elastic.github.io/eui/#/layout/page-header#tabs-in-the-page-header + + const tabs = getPageHeader(wrapper).tabs || []; + + return shallow( +
+ {tabs.map((tabProps, i) => ( + + ))} +
+ ); +}; + export const getPageHeaderChildren = (wrapper: ShallowWrapper) => { const children = getPageHeader(wrapper).children || null; diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts b/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts index 7903b4a31c8a9..35836d5526615 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/index.ts @@ -16,6 +16,7 @@ export { getPageDescription, getPageHeaderActions, getPageHeaderChildren, + getPageHeaderTabs, } from './get_page_header'; // Misc From 90ec827147ad5517bcb2a2e7be8918a59ee087a6 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 20 Sep 2021 13:59:27 -0400 Subject: [PATCH 07/69] [Alerting] Add telemetry for HTTP API calls with legacy terminology (#112173) * Deprecated attributes telemetry * Add additional term and improve test quality * PR feedback --- .../server/routes/aggregate_rules.test.ts | 43 +++++++++++++++++ .../alerting/server/routes/aggregate_rules.ts | 9 +++- .../alerting/server/routes/find_rules.test.ts | 42 ++++++++++++++++ .../alerting/server/routes/find_rules.ts | 12 ++++- .../plugins/alerting/server/routes/index.ts | 4 +- .../server/routes/legacy/aggregate.test.ts | 30 ++++++++++++ .../server/routes/legacy/aggregate.ts | 5 ++ .../server/routes/legacy/find.test.ts | 34 +++++++++++++ .../alerting/server/routes/legacy/find.ts | 7 +++ .../lib/track_legacy_terminology.test.ts | 48 +++++++++++++++++++ .../routes/lib/track_legacy_terminology.ts | 35 ++++++++++++++ 11 files changed, 265 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts create mode 100644 x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts index 233e605cf7f5b..c8d4372fb72cf 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts @@ -11,13 +11,21 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); +const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); +const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); jest.mock('../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +jest.mock('./lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -147,4 +155,39 @@ describe('aggregateRulesRoute', () => { expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + aggregateRulesRoute(router, licenseState, mockUsageCounter); + const aggregateResult = { + alertExecutionStatus: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + }; + rulesClient.aggregate.mockResolvedValueOnce(aggregateResult); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts index f91231f9ac48a..84c03e21ff36e 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -7,10 +7,12 @@ import { IRouter } from 'kibana/server'; import { schema } from '@kbn/config-schema'; +import { UsageCounter } from 'src/plugins/usage_collection/server'; import { ILicenseState } from '../lib'; import { AggregateResult, AggregateOptions } from '../rules_client'; import { RewriteResponseCase, RewriteRequestCase, verifyAccessAndContext } from './lib'; import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; // config definition const querySchema = schema.object({ @@ -53,7 +55,8 @@ const rewriteBodyRes: RewriteResponseCase = ({ export const aggregateRulesRoute = ( router: IRouter, - licenseState: ILicenseState + licenseState: ILicenseState, + usageCounter?: UsageCounter ) => { router.get( { @@ -69,6 +72,10 @@ export const aggregateRulesRoute = ( ...req.query, has_reference: req.query.has_reference || undefined, }); + trackLegacyTerminology( + [req.query.search, req.query.search_fields].filter(Boolean) as string[], + usageCounter + ); const aggregateResult = await rulesClient.aggregate({ options }); return res.ok({ body: rewriteBodyRes(aggregateResult), diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts index e0da7b0645748..23f636e962169 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -11,13 +11,21 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); +const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); +const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); jest.mock('../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +jest.mock('./lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -145,4 +153,38 @@ describe('findRulesRoute', () => { expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState, mockUsageCounter); + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + sort_field: 'alertTypeId', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + 'alertTypeId', + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index ef62341290228..22d701b2040a8 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -7,11 +7,13 @@ import { omit } from 'lodash'; import { IRouter } from 'kibana/server'; +import { UsageCounter } from 'src/plugins/usage_collection/server'; import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../lib'; import { FindOptions, FindResult } from '../rules_client'; import { RewriteRequestCase, RewriteResponseCase, verifyAccessAndContext } from './lib'; import { AlertTypeParams, AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; +import { trackLegacyTerminology } from './lib/track_legacy_terminology'; // query definition const querySchema = schema.object({ @@ -107,7 +109,8 @@ const rewriteBodyRes: RewriteResponseCase> = ({ export const findRulesRoute = ( router: IRouter, - licenseState: ILicenseState + licenseState: ILicenseState, + usageCounter?: UsageCounter ) => { router.get( { @@ -120,6 +123,13 @@ export const findRulesRoute = ( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = context.alerting.getRulesClient(); + trackLegacyTerminology( + [req.query.search, req.query.search_fields, req.query.sort_field].filter( + Boolean + ) as string[], + usageCounter + ); + const options = rewriteQueryReq({ ...req.query, has_reference: req.query.has_reference || undefined, diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index c1c7eae45109e..2db52fa2c143d 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -38,7 +38,7 @@ export interface RouteOptions { } export function defineRoutes(opts: RouteOptions) { - const { router, licenseState, encryptedSavedObjects } = opts; + const { router, licenseState, encryptedSavedObjects, usageCounter } = opts; defineLegacyRoutes(opts); createRuleRoute(opts); @@ -49,7 +49,7 @@ export function defineRoutes(opts: RouteOptions) { aggregateRulesRoute(router, licenseState); disableRuleRoute(router, licenseState); enableRuleRoute(router, licenseState); - findRulesRoute(router, licenseState); + findRulesRoute(router, licenseState, usageCounter); getRuleAlertSummaryRoute(router, licenseState); getRuleStateRoute(router, licenseState); healthRoute(router, licenseState, encryptedSavedObjects); diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts index d08e970eef69d..cab779a42ce20 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts @@ -12,6 +12,7 @@ import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from './../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); @@ -26,6 +27,10 @@ jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +jest.mock('../lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -166,4 +171,29 @@ describe('aggregateAlertRoute', () => { await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('aggregate', mockUsageCounter); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + aggregateAlertRoute(router, licenseState, mockUsageCounter); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts index d1e8d98a95409..bd4aa7750738c 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts @@ -14,6 +14,7 @@ import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; import { renameKeys } from './../lib/rename_keys'; import { FindOptions } from '../../rules_client'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; // config definition const querySchema = schema.object({ @@ -55,6 +56,10 @@ export const aggregateAlertRoute = ( const rulesClient = context.alerting.getRulesClient(); trackLegacyRouteUsage('aggregate', usageCounter); + trackLegacyTerminology( + [req.query.search, req.query.search_fields].filter(Boolean) as string[], + usageCounter + ); const query = req.query; const renameMap = { diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts index de12c62f1e4fd..1ddd1a662cbe4 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts @@ -12,6 +12,7 @@ import { verifyApiAccess } from '../../lib/license_api_access'; import { mockHandlerArguments } from './../_mock_handler_arguments'; import { rulesClientMock } from '../../rules_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; const rulesClient = rulesClientMock.create(); @@ -23,6 +24,10 @@ jest.mock('../../lib/track_legacy_route_usage', () => ({ trackLegacyRouteUsage: jest.fn(), })); +jest.mock('../lib/track_legacy_terminology', () => ({ + trackLegacyTerminology: jest.fn(), +})); + beforeEach(() => { jest.resetAllMocks(); }); @@ -160,4 +165,33 @@ describe('findAlertRoute', () => { await handler(context, req, res); expect(trackLegacyRouteUsage).toHaveBeenCalledWith('find', mockUsageCounter); }); + + it('should track calls with deprecated param values', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + findAlertRoute(router, licenseState, mockUsageCounter); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + search_fields: ['alertTypeId:1', 'message:foo'], + search: 'alertTypeId:2', + sort_field: 'alertTypeId', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(trackLegacyTerminology).toHaveBeenCalledTimes(1); + expect((trackLegacyTerminology as jest.Mock).mock.calls[0][0]).toStrictEqual([ + 'alertTypeId:2', + ['alertTypeId:1', 'message:foo'], + 'alertTypeId', + ]); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index f915f0e15afb6..5a87536a1ac94 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -15,6 +15,7 @@ import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; import { renameKeys } from './../lib/rename_keys'; import { FindOptions } from '../../rules_client'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { trackLegacyTerminology } from '../lib/track_legacy_terminology'; // config definition const querySchema = schema.object({ @@ -59,6 +60,12 @@ export const findAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('find', usageCounter); + trackLegacyTerminology( + [req.query.search, req.query.search_fields, req.query.sort_field].filter( + Boolean + ) as string[], + usageCounter + ); const rulesClient = context.alerting.getRulesClient(); const query = req.query; diff --git a/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts new file mode 100644 index 0000000000000..5c6838e144aa2 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; +import { trackLegacyTerminology, LEGACY_TERMS } from './track_legacy_terminology'; + +describe('trackLegacyTerminology', () => { + it('should call `usageCounter.incrementCounter`', () => { + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + trackLegacyTerminology( + ['shouldNotMatch', LEGACY_TERMS.map((lt) => `${lt}foo`)], + mockUsageCounter + ); + expect(mockUsageCounter.incrementCounter).toHaveBeenCalledTimes(LEGACY_TERMS.length); + LEGACY_TERMS.forEach((legacyTerm, index) => { + expect((mockUsageCounter.incrementCounter as jest.Mock).mock.calls[index][0]).toStrictEqual({ + counterName: `legacyTerm_${legacyTerm}`, + counterType: 'legacyTerminology', + incrementBy: 1, + }); + }); + }); + + it('should do nothing if no usage counter is provided', () => { + let err; + try { + trackLegacyTerminology(['test'], undefined); + } catch (e) { + err = e; + } + expect(err).toBeUndefined(); + }); + + it('should do nothing if no terms are provided', () => { + let err; + try { + trackLegacyTerminology([], undefined); + } catch (e) { + err = e; + } + expect(err).toBeUndefined(); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts new file mode 100644 index 0000000000000..e825cb990fc4d --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/track_legacy_terminology.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { flatten } from 'lodash'; +import { UsageCounter } from 'src/plugins/usage_collection/server'; + +export const LEGACY_TERMS = ['alertTypeId', 'actionTypeId']; + +export function trackLegacyTerminology( + terms: Array, + usageCounter?: UsageCounter +) { + if (!usageCounter) { + return null; + } + + if (!terms || terms.length === 0) { + return null; + } + + for (const legacyTerm of LEGACY_TERMS) { + for (const term of flatten(terms)) { + if (term.includes(legacyTerm)) { + usageCounter.incrementCounter({ + counterName: `legacyTerm_${legacyTerm}`, + counterType: 'legacyTerminology', + incrementBy: 1, + }); + } + } + } +} From 6f2815a76498e06e1180f7f30098fae3f225dc84 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 20 Sep 2021 20:13:09 +0200 Subject: [PATCH 08/69] [Discover] Improve saved query management functional service (#112055) --- test/functional/services/saved_query_management_component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/services/saved_query_management_component.ts b/test/functional/services/saved_query_management_component.ts index 37c6a45557f2f..a216f8cb0469e 100644 --- a/test/functional/services/saved_query_management_component.ts +++ b/test/functional/services/saved_query_management_component.ts @@ -166,8 +166,9 @@ export class SavedQueryManagementComponentService extends FtrService { const isOpenAlready = await this.testSubjects.exists('saved-query-management-popover'); if (isOpenAlready) return; + await this.testSubjects.click('saved-query-management-popover-button'); + await this.retry.waitFor('saved query management popover to have any text', async () => { - await this.testSubjects.click('saved-query-management-popover-button'); const queryText = await this.testSubjects.getVisibleText('saved-query-management-popover'); return queryText.length > 0; }); From 10ac814d8f5ede2c10c83aadc99304a59ac9682f Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Mon, 20 Sep 2021 15:29:52 -0400 Subject: [PATCH 09/69] [Cases] Migrate user actions connector ID (#108272) * Making progress * Fleshing out the extraction logic * Finishing migration logic and starting more tests * Finishing migration unit tests * Making progress on services * Finishing transform to es schema * Finishing transform functionality and unit tests * reverting migration data updates * Cleaning up type errors * fixing test error * Working migration tests * Refactoring retrieval of connector fields * Refactoring connector id in and tests in frontend * Fixing tests and finished refactoring parse string * Fixing integration test * Fixing integration tests * Removing some duplicate code and updating test name * Fixing create connector user action bug * Addressing feedback and logging error * Moving parsing function to common * Fixing type errors * Fixing type errors * Addressing feedback * Fixing lint errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/common/api/cases/case.ts | 16 +- .../cases/common/api/cases/user_actions.ts | 3 +- .../cases/common/api/connectors/index.ts | 12 +- x-pack/plugins/cases/common/index.ts | 1 + x-pack/plugins/cases/common/ui/types.ts | 2 + .../cases/common/utils/user_actions.ts | 18 + .../cases/public/common/user_actions/index.ts | 8 + .../common/user_actions/parsers.test.ts | 86 + .../public/common/user_actions/parsers.ts | 77 + .../components/edit_connector/helpers.test.ts | 187 ++ .../components/edit_connector/helpers.ts | 30 +- .../user_action_tree/helpers.test.tsx | 108 +- .../components/user_action_tree/helpers.tsx | 53 +- .../components/user_action_tree/index.tsx | 10 +- .../plugins/cases/public/containers/mock.ts | 123 +- .../use_get_case_user_actions.test.tsx | 237 +- .../containers/use_get_case_user_actions.tsx | 51 +- .../plugins/cases/public/containers/utils.ts | 8 - .../cases/server/client/attachments/add.ts | 6 +- .../cases/server/client/attachments/update.ts | 9 +- .../cases/server/client/cases/create.ts | 2 +- .../cases/server/client/cases/delete.ts | 2 +- .../plugins/cases/server/client/cases/mock.ts | 18 +- .../plugins/cases/server/client/cases/push.ts | 2 +- .../cases/server/client/cases/utils.test.ts | 6 +- .../cases/server/client/cases/utils.ts | 30 +- .../server/client/user_actions/get.test.ts | 106 + .../cases/server/client/user_actions/get.ts | 47 +- .../plugins/cases/server/common/constants.ts | 29 + .../migrations/cases.test.ts | 570 ++-- .../saved_object_types/migrations/cases.ts | 14 +- .../migrations/configuration.test.ts | 142 +- .../migrations/configuration.ts | 9 +- .../saved_object_types/migrations/index.ts | 57 +- .../migrations/user_actions.test.ts | 562 ++++ .../migrations/user_actions.ts | 159 + .../migrations/utils.test.ts | 229 -- .../saved_object_types/migrations/utils.ts | 73 - .../cases/server/services/cases/index.test.ts | 8 +- .../cases/server/services/test_utils.ts | 17 +- .../services/user_actions/helpers.test.ts | 332 ++ .../server/services/user_actions/helpers.ts | 192 +- .../services/user_actions/index.test.ts | 557 ++++ .../server/services/user_actions/index.ts | 101 +- .../services/user_actions/transform.test.ts | 1246 +++++++ .../server/services/user_actions/transform.ts | 320 ++ .../server/services/user_actions/types.ts | 14 + .../tests/common/cases/delete_cases.ts | 4 +- .../tests/common/cases/patch_cases.ts | 4 + .../tests/common/cases/post_case.ts | 7 +- .../tests/common/comments/post_comment.ts | 2 + .../user_actions/get_all_user_actions.ts | 19 +- .../tests/common/user_actions/migrations.ts | 149 +- .../tests/trial/cases/push_case.ts | 3 +- .../user_actions/get_all_user_actions.ts | 4 +- .../migrations/7.13_user_actions/data.json.gz | Bin 0 -> 2078 bytes .../7.13_user_actions/mappings.json | 2954 +++++++++++++++++ 57 files changed, 7895 insertions(+), 1140 deletions(-) create mode 100644 x-pack/plugins/cases/common/utils/user_actions.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/index.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.test.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.ts create mode 100644 x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts create mode 100644 x-pack/plugins/cases/server/client/user_actions/get.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/helpers.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/index.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/types.ts create mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz create mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 37a491cdad4c0..05a053307b29c 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -87,8 +87,11 @@ const CaseBasicRt = rt.type({ owner: rt.string, }); -export const CaseExternalServiceBasicRt = rt.type({ - connector_id: rt.union([rt.string, rt.null]), +/** + * This represents the push to service UserAction. It lacks the connector_id because that is stored in a different field + * within the user action object in the API response. + */ +export const CaseUserActionExternalServiceRt = rt.type({ connector_name: rt.string, external_id: rt.string, external_title: rt.string, @@ -97,7 +100,14 @@ export const CaseExternalServiceBasicRt = rt.type({ pushed_by: UserRT, }); -const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); +export const CaseExternalServiceBasicRt = rt.intersection([ + rt.type({ + connector_id: rt.union([rt.string, rt.null]), + }), + CaseUserActionExternalServiceRt, +]); + +export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); export const CaseAttributesRt = rt.intersection([ CaseBasicRt, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index 03912c550d77a..e86ce5248a6f9 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -34,7 +34,6 @@ const UserActionRt = rt.union([ rt.literal('push-to-service'), ]); -// TO DO change state to status const CaseUserActionBasicRT = rt.type({ action_field: UserActionFieldRt, action: UserActionRt, @@ -51,6 +50,8 @@ const CaseUserActionResponseRT = rt.intersection([ action_id: rt.string, case_id: rt.string, comment_id: rt.union([rt.string, rt.null]), + new_val_connector_id: rt.union([rt.string, rt.null]), + old_val_connector_id: rt.union([rt.string, rt.null]), }), rt.partial({ sub_case_id: rt.string }), ]); diff --git a/x-pack/plugins/cases/common/api/connectors/index.ts b/x-pack/plugins/cases/common/api/connectors/index.ts index 77af90b5d08cb..2b3483b4f6184 100644 --- a/x-pack/plugins/cases/common/api/connectors/index.ts +++ b/x-pack/plugins/cases/common/api/connectors/index.ts @@ -84,14 +84,22 @@ export const ConnectorTypeFieldsRt = rt.union([ ConnectorSwimlaneTypeFieldsRt, ]); +/** + * This type represents the connector's format when it is encoded within a user action. + */ +export const CaseUserActionConnectorRt = rt.intersection([ + rt.type({ name: rt.string }), + ConnectorTypeFieldsRt, +]); + export const CaseConnectorRt = rt.intersection([ rt.type({ id: rt.string, - name: rt.string, }), - ConnectorTypeFieldsRt, + CaseUserActionConnectorRt, ]); +export type CaseUserActionConnector = rt.TypeOf; export type CaseConnector = rt.TypeOf; export type ConnectorTypeFields = rt.TypeOf; export type ConnectorJiraTypeFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts index 5305318cc9aa6..d38b1a779981c 100644 --- a/x-pack/plugins/cases/common/index.ts +++ b/x-pack/plugins/cases/common/index.ts @@ -12,3 +12,4 @@ export * from './constants'; export * from './api'; export * from './ui/types'; export * from './utils/connectors_api'; +export * from './utils/user_actions'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index bf4ec0da6ee56..c89c3eb08263b 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -66,7 +66,9 @@ export interface CaseUserActions { caseId: string; commentId: string | null; newValue: string | null; + newValConnectorId: string | null; oldValue: string | null; + oldValConnectorId: string | null; } export interface CaseExternalService { diff --git a/x-pack/plugins/cases/common/utils/user_actions.ts b/x-pack/plugins/cases/common/utils/user_actions.ts new file mode 100644 index 0000000000000..7de0d7066eaed --- /dev/null +++ b/x-pack/plugins/cases/common/utils/user_actions.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function isCreateConnector(action?: string, actionFields?: string[]): boolean { + return action === 'create' && actionFields != null && actionFields.includes('connector'); +} + +export function isUpdateConnector(action?: string, actionFields?: string[]): boolean { + return action === 'update' && actionFields != null && actionFields.includes('connector'); +} + +export function isPush(action?: string, actionFields?: string[]): boolean { + return action === 'push-to-service' && actionFields != null && actionFields.includes('pushed'); +} diff --git a/x-pack/plugins/cases/public/common/user_actions/index.ts b/x-pack/plugins/cases/public/common/user_actions/index.ts new file mode 100644 index 0000000000000..507455f7102a7 --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './parsers'; diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts new file mode 100644 index 0000000000000..c6d13cc41686c --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConnectorTypes, noneConnectorId } from '../../../common'; +import { parseStringAsConnector, parseStringAsExternalService } from './parsers'; + +describe('user actions utility functions', () => { + describe('parseStringAsConnector', () => { + it('return null if the data is null', () => { + expect(parseStringAsConnector('', null)).toBeNull(); + }); + + it('return null if the data is not a json object', () => { + expect(parseStringAsConnector('', 'blah')).toBeNull(); + }); + + it('return null if the data is not a valid connector', () => { + expect(parseStringAsConnector('', JSON.stringify({ a: '1' }))).toBeNull(); + }); + + it('return null if id is null but the data is a connector other than none', () => { + expect( + parseStringAsConnector( + null, + JSON.stringify({ type: ConnectorTypes.jira, name: '', fields: null }) + ) + ).toBeNull(); + }); + + it('return the id as the none connector if the data is the none connector', () => { + expect( + parseStringAsConnector( + null, + JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }) + ) + ).toEqual({ id: noneConnectorId, type: ConnectorTypes.none, name: '', fields: null }); + }); + + it('returns a decoded connector with the specified id', () => { + expect( + parseStringAsConnector( + 'a', + JSON.stringify({ type: ConnectorTypes.jira, name: 'hi', fields: null }) + ) + ).toEqual({ id: 'a', type: ConnectorTypes.jira, name: 'hi', fields: null }); + }); + }); + + describe('parseStringAsExternalService', () => { + it('returns null when the data is null', () => { + expect(parseStringAsExternalService('', null)).toBeNull(); + }); + + it('returns null when the data is not valid json', () => { + expect(parseStringAsExternalService('', 'blah')).toBeNull(); + }); + + it('returns null when the data is not a valid external service object', () => { + expect(parseStringAsExternalService('', JSON.stringify({ a: '1' }))).toBeNull(); + }); + + it('returns the decoded external service with the connector_id field added', () => { + const externalServiceInfo = { + connector_name: 'name', + external_id: '1', + external_title: 'title', + external_url: 'abc', + pushed_at: '1', + pushed_by: { + username: 'a', + email: 'a@a.com', + full_name: 'a', + }, + }; + + expect(parseStringAsExternalService('500', JSON.stringify(externalServiceInfo))).toEqual({ + ...externalServiceInfo, + connector_id: '500', + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.ts new file mode 100644 index 0000000000000..dfea22443aa51 --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/parsers.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CaseUserActionConnectorRt, + CaseConnector, + ConnectorTypes, + noneConnectorId, + CaseFullExternalService, + CaseUserActionExternalServiceRt, +} from '../../../common'; + +export const parseStringAsConnector = ( + id: string | null, + encodedData: string | null +): CaseConnector | null => { + if (encodedData == null) { + return null; + } + + const decodedConnector = parseString(encodedData); + + if (!CaseUserActionConnectorRt.is(decodedConnector)) { + return null; + } + + if (id == null && decodedConnector.type === ConnectorTypes.none) { + return { + ...decodedConnector, + id: noneConnectorId, + }; + } else if (id == null) { + return null; + } else { + // id does not equal null or undefined and the connector type does not equal none + // so return the connector with its id + return { + ...decodedConnector, + id, + }; + } +}; + +const parseString = (params: string | null): unknown | null => { + if (params == null) { + return null; + } + + try { + return JSON.parse(params); + } catch { + return null; + } +}; + +export const parseStringAsExternalService = ( + id: string | null, + encodedData: string | null +): CaseFullExternalService => { + if (encodedData == null) { + return null; + } + + const decodedExternalService = parseString(encodedData); + if (!CaseUserActionExternalServiceRt.is(decodedExternalService)) { + return null; + } + + return { + ...decodedExternalService, + connector_id: id, + }; +}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts new file mode 100644 index 0000000000000..e20d6b37258bc --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseUserActionConnector, ConnectorTypes } from '../../../common'; +import { CaseUserActions } from '../../containers/types'; +import { getConnectorFieldsFromUserActions } from './helpers'; + +describe('helpers', () => { + describe('getConnectorFieldsFromUserActions', () => { + it('returns null when it cannot find the connector id', () => { + expect(getConnectorFieldsFromUserActions('a', [])).toBeNull(); + }); + + it('returns null when the value fields are not valid encoded fields', () => { + expect( + getConnectorFieldsFromUserActions('a', [createUserAction({ newValue: 'a', oldValue: 'a' })]) + ).toBeNull(); + }); + + it('returns null when it cannot find the connector id in a non empty array', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: JSON.stringify({ a: '1' }), + oldValue: JSON.stringify({ a: '1' }), + }), + ]) + ).toBeNull(); + }); + + it('returns the fields when it finds the connector id in the new value', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: JSON.stringify({ a: '1' }), + newValConnectorId: 'a', + }), + ]) + ).toEqual(defaultJiraFields); + }); + + it('returns the fields when it finds the connector id in the new value and the old value is null', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + newValConnectorId: 'a', + }), + ]) + ).toEqual(defaultJiraFields); + }); + + it('returns the fields when it finds the connector id in the old value', () => { + const expectedFields = { ...defaultJiraFields, issueType: '5' }; + + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector({ + fields: expectedFields, + }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + }), + ]) + ).toEqual(expectedFields); + }); + + it('returns the fields when it finds the connector id in the second user action', () => { + const expectedFields = { ...defaultJiraFields, issueType: '5' }; + + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector(), + newValConnectorId: 'b', + oldValConnectorId: 'a', + }), + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector({ fields: expectedFields }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + }), + ]) + ).toEqual(expectedFields); + }); + + it('ignores a parse failure and finds the right user action', () => { + expect( + getConnectorFieldsFromUserActions('none', [ + createUserAction({ + newValue: 'b', + newValConnectorId: null, + }), + createUserAction({ + newValue: createEncodedJiraConnector({ + type: ConnectorTypes.none, + name: '', + fields: null, + }), + newValConnectorId: null, + }), + ]) + ).toBeNull(); + }); + + it('returns null when the id matches but the encoded value is null', () => { + expect( + getConnectorFieldsFromUserActions('b', [ + createUserAction({ + newValue: null, + newValConnectorId: 'b', + }), + ]) + ).toBeNull(); + }); + + it('returns null when the action fields is not of length 1', () => { + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: JSON.stringify({ a: '1', fields: { hello: '1' } }), + oldValue: JSON.stringify({ a: '1', fields: { hi: '2' } }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + actionField: ['connector', 'connector'], + }), + ]) + ).toBeNull(); + }); + + it('matches the none connector the searched for id is none', () => { + expect( + getConnectorFieldsFromUserActions('none', [ + createUserAction({ + newValue: createEncodedJiraConnector({ + type: ConnectorTypes.none, + name: '', + fields: null, + }), + newValConnectorId: null, + }), + ]) + ).toBeNull(); + }); + }); +}); + +function createUserAction(fields: Partial): CaseUserActions { + return { + action: 'update', + actionAt: '', + actionBy: {}, + actionField: ['connector'], + actionId: '', + caseId: '', + commentId: '', + newValConnectorId: null, + oldValConnectorId: null, + newValue: null, + oldValue: null, + ...fields, + }; +} + +function createEncodedJiraConnector(fields?: Partial): string { + return JSON.stringify({ + type: ConnectorTypes.jira, + name: 'name', + fields: defaultJiraFields, + ...fields, + }); +} + +const defaultJiraFields = { + issueType: '1', + parent: null, + priority: null, +}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts index 36eb3f58c8aaf..b97035c458aca 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts @@ -5,23 +5,33 @@ * 2.0. */ +import { ConnectorTypeFields } from '../../../common'; import { CaseUserActions } from '../../containers/types'; +import { parseStringAsConnector } from '../../common/user_actions'; -export const getConnectorFieldsFromUserActions = (id: string, userActions: CaseUserActions[]) => { +export const getConnectorFieldsFromUserActions = ( + id: string, + userActions: CaseUserActions[] +): ConnectorTypeFields['fields'] => { try { for (const action of [...userActions].reverse()) { if (action.actionField.length === 1 && action.actionField[0] === 'connector') { - if (action.oldValue && action.newValue) { - const oldValue = JSON.parse(action.oldValue); - const newValue = JSON.parse(action.newValue); + const parsedNewConnector = parseStringAsConnector( + action.newValConnectorId, + action.newValue + ); - if (newValue.id === id) { - return newValue.fields; - } + if (parsedNewConnector && id === parsedNewConnector.id) { + return parsedNewConnector.fields; + } + + const parsedOldConnector = parseStringAsConnector( + action.oldValConnectorId, + action.oldValue + ); - if (oldValue.id === id) { - return oldValue.fields; - } + if (parsedOldConnector && id === parsedOldConnector.id) { + return parsedOldConnector.fields; } } } diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx index b49a010cff38f..841f0d36bbf17 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../common'; +import { CaseStatuses, ConnectorTypes } from '../../../common'; import { basicPush, getUserAction } from '../../containers/mock'; import { getLabelTitle, @@ -129,7 +129,7 @@ describe('User action tree helpers', () => { `${i18n.PUSHED_NEW_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue).external_url + JSON.parse(action.newValue!).external_url ); }); @@ -142,50 +142,74 @@ describe('User action tree helpers', () => { `${i18n.UPDATE_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue).external_url + JSON.parse(action.newValue!).external_url ); }); - it('label title generated for update connector - change connector', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'resilient-2' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, - }); - - expect(result).toEqual('selected My Connector 2 as incident management system'); - }); - - it('label title generated for update connector - change connector to none', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'none' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, + describe('getConnectorLabelTitle', () => { + it('returns an empty string when the encoded old value is null', () => { + const result = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { oldValue: null }), + connectors, + }); + + expect(result).toEqual(''); + }); + + it('returns an empty string when the encoded new value is null', () => { + const result = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { newValue: null }), + connectors, + }); + + expect(result).toEqual(''); + }); + + it('returns the change connector label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ + type: ConnectorTypes.serviceNowITSM, + name: 'a', + fields: null, + }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.resilient, name: 'a', fields: null }), + newValConnectorId: 'resilient-2', + }), + connectors, + }); + + expect(result).toEqual('selected My Connector 2 as incident management system'); + }); + + it('returns the removed connector label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }), + newValConnectorId: 'none', + }), + connectors, + }); + + expect(result).toEqual('removed external incident management system'); + }); + + it('returns the connector fields changed label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + newValConnectorId: 'servicenow-1', + }), + connectors, + }); + + expect(result).toEqual('changed connector field'); }); - - expect(result).toEqual('removed external incident management system'); - }); - - it('label title generated for update connector - field change', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'servicenow-1' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, - }); - - expect(result).toEqual('changed connector field'); }); describe('toStringArray', () => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx index 744b14926b358..2eb44f91190c6 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx @@ -23,10 +23,11 @@ import { CommentType, Comment, CommentRequestActionsType, + noneConnectorId, } from '../../../common'; import { CaseUserActions } from '../../containers/types'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseString } from '../../containers/utils'; +import { parseStringAsConnector, parseStringAsExternalService } from '../../common/user_actions'; import { Tags } from '../tag_list/tags'; import { UserActionUsernameWithAvatar } from './user_action_username_with_avatar'; import { UserActionTimestamp } from './user_action_timestamp'; @@ -97,23 +98,27 @@ export const getConnectorLabelTitle = ({ action: CaseUserActions; connectors: ActionConnector[]; }) => { - const oldValue = parseString(`${action.oldValue}`); - const newValue = parseString(`${action.newValue}`); + const oldConnector = parseStringAsConnector(action.oldValConnectorId, action.oldValue); + const newConnector = parseStringAsConnector(action.newValConnectorId, action.newValue); - if (oldValue === null || newValue === null) { + if (!oldConnector || !newConnector) { return ''; } - // Connector changed - if (oldValue.id !== newValue.id) { - const newConnector = connectors.find((c) => c.id === newValue.id); - return newValue.id != null && newValue.id !== 'none' && newConnector != null - ? i18n.SELECTED_THIRD_PARTY(newConnector.name) - : i18n.REMOVED_THIRD_PARTY; - } else { - // Field changed + // if the ids are the same, assume we just changed the fields + if (oldConnector.id === newConnector.id) { return i18n.CHANGED_CONNECTOR_FIELD; } + + // ids are not the same so check and see if the id is a valid connector and then return its name + // if the connector id is the none connector value then it must have been removed + const newConnectorActionInfo = connectors.find((c) => c.id === newConnector.id); + if (newConnector.id !== noneConnectorId && newConnectorActionInfo != null) { + return i18n.SELECTED_THIRD_PARTY(newConnectorActionInfo.name); + } + + // it wasn't a valid connector or it was the none connector, so it must have been removed + return i18n.REMOVED_THIRD_PARTY; }; const getTagsLabelTitle = (action: CaseUserActions) => { @@ -133,7 +138,8 @@ const getTagsLabelTitle = (action: CaseUserActions) => { }; export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: boolean) => { - const pushedVal = JSON.parse(action.newValue ?? '') as CaseFullExternalService; + const externalService = parseStringAsExternalService(action.newValConnectorId, action.newValue); + return ( {`${firstPush ? i18n.PUSHED_NEW_INCIDENT : i18n.UPDATE_INCIDENT} ${ - pushedVal?.connector_name + externalService?.connector_name }`} - - {pushedVal?.external_title} + + {externalService?.external_title} @@ -157,20 +163,19 @@ export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: b export const getPushInfo = ( caseServices: CaseServices, - // a JSON parse failure will result in null for parsedValue - parsedValue: { connector_id: string | null; connector_name: string } | null, + externalService: CaseFullExternalService | undefined, index: number ) => - parsedValue != null && parsedValue.connector_id != null + externalService != null && externalService.connector_id != null ? { - firstPush: caseServices[parsedValue.connector_id]?.firstPushIndex === index, - parsedConnectorId: parsedValue.connector_id, - parsedConnectorName: parsedValue.connector_name, + firstPush: caseServices[externalService.connector_id]?.firstPushIndex === index, + parsedConnectorId: externalService.connector_id, + parsedConnectorName: externalService.connector_name, } : { firstPush: false, - parsedConnectorId: 'none', - parsedConnectorName: 'none', + parsedConnectorId: noneConnectorId, + parsedConnectorName: noneConnectorId, }; const getUpdateActionIcon = (actionField: string): string => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx index 784817229caf9..7ea415324194c 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx @@ -35,7 +35,7 @@ import { Ecs, } from '../../../common'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseString } from '../../containers/utils'; +import { parseStringAsExternalService } from '../../common/user_actions'; import { OnUpdateFields } from '../case_view'; import { getConnectorLabelTitle, @@ -512,10 +512,14 @@ export const UserActionTree = React.memo( // Pushed information if (action.actionField.length === 1 && action.actionField[0] === 'pushed') { - const parsedValue = parseString(`${action.newValue}`); + const parsedExternalService = parseStringAsExternalService( + action.newValConnectorId, + action.newValue + ); + const { firstPush, parsedConnectorId, parsedConnectorName } = getPushInfo( caseServices, - parsedValue, + parsedExternalService, index ); diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index c955bb34240e2..fcd564969d486 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -9,6 +9,7 @@ import { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } import { AssociationType, + CaseUserActionConnector, CaseResponse, CasesFindResponse, CasesResponse, @@ -19,6 +20,9 @@ import { CommentResponse, CommentType, ConnectorTypes, + isCreateConnector, + isPush, + isUpdateConnector, SECURITY_SOLUTION_OWNER, UserAction, UserActionField, @@ -240,7 +244,9 @@ export const pushedCase: Case = { const basicAction = { actionAt: basicCreatedAt, actionBy: elasticUser, + oldValConnectorId: null, oldValue: null, + newValConnectorId: null, newValue: 'what a cool value', caseId: basicCaseId, commentId: null, @@ -308,12 +314,7 @@ export const basicCaseSnake: CaseResponse = { closed_at: null, closed_by: null, comments: [basicCommentSnake], - connector: { - id: 'none', - name: 'My Connector', - type: ConnectorTypes.none, - fields: null, - }, + connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, created_at: basicCreatedAt, created_by: elasticUserSnake, external_service: null, @@ -328,8 +329,8 @@ export const casesStatusSnake: CasesStatusResponse = { count_open_cases: 20, }; +export const pushConnectorId = '123'; export const pushSnake = { - connector_id: '123', connector_name: 'connector name', external_id: 'external_id', external_title: 'external title', @@ -350,7 +351,7 @@ export const pushedCaseSnake = { type: ConnectorTypes.jira, fields: null, }, - external_service: basicPushSnake, + external_service: { ...basicPushSnake, connector_id: pushConnectorId }, }; export const reporters: string[] = ['alexis', 'kim', 'maria', 'steph']; @@ -385,17 +386,20 @@ const basicActionSnake = { comment_id: null, owner: SECURITY_SOLUTION_OWNER, }; -export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ - ...basicActionSnake, - action_id: `${af[0]}-${a}`, - action_field: af, - action: a, - comment_id: af[0] === 'comment' ? basicCommentId : null, - new_value: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); +export const getUserActionSnake = (af: UserActionField, a: UserAction) => { + const isPushToService = a === 'push-to-service' && af[0] === 'pushed'; + + return { + ...basicActionSnake, + action_id: `${af[0]}-${a}`, + action_field: af, + action: a, + comment_id: af[0] === 'comment' ? basicCommentId : null, + new_value: isPushToService ? JSON.stringify(basicPushSnake) : basicAction.newValue, + new_val_connector_id: isPushToService ? pushConnectorId : null, + old_val_connector_id: null, + }; +}; export const caseUserActionsSnake: CaseUserActionsResponse = [ getUserActionSnake(['description'], 'create'), @@ -405,17 +409,76 @@ export const caseUserActionsSnake: CaseUserActionsResponse = [ // user actions -export const getUserAction = (af: UserActionField, a: UserAction) => ({ - ...basicAction, - actionId: `${af[0]}-${a}`, - actionField: af, - action: a, - commentId: af[0] === 'comment' ? basicCommentId : null, - newValue: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); +export const getUserAction = ( + af: UserActionField, + a: UserAction, + overrides?: Partial +): CaseUserActions => { + return { + ...basicAction, + actionId: `${af[0]}-${a}`, + actionField: af, + action: a, + commentId: af[0] === 'comment' ? basicCommentId : null, + ...getValues(a, af, overrides), + }; +}; + +const getValues = ( + userAction: UserAction, + actionFields: UserActionField, + overrides?: Partial +): Partial => { + if (isCreateConnector(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined ? JSON.stringify(basicCaseSnake) : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: null, + oldValConnectorId: null, + }; + } else if (isUpdateConnector(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined + ? JSON.stringify({ name: 'My Connector', type: ConnectorTypes.none, fields: null }) + : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: + overrides?.oldValue === undefined + ? JSON.stringify({ name: 'My Connector2', type: ConnectorTypes.none, fields: null }) + : overrides.oldValue, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } else if (isPush(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined ? JSON.stringify(basicPushSnake) : overrides?.newValue, + newValConnectorId: + overrides?.newValConnectorId === undefined ? pushConnectorId : overrides.newValConnectorId, + oldValue: overrides?.oldValue ?? null, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } else { + return { + newValue: overrides?.newValue === undefined ? basicAction.newValue : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: overrides?.oldValue ?? null, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } +}; + +export const getJiraConnectorWithoutId = (overrides?: Partial) => { + return JSON.stringify({ + name: 'jira1', + type: ConnectorTypes.jira, + ...jiraFields, + ...overrides, + }); +}; + +export const jiraFields = { fields: { issueType: '10006', priority: null, parent: null } }; export const getAlertUserAction = () => ({ ...basicAction, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx index 62b4cf92434cd..e7e46fa46c7cc 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx @@ -18,7 +18,9 @@ import { basicPushSnake, caseUserActions, elasticUser, + getJiraConnectorWithoutId, getUserAction, + jiraFields, } from './mock'; import * as api from './api'; @@ -299,15 +301,14 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, @@ -346,15 +347,14 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, @@ -392,11 +392,7 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -418,11 +414,7 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123To456UserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -444,16 +436,8 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - }, + createChangeConnector123To456UserAction(), + createChangeConnector456To123UserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -474,22 +458,10 @@ describe('useGetCaseUserActions', () => { it('Change fields and connector after push - hasDataToPush: true', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createChangeConnector456To123PriorityLowUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -510,22 +482,10 @@ describe('useGetCaseUserActions', () => { it('Change only connector after push - hasDataToPush: false', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createChangeConnector456To123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -547,45 +507,24 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), pushAction123, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), pushAction456, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, + createChangeConnector456To123PriorityLowUserAction(), + createChangeConnector123LowPriorityTo456UserAction(), + createChangeConnector456To123PriorityLowUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -617,34 +556,22 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + newValConnectorId: '456', newValue: JSON.stringify(push456), - }; + }); + const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), pushAction123, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), pushAction456, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createChangeConnector456To123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -675,22 +602,10 @@ describe('useGetCaseUserActions', () => { it('Changing other connectors fields does not count as an update', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '3' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createUpdateConnectorFields456HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -709,3 +624,83 @@ describe('useGetCaseUserActions', () => { }); }); }); + +const jira123HighPriorityFields = { + fields: { ...jiraFields.fields, priority: 'High' }, +}; + +const jira123LowPriorityFields = { + fields: { ...jiraFields.fields, priority: 'Low' }, +}; + +const jira456Fields = { + fields: { issueType: '10', parent: null, priority: null }, +}; + +const jira456HighPriorityFields = { + fields: { ...jira456Fields.fields, priority: 'High' }, +}; + +const createUpdateConnectorFields123HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(), + newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + oldValConnectorId: '123', + newValConnectorId: '123', + }); + +const createUpdateConnectorFields456HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + newValue: getJiraConnectorWithoutId(jira456HighPriorityFields), + oldValConnectorId: '456', + newValConnectorId: '456', + }); + +const createChangeConnector123HighPriorityTo456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector123To456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector123LowPriorityTo456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira123LowPriorityFields), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector456To123UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(), + newValConnectorId: '123', + }); + +const createChangeConnector456To123HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + newValConnectorId: '123', + }); + +const createChangeConnector456To123PriorityLowUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(jira123LowPriorityFields), + newValConnectorId: '123', + }); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx index e481519ba19a3..36d600c3f1c9d 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx @@ -18,7 +18,8 @@ import { } from '../../common'; import { getCaseUserActions, getSubCaseUserActions } from './api'; import * as i18n from './translations'; -import { convertToCamelCase, parseString } from './utils'; +import { convertToCamelCase } from './utils'; +import { parseStringAsConnector, parseStringAsExternalService } from '../common/user_actions'; import { useToasts } from '../common/lib/kibana'; export interface CaseService extends CaseExternalService { @@ -58,8 +59,24 @@ export interface UseGetCaseUserActions extends CaseUserActionsState { ) => Promise; } -const getExternalService = (value: string): CaseExternalService | null => - convertToCamelCase(parseString(`${value}`)); +const unknownExternalServiceConnectorId = 'unknown'; + +const getExternalService = ( + connectorId: string | null, + encodedValue: string | null +): CaseExternalService | null => { + const decodedValue = parseStringAsExternalService(connectorId, encodedValue); + + if (decodedValue == null) { + return null; + } + return { + ...convertToCamelCase(decodedValue), + // if in the rare case that the connector id is null we'll set it to unknown if we need to reference it in the UI + // anywhere. The id would only ever be null if a migration failed or some logic error within the backend occurred + connectorId: connectorId ?? unknownExternalServiceConnectorId, + }; +}; const groupConnectorFields = ( userActions: CaseUserActions[] @@ -69,22 +86,26 @@ const groupConnectorFields = ( return acc; } - const oldValue = parseString(`${mua.oldValue}`); - const newValue = parseString(`${mua.newValue}`); + const oldConnector = parseStringAsConnector(mua.oldValConnectorId, mua.oldValue); + const newConnector = parseStringAsConnector(mua.newValConnectorId, mua.newValue); - if (oldValue == null || newValue == null) { + if (!oldConnector || !newConnector) { return acc; } return { ...acc, - [oldValue.id]: [ - ...(acc[oldValue.id] || []), - ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [oldValue.fields]), + [oldConnector.id]: [ + ...(acc[oldConnector.id] || []), + ...(oldConnector.id === newConnector.id + ? [oldConnector.fields, newConnector.fields] + : [oldConnector.fields]), ], - [newValue.id]: [ - ...(acc[newValue.id] || []), - ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [newValue.fields]), + [newConnector.id]: [ + ...(acc[newConnector.id] || []), + ...(oldConnector.id === newConnector.id + ? [oldConnector.fields, newConnector.fields] + : [newConnector.fields]), ], }; }, {} as Record>); @@ -137,9 +158,7 @@ export const getPushedInfo = ( const hasDataToPushForConnector = (connectorId: string): boolean => { const caseUserActionsReversed = [...caseUserActions].reverse(); const lastPushOfConnectorReversedIndex = caseUserActionsReversed.findIndex( - (mua) => - mua.action === 'push-to-service' && - getExternalService(`${mua.newValue}`)?.connectorId === connectorId + (mua) => mua.action === 'push-to-service' && mua.newValConnectorId === connectorId ); if (lastPushOfConnectorReversedIndex === -1) { @@ -190,7 +209,7 @@ export const getPushedInfo = ( return acc; } - const externalService = getExternalService(`${cua.newValue}`); + const externalService = getExternalService(cua.newValConnectorId, cua.newValue); if (externalService === null) { return acc; } diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index de67b1cfbd6fa..b0cc0c72fee78 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -36,14 +36,6 @@ import * as i18n from './translations'; export const getTypedPayload = (a: unknown): T => a as T; -export const parseString = (params: string) => { - try { - return JSON.parse(params); - } catch { - return null; - } -}; - export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => arrayOfSnakes.reduce((acc: unknown[], value) => { if (isArray(value)) { diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index 507405d58cef1..b84a6bd84c43b 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -106,7 +106,7 @@ async function getSubCase({ caseId, subCaseId: newSubCase.id, fields: ['status', 'sub_case'], - newValue: JSON.stringify({ status: newSubCase.attributes.status }), + newValue: { status: newSubCase.attributes.status }, owner: newSubCase.attributes.owner, }), ], @@ -220,7 +220,7 @@ const addGeneratedAlerts = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: JSON.stringify(query), + newValue: query, owner: newComment.attributes.owner, }), ], @@ -408,7 +408,7 @@ export const addComment = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: JSON.stringify(query), + newValue: query, owner: newComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/attachments/update.ts b/x-pack/plugins/cases/server/client/attachments/update.ts index 9816efd9a8452..b5e9e6c372355 100644 --- a/x-pack/plugins/cases/server/client/attachments/update.ts +++ b/x-pack/plugins/cases/server/client/attachments/update.ts @@ -17,6 +17,7 @@ import { SUB_CASE_SAVED_OBJECT, CaseResponse, CommentPatchRequest, + CommentRequest, } from '../../../common'; import { AttachmentService, CasesService } from '../../services'; import { CasesClientArgs } from '..'; @@ -193,12 +194,12 @@ export async function update( subCaseId: subCaseID, commentId: updatedComment.id, fields: ['comment'], - newValue: JSON.stringify(queryRestAttributes), - oldValue: JSON.stringify( + // casting because typescript is complaining that it's not a Record even though it is + newValue: queryRestAttributes as CommentRequest, + oldValue: // We are interested only in ContextBasicRt attributes // myComment.attribute contains also CommentAttributesBasicRt attributes - pick(Object.keys(queryRestAttributes), myComment.attributes) - ), + pick(Object.keys(queryRestAttributes), myComment.attributes), owner: myComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 887990fef8938..488bc523f7796 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -106,7 +106,7 @@ export const create = async ( actionBy: { username, full_name, email }, caseId: newCase.id, fields: ['description', 'status', 'tags', 'title', 'connector', 'settings', OWNER_FIELD], - newValue: JSON.stringify(query), + newValue: query, owner: newCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 80a687a0e72f8..4333535f17a24 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -168,7 +168,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P 'settings', OWNER_FIELD, 'comment', - ...(ENABLE_CASE_CONNECTOR ? ['sub_case'] : []), + ...(ENABLE_CASE_CONNECTOR ? ['sub_case' as const] : []), ], owner: caseInfo.attributes.owner, }) diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 313d6cd12a6db..22520cea11014 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -231,8 +231,10 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"id":"456","name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', + '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', + new_val_connector_id: '456', old_value: null, + old_val_connector_id: null, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -248,7 +250,9 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '456', + old_val_connector_id: null, old_value: null, action_id: '0a801750-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -265,6 +269,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: '{"type":"alert","alertId":"alert-id-1","index":".siem-signals-default-000008"}', + new_val_connector_id: null, + old_val_connector_id: null, old_value: null, action_id: '7373eb60-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -282,6 +288,8 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"type":"alert","alertId":"alert-id-2","index":".siem-signals-default-000008"}', old_value: null, + new_val_connector_id: null, + old_val_connector_id: null, action_id: '7abc6410-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-2', @@ -297,8 +305,10 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '456', old_value: null, + old_val_connector_id: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -315,6 +325,8 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"comment":"a comment!","type":"user"}', old_value: null, + new_val_connector_id: null, + old_val_connector_id: null, action_id: '0818e5e0-6648-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-user-1', diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 3048cf01bb3ba..1b090a653546d 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -241,7 +241,7 @@ export const push = async ( actionBy: { username, full_name, email }, caseId, fields: ['pushed'], - newValue: JSON.stringify(externalService), + newValue: externalService, owner: myCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index d7c45d3e1e9ae..315e9966d347b 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -799,8 +799,10 @@ describe('utils', () => { username: 'elastic', }, new_value: - // The connector id is 123 - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"123","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + // The connector id is 123 + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '123', + old_val_connector_id: null, old_value: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index 359ad4b41ead0..f5cf2fe4b3f51 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -20,6 +20,8 @@ import { CommentRequestUserType, CommentRequestAlertType, CommentRequestActionsType, + CaseUserActionResponse, + isPush, } from '../../../common'; import { ActionsClient } from '../../../../actions/server'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -55,22 +57,36 @@ export const getLatestPushInfo = ( userActions: CaseUserActionsResponse ): { index: number; pushedInfo: CaseFullExternalService } | null => { for (const [index, action] of [...userActions].reverse().entries()) { - if (action.action === 'push-to-service' && action.new_value) + if ( + isPush(action.action, action.action_field) && + isValidNewValue(action) && + connectorId === action.new_val_connector_id + ) { try { const pushedInfo = JSON.parse(action.new_value); - if (pushedInfo.connector_id === connectorId) { - // We returned the index of the element in the userActions array. - // As we traverse the userActions in reverse we need to calculate the index of a normal traversal - return { index: userActions.length - index - 1, pushedInfo }; - } + // We returned the index of the element in the userActions array. + // As we traverse the userActions in reverse we need to calculate the index of a normal traversal + return { + index: userActions.length - index - 1, + pushedInfo: { ...pushedInfo, connector_id: connectorId }, + }; } catch (e) { - // Silence JSON parse errors + // ignore parse failures and check the next user action } + } } return null; }; +type NonNullNewValueAction = Omit & { + new_value: string; + new_val_connector_id: string; +}; + +const isValidNewValue = (userAction: CaseUserActionResponse): userAction is NonNullNewValueAction => + userAction.new_val_connector_id != null && userAction.new_value != null; + const getCommentContent = (comment: CommentResponse): string => { if (comment.type === CommentType.user) { return comment.comment; diff --git a/x-pack/plugins/cases/server/client/user_actions/get.test.ts b/x-pack/plugins/cases/server/client/user_actions/get.test.ts new file mode 100644 index 0000000000000..302e069cde4d1 --- /dev/null +++ b/x-pack/plugins/cases/server/client/user_actions/get.test.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseUserActionResponse, SUB_CASE_SAVED_OBJECT } from '../../../common'; +import { SUB_CASE_REF_NAME } from '../../common'; +import { extractAttributesWithoutSubCases } from './get'; + +describe('get', () => { + describe('extractAttributesWithoutSubCases', () => { + it('returns an empty array when given an empty array', () => { + expect( + extractAttributesWithoutSubCases({ ...getFindResponseFields(), saved_objects: [] }) + ).toEqual([]); + }); + + it('filters out saved objects with a sub case reference', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], + id: 'b', + score: 0, + attributes: {} as CaseUserActionResponse, + }, + ], + }) + ).toEqual([]); + }); + + it('filters out saved objects with a sub case reference with other references', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: {} as CaseUserActionResponse, + }, + ], + }) + ).toEqual([]); + }); + + it('keeps saved objects that do not have a sub case reference', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: { field: '1' } as unknown as CaseUserActionResponse, + }, + ], + }) + ).toEqual([{ field: '1' }]); + }); + + it('filters multiple saved objects correctly', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: { field: '2' } as unknown as CaseUserActionResponse, + }, + { + type: 'a', + references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], + id: 'b', + score: 0, + attributes: { field: '1' } as unknown as CaseUserActionResponse, + }, + ], + }) + ).toEqual([{ field: '2' }]); + }); + }); +}); + +const getFindResponseFields = () => ({ page: 1, per_page: 1, total: 0 }); diff --git a/x-pack/plugins/cases/server/client/user_actions/get.ts b/x-pack/plugins/cases/server/client/user_actions/get.ts index 2a6608014c800..660cf1b6a336e 100644 --- a/x-pack/plugins/cases/server/client/user_actions/get.ts +++ b/x-pack/plugins/cases/server/client/user_actions/get.ts @@ -5,14 +5,14 @@ * 2.0. */ +import { SavedObjectReference, SavedObjectsFindResponse } from 'kibana/server'; import { - CASE_COMMENT_SAVED_OBJECT, - CASE_SAVED_OBJECT, CaseUserActionsResponse, CaseUserActionsResponseRt, SUB_CASE_SAVED_OBJECT, + CaseUserActionResponse, } from '../../../common'; -import { createCaseError, checkEnabledCaseConnectorOrThrow } from '../../common'; +import { createCaseError, checkEnabledCaseConnectorOrThrow, SUB_CASE_REF_NAME } from '../../common'; import { CasesClientArgs } from '..'; import { Operations } from '../../authorization'; import { UserActionGet } from './client'; @@ -40,23 +40,12 @@ export const get = async ( operation: Operations.getUserActions, }); - return CaseUserActionsResponseRt.encode( - userActions.saved_objects.reduce((acc, ua) => { - if (subCaseId == null && ua.references.some((uar) => uar.type === SUB_CASE_SAVED_OBJECT)) { - return acc; - } - return [ - ...acc, - { - ...ua.attributes, - action_id: ua.id, - case_id: ua.references.find((r) => r.type === CASE_SAVED_OBJECT)?.id ?? '', - comment_id: ua.references.find((r) => r.type === CASE_COMMENT_SAVED_OBJECT)?.id ?? null, - sub_case_id: ua.references.find((r) => r.type === SUB_CASE_SAVED_OBJECT)?.id ?? '', - }, - ]; - }, []) - ); + const resultsToEncode = + subCaseId == null + ? extractAttributesWithoutSubCases(userActions) + : extractAttributes(userActions); + + return CaseUserActionsResponseRt.encode(resultsToEncode); } catch (error) { throw createCaseError({ message: `Failed to retrieve user actions case id: ${caseId} sub case id: ${subCaseId}: ${error}`, @@ -65,3 +54,21 @@ export const get = async ( }); } }; + +export function extractAttributesWithoutSubCases( + userActions: SavedObjectsFindResponse +): CaseUserActionsResponse { + // exclude user actions relating to sub cases from the results + const hasSubCaseReference = (references: SavedObjectReference[]) => + references.find((ref) => ref.type === SUB_CASE_SAVED_OBJECT && ref.name === SUB_CASE_REF_NAME); + + return userActions.saved_objects + .filter((so) => !hasSubCaseReference(so.references)) + .map((so) => so.attributes); +} + +function extractAttributes( + userActions: SavedObjectsFindResponse +): CaseUserActionsResponse { + return userActions.saved_objects.map((so) => so.attributes); +} diff --git a/x-pack/plugins/cases/server/common/constants.ts b/x-pack/plugins/cases/server/common/constants.ts index 1f6af310d6ece..eba0a64a5c0be 100644 --- a/x-pack/plugins/cases/server/common/constants.ts +++ b/x-pack/plugins/cases/server/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../common'; + /** * The name of the saved object reference indicating the action connector ID. This is stored in the Saved Object reference * field's name property. @@ -15,3 +17,30 @@ export const CONNECTOR_ID_REFERENCE_NAME = 'connectorId'; * The name of the saved object reference indicating the action connector ID that was used to push a case. */ export const PUSH_CONNECTOR_ID_REFERENCE_NAME = 'pushConnectorId'; + +/** + * The name of the saved object reference indicating the action connector ID that was used for + * adding a connector, or updating the existing connector for a user action's old_value field. + */ +export const USER_ACTION_OLD_ID_REF_NAME = 'oldConnectorId'; + +/** + * The name of the saved object reference indicating the action connector ID that was used for pushing a case, + * for a user action's old_value field. + */ +export const USER_ACTION_OLD_PUSH_ID_REF_NAME = 'oldPushConnectorId'; + +/** + * The name of the saved object reference indicating the caseId reference + */ +export const CASE_REF_NAME = `associated-${CASE_SAVED_OBJECT}`; + +/** + * The name of the saved object reference indicating the commentId reference + */ +export const COMMENT_REF_NAME = `associated-${CASE_COMMENT_SAVED_OBJECT}`; + +/** + * The name of the saved object reference indicating the subCaseId reference + */ +export const SUB_CASE_REF_NAME = `associated-${SUB_CASE_SAVED_OBJECT}`; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index bca12a86a544e..9020f65ae352c 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -30,322 +30,324 @@ const create_7_14_0_case = ({ }, }); -describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector.id is none', () => { - const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); +describe('case migrations', () => { + describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector.id is none', () => { + const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the connector is undefined', () => { - const caseSavedObject = create_7_14_0_case(); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + it('does not create a reference when the connector is undefined', () => { + const caseSavedObject = create_7_14_0_case(); - it('sets the connector to the default none connector if the connector.id is undefined', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - fields: null, - name: ConnectorTypes.jira, - type: ConnectorTypes.jira, - } as ESCaseConnectorWithId, - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the external_service is null', () => { - const caseSavedObject = create_7_14_0_case({ externalService: null }); + it('sets the connector to the default none connector if the connector.id is undefined', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + fields: null, + name: ConnectorTypes.jira, + type: ConnectorTypes.jira, + } as ESCaseConnectorWithId, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the external_service is null', () => { + const caseSavedObject = create_7_14_0_case({ externalService: null }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - it('does not create a reference when the external_service is undefined and sets external_service to null', () => { - const caseSavedObject = create_7_14_0_case(); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the external_service is undefined and sets external_service to null', () => { + const caseSavedObject = create_7_14_0_case(); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - it('does not create a reference when the external_service.connector_id is none', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: createExternalService({ connector_id: noneConnectorId }), + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('preserves the existing references when migrating', () => { - const caseSavedObject = { - ...create_7_14_0_case(), - references: [{ id: '1', name: 'awesome', type: 'hello' }], - }; + it('does not create a reference when the external_service.connector_id is none', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: createExternalService({ connector_id: noneConnectorId }), + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` Object { - "id": "1", - "name": "awesome", - "type": "hello", - }, - ] - `); - }); + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); - it('creates a connector reference and removes the connector.id field', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }, + it('preserves the existing references when migrating', () => { + const caseSavedObject = { + ...create_7_14_0_case(), + references: [{ id: '1', name: 'awesome', type: 'hello' }], + }; + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "awesome", + "type": "hello", + }, + ] + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", + it('creates a connector reference and removes the connector.id field', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, }, - ] - `); - }); + }); - it('creates a push connector reference and removes the connector_id field', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); - it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: null, - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', + it('creates a push connector reference and removes the connector_id field', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - }, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: null, + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - } - `); - }); + }); - it('migrates both connector and external_service when provided', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(2); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + it('migrates both connector and external_service when provided', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - } - `); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, }, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(2); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", + }, + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index bffd4171270ef..80f02fa3bf6a6 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -14,7 +14,11 @@ import { } from '../../../../../../src/core/server'; import { ESConnectorFields } from '../../services'; import { ConnectorTypes, CaseType } from '../../../common'; -import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; +import { + transformConnectorIdToReference, + transformPushConnectorIdToReference, +} from '../../services/user_actions/transform'; +import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../../common'; interface UnsanitizedCaseConnector { connector_id: string; @@ -50,11 +54,13 @@ export const caseConnectorIdMigration = ( // removing the id field since it will be stored in the references instead const { connector, external_service, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = - transformConnectorIdToReference(connector); + const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + connector + ); const { transformedPushConnector, references: pushConnectorReferences } = - transformPushConnectorIdToReference(external_service); + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, external_service); const { references = [] } = doc; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts index 4467b499817a5..9ae0285598dbf 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts @@ -40,87 +40,89 @@ const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ }, }); -describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector ID is none', () => { - const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); +describe('configuration migrations', () => { + describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector ID is none', () => { + const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('creates a reference using the connector id', () => { - const configureSavedObject = create_7_14_0_configSchema({ - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + it('creates a reference using the connector id', () => { + const configureSavedObject = create_7_14_0_configSchema({ + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }); - expect(migratedConnector.references).toEqual([ - { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, - ]); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - it('returns the other attributes and default connector when the connector is undefined', () => { - const configureSavedObject = create_7_14_0_configSchema(); + expect(migratedConnector.references).toEqual([ + { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, + ]); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + it('returns the other attributes and default connector when the connector is undefined', () => { + const configureSavedObject = create_7_14_0_configSchema(); - expect(migratedConnector).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "closure_type": "close-by-pushing", - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - "created_at": "2020-04-09T09:43:51.778Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "owner": "securitySolution", - "updated_at": "2020-04-09T09:43:51.778Z", - "updated_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "closure_type": "close-by-pushing", + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + "owner": "securitySolution", + "updated_at": "2020-04-09T09:43:51.778Z", + "updated_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, }, - }, - "id": "1", - "references": Array [], - "type": "cases-configure", - } - `); + "id": "1", + "references": Array [], + "type": "cases-configure", + } + `); + }); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts index 527d40fca2e35..f9937253e0d2f 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts @@ -13,7 +13,8 @@ import { } from '../../../../../../src/core/server'; import { ConnectorTypes } from '../../../common'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { transformConnectorIdToReference } from './utils'; +import { transformConnectorIdToReference } from '../../services/user_actions/transform'; +import { CONNECTOR_ID_REFERENCE_NAME } from '../../common'; interface UnsanitizedConfigureConnector { connector_id: string; @@ -34,8 +35,10 @@ export const configureConnectorIdMigration = ( ): SavedObjectSanitizedDoc => { // removing the id field since it will be stored in the references instead const { connector, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = - transformConnectorIdToReference(connector); + const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + connector + ); const { references = [] } = doc; return { diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts index a445131073d19..a4f50fbfcde5b 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts @@ -5,24 +5,17 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ - import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, } from '../../../../../../src/core/server'; -import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; export { caseMigrations } from './cases'; export { configureMigrations } from './configuration'; +export { userActionsMigrations } from './user_actions'; export { createCommentsMigrations, CreateCommentsMigrationsDeps } from './comments'; -interface UserActions { - action_field: string[]; - new_value: string; - old_value: string; -} - export interface SanitizedCaseOwner { owner: string; } @@ -38,52 +31,6 @@ export const addOwnerToSO = >( references: doc.references || [], }); -export const userActionsMigrations = { - '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { - const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; - - if ( - action_field == null || - !Array.isArray(action_field) || - action_field[0] !== 'connector_id' - ) { - return { ...doc, references: doc.references || [] }; - } - - return { - ...doc, - attributes: { - ...restAttributes, - action_field: ['connector'], - new_value: - new_value != null - ? JSON.stringify({ - id: new_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : new_value, - old_value: - old_value != null - ? JSON.stringify({ - id: old_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : old_value, - }, - references: doc.references || [], - }; - }, - '7.14.0': ( - doc: SavedObjectUnsanitizedDoc> - ): SavedObjectSanitizedDoc => { - return addOwnerToSO(doc); - }, -}; - export const connectorMappingsMigrations = { '7.14.0': ( doc: SavedObjectUnsanitizedDoc> diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts new file mode 100644 index 0000000000000..e71c8db0db694 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts @@ -0,0 +1,562 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { SavedObjectMigrationContext, SavedObjectSanitizedDoc } from 'kibana/server'; +import { migrationMocks } from 'src/core/server/mocks'; +import { CaseUserActionAttributes, CASE_USER_ACTION_SAVED_OBJECT } from '../../../common'; +import { + createConnectorObject, + createExternalService, + createJiraConnector, +} from '../../services/test_utils'; +import { userActionsConnectorIdMigration } from './user_actions'; + +const create_7_14_0_userAction = ( + params: { + action?: string; + action_field?: string[]; + new_value?: string | null | object; + old_value?: string | null | object; + } = {} +) => { + const { new_value, old_value, ...restParams } = params; + + return { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '1', + attributes: { + ...restParams, + new_value: new_value && typeof new_value === 'object' ? JSON.stringify(new_value) : new_value, + old_value: old_value && typeof old_value === 'object' ? JSON.stringify(old_value) : old_value, + }, + }; +}; + +describe('user action migrations', () => { + describe('7.15.0 connector ID migration', () => { + describe('userActionsConnectorIdMigration', () => { + let context: jest.Mocked; + + beforeEach(() => { + context = migrationMocks.createContext(); + }); + + describe('push user action', () => { + it('extracts the external_service connector_id to references for a new pushed user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedExternalService).not.toHaveProperty('connector_id'); + expect(parsedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extract the external_service connector_id to references for new and old pushed user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: createExternalService({ connector_id: '5' }), + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(migratedUserAction.references).toEqual([ + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the external_service connector_id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: createExternalService({ connector_id: '5' }), + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid push user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['invalid field'], + new_value: 'hello', + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction.attributes.old_value).toBeNull(); + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": null, + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when it new value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: '{a', + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction.attributes.old_value).toBeNull(); + expect(migratedUserAction.attributes.new_value).toEqual('{a'); + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_field": Array [ + "pushed", + ], + "new_value": "{a", + "old_value": null, + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error new value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: '{a', + old_value: null, + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + + describe('update connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedConnector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: { ...createJiraConnector(), id: '5' }, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); + + expect(parsedNewConnector).not.toHaveProperty('id'); + expect(parsedOldConnector).not.toHaveProperty('id'); + + expect(migratedUserAction.references).toEqual([ + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the connector.id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: { ...createJiraConnector(), id: '5' }, + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewConnectorId).not.toHaveProperty('id'); + expect(parsedOldConnectorId).not.toHaveProperty('id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['invalid action'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when old_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: '{}', + old_value: '{b', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_field": Array [ + "connector", + ], + "new_value": "{}", + "old_value": "{b", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error message when old_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: '{b', + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + + describe('create connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedConnector.connector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: createConnectorObject({ id: '5' }), + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); + + expect(parsedNewConnector.connector).not.toHaveProperty('id'); + expect(parsedOldConnector.connector).not.toHaveProperty('id'); + + expect(migratedUserAction.references).toEqual([ + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the connector.id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: createConnectorObject({ id: '5' }), + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewConnectorId.connector).not.toHaveProperty('id'); + expect(parsedOldConnectorId.connector).not.toHaveProperty('id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['invalid action'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when new_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_field": Array [ + "connector", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error message when new_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: 'new json value', + old_value: 'old value', + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts new file mode 100644 index 0000000000000..ed6b57ef647f9 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { addOwnerToSO, SanitizedCaseOwner } from '.'; +import { + SavedObjectUnsanitizedDoc, + SavedObjectSanitizedDoc, + SavedObjectMigrationContext, + LogMeta, +} from '../../../../../../src/core/server'; +import { ConnectorTypes, isCreateConnector, isPush, isUpdateConnector } from '../../../common'; + +import { extractConnectorIdFromJson } from '../../services/user_actions/transform'; +import { UserActionFieldType } from '../../services/user_actions/types'; + +interface UserActions { + action_field: string[]; + new_value: string; + old_value: string; +} + +interface UserActionUnmigratedConnectorDocument { + action?: string; + action_field?: string[]; + new_value?: string | null; + old_value?: string | null; +} + +interface UserActionLogMeta extends LogMeta { + migrations: { userAction: { id: string } }; +} + +export function userActionsConnectorIdMigration( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext +): SavedObjectSanitizedDoc { + const originalDocWithReferences = { ...doc, references: doc.references ?? [] }; + + if (!isConnectorUserAction(doc.attributes.action, doc.attributes.action_field)) { + return originalDocWithReferences; + } + + try { + return formatDocumentWithConnectorReferences(doc); + } catch (error) { + logError(doc.id, context, error); + + return originalDocWithReferences; + } +} + +function isConnectorUserAction(action?: string, actionFields?: string[]): boolean { + return ( + isCreateConnector(action, actionFields) || + isUpdateConnector(action, actionFields) || + isPush(action, actionFields) + ); +} + +function formatDocumentWithConnectorReferences( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc { + const { new_value, old_value, action, action_field, ...restAttributes } = doc.attributes; + const { references = [] } = doc; + + const { transformedActionDetails: transformedNewValue, references: newValueConnectorRefs } = + extractConnectorIdFromJson({ + action, + actionFields: action_field, + actionDetails: new_value, + fieldType: UserActionFieldType.New, + }); + + const { transformedActionDetails: transformedOldValue, references: oldValueConnectorRefs } = + extractConnectorIdFromJson({ + action, + actionFields: action_field, + actionDetails: old_value, + fieldType: UserActionFieldType.Old, + }); + + return { + ...doc, + attributes: { + ...restAttributes, + action, + action_field, + new_value: transformedNewValue, + old_value: transformedOldValue, + }, + references: [...references, ...newValueConnectorRefs, ...oldValueConnectorRefs], + }; +} + +function logError(id: string, context: SavedObjectMigrationContext, error: Error) { + context.log.error( + `Failed to migrate user action connector doc id: ${id} version: ${context.migrationVersion} error: ${error.message}`, + { + migrations: { + userAction: { + id, + }, + }, + } + ); +} + +export const userActionsMigrations = { + '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { + const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; + + if ( + action_field == null || + !Array.isArray(action_field) || + action_field[0] !== 'connector_id' + ) { + return { ...doc, references: doc.references || [] }; + } + + return { + ...doc, + attributes: { + ...restAttributes, + action_field: ['connector'], + new_value: + new_value != null + ? JSON.stringify({ + id: new_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : new_value, + old_value: + old_value != null + ? JSON.stringify({ + id: old_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : old_value, + }, + references: doc.references || [], + }; + }, + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc> + ): SavedObjectSanitizedDoc => { + return addOwnerToSO(doc); + }, + '7.16.0': userActionsConnectorIdMigration, +}; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts deleted file mode 100644 index f591bef6b3236..0000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { noneConnectorId } from '../../../common'; -import { createExternalService, createJiraConnector } from '../../services/test_utils'; -import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; - -describe('migration utils', () => { - describe('transformConnectorIdToReference', () => { - it('returns the default none connector when the connector is undefined', () => { - expect(transformConnectorIdToReference().transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is undefined', () => { - expect(transformConnectorIdToReference({ id: undefined }).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none', () => { - expect(transformConnectorIdToReference({ id: noneConnectorId }).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none and other fields are defined', () => { - expect( - transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns an empty array of references when the connector is undefined', () => { - expect(transformConnectorIdToReference().references.length).toBe(0); - }); - - it('returns an empty array of references when the id is undefined', () => { - expect(transformConnectorIdToReference({ id: undefined }).references.length).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector', () => { - expect(transformConnectorIdToReference({ id: noneConnectorId }).references.length).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector and other fields are defined', () => { - expect( - transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) - .references.length - ).toBe(0); - }); - - it('returns a jira connector', () => { - const transformedFields = transformConnectorIdToReference(createJiraConnector()); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('transformPushConnectorIdToReference', () => { - it('sets external_service to null when it is undefined', () => { - expect(transformPushConnectorIdToReference().transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('sets external_service to null when it is null', () => { - expect(transformPushConnectorIdToReference(null).transformedPushConnector) - .toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is null', () => { - expect(transformPushConnectorIdToReference({ connector_id: null }).transformedPushConnector) - .toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is none', () => { - const otherFields = { otherField: 'hi' }; - - expect( - transformPushConnectorIdToReference({ ...otherFields, connector_id: noneConnectorId }) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "otherField": "hi", - }, - } - `); - }); - - it('returns an empty array of references when the external_service is undefined', () => { - expect(transformPushConnectorIdToReference().references.length).toBe(0); - }); - - it('returns an empty array of references when the external_service is null', () => { - expect(transformPushConnectorIdToReference(null).references.length).toBe(0); - }); - - it('returns an empty array of references when the connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is null', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector', () => { - expect( - transformPushConnectorIdToReference({ connector_id: noneConnectorId }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { - expect( - transformPushConnectorIdToReference({ - ...createExternalService(), - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns the external_service connector', () => { - const transformedFields = transformPushConnectorIdToReference(createExternalService()); - expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts deleted file mode 100644 index 0100a04cde679..0000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { noneConnectorId } from '../../../common'; -import { SavedObjectReference } from '../../../../../../src/core/server'; -import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { - getNoneCaseConnector, - CONNECTOR_ID_REFERENCE_NAME, - PUSH_CONNECTOR_ID_REFERENCE_NAME, -} from '../../common'; - -export const transformConnectorIdToReference = (connector?: { - id?: string; -}): { transformedConnector: Record; references: SavedObjectReference[] } => { - const { id: connectorId, ...restConnector } = connector ?? {}; - - const references = createConnectorReference( - connectorId, - ACTION_SAVED_OBJECT_TYPE, - CONNECTOR_ID_REFERENCE_NAME - ); - - const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); - const connectorFieldsToReturn = - connector && references.length > 0 ? restConnector : restNoneConnector; - - return { - transformedConnector: { - connector: connectorFieldsToReturn, - }, - references, - }; -}; - -const createConnectorReference = ( - id: string | null | undefined, - type: string, - name: string -): SavedObjectReference[] => { - return id && id !== noneConnectorId - ? [ - { - id, - type, - name, - }, - ] - : []; -}; - -export const transformPushConnectorIdToReference = ( - external_service?: { connector_id?: string | null } | null -): { transformedPushConnector: Record; references: SavedObjectReference[] } => { - const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; - - const references = createConnectorReference( - pushConnectorId, - ACTION_SAVED_OBJECT_TYPE, - PUSH_CONNECTOR_ID_REFERENCE_NAME - ); - - return { - transformedPushConnector: { external_service: external_service ? restExternalService : null }, - references, - }; -}; diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 18f4ff867cfa9..8c71abe5bff4f 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -40,6 +40,7 @@ import { createSavedObjectReferences, createCaseSavedObjectResponse, basicCaseFields, + createSOFindResponse, } from '../test_utils'; import { ESCaseAttributes } from './types'; @@ -87,13 +88,6 @@ const createFindSO = ( score: 0, }); -const createSOFindResponse = (savedObjects: Array>) => ({ - saved_objects: savedObjects, - total: savedObjects.length, - per_page: savedObjects.length, - page: 1, -}); - const createCaseUpdateParams = ( connector?: CaseConnector, externalService?: CaseFullExternalService diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index b712ea07f9c71..07743eda61212 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference } from 'kibana/server'; +import { SavedObject, SavedObjectReference, SavedObjectsFindResult } from 'kibana/server'; import { ESConnectorFields } from '.'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../common'; import { @@ -54,7 +54,7 @@ export const createESJiraConnector = ( { key: 'parent', value: '2' }, ], type: ConnectorTypes.jira, - ...(overrides && { ...overrides }), + ...overrides, }; }; @@ -94,7 +94,7 @@ export const createExternalService = ( email: 'testemail@elastic.co', username: 'elastic', }, - ...(overrides && { ...overrides }), + ...overrides, }); export const basicCaseFields = { @@ -198,3 +198,14 @@ export const createSavedObjectReferences = ({ ] : []), ]; + +export const createConnectorObject = (overrides?: Partial) => ({ + connector: { ...createJiraConnector(), ...overrides }, +}); + +export const createSOFindResponse = (savedObjects: Array>) => ({ + saved_objects: savedObjects, + total: savedObjects.length, + per_page: savedObjects.length, + page: 1, +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts new file mode 100644 index 0000000000000..7bcbaf58d0f6e --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts @@ -0,0 +1,332 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserActionField } from '../../../common'; +import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; +import { buildCaseUserActionItem } from './helpers'; + +const defaultFields = () => ({ + actionAt: 'now', + actionBy: { + email: 'a', + full_name: 'j', + username: '1', + }, + caseId: '300', + owner: 'securitySolution', +}); + +describe('user action helpers', () => { + describe('buildCaseUserActionItem', () => { + describe('push user action', () => { + it('extracts the external_service connector_id to references for a new pushed user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + }); + + const parsedExternalService = JSON.parse(userAction.attributes.new_value!); + expect(parsedExternalService).not.toHaveProperty('connector_id'); + expect(parsedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extract the external_service connector_id to references for new and old pushed user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + oldValue: createExternalService({ connector_id: '5' }), + }); + + const parsedNewExternalService = JSON.parse(userAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(userAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid push user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['invalid field'] as unknown as UserActionField, + newValue: 'hello' as unknown as Record, + }); + + expect(userAction.attributes.old_value).toBeNull(); + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": null, + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + + describe('update connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + }); + + const parsedConnector = JSON.parse(userAction.attributes.new_value!); + expect(parsedConnector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + oldValue: { ...createJiraConnector(), id: '5' }, + }); + + const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); + + expect(parsedNewConnector).not.toHaveProperty('id'); + expect(parsedOldConnector).not.toHaveProperty('id'); + + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['invalid field'] as unknown as UserActionField, + newValue: 'hello' as unknown as Record, + oldValue: 'old value' as unknown as Record, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": "old value", + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + + describe('create connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + }); + + const parsedConnector = JSON.parse(userAction.attributes.new_value!); + expect(parsedConnector.connector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + oldValue: createConnectorObject({ id: '5' }), + }); + + const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); + + expect(parsedNewConnector.connector).not.toHaveProperty('id'); + expect(parsedOldConnector.connector).not.toHaveProperty('id'); + + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['invalid action'] as unknown as UserActionField, + newValue: 'new json value' as unknown as Record, + oldValue: 'old value' as unknown as Record, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index 223e731aa8d9b..e91b69f0995bd 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectsUpdateResponse } from 'kibana/server'; +import { SavedObject, SavedObjectReference, SavedObjectsUpdateResponse } from 'kibana/server'; import { get, isPlainObject, isString } from 'lodash'; import deepEqual from 'fast-deep-equal'; @@ -23,8 +23,68 @@ import { } from '../../../common'; import { isTwoArraysDifference } from '../../client/utils'; import { UserActionItem } from '.'; +import { extractConnectorId } from './transform'; +import { UserActionFieldType } from './types'; +import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -export const transformNewUserAction = ({ +interface BuildCaseUserActionParams { + action: UserAction; + actionAt: string; + actionBy: User; + caseId: string; + owner: string; + fields: UserActionField; + newValue?: Record | string | null; + oldValue?: Record | string | null; + subCaseId?: string; +} + +export const buildCaseUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCaseUserActionParams): UserActionItem => { + const { transformedActionDetails: transformedNewValue, references: newValueReferences } = + extractConnectorId({ + action, + actionFields: fields, + actionDetails: newValue, + fieldType: UserActionFieldType.New, + }); + + const { transformedActionDetails: transformedOldValue, references: oldValueReferences } = + extractConnectorId({ + action, + actionFields: fields, + actionDetails: oldValue, + fieldType: UserActionFieldType.Old, + }); + + return { + attributes: transformNewUserAction({ + actionField: fields, + action, + actionAt, + owner, + ...actionBy, + newValue: transformedNewValue, + oldValue: transformedOldValue, + }), + references: [ + ...createCaseReferences(caseId, subCaseId), + ...newValueReferences, + ...oldValueReferences, + ], + }; +}; + +const transformNewUserAction = ({ actionField, action, actionAt, @@ -55,103 +115,43 @@ export const transformNewUserAction = ({ owner, }); -interface BuildCaseUserAction { - action: UserAction; - actionAt: string; - actionBy: User; - caseId: string; - owner: string; - fields: UserActionField | unknown[]; - newValue?: string | unknown; - oldValue?: string | unknown; - subCaseId?: string; -} +const createCaseReferences = (caseId: string, subCaseId?: string): SavedObjectReference[] => [ + { + type: CASE_SAVED_OBJECT, + name: CASE_REF_NAME, + id: caseId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + name: SUB_CASE_REF_NAME, + id: subCaseId, + }, + ] + : []), +]; -interface BuildCommentUserActionItem extends BuildCaseUserAction { +interface BuildCommentUserActionItem extends BuildCaseUserActionParams { commentId: string; } -export const buildCommentUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - commentId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCommentUserActionItem): UserActionItem => ({ - attributes: transformNewUserAction({ - actionField: fields as UserActionField, - action, - actionAt, - owner, - ...actionBy, - newValue: newValue as string, - oldValue: oldValue as string, - }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: caseId, - }, - { - type: CASE_COMMENT_SAVED_OBJECT, - name: `associated-${CASE_COMMENT_SAVED_OBJECT}`, - id: commentId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - id: subCaseId, - name: `associated-${SUB_CASE_SAVED_OBJECT}`, - }, - ] - : []), - ], -}); +export const buildCommentUserActionItem = (params: BuildCommentUserActionItem): UserActionItem => { + const { commentId } = params; + const { attributes, references } = buildCaseUserActionItem(params); -export const buildCaseUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCaseUserAction): UserActionItem => ({ - attributes: transformNewUserAction({ - actionField: fields as UserActionField, - action, - actionAt, - owner, - ...actionBy, - newValue: newValue as string, - oldValue: oldValue as string, - }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: caseId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - name: `associated-${SUB_CASE_SAVED_OBJECT}`, - id: subCaseId, - }, - ] - : []), - ], -}); + return { + attributes, + references: [ + ...references, + { + type: CASE_COMMENT_SAVED_OBJECT, + name: COMMENT_REF_NAME, + id: commentId, + }, + ], + }; +}; const userActionFieldsAllowed: UserActionField = [ 'comment', @@ -278,8 +278,8 @@ const buildGenericCaseUserActions = ({ caseId, subCaseId, fields: [field], - newValue: JSON.stringify(updatedValue), - oldValue: JSON.stringify(origValue), + newValue: updatedValue, + oldValue: origValue, owner: originalItem.attributes.owner, }), ]; diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts new file mode 100644 index 0000000000000..c4a350f4ac015 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/index.test.ts @@ -0,0 +1,557 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObject, SavedObjectsFindResult } from 'kibana/server'; +import { transformFindResponseToExternalModel, UserActionItem } from '.'; +import { + CaseUserActionAttributes, + CASE_USER_ACTION_SAVED_OBJECT, + UserAction, + UserActionField, +} from '../../../common'; + +import { + createConnectorObject, + createExternalService, + createJiraConnector, + createSOFindResponse, +} from '../test_utils'; +import { buildCaseUserActionItem, buildCommentUserActionItem } from './helpers'; + +const createConnectorUserAction = ( + subCaseId?: string, + overrides?: Partial +): SavedObject => { + return { + ...createUserActionSO({ + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const updateConnectorUserAction = ({ + subCaseId, + overrides, + oldValue, +}: { + subCaseId?: string; + overrides?: Partial; + oldValue?: string | null | Record; +} = {}): SavedObject => { + return { + ...createUserActionSO({ + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + oldValue, + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const pushConnectorUserAction = ({ + subCaseId, + overrides, + oldValue, +}: { + subCaseId?: string; + overrides?: Partial; + oldValue?: string | null | Record; +} = {}): SavedObject => { + return { + ...createUserActionSO({ + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + oldValue, + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const createUserActionFindSO = ( + userAction: SavedObject +): SavedObjectsFindResult => ({ + ...userAction, + score: 0, +}); + +const createUserActionSO = ({ + action, + fields, + subCaseId, + newValue, + oldValue, + attributesOverrides, + commentId, +}: { + action: UserAction; + fields: UserActionField; + subCaseId?: string; + newValue?: string | null | Record; + oldValue?: string | null | Record; + attributesOverrides?: Partial; + commentId?: string; +}): SavedObject => { + const defaultParams = { + action, + actionAt: 'abc', + actionBy: { + email: 'a', + username: 'b', + full_name: 'abc', + }, + caseId: '1', + subCaseId, + fields, + newValue, + oldValue, + owner: 'securitySolution', + }; + + let userAction: UserActionItem; + + if (commentId) { + userAction = buildCommentUserActionItem({ + commentId, + ...defaultParams, + }); + } else { + userAction = buildCaseUserActionItem(defaultParams); + } + + return { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '100', + attributes: { + ...userAction.attributes, + ...(attributesOverrides && { ...attributesOverrides }), + }, + references: userAction.references, + }; +}; + +describe('CaseUserActionService', () => { + describe('transformFindResponseToExternalModel', () => { + it('does not populate the ids when the response is an empty array', () => { + expect(transformFindResponseToExternalModel(createSOFindResponse([]))).toMatchInlineSnapshot(` + Object { + "page": 1, + "per_page": 0, + "saved_objects": Array [], + "total": 0, + } + `); + }); + + it('preserves the saved object fields and attributes when inject the ids', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(createConnectorUserAction())]) + ); + + expect(transformed).toMatchInlineSnapshot(` + Object { + "page": 1, + "per_page": 1, + "saved_objects": Array [ + Object { + "attributes": Object { + "action": "create", + "action_at": "abc", + "action_by": Object { + "email": "a", + "full_name": "abc", + "username": "b", + }, + "action_field": Array [ + "connector", + ], + "action_id": "100", + "case_id": "1", + "comment_id": null, + "new_val_connector_id": "1", + "new_value": "{\\"connector\\":{\\"name\\":\\".jira\\",\\"type\\":\\".jira\\",\\"fields\\":{\\"issueType\\":\\"bug\\",\\"priority\\":\\"high\\",\\"parent\\":\\"2\\"}}}", + "old_val_connector_id": null, + "old_value": null, + "owner": "securitySolution", + "sub_case_id": "", + }, + "id": "100", + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ], + "score": 0, + "type": "cases-user-actions", + }, + ], + "total": 1, + } + `); + }); + + it('populates the new_val_connector_id for multiple user actions', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(createConnectorUserAction()), + createUserActionFindSO(createConnectorUserAction()), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + expect(transformed.saved_objects[1].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id for multiple user actions', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO( + createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }) + ), + createUserActionFindSO( + createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject({ id: '10' }), + }) + ), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + expect(transformed.saved_objects[1].attributes.old_val_connector_id).toEqual('10'); + }); + + describe('reference ids', () => { + it('sets case_id to an empty string when it cannot find the reference', () => { + const userAction = { + ...createConnectorUserAction(), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.case_id).toEqual(''); + }); + + it('sets comment_id to null when it cannot find the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], commentId: '5' }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); + }); + + it('sets sub_case_id to an empty string when it cannot find the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); + }); + + it('sets case_id correctly when it finds the reference', () => { + const userAction = createConnectorUserAction(); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.case_id).toEqual('1'); + }); + + it('sets comment_id correctly when it finds the reference', () => { + const userAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + commentId: '5', + }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toEqual('5'); + }); + + it('sets sub_case_id correctly when it finds the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + }; + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.sub_case_id).toEqual('5'); + }); + + it('sets action_id correctly to the saved object id', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + }; + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.action_id).toEqual('100'); + }); + }); + + describe('create connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...createConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { ...createConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = createConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = createConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + }); + }); + + describe('update connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...updateConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { + ...updateConnectorUserAction({ oldValue: createJiraConnector() }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = updateConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = updateConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + }); + }); + + describe('push connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...pushConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { + ...pushConnectorUserAction({ oldValue: createExternalService() }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = pushConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = pushConnectorUserAction({ oldValue: createExternalService() }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = pushConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('100'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = pushConnectorUserAction({ oldValue: createExternalService() }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('100'); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index b702448165554..4f158862e3d63 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { Logger, SavedObjectReference } from 'kibana/server'; +import { + Logger, + SavedObjectReference, + SavedObjectsFindResponse, + SavedObjectsFindResult, +} from 'kibana/server'; import { CASE_SAVED_OBJECT, @@ -13,8 +18,17 @@ import { CaseUserActionAttributes, MAX_DOCS_PER_PAGE, SUB_CASE_SAVED_OBJECT, + CaseUserActionResponse, + CASE_COMMENT_SAVED_OBJECT, + isCreateConnector, + isPush, + isUpdateConnector, } from '../../../common'; import { ClientArgs } from '..'; +import { UserActionFieldType } from './types'; +import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; +import { ConnectorIdReferenceName, PushConnectorIdReferenceName } from './transform'; +import { findConnectorIdReference } from '../transform'; interface GetCaseUserActionArgs extends ClientArgs { caseId: string; @@ -33,12 +47,16 @@ interface PostCaseUserActionArgs extends ClientArgs { export class CaseUserActionService { constructor(private readonly log: Logger) {} - public async getAll({ unsecuredSavedObjectsClient, caseId, subCaseId }: GetCaseUserActionArgs) { + public async getAll({ + unsecuredSavedObjectsClient, + caseId, + subCaseId, + }: GetCaseUserActionArgs): Promise> { try { const id = subCaseId ?? caseId; const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT; - return await unsecuredSavedObjectsClient.find({ + const userActions = await unsecuredSavedObjectsClient.find({ type: CASE_USER_ACTION_SAVED_OBJECT, hasReference: { type, id }, page: 1, @@ -46,17 +64,22 @@ export class CaseUserActionService { sortField: 'action_at', sortOrder: 'asc', }); + + return transformFindResponseToExternalModel(userActions); } catch (error) { this.log.error(`Error on GET case user action case id: ${caseId}: ${error}`); throw error; } } - public async bulkCreate({ unsecuredSavedObjectsClient, actions }: PostCaseUserActionArgs) { + public async bulkCreate({ + unsecuredSavedObjectsClient, + actions, + }: PostCaseUserActionArgs): Promise { try { this.log.debug(`Attempting to POST a new case user action`); - return await unsecuredSavedObjectsClient.bulkCreate( + await unsecuredSavedObjectsClient.bulkCreate( actions.map((action) => ({ type: CASE_USER_ACTION_SAVED_OBJECT, ...action })) ); } catch (error) { @@ -65,3 +88,71 @@ export class CaseUserActionService { } } } + +export function transformFindResponseToExternalModel( + userActions: SavedObjectsFindResponse +): SavedObjectsFindResponse { + return { + ...userActions, + saved_objects: userActions.saved_objects.map((so) => ({ + ...so, + ...transformToExternalModel(so), + })), + }; +} + +function transformToExternalModel( + userAction: SavedObjectsFindResult +): SavedObjectsFindResult { + const { references } = userAction; + + const newValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.New, userAction); + const oldValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.Old, userAction); + + const caseId = findReferenceId(CASE_REF_NAME, CASE_SAVED_OBJECT, references) ?? ''; + const commentId = + findReferenceId(COMMENT_REF_NAME, CASE_COMMENT_SAVED_OBJECT, references) ?? null; + const subCaseId = findReferenceId(SUB_CASE_REF_NAME, SUB_CASE_SAVED_OBJECT, references) ?? ''; + + return { + ...userAction, + attributes: { + ...userAction.attributes, + action_id: userAction.id, + case_id: caseId, + comment_id: commentId, + sub_case_id: subCaseId, + new_val_connector_id: newValueConnectorId, + old_val_connector_id: oldValueConnectorId, + }, + }; +} + +function getConnectorIdFromReferences( + fieldType: UserActionFieldType, + userAction: SavedObjectsFindResult +): string | null { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + attributes: { action, action_field }, + references, + } = userAction; + + if (isCreateConnector(action, action_field) || isUpdateConnector(action, action_field)) { + return findConnectorIdReference(ConnectorIdReferenceName[fieldType], references)?.id ?? null; + } else if (isPush(action, action_field)) { + return ( + findConnectorIdReference(PushConnectorIdReferenceName[fieldType], references)?.id ?? null + ); + } + + return null; +} + +function findReferenceId( + name: string, + type: string, + references: SavedObjectReference[] +): string | undefined { + return references.find((ref) => ref.name === name && ref.type === type)?.id; +} diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts new file mode 100644 index 0000000000000..2d28770617094 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts @@ -0,0 +1,1246 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { noneConnectorId } from '../../../common'; +import { + CONNECTOR_ID_REFERENCE_NAME, + getNoneCaseConnector, + PUSH_CONNECTOR_ID_REFERENCE_NAME, + USER_ACTION_OLD_ID_REF_NAME, + USER_ACTION_OLD_PUSH_ID_REF_NAME, +} from '../../common'; +import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; +import { + extractConnectorIdHelper, + extractConnectorIdFromJson, + extractConnectorId, + transformConnectorIdToReference, + transformPushConnectorIdToReference, +} from './transform'; +import { UserActionFieldType } from './types'; + +describe('user action transform utils', () => { + describe('transformConnectorIdToReference', () => { + it('returns the default none connector when the connector is undefined', () => { + expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is undefined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none and other fields are defined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { + ...createJiraConnector(), + id: noneConnectorId, + }).transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns an empty array of references when the connector is undefined', () => { + expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).references.length).toBe( + 0 + ); + }); + + it('returns an empty array of references when the id is undefined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }).references + .length + ).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) + .references.length + ).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector and other fields are defined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { + ...createJiraConnector(), + id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns a jira connector', () => { + const transformedFields = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + createJiraConnector() + ); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('returns a jira connector with the user action reference name', () => { + const transformedFields = transformConnectorIdToReference( + USER_ACTION_OLD_ID_REF_NAME, + createJiraConnector() + ); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('transformPushConnectorIdToReference', () => { + it('sets external_service to null when it is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('sets external_service to null when it is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: null, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is none', () => { + const otherFields = { otherField: 'hi' }; + + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + ...otherFields, + connector_id: noneConnectorId, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "otherField": "hi", + }, + } + `); + }); + + it('returns an empty array of references when the external_service is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the external_service is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null).references + .length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + ...createExternalService(), + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns the external_service connector', () => { + const transformedFields = transformPushConnectorIdToReference( + PUSH_CONNECTOR_ID_REFERENCE_NAME, + createExternalService() + ); + expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns the external_service connector with a user actions reference name', () => { + const transformedFields = transformPushConnectorIdToReference( + USER_ACTION_OLD_PUSH_ID_REF_NAME, + createExternalService() + ); + + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('extractConnectorIdHelper', () => { + it('throws an error when action details has a circular reference', () => { + const circularRef = { prop: {} }; + circularRef.prop = circularRef; + + expect(() => { + extractConnectorIdHelper({ + action: 'a', + actionFields: [], + actionDetails: circularRef, + fieldType: UserActionFieldType.New, + }); + }).toThrow(); + }); + + describe('create action', () => { + it('returns no references and untransformed json when actionDetails is not a valid connector', () => { + expect( + extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is create and action fields does not contain connector', () => { + expect( + extractConnectorIdHelper({ + action: 'create', + actionFields: ['', 'something', 'onnector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns the stringified json without the id', () => { + const jiraConnector = createConnectorObject(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails)).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + }); + + it('removes the connector.id when the connector is none', () => { + const connector = { connector: getNoneCaseConnector() }; + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + const parsedJson = JSON.parse(transformedActionDetails); + + expect(parsedJson.connector).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('does not return a reference when the connector is none', () => { + const connector = { connector: getNoneCaseConnector() }; + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toEqual([]); + }); + + it('returns a reference to the connector.id', () => { + const connector = createConnectorObject(); + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('returns an old reference name to the connector.id', () => { + const connector = createConnectorObject(); + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns the transformed connector and the description', () => { + const details = { ...createConnectorObject(), description: 'a description' }; + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: details, + fieldType: UserActionFieldType.Old, + })!; + + const parsedJson = JSON.parse(transformedActionDetails); + + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + "description": "a description", + } + `); + }); + }); + + describe('update action', () => { + it('returns no references and untransformed json when actionDetails is not a valid connector', () => { + expect( + extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is update and action fields does not contain connector', () => { + expect( + extractConnectorIdHelper({ + action: 'update', + actionFields: ['', 'something', 'onnector'], + actionDetails: 5, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "5", + } + `); + }); + + it('returns the stringified json without the id', () => { + const jiraConnector = createJiraConnector(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails!); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + }); + + it('returns the stringified json without the id when the connector is none', () => { + const connector = getNoneCaseConnector(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('does not return a reference when the connector is none', () => { + const connector = getNoneCaseConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toEqual([]); + }); + + it('returns an old reference name to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns no references and untransformed json when actionDetails is not a valid external_service', () => { + expect( + extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is push-to-service and action fields does not contain pushed', () => { + expect( + extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['', 'something', 'ushed'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns an old reference name to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); + + describe('extractConnectorId', () => { + it('returns null when the action details has a circular reference', () => { + const circularRef = { prop: {} }; + circularRef.prop = circularRef; + + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: circularRef, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeNull(); + expect(references).toEqual([]); + }); + + describe('fails to extract the id', () => { + it('returns a null transformed action details when it is initially null', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: null, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeNull(); + expect(references).toEqual([]); + }); + + it('returns an undefined transformed action details when it is initially undefined', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: undefined, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeUndefined(); + expect(references).toEqual([]); + }); + + it('returns a json encoded string and empty references when the action is not a valid connector', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails!)).toEqual({ a: 'hello' }); + expect(references).toEqual([]); + }); + + it('returns a json encoded string and empty references when the action details is an invalid object', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: 5 as unknown as Record, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails!).toEqual('5'); + expect(references).toEqual([]); + }); + }); + + describe('create', () => { + it('extracts the connector.id from a new create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'create', + actionFields: ['connector'], + actionDetails: createConnectorObject(), + fieldType: UserActionFieldType.New, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('extracts the connector.id from an old create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'create', + actionFields: ['connector'], + actionDetails: createConnectorObject(), + fieldType: UserActionFieldType.Old, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('update', () => { + it('extracts the connector.id from a new create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'update', + actionFields: ['connector'], + actionDetails: createJiraConnector(), + fieldType: UserActionFieldType.New, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('extracts the connector.id from an old create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'update', + actionFields: ['connector'], + actionDetails: createJiraConnector(), + fieldType: UserActionFieldType.Old, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails!); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns a reference to the old action details connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); + + describe('extractConnectorIdFromJson', () => { + describe('fails to extract the id', () => { + it('returns no references and null transformed json when action is undefined', () => { + expect( + extractConnectorIdFromJson({ + actionFields: [], + actionDetails: undefined, + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionFields is undefined', () => { + expect( + extractConnectorIdFromJson({ action: 'a', fieldType: UserActionFieldType.New }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionDetails is undefined', () => { + expect( + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionDetails is null', () => { + expect( + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + actionDetails: null, + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: null, + references: [], + }); + }); + + it('throws an error when actionDetails is invalid json', () => { + expect(() => + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + actionDetails: '{a', + fieldType: UserActionFieldType.New, + }) + ).toThrow(); + }); + }); + + describe('create action', () => { + it('returns the stringified json without the id', () => { + const jiraConnector = createConnectorObject(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'create', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails!)).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createConnectorObject(); + + const { references } = extractConnectorIdFromJson({ + action: 'create', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('update action', () => { + it('returns the stringified json without the id', () => { + const jiraConnector = createJiraConnector(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'update', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails!); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdFromJson({ + action: 'update', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: JSON.stringify(externalService), + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails!); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdFromJson({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: JSON.stringify(externalService), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts new file mode 100644 index 0000000000000..93595374208a3 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/transform.ts @@ -0,0 +1,320 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as rt from 'io-ts'; +import { isString } from 'lodash'; + +import { SavedObjectReference } from '../../../../../../src/core/server'; +import { + CaseAttributes, + CaseConnector, + CaseConnectorRt, + CaseExternalServiceBasicRt, + isCreateConnector, + isPush, + isUpdateConnector, + noneConnectorId, +} from '../../../common'; +import { + CONNECTOR_ID_REFERENCE_NAME, + getNoneCaseConnector, + PUSH_CONNECTOR_ID_REFERENCE_NAME, + USER_ACTION_OLD_ID_REF_NAME, + USER_ACTION_OLD_PUSH_ID_REF_NAME, +} from '../../common'; +import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; +import { UserActionFieldType } from './types'; + +/** + * Extracts the connector id from a json encoded string and formats it as a saved object reference. This will remove + * the field it extracted the connector id from. + */ +export function extractConnectorIdFromJson({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action?: string; + actionFields?: string[]; + actionDetails?: string | null; + fieldType: UserActionFieldType; +}): { transformedActionDetails?: string | null; references: SavedObjectReference[] } { + if (!action || !actionFields || !actionDetails) { + return { transformedActionDetails: actionDetails, references: [] }; + } + + const decodedJson = JSON.parse(actionDetails); + + return extractConnectorIdHelper({ + action, + actionFields, + actionDetails: decodedJson, + fieldType, + }); +} + +/** + * Extracts the connector id from an unencoded object and formats it as a saved object reference. + * This will remove the field it extracted the connector id from. + */ +export function extractConnectorId({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action: string; + actionFields: string[]; + actionDetails?: Record | string | null; + fieldType: UserActionFieldType; +}): { + transformedActionDetails?: string | null; + references: SavedObjectReference[]; +} { + if (!actionDetails || isString(actionDetails)) { + // the action was null, undefined, or a regular string so just return it unmodified and not encoded + return { transformedActionDetails: actionDetails, references: [] }; + } + + try { + return extractConnectorIdHelper({ + action, + actionFields, + actionDetails, + fieldType, + }); + } catch (error) { + return { transformedActionDetails: encodeActionDetails(actionDetails), references: [] }; + } +} + +function encodeActionDetails(actionDetails: Record): string | null { + try { + return JSON.stringify(actionDetails); + } catch (error) { + return null; + } +} + +/** + * Internal helper function for extracting the connector id. This is only exported for usage in unit tests. + * This function handles encoding the transformed fields as a json string + */ +export function extractConnectorIdHelper({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action: string; + actionFields: string[]; + actionDetails: unknown; + fieldType: UserActionFieldType; +}): { transformedActionDetails: string; references: SavedObjectReference[] } { + let transformedActionDetails: unknown = actionDetails; + let referencesToReturn: SavedObjectReference[] = []; + + try { + if (isCreateCaseConnector(action, actionFields, actionDetails)) { + const { transformedActionDetails: transformedConnectorPortion, references } = + transformConnectorFromCreateAndUpdateAction(actionDetails.connector, fieldType); + + // the above call only transforms the connector portion of the action details so let's add back + // the rest of the details and we'll overwrite the connector portion when the transformed one + transformedActionDetails = { + ...actionDetails, + ...transformedConnectorPortion, + }; + referencesToReturn = references; + } else if (isUpdateCaseConnector(action, actionFields, actionDetails)) { + const { + transformedActionDetails: { connector: transformedConnector }, + references, + } = transformConnectorFromCreateAndUpdateAction(actionDetails, fieldType); + + transformedActionDetails = transformedConnector; + referencesToReturn = references; + } else if (isPushConnector(action, actionFields, actionDetails)) { + ({ transformedActionDetails, references: referencesToReturn } = + transformConnectorFromPushAction(actionDetails, fieldType)); + } + } catch (error) { + // ignore any errors, we'll just return whatever was passed in for action details in that case + } + + return { + transformedActionDetails: JSON.stringify(transformedActionDetails), + references: referencesToReturn, + }; +} + +function isCreateCaseConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is { connector: CaseConnector } { + try { + const unsafeCase = actionDetails as CaseAttributes; + + return ( + isCreateConnector(action, actionFields) && + unsafeCase.connector !== undefined && + CaseConnectorRt.is(unsafeCase.connector) + ); + } catch { + return false; + } +} + +export const ConnectorIdReferenceName: Record = { + [UserActionFieldType.New]: CONNECTOR_ID_REFERENCE_NAME, + [UserActionFieldType.Old]: USER_ACTION_OLD_ID_REF_NAME, +}; + +function transformConnectorFromCreateAndUpdateAction( + connector: CaseConnector, + fieldType: UserActionFieldType +): { + transformedActionDetails: { connector: unknown }; + references: SavedObjectReference[]; +} { + const { transformedConnector, references } = transformConnectorIdToReference( + ConnectorIdReferenceName[fieldType], + connector + ); + + return { + transformedActionDetails: transformedConnector, + references, + }; +} + +type ConnectorIdRefNameType = + | typeof CONNECTOR_ID_REFERENCE_NAME + | typeof USER_ACTION_OLD_ID_REF_NAME; + +export const transformConnectorIdToReference = ( + referenceName: ConnectorIdRefNameType, + connector?: { + id?: string; + } +): { + transformedConnector: { connector: unknown }; + references: SavedObjectReference[]; +} => { + const { id: connectorId, ...restConnector } = connector ?? {}; + + const references = createConnectorReference(connectorId, ACTION_SAVED_OBJECT_TYPE, referenceName); + + const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); + const connectorFieldsToReturn = + connector && isConnectorIdValid(connectorId) ? restConnector : restNoneConnector; + + return { + transformedConnector: { + connector: connectorFieldsToReturn, + }, + references, + }; +}; + +const createConnectorReference = ( + id: string | null | undefined, + type: string, + name: string +): SavedObjectReference[] => { + return isConnectorIdValid(id) + ? [ + { + id, + type, + name, + }, + ] + : []; +}; + +const isConnectorIdValid = (id: string | null | undefined): id is string => + id != null && id !== noneConnectorId; + +function isUpdateCaseConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is CaseConnector { + try { + return isUpdateConnector(action, actionFields) && CaseConnectorRt.is(actionDetails); + } catch { + return false; + } +} + +type CaseExternalService = rt.TypeOf; + +function isPushConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is CaseExternalService { + try { + return isPush(action, actionFields) && CaseExternalServiceBasicRt.is(actionDetails); + } catch { + return false; + } +} + +export const PushConnectorIdReferenceName: Record = + { + [UserActionFieldType.New]: PUSH_CONNECTOR_ID_REFERENCE_NAME, + [UserActionFieldType.Old]: USER_ACTION_OLD_PUSH_ID_REF_NAME, + }; + +function transformConnectorFromPushAction( + externalService: CaseExternalService, + fieldType: UserActionFieldType +): { + transformedActionDetails: {} | null; + references: SavedObjectReference[]; +} { + const { transformedPushConnector, references } = transformPushConnectorIdToReference( + PushConnectorIdReferenceName[fieldType], + externalService + ); + + return { + transformedActionDetails: transformedPushConnector.external_service, + references, + }; +} + +type PushConnectorIdRefNameType = + | typeof PUSH_CONNECTOR_ID_REFERENCE_NAME + | typeof USER_ACTION_OLD_PUSH_ID_REF_NAME; + +export const transformPushConnectorIdToReference = ( + referenceName: PushConnectorIdRefNameType, + external_service?: { connector_id?: string | null } | null +): { + transformedPushConnector: { external_service: {} | null }; + references: SavedObjectReference[]; +} => { + const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; + + const references = createConnectorReference( + pushConnectorId, + ACTION_SAVED_OBJECT_TYPE, + referenceName + ); + + return { + transformedPushConnector: { external_service: external_service ? restExternalService : null }, + references, + }; +}; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts new file mode 100644 index 0000000000000..3c67535255ecc --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/types.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Indicates whether which user action field is being parsed, the new_value or the old_value. + */ +export enum UserActionFieldType { + New = 'New', + Old = 'Old', +} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 964e9135aba7b..68f0ba43d889b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should create a user action when creating a case', async () => { + it('should create a user action when deleting a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); await deleteCases({ supertest, caseIDs: [postedCase.id] }); const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); @@ -106,6 +106,8 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, old_value: null, new_value: null, + new_val_connector_id: null, + old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 63b2f2e9b90ed..d7c506a6b69d2 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -126,6 +126,8 @@ export default ({ getService }: FtrProviderContext): void => { action: 'update', action_by: defaultUser, new_value: CaseStatuses.closed, + new_val_connector_id: null, + old_val_connector_id: null, old_value: CaseStatuses.open, case_id: `${postedCase.id}`, comment_id: null, @@ -165,6 +167,8 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, new_value: CaseStatuses['in-progress'], old_value: CaseStatuses.open, + old_val_connector_id: null, + new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 96709ee7c309d..13408c5d309d9 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -114,6 +114,8 @@ export default ({ getService }: FtrProviderContext): void => { const { new_value, ...rest } = creationUserAction as CaseUserActionResponse; const parsedNewValue = JSON.parse(new_value!); + const { id: connectorId, ...restCaseConnector } = postedCase.connector; + expect(rest).to.eql({ action_field: [ 'description', @@ -127,6 +129,9 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, old_value: null, + old_val_connector_id: null, + // the connector id will be null here because it the connector is none + new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -138,7 +143,7 @@ export default ({ getService }: FtrProviderContext): void => { description: postedCase.description, title: postedCase.title, tags: postedCase.tags, - connector: postedCase.connector, + connector: restCaseConnector, settings: postedCase.settings, owner: postedCase.owner, }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index f4c31c052cddd..942293437b03f 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -148,7 +148,9 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}","owner":"securitySolutionFixture"}`, + new_val_connector_id: null, old_value: null, + old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: `${patchedCase.comments![0].id}`, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 35ebb1a4bf7b1..4cae10510d28e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -48,6 +48,15 @@ export default ({ getService }: FtrProviderContext): void => { }); it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector', 'settings, owner]`, async () => { + const { id: connectorId, ...restConnector } = userActionPostResp.connector; + + const userActionNewValueNoId = { + ...userActionPostResp, + connector: { + ...restConnector, + }, + }; + const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -73,7 +82,10 @@ export default ({ getService }: FtrProviderContext): void => { ]); expect(body[0].action).to.eql('create'); expect(body[0].old_value).to.eql(null); - expect(JSON.parse(body[0].new_value)).to.eql(userActionPostResp); + expect(body[0].old_val_connector_id).to.eql(null); + // this will be null because it is for the none connector + expect(body[0].new_val_connector_id).to.eql(null); + expect(JSON.parse(body[0].new_value)).to.eql(userActionNewValueNoId); }); it(`on close case, user action: 'update' should be called with actionFields: ['status']`, async () => { @@ -147,18 +159,19 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.length).to.eql(2); expect(body[1].action_field).to.eql(['connector']); expect(body[1].action).to.eql('update'); + // this is null because it is the none connector + expect(body[1].old_val_connector_id).to.eql(null); expect(JSON.parse(body[1].old_value)).to.eql({ - id: 'none', name: 'none', type: '.none', fields: null, }); expect(JSON.parse(body[1].new_value)).to.eql({ - id: '123', name: 'Connector', type: '.jira', fields: { issueType: 'Task', priority: 'High', parent: null }, }); + expect(body[1].new_val_connector_id).to.eql('123'); }); it(`on update tags, user action: 'add' and 'delete' should be called with actionFields: ['tags']`, async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts index b4c2dca47bf5f..f9e66880c5230 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts @@ -12,6 +12,10 @@ import { SECURITY_SOLUTION_OWNER, } from '../../../../../../plugins/cases/common/constants'; import { getCaseUserActions } from '../../../../common/lib/utils'; +import { + CaseUserActionResponse, + CaseUserActionsResponse, +} from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { @@ -41,14 +45,18 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(connectorUserAction.action_field.length).eql(1); expect(connectorUserAction.action_field[0]).eql('connector'); + expect(connectorUserAction.old_val_connector_id).to.eql( + 'c1900ac0-017f-11eb-93f8-d161651bf509' + ); expect(oldValue).to.eql({ - id: 'c1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, }); + expect(connectorUserAction.new_val_connector_id).to.eql( + 'b1900ac0-017f-11eb-93f8-d161651bf509' + ); expect(newValue).to.eql({ - id: 'b1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, @@ -77,5 +85,142 @@ export default function createGetTests({ getService }: FtrProviderContext) { } }); }); + + describe('7.13 connector id extraction', () => { + let userActions: CaseUserActionsResponse; + + before(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' + ); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' + ); + }); + + describe('none connector case', () => { + it('removes the connector id from the case create user action and sets the ids to null', async () => { + userActions = await getCaseUserActions({ + supertest, + caseID: 'aa8ac630-005e-11ec-91f1-6daf2ab59fb5', + }); + + const userAction = getUserActionById( + userActions, + 'ab43b5f0-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.description).to.be('a description'); + expect(newValDecoded.title).to.be('a case'); + expect(newValDecoded.connector).not.have.property('id'); + // the connector id should be none so it should be removed + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('sets the connector ids to null for a create user action with null new and old values', async () => { + const userAction = getUserActionById( + userActions, + 'b3094de0-005e-11ec-91f1-6daf2ab59fb5' + )!; + + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + }); + + describe('case with many user actions', () => { + before(async () => { + userActions = await getCaseUserActions({ + supertest, + caseID: 'e6fa9370-005e-11ec-91f1-6daf2ab59fb5', + }); + }); + + it('removes the connector id field for a created case user action', async () => { + const userAction = getUserActionById( + userActions, + 'e7882d70-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.description).to.be('a description'); + expect(newValDecoded.title).to.be('a case'); + + expect(newValDecoded.connector).to.not.have.property('id'); + expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('removes the connector id from the external service new value', async () => { + const userAction = getUserActionById( + userActions, + 'e9471b80-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.connector_name).to.be('a jira connector'); + expect(newValDecoded).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('sets the connector ids to null for a comment user action', async () => { + const userAction = getUserActionById( + userActions, + 'efe9de50-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.comment).to.be('a comment'); + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('removes the connector id for an update connector action', async () => { + const userAction = getUserActionById( + userActions, + '16cd9e30-005f-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + const oldValDecoded = JSON.parse(userAction.old_value!); + + expect(newValDecoded.name).to.be('a different jira connector'); + expect(oldValDecoded.name).to.be('a jira connector'); + + expect(newValDecoded).to.not.have.property('id'); + expect(oldValDecoded).to.not.have.property('id'); + expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + }); + + it('removes the connector id from the external service new value for second push', async () => { + const userAction = getUserActionById( + userActions, + '1ea33bb0-005f-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + + expect(newValDecoded.connector_name).to.be('a different jira connector'); + + expect(newValDecoded).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + }); + }); }); } + +function getUserActionById( + userActions: CaseUserActionsResponse, + id: string +): CaseUserActionResponse | undefined { + return userActions.find((userAction) => userAction.action_id === id); +} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 94fe494fc7cc4..0ea66d35b63b8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -275,6 +275,8 @@ export default ({ getService }: FtrProviderContext): void => { action: 'push-to-service', action_by: defaultUser, old_value: null, + old_val_connector_id: null, + new_val_connector_id: connector.id, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -284,7 +286,6 @@ export default ({ getService }: FtrProviderContext): void => { expect(parsedNewValue).to.eql({ pushed_at: pushedCase.external_service!.pushed_at, pushed_by: defaultUser, - connector_id: connector.id, connector_name: connector.name, external_id: '123', external_title: 'INC01', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 79af6bb279a3d..255a2a4ce28b5 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -108,8 +108,10 @@ export default ({ getService }: FtrProviderContext): void => { expect(body[1].action_field).to.eql(['pushed']); expect(body[1].action).to.eql('push-to-service'); expect(body[1].old_value).to.eql(null); + expect(body[1].old_val_connector_id).to.eql(null); + expect(body[1].new_val_connector_id).to.eql(configure.connector.id); const newValue = JSON.parse(body[1].new_value); - expect(newValue.connector_id).to.eql(configure.connector.id); + expect(newValue).to.not.have.property('connector_id'); expect(newValue.pushed_by).to.eql(defaultUser); }); }); diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..5f73dfd89d1660ef207e6ec26ce7198f2a722616 GIT binary patch literal 2078 zcmV+(2;ui1iwFpikRD+G17u-zVJ>QOZ*BnXTuXDKI1s+)S5TY-LE>SvxgZy+IT5F>ahZaIhWE-j?DT+?5t5lioJNZyKPi$UR$bs&%<`)2h3okV(a3a zL#Oy&DHWJDNxNT|jw-rg7~^}oGPm^>`SOTBQ4}aL)$g~xMn9kcdl7$V`cR|B4O~BP zqt_0h|8$>z;4rF*UHSD`?VD}BV?wB@n?Bo~_4Cj^nGOj*-2e&YwlJ-7)stzXQl$6l zh6Wf#xgLm6fk0B05?J0t#ZnXtXtJuQ4`NG?L_HWCza&<@-~_JN!GM!|m2&)MuBmhx9q}HCvC}gmQ#_ z+fhiAP6!kL7MvE58|{)2@BjUW$f9tX#Q|M!@kbOLLhJ4>F>@&*`saF2GK$DLhc5DD z4@Y;Kz<&wxUF&#wWg|CggBy49oDxweD&-< zb~uP=O%%kqy?2D7n6?o817_4HLQi#A6T|p`gHoi&YV&;Kh!&9DU`EQUkpQ>*>^d>p zRcqse!#BjXO(=RyYKS+rwIfCl;J@9c08#t<4+mjpmkSxFvQ~^M?wf6@-Y9cWv>TKa zu`x}6x0rysjjNkcEPS!H={QAkk41(0LpobFS25I@I_w+z$nQ5dZ){!e%NyRD_2&{@ zND0~Wv59lv5O~qI{Q!pV-rF3!&^BRRE7IabxCFx4Jx^+cnue!l zJ|JK(w>}RR!qB%}icb;?Te-?HnAYk5A6wi@%Om%3Gnt)hGE~fmCjQn@F_nTKW4MH^O>YEbb(vmNHhKZMDpBhSK34k%}} z16_HGrVj!F1sK!E{n*qNV6(h{xStZ5QshfHTiojakdfp|8LD-HbS4LYl*jGQQxsHj z!VB=0BB2Tjia4bPa*30UROFOT$R9W+8NjqyjU)w({mj!hArW} zQJfE0UYX=hCb>5jcO!7gI@3y)b*7W$sRJ22D+)~AP4dz|^A+@W_Od={mc5oI0DI-E zzZ?qdvI>F<+l@p02 zaX5v>QMt(~d?G+WQ&tye2or%}J0?!wI&9AhQ0W5o2Pj~v#CY_Xo~g@Vt~W$w5tM@T z1zZ}PJ64aN9Cw?bKr~e>^_Txb<0w8>igODIIjNCXWIszp2WS?80jGJr$O0xts=C=+ z0AP-0BLRIkyO?XL)=AmOVkxF$BYWd_D;wE~z}Nxr4lAb1^Z{+a&N!<1|<$U5Iw zRxBaS99|>5;$A8+ zg$Yj)35^VZxS__-2xA(;gQaD+1~znb0krJUJOG&Z8TzH+JZagnc>ptcZO)~I=bF+m z$x-a9QQ19?V Date: Mon, 20 Sep 2021 12:54:00 -0700 Subject: [PATCH 10/69] [CI] Increase heap for Jest Integration (#112594) Signed-off-by: Tyler Smalley --- .buildkite/scripts/steps/test/jest_integration.sh | 2 +- test/scripts/test/jest_integration.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.buildkite/scripts/steps/test/jest_integration.sh b/.buildkite/scripts/steps/test/jest_integration.sh index 9f0228fd910bf..458651df2df2a 100755 --- a/.buildkite/scripts/steps/test/jest_integration.sh +++ b/.buildkite/scripts/steps/test/jest_integration.sh @@ -10,4 +10,4 @@ is_test_execution_step echo '--- Jest Integration Tests' checks-reporter-with-killswitch "Jest Integration Tests" \ - node scripts/jest_integration --ci --verbose + node --max-old-space-size=5120 scripts/jest_integration --ci diff --git a/test/scripts/test/jest_integration.sh b/test/scripts/test/jest_integration.sh index aaffdb2fd9e90..89390657d1b48 100755 --- a/test/scripts/test/jest_integration.sh +++ b/test/scripts/test/jest_integration.sh @@ -3,4 +3,4 @@ source src/dev/ci_setup/setup_env.sh checks-reporter-with-killswitch "Jest Integration Tests" \ - node scripts/jest_integration --ci + node --max-old-space-size=5120 scripts/jest_integration --ci From 49977b2e4be9174c36d903da1d5ca3669d048181 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 20 Sep 2021 16:28:35 -0400 Subject: [PATCH 11/69] [Alerting] Track fields usage in find api (#112096) * Track fields usage * Add test * Add missing change --- .../alerting/server/routes/find_rules.test.ts | 39 ++++++++++++++++++- .../alerting/server/routes/find_rules.ts | 8 ++++ .../server/routes/legacy/find.test.ts | 26 +++++++++++++ .../alerting/server/routes/legacy/find.ts | 8 ++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts index 23f636e962169..692fdca6c95ae 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.test.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; import { findRulesRoute } from './find_rules'; import { httpServiceMock } from 'src/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; @@ -12,7 +12,6 @@ import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { rulesClientMock } from '../rules_client.mock'; import { trackLegacyTerminology } from './lib/track_legacy_terminology'; -import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; const rulesClient = rulesClientMock.create(); const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); @@ -187,4 +186,40 @@ describe('findRulesRoute', () => { 'alertTypeId', ]); }); + + it('should track calls to deprecated functionality', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState, mockUsageCounter); + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + rulesClient.find.mockResolvedValueOnce(findResult); + + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + fields: ['foo', 'bar'], + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `alertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index 22d701b2040a8..a4a066728555d 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -136,6 +136,14 @@ export const findRulesRoute = ( search_fields: searchFieldsAsArray(req.query.search_fields), }); + if (req.query.fields) { + usageCounter?.incrementCounter({ + counterName: `alertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + } + const findResult = await rulesClient.find({ options }); return res.ok({ body: rewriteBodyRes(findResult), diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts index 1ddd1a662cbe4..ef346fab1e621 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts @@ -194,4 +194,30 @@ describe('findAlertRoute', () => { 'alertTypeId', ]); }); + + it('should track calls to deprecated functionality', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); + const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); + + findAlertRoute(router, licenseState, mockUsageCounter); + const [, handler] = router.get.mock.calls[0]; + const [context, req, res] = mockHandlerArguments( + { rulesClient }, + { + params: {}, + query: { + fields: ['foo', 'bar'], + }, + }, + ['ok'] + ); + await handler(context, req, res); + expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: `legacyAlertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index 5a87536a1ac94..328fade491642 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -89,6 +89,14 @@ export const findAlertRoute = ( : [query.search_fields]; } + if (query.fields) { + usageCounter?.incrementCounter({ + counterName: `legacyAlertingFieldsUsage`, + counterType: 'alertingFieldsUsage', + incrementBy: 1, + }); + } + const findResult = await rulesClient.find({ options }); return res.ok({ body: findResult, From 817a0fba74daea722c47252eb8a5bd0147d2c283 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 20 Sep 2021 16:29:00 -0400 Subject: [PATCH 12/69] [CI] [Buildkite] increase xpack cigroup timeout and add snapshot verify timeouts (#112610) --- .buildkite/pipelines/es_snapshots/verify.yml | 5 +++++ .buildkite/pipelines/hourly.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index aa49ba5d2641f..10c996c5aceca 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -29,6 +29,7 @@ steps: agents: queue: ci-group-6 depends_on: build + timeout_in_minutes: 150 key: default-cigroup retry: automatic: @@ -40,6 +41,7 @@ steps: agents: queue: ci-group-6 depends_on: build + timeout_in_minutes: 120 key: default-cigroup-docker retry: automatic: @@ -52,6 +54,7 @@ steps: agents: queue: ci-group-4d depends_on: build + timeout_in_minutes: 120 key: oss-cigroup retry: automatic: @@ -62,6 +65,7 @@ steps: label: 'Jest Integration Tests' agents: queue: jest + timeout_in_minutes: 120 key: jest-integration retry: automatic: @@ -72,6 +76,7 @@ steps: label: 'API Integration Tests' agents: queue: jest + timeout_in_minutes: 120 key: api-integration - command: .buildkite/scripts/steps/es_snapshots/trigger_promote.sh diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 0dc8ad3e08d08..b78db4698c01b 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -17,7 +17,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 120 + timeout_in_minutes: 150 key: default-cigroup retry: automatic: From e07b0e593ec062879d8da043bfbb5712d9ed4b16 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Mon, 20 Sep 2021 15:35:54 -0700 Subject: [PATCH 13/69] Revert "[Cases] Migrate user actions connector ID (#108272)" This reverts commit 10ac814d8f5ede2c10c83aadc99304a59ac9682f. --- x-pack/plugins/cases/common/api/cases/case.ts | 16 +- .../cases/common/api/cases/user_actions.ts | 3 +- .../cases/common/api/connectors/index.ts | 12 +- x-pack/plugins/cases/common/index.ts | 1 - x-pack/plugins/cases/common/ui/types.ts | 2 - .../cases/common/utils/user_actions.ts | 18 - .../cases/public/common/user_actions/index.ts | 8 - .../common/user_actions/parsers.test.ts | 86 - .../public/common/user_actions/parsers.ts | 77 - .../components/edit_connector/helpers.test.ts | 187 -- .../components/edit_connector/helpers.ts | 30 +- .../user_action_tree/helpers.test.tsx | 108 +- .../components/user_action_tree/helpers.tsx | 53 +- .../components/user_action_tree/index.tsx | 10 +- .../plugins/cases/public/containers/mock.ts | 123 +- .../use_get_case_user_actions.test.tsx | 237 +- .../containers/use_get_case_user_actions.tsx | 51 +- .../plugins/cases/public/containers/utils.ts | 8 + .../cases/server/client/attachments/add.ts | 6 +- .../cases/server/client/attachments/update.ts | 9 +- .../cases/server/client/cases/create.ts | 2 +- .../cases/server/client/cases/delete.ts | 2 +- .../plugins/cases/server/client/cases/mock.ts | 18 +- .../plugins/cases/server/client/cases/push.ts | 2 +- .../cases/server/client/cases/utils.test.ts | 6 +- .../cases/server/client/cases/utils.ts | 30 +- .../server/client/user_actions/get.test.ts | 106 - .../cases/server/client/user_actions/get.ts | 47 +- .../plugins/cases/server/common/constants.ts | 29 - .../migrations/cases.test.ts | 570 ++-- .../saved_object_types/migrations/cases.ts | 14 +- .../migrations/configuration.test.ts | 142 +- .../migrations/configuration.ts | 9 +- .../saved_object_types/migrations/index.ts | 57 +- .../migrations/user_actions.test.ts | 562 ---- .../migrations/user_actions.ts | 159 - .../migrations/utils.test.ts | 229 ++ .../saved_object_types/migrations/utils.ts | 73 + .../cases/server/services/cases/index.test.ts | 8 +- .../cases/server/services/test_utils.ts | 17 +- .../services/user_actions/helpers.test.ts | 332 -- .../server/services/user_actions/helpers.ts | 192 +- .../services/user_actions/index.test.ts | 557 ---- .../server/services/user_actions/index.ts | 101 +- .../services/user_actions/transform.test.ts | 1246 ------- .../server/services/user_actions/transform.ts | 320 -- .../server/services/user_actions/types.ts | 14 - .../tests/common/cases/delete_cases.ts | 4 +- .../tests/common/cases/patch_cases.ts | 4 - .../tests/common/cases/post_case.ts | 7 +- .../tests/common/comments/post_comment.ts | 2 - .../user_actions/get_all_user_actions.ts | 19 +- .../tests/common/user_actions/migrations.ts | 149 +- .../tests/trial/cases/push_case.ts | 3 +- .../user_actions/get_all_user_actions.ts | 4 +- .../migrations/7.13_user_actions/data.json.gz | Bin 2078 -> 0 bytes .../7.13_user_actions/mappings.json | 2954 ----------------- 57 files changed, 1140 insertions(+), 7895 deletions(-) delete mode 100644 x-pack/plugins/cases/common/utils/user_actions.ts delete mode 100644 x-pack/plugins/cases/public/common/user_actions/index.ts delete mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.test.ts delete mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.ts delete mode 100644 x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts delete mode 100644 x-pack/plugins/cases/server/client/user_actions/get.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/helpers.test.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/index.test.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.test.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.ts delete mode 100644 x-pack/plugins/cases/server/services/user_actions/types.ts delete mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz delete mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 05a053307b29c..37a491cdad4c0 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -87,11 +87,8 @@ const CaseBasicRt = rt.type({ owner: rt.string, }); -/** - * This represents the push to service UserAction. It lacks the connector_id because that is stored in a different field - * within the user action object in the API response. - */ -export const CaseUserActionExternalServiceRt = rt.type({ +export const CaseExternalServiceBasicRt = rt.type({ + connector_id: rt.union([rt.string, rt.null]), connector_name: rt.string, external_id: rt.string, external_title: rt.string, @@ -100,14 +97,7 @@ export const CaseUserActionExternalServiceRt = rt.type({ pushed_by: UserRT, }); -export const CaseExternalServiceBasicRt = rt.intersection([ - rt.type({ - connector_id: rt.union([rt.string, rt.null]), - }), - CaseUserActionExternalServiceRt, -]); - -export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); +const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); export const CaseAttributesRt = rt.intersection([ CaseBasicRt, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index e86ce5248a6f9..03912c550d77a 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -34,6 +34,7 @@ const UserActionRt = rt.union([ rt.literal('push-to-service'), ]); +// TO DO change state to status const CaseUserActionBasicRT = rt.type({ action_field: UserActionFieldRt, action: UserActionRt, @@ -50,8 +51,6 @@ const CaseUserActionResponseRT = rt.intersection([ action_id: rt.string, case_id: rt.string, comment_id: rt.union([rt.string, rt.null]), - new_val_connector_id: rt.union([rt.string, rt.null]), - old_val_connector_id: rt.union([rt.string, rt.null]), }), rt.partial({ sub_case_id: rt.string }), ]); diff --git a/x-pack/plugins/cases/common/api/connectors/index.ts b/x-pack/plugins/cases/common/api/connectors/index.ts index 2b3483b4f6184..77af90b5d08cb 100644 --- a/x-pack/plugins/cases/common/api/connectors/index.ts +++ b/x-pack/plugins/cases/common/api/connectors/index.ts @@ -84,22 +84,14 @@ export const ConnectorTypeFieldsRt = rt.union([ ConnectorSwimlaneTypeFieldsRt, ]); -/** - * This type represents the connector's format when it is encoded within a user action. - */ -export const CaseUserActionConnectorRt = rt.intersection([ - rt.type({ name: rt.string }), - ConnectorTypeFieldsRt, -]); - export const CaseConnectorRt = rt.intersection([ rt.type({ id: rt.string, + name: rt.string, }), - CaseUserActionConnectorRt, + ConnectorTypeFieldsRt, ]); -export type CaseUserActionConnector = rt.TypeOf; export type CaseConnector = rt.TypeOf; export type ConnectorTypeFields = rt.TypeOf; export type ConnectorJiraTypeFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts index d38b1a779981c..5305318cc9aa6 100644 --- a/x-pack/plugins/cases/common/index.ts +++ b/x-pack/plugins/cases/common/index.ts @@ -12,4 +12,3 @@ export * from './constants'; export * from './api'; export * from './ui/types'; export * from './utils/connectors_api'; -export * from './utils/user_actions'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index c89c3eb08263b..bf4ec0da6ee56 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -66,9 +66,7 @@ export interface CaseUserActions { caseId: string; commentId: string | null; newValue: string | null; - newValConnectorId: string | null; oldValue: string | null; - oldValConnectorId: string | null; } export interface CaseExternalService { diff --git a/x-pack/plugins/cases/common/utils/user_actions.ts b/x-pack/plugins/cases/common/utils/user_actions.ts deleted file mode 100644 index 7de0d7066eaed..0000000000000 --- a/x-pack/plugins/cases/common/utils/user_actions.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export function isCreateConnector(action?: string, actionFields?: string[]): boolean { - return action === 'create' && actionFields != null && actionFields.includes('connector'); -} - -export function isUpdateConnector(action?: string, actionFields?: string[]): boolean { - return action === 'update' && actionFields != null && actionFields.includes('connector'); -} - -export function isPush(action?: string, actionFields?: string[]): boolean { - return action === 'push-to-service' && actionFields != null && actionFields.includes('pushed'); -} diff --git a/x-pack/plugins/cases/public/common/user_actions/index.ts b/x-pack/plugins/cases/public/common/user_actions/index.ts deleted file mode 100644 index 507455f7102a7..0000000000000 --- a/x-pack/plugins/cases/public/common/user_actions/index.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './parsers'; diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts deleted file mode 100644 index c6d13cc41686c..0000000000000 --- a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ConnectorTypes, noneConnectorId } from '../../../common'; -import { parseStringAsConnector, parseStringAsExternalService } from './parsers'; - -describe('user actions utility functions', () => { - describe('parseStringAsConnector', () => { - it('return null if the data is null', () => { - expect(parseStringAsConnector('', null)).toBeNull(); - }); - - it('return null if the data is not a json object', () => { - expect(parseStringAsConnector('', 'blah')).toBeNull(); - }); - - it('return null if the data is not a valid connector', () => { - expect(parseStringAsConnector('', JSON.stringify({ a: '1' }))).toBeNull(); - }); - - it('return null if id is null but the data is a connector other than none', () => { - expect( - parseStringAsConnector( - null, - JSON.stringify({ type: ConnectorTypes.jira, name: '', fields: null }) - ) - ).toBeNull(); - }); - - it('return the id as the none connector if the data is the none connector', () => { - expect( - parseStringAsConnector( - null, - JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }) - ) - ).toEqual({ id: noneConnectorId, type: ConnectorTypes.none, name: '', fields: null }); - }); - - it('returns a decoded connector with the specified id', () => { - expect( - parseStringAsConnector( - 'a', - JSON.stringify({ type: ConnectorTypes.jira, name: 'hi', fields: null }) - ) - ).toEqual({ id: 'a', type: ConnectorTypes.jira, name: 'hi', fields: null }); - }); - }); - - describe('parseStringAsExternalService', () => { - it('returns null when the data is null', () => { - expect(parseStringAsExternalService('', null)).toBeNull(); - }); - - it('returns null when the data is not valid json', () => { - expect(parseStringAsExternalService('', 'blah')).toBeNull(); - }); - - it('returns null when the data is not a valid external service object', () => { - expect(parseStringAsExternalService('', JSON.stringify({ a: '1' }))).toBeNull(); - }); - - it('returns the decoded external service with the connector_id field added', () => { - const externalServiceInfo = { - connector_name: 'name', - external_id: '1', - external_title: 'title', - external_url: 'abc', - pushed_at: '1', - pushed_by: { - username: 'a', - email: 'a@a.com', - full_name: 'a', - }, - }; - - expect(parseStringAsExternalService('500', JSON.stringify(externalServiceInfo))).toEqual({ - ...externalServiceInfo, - connector_id: '500', - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.ts deleted file mode 100644 index dfea22443aa51..0000000000000 --- a/x-pack/plugins/cases/public/common/user_actions/parsers.ts +++ /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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - CaseUserActionConnectorRt, - CaseConnector, - ConnectorTypes, - noneConnectorId, - CaseFullExternalService, - CaseUserActionExternalServiceRt, -} from '../../../common'; - -export const parseStringAsConnector = ( - id: string | null, - encodedData: string | null -): CaseConnector | null => { - if (encodedData == null) { - return null; - } - - const decodedConnector = parseString(encodedData); - - if (!CaseUserActionConnectorRt.is(decodedConnector)) { - return null; - } - - if (id == null && decodedConnector.type === ConnectorTypes.none) { - return { - ...decodedConnector, - id: noneConnectorId, - }; - } else if (id == null) { - return null; - } else { - // id does not equal null or undefined and the connector type does not equal none - // so return the connector with its id - return { - ...decodedConnector, - id, - }; - } -}; - -const parseString = (params: string | null): unknown | null => { - if (params == null) { - return null; - } - - try { - return JSON.parse(params); - } catch { - return null; - } -}; - -export const parseStringAsExternalService = ( - id: string | null, - encodedData: string | null -): CaseFullExternalService => { - if (encodedData == null) { - return null; - } - - const decodedExternalService = parseString(encodedData); - if (!CaseUserActionExternalServiceRt.is(decodedExternalService)) { - return null; - } - - return { - ...decodedExternalService, - connector_id: id, - }; -}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts deleted file mode 100644 index e20d6b37258bc..0000000000000 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CaseUserActionConnector, ConnectorTypes } from '../../../common'; -import { CaseUserActions } from '../../containers/types'; -import { getConnectorFieldsFromUserActions } from './helpers'; - -describe('helpers', () => { - describe('getConnectorFieldsFromUserActions', () => { - it('returns null when it cannot find the connector id', () => { - expect(getConnectorFieldsFromUserActions('a', [])).toBeNull(); - }); - - it('returns null when the value fields are not valid encoded fields', () => { - expect( - getConnectorFieldsFromUserActions('a', [createUserAction({ newValue: 'a', oldValue: 'a' })]) - ).toBeNull(); - }); - - it('returns null when it cannot find the connector id in a non empty array', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createUserAction({ - newValue: JSON.stringify({ a: '1' }), - oldValue: JSON.stringify({ a: '1' }), - }), - ]) - ).toBeNull(); - }); - - it('returns the fields when it finds the connector id in the new value', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: JSON.stringify({ a: '1' }), - newValConnectorId: 'a', - }), - ]) - ).toEqual(defaultJiraFields); - }); - - it('returns the fields when it finds the connector id in the new value and the old value is null', () => { - expect( - getConnectorFieldsFromUserActions('a', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - newValConnectorId: 'a', - }), - ]) - ).toEqual(defaultJiraFields); - }); - - it('returns the fields when it finds the connector id in the old value', () => { - const expectedFields = { ...defaultJiraFields, issueType: '5' }; - - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: createEncodedJiraConnector({ - fields: expectedFields, - }), - newValConnectorId: 'b', - oldValConnectorId: 'id-to-find', - }), - ]) - ).toEqual(expectedFields); - }); - - it('returns the fields when it finds the connector id in the second user action', () => { - const expectedFields = { ...defaultJiraFields, issueType: '5' }; - - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: createEncodedJiraConnector(), - newValConnectorId: 'b', - oldValConnectorId: 'a', - }), - createUserAction({ - newValue: createEncodedJiraConnector(), - oldValue: createEncodedJiraConnector({ fields: expectedFields }), - newValConnectorId: 'b', - oldValConnectorId: 'id-to-find', - }), - ]) - ).toEqual(expectedFields); - }); - - it('ignores a parse failure and finds the right user action', () => { - expect( - getConnectorFieldsFromUserActions('none', [ - createUserAction({ - newValue: 'b', - newValConnectorId: null, - }), - createUserAction({ - newValue: createEncodedJiraConnector({ - type: ConnectorTypes.none, - name: '', - fields: null, - }), - newValConnectorId: null, - }), - ]) - ).toBeNull(); - }); - - it('returns null when the id matches but the encoded value is null', () => { - expect( - getConnectorFieldsFromUserActions('b', [ - createUserAction({ - newValue: null, - newValConnectorId: 'b', - }), - ]) - ).toBeNull(); - }); - - it('returns null when the action fields is not of length 1', () => { - expect( - getConnectorFieldsFromUserActions('id-to-find', [ - createUserAction({ - newValue: JSON.stringify({ a: '1', fields: { hello: '1' } }), - oldValue: JSON.stringify({ a: '1', fields: { hi: '2' } }), - newValConnectorId: 'b', - oldValConnectorId: 'id-to-find', - actionField: ['connector', 'connector'], - }), - ]) - ).toBeNull(); - }); - - it('matches the none connector the searched for id is none', () => { - expect( - getConnectorFieldsFromUserActions('none', [ - createUserAction({ - newValue: createEncodedJiraConnector({ - type: ConnectorTypes.none, - name: '', - fields: null, - }), - newValConnectorId: null, - }), - ]) - ).toBeNull(); - }); - }); -}); - -function createUserAction(fields: Partial): CaseUserActions { - return { - action: 'update', - actionAt: '', - actionBy: {}, - actionField: ['connector'], - actionId: '', - caseId: '', - commentId: '', - newValConnectorId: null, - oldValConnectorId: null, - newValue: null, - oldValue: null, - ...fields, - }; -} - -function createEncodedJiraConnector(fields?: Partial): string { - return JSON.stringify({ - type: ConnectorTypes.jira, - name: 'name', - fields: defaultJiraFields, - ...fields, - }); -} - -const defaultJiraFields = { - issueType: '1', - parent: null, - priority: null, -}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts index b97035c458aca..36eb3f58c8aaf 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts @@ -5,33 +5,23 @@ * 2.0. */ -import { ConnectorTypeFields } from '../../../common'; import { CaseUserActions } from '../../containers/types'; -import { parseStringAsConnector } from '../../common/user_actions'; -export const getConnectorFieldsFromUserActions = ( - id: string, - userActions: CaseUserActions[] -): ConnectorTypeFields['fields'] => { +export const getConnectorFieldsFromUserActions = (id: string, userActions: CaseUserActions[]) => { try { for (const action of [...userActions].reverse()) { if (action.actionField.length === 1 && action.actionField[0] === 'connector') { - const parsedNewConnector = parseStringAsConnector( - action.newValConnectorId, - action.newValue - ); + if (action.oldValue && action.newValue) { + const oldValue = JSON.parse(action.oldValue); + const newValue = JSON.parse(action.newValue); - if (parsedNewConnector && id === parsedNewConnector.id) { - return parsedNewConnector.fields; - } - - const parsedOldConnector = parseStringAsConnector( - action.oldValConnectorId, - action.oldValue - ); + if (newValue.id === id) { + return newValue.fields; + } - if (parsedOldConnector && id === parsedOldConnector.id) { - return parsedOldConnector.fields; + if (oldValue.id === id) { + return oldValue.fields; + } } } } diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx index 841f0d36bbf17..b49a010cff38f 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses, ConnectorTypes } from '../../../common'; +import { CaseStatuses } from '../../../common'; import { basicPush, getUserAction } from '../../containers/mock'; import { getLabelTitle, @@ -129,7 +129,7 @@ describe('User action tree helpers', () => { `${i18n.PUSHED_NEW_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue!).external_url + JSON.parse(action.newValue).external_url ); }); @@ -142,74 +142,50 @@ describe('User action tree helpers', () => { `${i18n.UPDATE_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue!).external_url + JSON.parse(action.newValue).external_url ); }); - describe('getConnectorLabelTitle', () => { - it('returns an empty string when the encoded old value is null', () => { - const result = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { oldValue: null }), - connectors, - }); - - expect(result).toEqual(''); - }); - - it('returns an empty string when the encoded new value is null', () => { - const result = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { newValue: null }), - connectors, - }); - - expect(result).toEqual(''); - }); - - it('returns the change connector label', () => { - const result: string | JSX.Element = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { - oldValue: JSON.stringify({ - type: ConnectorTypes.serviceNowITSM, - name: 'a', - fields: null, - }), - oldValConnectorId: 'servicenow-1', - newValue: JSON.stringify({ type: ConnectorTypes.resilient, name: 'a', fields: null }), - newValConnectorId: 'resilient-2', - }), - connectors, - }); - - expect(result).toEqual('selected My Connector 2 as incident management system'); - }); - - it('returns the removed connector label', () => { - const result: string | JSX.Element = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { - oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), - oldValConnectorId: 'servicenow-1', - newValue: JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }), - newValConnectorId: 'none', - }), - connectors, - }); - - expect(result).toEqual('removed external incident management system'); - }); - - it('returns the connector fields changed label', () => { - const result: string | JSX.Element = getConnectorLabelTitle({ - action: getUserAction(['connector'], 'update', { - oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), - oldValConnectorId: 'servicenow-1', - newValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), - newValConnectorId: 'servicenow-1', - }), - connectors, - }); - - expect(result).toEqual('changed connector field'); + it('label title generated for update connector - change connector', () => { + const action = { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: 'servicenow-1' }), + newValue: JSON.stringify({ id: 'resilient-2' }), + }; + const result: string | JSX.Element = getConnectorLabelTitle({ + action, + connectors, + }); + + expect(result).toEqual('selected My Connector 2 as incident management system'); + }); + + it('label title generated for update connector - change connector to none', () => { + const action = { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: 'servicenow-1' }), + newValue: JSON.stringify({ id: 'none' }), + }; + const result: string | JSX.Element = getConnectorLabelTitle({ + action, + connectors, }); + + expect(result).toEqual('removed external incident management system'); + }); + + it('label title generated for update connector - field change', () => { + const action = { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: 'servicenow-1' }), + newValue: JSON.stringify({ id: 'servicenow-1' }), + }; + const result: string | JSX.Element = getConnectorLabelTitle({ + action, + connectors, + }); + + expect(result).toEqual('changed connector field'); }); describe('toStringArray', () => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx index 2eb44f91190c6..744b14926b358 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx @@ -23,11 +23,10 @@ import { CommentType, Comment, CommentRequestActionsType, - noneConnectorId, } from '../../../common'; import { CaseUserActions } from '../../containers/types'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseStringAsConnector, parseStringAsExternalService } from '../../common/user_actions'; +import { parseString } from '../../containers/utils'; import { Tags } from '../tag_list/tags'; import { UserActionUsernameWithAvatar } from './user_action_username_with_avatar'; import { UserActionTimestamp } from './user_action_timestamp'; @@ -98,27 +97,23 @@ export const getConnectorLabelTitle = ({ action: CaseUserActions; connectors: ActionConnector[]; }) => { - const oldConnector = parseStringAsConnector(action.oldValConnectorId, action.oldValue); - const newConnector = parseStringAsConnector(action.newValConnectorId, action.newValue); + const oldValue = parseString(`${action.oldValue}`); + const newValue = parseString(`${action.newValue}`); - if (!oldConnector || !newConnector) { + if (oldValue === null || newValue === null) { return ''; } - // if the ids are the same, assume we just changed the fields - if (oldConnector.id === newConnector.id) { + // Connector changed + if (oldValue.id !== newValue.id) { + const newConnector = connectors.find((c) => c.id === newValue.id); + return newValue.id != null && newValue.id !== 'none' && newConnector != null + ? i18n.SELECTED_THIRD_PARTY(newConnector.name) + : i18n.REMOVED_THIRD_PARTY; + } else { + // Field changed return i18n.CHANGED_CONNECTOR_FIELD; } - - // ids are not the same so check and see if the id is a valid connector and then return its name - // if the connector id is the none connector value then it must have been removed - const newConnectorActionInfo = connectors.find((c) => c.id === newConnector.id); - if (newConnector.id !== noneConnectorId && newConnectorActionInfo != null) { - return i18n.SELECTED_THIRD_PARTY(newConnectorActionInfo.name); - } - - // it wasn't a valid connector or it was the none connector, so it must have been removed - return i18n.REMOVED_THIRD_PARTY; }; const getTagsLabelTitle = (action: CaseUserActions) => { @@ -138,8 +133,7 @@ const getTagsLabelTitle = (action: CaseUserActions) => { }; export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: boolean) => { - const externalService = parseStringAsExternalService(action.newValConnectorId, action.newValue); - + const pushedVal = JSON.parse(action.newValue ?? '') as CaseFullExternalService; return ( {`${firstPush ? i18n.PUSHED_NEW_INCIDENT : i18n.UPDATE_INCIDENT} ${ - externalService?.connector_name + pushedVal?.connector_name }`} - - {externalService?.external_title} + + {pushedVal?.external_title} @@ -163,19 +157,20 @@ export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: b export const getPushInfo = ( caseServices: CaseServices, - externalService: CaseFullExternalService | undefined, + // a JSON parse failure will result in null for parsedValue + parsedValue: { connector_id: string | null; connector_name: string } | null, index: number ) => - externalService != null && externalService.connector_id != null + parsedValue != null && parsedValue.connector_id != null ? { - firstPush: caseServices[externalService.connector_id]?.firstPushIndex === index, - parsedConnectorId: externalService.connector_id, - parsedConnectorName: externalService.connector_name, + firstPush: caseServices[parsedValue.connector_id]?.firstPushIndex === index, + parsedConnectorId: parsedValue.connector_id, + parsedConnectorName: parsedValue.connector_name, } : { firstPush: false, - parsedConnectorId: noneConnectorId, - parsedConnectorName: noneConnectorId, + parsedConnectorId: 'none', + parsedConnectorName: 'none', }; const getUpdateActionIcon = (actionField: string): string => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx index 7ea415324194c..784817229caf9 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx @@ -35,7 +35,7 @@ import { Ecs, } from '../../../common'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseStringAsExternalService } from '../../common/user_actions'; +import { parseString } from '../../containers/utils'; import { OnUpdateFields } from '../case_view'; import { getConnectorLabelTitle, @@ -512,14 +512,10 @@ export const UserActionTree = React.memo( // Pushed information if (action.actionField.length === 1 && action.actionField[0] === 'pushed') { - const parsedExternalService = parseStringAsExternalService( - action.newValConnectorId, - action.newValue - ); - + const parsedValue = parseString(`${action.newValue}`); const { firstPush, parsedConnectorId, parsedConnectorName } = getPushInfo( caseServices, - parsedExternalService, + parsedValue, index ); diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index fcd564969d486..c955bb34240e2 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -9,7 +9,6 @@ import { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } import { AssociationType, - CaseUserActionConnector, CaseResponse, CasesFindResponse, CasesResponse, @@ -20,9 +19,6 @@ import { CommentResponse, CommentType, ConnectorTypes, - isCreateConnector, - isPush, - isUpdateConnector, SECURITY_SOLUTION_OWNER, UserAction, UserActionField, @@ -244,9 +240,7 @@ export const pushedCase: Case = { const basicAction = { actionAt: basicCreatedAt, actionBy: elasticUser, - oldValConnectorId: null, oldValue: null, - newValConnectorId: null, newValue: 'what a cool value', caseId: basicCaseId, commentId: null, @@ -314,7 +308,12 @@ export const basicCaseSnake: CaseResponse = { closed_at: null, closed_by: null, comments: [basicCommentSnake], - connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, + connector: { + id: 'none', + name: 'My Connector', + type: ConnectorTypes.none, + fields: null, + }, created_at: basicCreatedAt, created_by: elasticUserSnake, external_service: null, @@ -329,8 +328,8 @@ export const casesStatusSnake: CasesStatusResponse = { count_open_cases: 20, }; -export const pushConnectorId = '123'; export const pushSnake = { + connector_id: '123', connector_name: 'connector name', external_id: 'external_id', external_title: 'external title', @@ -351,7 +350,7 @@ export const pushedCaseSnake = { type: ConnectorTypes.jira, fields: null, }, - external_service: { ...basicPushSnake, connector_id: pushConnectorId }, + external_service: basicPushSnake, }; export const reporters: string[] = ['alexis', 'kim', 'maria', 'steph']; @@ -386,20 +385,17 @@ const basicActionSnake = { comment_id: null, owner: SECURITY_SOLUTION_OWNER, }; -export const getUserActionSnake = (af: UserActionField, a: UserAction) => { - const isPushToService = a === 'push-to-service' && af[0] === 'pushed'; - - return { - ...basicActionSnake, - action_id: `${af[0]}-${a}`, - action_field: af, - action: a, - comment_id: af[0] === 'comment' ? basicCommentId : null, - new_value: isPushToService ? JSON.stringify(basicPushSnake) : basicAction.newValue, - new_val_connector_id: isPushToService ? pushConnectorId : null, - old_val_connector_id: null, - }; -}; +export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ + ...basicActionSnake, + action_id: `${af[0]}-${a}`, + action_field: af, + action: a, + comment_id: af[0] === 'comment' ? basicCommentId : null, + new_value: + a === 'push-to-service' && af[0] === 'pushed' + ? JSON.stringify(basicPushSnake) + : basicAction.newValue, +}); export const caseUserActionsSnake: CaseUserActionsResponse = [ getUserActionSnake(['description'], 'create'), @@ -409,76 +405,17 @@ export const caseUserActionsSnake: CaseUserActionsResponse = [ // user actions -export const getUserAction = ( - af: UserActionField, - a: UserAction, - overrides?: Partial -): CaseUserActions => { - return { - ...basicAction, - actionId: `${af[0]}-${a}`, - actionField: af, - action: a, - commentId: af[0] === 'comment' ? basicCommentId : null, - ...getValues(a, af, overrides), - }; -}; - -const getValues = ( - userAction: UserAction, - actionFields: UserActionField, - overrides?: Partial -): Partial => { - if (isCreateConnector(userAction, actionFields)) { - return { - newValue: - overrides?.newValue === undefined ? JSON.stringify(basicCaseSnake) : overrides.newValue, - newValConnectorId: overrides?.newValConnectorId ?? null, - oldValue: null, - oldValConnectorId: null, - }; - } else if (isUpdateConnector(userAction, actionFields)) { - return { - newValue: - overrides?.newValue === undefined - ? JSON.stringify({ name: 'My Connector', type: ConnectorTypes.none, fields: null }) - : overrides.newValue, - newValConnectorId: overrides?.newValConnectorId ?? null, - oldValue: - overrides?.oldValue === undefined - ? JSON.stringify({ name: 'My Connector2', type: ConnectorTypes.none, fields: null }) - : overrides.oldValue, - oldValConnectorId: overrides?.oldValConnectorId ?? null, - }; - } else if (isPush(userAction, actionFields)) { - return { - newValue: - overrides?.newValue === undefined ? JSON.stringify(basicPushSnake) : overrides?.newValue, - newValConnectorId: - overrides?.newValConnectorId === undefined ? pushConnectorId : overrides.newValConnectorId, - oldValue: overrides?.oldValue ?? null, - oldValConnectorId: overrides?.oldValConnectorId ?? null, - }; - } else { - return { - newValue: overrides?.newValue === undefined ? basicAction.newValue : overrides.newValue, - newValConnectorId: overrides?.newValConnectorId ?? null, - oldValue: overrides?.oldValue ?? null, - oldValConnectorId: overrides?.oldValConnectorId ?? null, - }; - } -}; - -export const getJiraConnectorWithoutId = (overrides?: Partial) => { - return JSON.stringify({ - name: 'jira1', - type: ConnectorTypes.jira, - ...jiraFields, - ...overrides, - }); -}; - -export const jiraFields = { fields: { issueType: '10006', priority: null, parent: null } }; +export const getUserAction = (af: UserActionField, a: UserAction) => ({ + ...basicAction, + actionId: `${af[0]}-${a}`, + actionField: af, + action: a, + commentId: af[0] === 'comment' ? basicCommentId : null, + newValue: + a === 'push-to-service' && af[0] === 'pushed' + ? JSON.stringify(basicPushSnake) + : basicAction.newValue, +}); export const getAlertUserAction = () => ({ ...basicAction, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx index e7e46fa46c7cc..62b4cf92434cd 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx @@ -18,9 +18,7 @@ import { basicPushSnake, caseUserActions, elasticUser, - getJiraConnectorWithoutId, getUserAction, - jiraFields, } from './mock'; import * as api from './api'; @@ -301,14 +299,15 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - newValConnectorId: '456', - }); + }; const userActions = [ ...caseUserActions, @@ -347,14 +346,15 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - newValConnectorId: '456', - }); + }; const userActions = [ ...caseUserActions, @@ -392,7 +392,11 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -414,7 +418,11 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123To456UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -436,8 +444,16 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123To456UserAction(), - createChangeConnector456To123UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -458,10 +474,22 @@ describe('useGetCaseUserActions', () => { it('Change fields and connector after push - hasDataToPush: true', () => { const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123HighPriorityTo456UserAction(), - createChangeConnector456To123PriorityLowUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -482,10 +510,22 @@ describe('useGetCaseUserActions', () => { it('Change only connector after push - hasDataToPush: false', () => { const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123HighPriorityTo456UserAction(), - createChangeConnector456To123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -507,24 +547,45 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - newValConnectorId: '456', - }); + }; const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, pushAction123, - createChangeConnector123HighPriorityTo456UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, pushAction456, - createChangeConnector456To123PriorityLowUserAction(), - createChangeConnector123LowPriorityTo456UserAction(), - createChangeConnector456To123PriorityLowUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -556,22 +617,34 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, + connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = getUserAction(['pushed'], 'push-to-service', { - newValConnectorId: '456', + const pushAction456 = { + ...getUserAction(['pushed'], 'push-to-service'), newValue: JSON.stringify(push456), - }); - + }; const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, pushAction123, - createChangeConnector123HighPriorityTo456UserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, pushAction456, - createChangeConnector456To123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -602,10 +675,22 @@ describe('useGetCaseUserActions', () => { it('Changing other connectors fields does not count as an update', () => { const userActions = [ ...caseUserActions, - createUpdateConnectorFields123HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), + newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + }, getUserAction(['pushed'], 'push-to-service'), - createChangeConnector123HighPriorityTo456UserAction(), - createUpdateConnectorFields456HighPriorityUserAction(), + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + }, + { + ...getUserAction(['connector'], 'update'), + oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), + newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '3' } }), + }, ]; const result = getPushedInfo(userActions, '123'); @@ -624,83 +709,3 @@ describe('useGetCaseUserActions', () => { }); }); }); - -const jira123HighPriorityFields = { - fields: { ...jiraFields.fields, priority: 'High' }, -}; - -const jira123LowPriorityFields = { - fields: { ...jiraFields.fields, priority: 'Low' }, -}; - -const jira456Fields = { - fields: { issueType: '10', parent: null, priority: null }, -}; - -const jira456HighPriorityFields = { - fields: { ...jira456Fields.fields, priority: 'High' }, -}; - -const createUpdateConnectorFields123HighPriorityUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(), - newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), - oldValConnectorId: '123', - newValConnectorId: '123', - }); - -const createUpdateConnectorFields456HighPriorityUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - newValue: getJiraConnectorWithoutId(jira456HighPriorityFields), - oldValConnectorId: '456', - newValConnectorId: '456', - }); - -const createChangeConnector123HighPriorityTo456UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira123HighPriorityFields), - oldValConnectorId: '123', - newValue: getJiraConnectorWithoutId(jira456Fields), - newValConnectorId: '456', - }); - -const createChangeConnector123To456UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(), - oldValConnectorId: '123', - newValue: getJiraConnectorWithoutId(jira456Fields), - newValConnectorId: '456', - }); - -const createChangeConnector123LowPriorityTo456UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira123LowPriorityFields), - oldValConnectorId: '123', - newValue: getJiraConnectorWithoutId(jira456Fields), - newValConnectorId: '456', - }); - -const createChangeConnector456To123UserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - oldValConnectorId: '456', - newValue: getJiraConnectorWithoutId(), - newValConnectorId: '123', - }); - -const createChangeConnector456To123HighPriorityUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - oldValConnectorId: '456', - newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), - newValConnectorId: '123', - }); - -const createChangeConnector456To123PriorityLowUserAction = () => - getUserAction(['connector'], 'update', { - oldValue: getJiraConnectorWithoutId(jira456Fields), - oldValConnectorId: '456', - newValue: getJiraConnectorWithoutId(jira123LowPriorityFields), - newValConnectorId: '123', - }); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx index 36d600c3f1c9d..e481519ba19a3 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx @@ -18,8 +18,7 @@ import { } from '../../common'; import { getCaseUserActions, getSubCaseUserActions } from './api'; import * as i18n from './translations'; -import { convertToCamelCase } from './utils'; -import { parseStringAsConnector, parseStringAsExternalService } from '../common/user_actions'; +import { convertToCamelCase, parseString } from './utils'; import { useToasts } from '../common/lib/kibana'; export interface CaseService extends CaseExternalService { @@ -59,24 +58,8 @@ export interface UseGetCaseUserActions extends CaseUserActionsState { ) => Promise; } -const unknownExternalServiceConnectorId = 'unknown'; - -const getExternalService = ( - connectorId: string | null, - encodedValue: string | null -): CaseExternalService | null => { - const decodedValue = parseStringAsExternalService(connectorId, encodedValue); - - if (decodedValue == null) { - return null; - } - return { - ...convertToCamelCase(decodedValue), - // if in the rare case that the connector id is null we'll set it to unknown if we need to reference it in the UI - // anywhere. The id would only ever be null if a migration failed or some logic error within the backend occurred - connectorId: connectorId ?? unknownExternalServiceConnectorId, - }; -}; +const getExternalService = (value: string): CaseExternalService | null => + convertToCamelCase(parseString(`${value}`)); const groupConnectorFields = ( userActions: CaseUserActions[] @@ -86,26 +69,22 @@ const groupConnectorFields = ( return acc; } - const oldConnector = parseStringAsConnector(mua.oldValConnectorId, mua.oldValue); - const newConnector = parseStringAsConnector(mua.newValConnectorId, mua.newValue); + const oldValue = parseString(`${mua.oldValue}`); + const newValue = parseString(`${mua.newValue}`); - if (!oldConnector || !newConnector) { + if (oldValue == null || newValue == null) { return acc; } return { ...acc, - [oldConnector.id]: [ - ...(acc[oldConnector.id] || []), - ...(oldConnector.id === newConnector.id - ? [oldConnector.fields, newConnector.fields] - : [oldConnector.fields]), + [oldValue.id]: [ + ...(acc[oldValue.id] || []), + ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [oldValue.fields]), ], - [newConnector.id]: [ - ...(acc[newConnector.id] || []), - ...(oldConnector.id === newConnector.id - ? [oldConnector.fields, newConnector.fields] - : [newConnector.fields]), + [newValue.id]: [ + ...(acc[newValue.id] || []), + ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [newValue.fields]), ], }; }, {} as Record>); @@ -158,7 +137,9 @@ export const getPushedInfo = ( const hasDataToPushForConnector = (connectorId: string): boolean => { const caseUserActionsReversed = [...caseUserActions].reverse(); const lastPushOfConnectorReversedIndex = caseUserActionsReversed.findIndex( - (mua) => mua.action === 'push-to-service' && mua.newValConnectorId === connectorId + (mua) => + mua.action === 'push-to-service' && + getExternalService(`${mua.newValue}`)?.connectorId === connectorId ); if (lastPushOfConnectorReversedIndex === -1) { @@ -209,7 +190,7 @@ export const getPushedInfo = ( return acc; } - const externalService = getExternalService(cua.newValConnectorId, cua.newValue); + const externalService = getExternalService(`${cua.newValue}`); if (externalService === null) { return acc; } diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index b0cc0c72fee78..de67b1cfbd6fa 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -36,6 +36,14 @@ import * as i18n from './translations'; export const getTypedPayload = (a: unknown): T => a as T; +export const parseString = (params: string) => { + try { + return JSON.parse(params); + } catch { + return null; + } +}; + export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => arrayOfSnakes.reduce((acc: unknown[], value) => { if (isArray(value)) { diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index b84a6bd84c43b..507405d58cef1 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -106,7 +106,7 @@ async function getSubCase({ caseId, subCaseId: newSubCase.id, fields: ['status', 'sub_case'], - newValue: { status: newSubCase.attributes.status }, + newValue: JSON.stringify({ status: newSubCase.attributes.status }), owner: newSubCase.attributes.owner, }), ], @@ -220,7 +220,7 @@ const addGeneratedAlerts = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: query, + newValue: JSON.stringify(query), owner: newComment.attributes.owner, }), ], @@ -408,7 +408,7 @@ export const addComment = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: query, + newValue: JSON.stringify(query), owner: newComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/attachments/update.ts b/x-pack/plugins/cases/server/client/attachments/update.ts index b5e9e6c372355..9816efd9a8452 100644 --- a/x-pack/plugins/cases/server/client/attachments/update.ts +++ b/x-pack/plugins/cases/server/client/attachments/update.ts @@ -17,7 +17,6 @@ import { SUB_CASE_SAVED_OBJECT, CaseResponse, CommentPatchRequest, - CommentRequest, } from '../../../common'; import { AttachmentService, CasesService } from '../../services'; import { CasesClientArgs } from '..'; @@ -194,12 +193,12 @@ export async function update( subCaseId: subCaseID, commentId: updatedComment.id, fields: ['comment'], - // casting because typescript is complaining that it's not a Record even though it is - newValue: queryRestAttributes as CommentRequest, - oldValue: + newValue: JSON.stringify(queryRestAttributes), + oldValue: JSON.stringify( // We are interested only in ContextBasicRt attributes // myComment.attribute contains also CommentAttributesBasicRt attributes - pick(Object.keys(queryRestAttributes), myComment.attributes), + pick(Object.keys(queryRestAttributes), myComment.attributes) + ), owner: myComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 488bc523f7796..887990fef8938 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -106,7 +106,7 @@ export const create = async ( actionBy: { username, full_name, email }, caseId: newCase.id, fields: ['description', 'status', 'tags', 'title', 'connector', 'settings', OWNER_FIELD], - newValue: query, + newValue: JSON.stringify(query), owner: newCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 4333535f17a24..80a687a0e72f8 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -168,7 +168,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P 'settings', OWNER_FIELD, 'comment', - ...(ENABLE_CASE_CONNECTOR ? ['sub_case' as const] : []), + ...(ENABLE_CASE_CONNECTOR ? ['sub_case'] : []), ], owner: caseInfo.attributes.owner, }) diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 22520cea11014..313d6cd12a6db 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -231,10 +231,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', - new_val_connector_id: '456', + '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"id":"456","name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', old_value: null, - old_val_connector_id: null, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -250,9 +248,7 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', - new_val_connector_id: '456', - old_val_connector_id: null, + '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', old_value: null, action_id: '0a801750-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -269,8 +265,6 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: '{"type":"alert","alertId":"alert-id-1","index":".siem-signals-default-000008"}', - new_val_connector_id: null, - old_val_connector_id: null, old_value: null, action_id: '7373eb60-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -288,8 +282,6 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"type":"alert","alertId":"alert-id-2","index":".siem-signals-default-000008"}', old_value: null, - new_val_connector_id: null, - old_val_connector_id: null, action_id: '7abc6410-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-2', @@ -305,10 +297,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', - new_val_connector_id: '456', + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', old_value: null, - old_val_connector_id: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -325,8 +315,6 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"comment":"a comment!","type":"user"}', old_value: null, - new_val_connector_id: null, - old_val_connector_id: null, action_id: '0818e5e0-6648-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-user-1', diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 1b090a653546d..3048cf01bb3ba 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -241,7 +241,7 @@ export const push = async ( actionBy: { username, full_name, email }, caseId, fields: ['pushed'], - newValue: externalService, + newValue: JSON.stringify(externalService), owner: myCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index 315e9966d347b..d7c45d3e1e9ae 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -799,10 +799,8 @@ describe('utils', () => { username: 'elastic', }, new_value: - // The connector id is 123 - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', - new_val_connector_id: '123', - old_val_connector_id: null, + // The connector id is 123 + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"123","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', old_value: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index f5cf2fe4b3f51..359ad4b41ead0 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -20,8 +20,6 @@ import { CommentRequestUserType, CommentRequestAlertType, CommentRequestActionsType, - CaseUserActionResponse, - isPush, } from '../../../common'; import { ActionsClient } from '../../../../actions/server'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -57,36 +55,22 @@ export const getLatestPushInfo = ( userActions: CaseUserActionsResponse ): { index: number; pushedInfo: CaseFullExternalService } | null => { for (const [index, action] of [...userActions].reverse().entries()) { - if ( - isPush(action.action, action.action_field) && - isValidNewValue(action) && - connectorId === action.new_val_connector_id - ) { + if (action.action === 'push-to-service' && action.new_value) try { const pushedInfo = JSON.parse(action.new_value); - // We returned the index of the element in the userActions array. - // As we traverse the userActions in reverse we need to calculate the index of a normal traversal - return { - index: userActions.length - index - 1, - pushedInfo: { ...pushedInfo, connector_id: connectorId }, - }; + if (pushedInfo.connector_id === connectorId) { + // We returned the index of the element in the userActions array. + // As we traverse the userActions in reverse we need to calculate the index of a normal traversal + return { index: userActions.length - index - 1, pushedInfo }; + } } catch (e) { - // ignore parse failures and check the next user action + // Silence JSON parse errors } - } } return null; }; -type NonNullNewValueAction = Omit & { - new_value: string; - new_val_connector_id: string; -}; - -const isValidNewValue = (userAction: CaseUserActionResponse): userAction is NonNullNewValueAction => - userAction.new_val_connector_id != null && userAction.new_value != null; - const getCommentContent = (comment: CommentResponse): string => { if (comment.type === CommentType.user) { return comment.comment; diff --git a/x-pack/plugins/cases/server/client/user_actions/get.test.ts b/x-pack/plugins/cases/server/client/user_actions/get.test.ts deleted file mode 100644 index 302e069cde4d1..0000000000000 --- a/x-pack/plugins/cases/server/client/user_actions/get.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { CaseUserActionResponse, SUB_CASE_SAVED_OBJECT } from '../../../common'; -import { SUB_CASE_REF_NAME } from '../../common'; -import { extractAttributesWithoutSubCases } from './get'; - -describe('get', () => { - describe('extractAttributesWithoutSubCases', () => { - it('returns an empty array when given an empty array', () => { - expect( - extractAttributesWithoutSubCases({ ...getFindResponseFields(), saved_objects: [] }) - ).toEqual([]); - }); - - it('filters out saved objects with a sub case reference', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], - id: 'b', - score: 0, - attributes: {} as CaseUserActionResponse, - }, - ], - }) - ).toEqual([]); - }); - - it('filters out saved objects with a sub case reference with other references', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [ - { name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }, - { name: 'a', type: 'b', id: '5' }, - ], - id: 'b', - score: 0, - attributes: {} as CaseUserActionResponse, - }, - ], - }) - ).toEqual([]); - }); - - it('keeps saved objects that do not have a sub case reference', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [ - { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, - { name: 'a', type: 'b', id: '5' }, - ], - id: 'b', - score: 0, - attributes: { field: '1' } as unknown as CaseUserActionResponse, - }, - ], - }) - ).toEqual([{ field: '1' }]); - }); - - it('filters multiple saved objects correctly', () => { - expect( - extractAttributesWithoutSubCases({ - ...getFindResponseFields(), - saved_objects: [ - { - type: 'a', - references: [ - { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, - { name: 'a', type: 'b', id: '5' }, - ], - id: 'b', - score: 0, - attributes: { field: '2' } as unknown as CaseUserActionResponse, - }, - { - type: 'a', - references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], - id: 'b', - score: 0, - attributes: { field: '1' } as unknown as CaseUserActionResponse, - }, - ], - }) - ).toEqual([{ field: '2' }]); - }); - }); -}); - -const getFindResponseFields = () => ({ page: 1, per_page: 1, total: 0 }); diff --git a/x-pack/plugins/cases/server/client/user_actions/get.ts b/x-pack/plugins/cases/server/client/user_actions/get.ts index 660cf1b6a336e..2a6608014c800 100644 --- a/x-pack/plugins/cases/server/client/user_actions/get.ts +++ b/x-pack/plugins/cases/server/client/user_actions/get.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { SavedObjectReference, SavedObjectsFindResponse } from 'kibana/server'; import { + CASE_COMMENT_SAVED_OBJECT, + CASE_SAVED_OBJECT, CaseUserActionsResponse, CaseUserActionsResponseRt, SUB_CASE_SAVED_OBJECT, - CaseUserActionResponse, } from '../../../common'; -import { createCaseError, checkEnabledCaseConnectorOrThrow, SUB_CASE_REF_NAME } from '../../common'; +import { createCaseError, checkEnabledCaseConnectorOrThrow } from '../../common'; import { CasesClientArgs } from '..'; import { Operations } from '../../authorization'; import { UserActionGet } from './client'; @@ -40,12 +40,23 @@ export const get = async ( operation: Operations.getUserActions, }); - const resultsToEncode = - subCaseId == null - ? extractAttributesWithoutSubCases(userActions) - : extractAttributes(userActions); - - return CaseUserActionsResponseRt.encode(resultsToEncode); + return CaseUserActionsResponseRt.encode( + userActions.saved_objects.reduce((acc, ua) => { + if (subCaseId == null && ua.references.some((uar) => uar.type === SUB_CASE_SAVED_OBJECT)) { + return acc; + } + return [ + ...acc, + { + ...ua.attributes, + action_id: ua.id, + case_id: ua.references.find((r) => r.type === CASE_SAVED_OBJECT)?.id ?? '', + comment_id: ua.references.find((r) => r.type === CASE_COMMENT_SAVED_OBJECT)?.id ?? null, + sub_case_id: ua.references.find((r) => r.type === SUB_CASE_SAVED_OBJECT)?.id ?? '', + }, + ]; + }, []) + ); } catch (error) { throw createCaseError({ message: `Failed to retrieve user actions case id: ${caseId} sub case id: ${subCaseId}: ${error}`, @@ -54,21 +65,3 @@ export const get = async ( }); } }; - -export function extractAttributesWithoutSubCases( - userActions: SavedObjectsFindResponse -): CaseUserActionsResponse { - // exclude user actions relating to sub cases from the results - const hasSubCaseReference = (references: SavedObjectReference[]) => - references.find((ref) => ref.type === SUB_CASE_SAVED_OBJECT && ref.name === SUB_CASE_REF_NAME); - - return userActions.saved_objects - .filter((so) => !hasSubCaseReference(so.references)) - .map((so) => so.attributes); -} - -function extractAttributes( - userActions: SavedObjectsFindResponse -): CaseUserActionsResponse { - return userActions.saved_objects.map((so) => so.attributes); -} diff --git a/x-pack/plugins/cases/server/common/constants.ts b/x-pack/plugins/cases/server/common/constants.ts index eba0a64a5c0be..1f6af310d6ece 100644 --- a/x-pack/plugins/cases/server/common/constants.ts +++ b/x-pack/plugins/cases/server/common/constants.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../common'; - /** * The name of the saved object reference indicating the action connector ID. This is stored in the Saved Object reference * field's name property. @@ -17,30 +15,3 @@ export const CONNECTOR_ID_REFERENCE_NAME = 'connectorId'; * The name of the saved object reference indicating the action connector ID that was used to push a case. */ export const PUSH_CONNECTOR_ID_REFERENCE_NAME = 'pushConnectorId'; - -/** - * The name of the saved object reference indicating the action connector ID that was used for - * adding a connector, or updating the existing connector for a user action's old_value field. - */ -export const USER_ACTION_OLD_ID_REF_NAME = 'oldConnectorId'; - -/** - * The name of the saved object reference indicating the action connector ID that was used for pushing a case, - * for a user action's old_value field. - */ -export const USER_ACTION_OLD_PUSH_ID_REF_NAME = 'oldPushConnectorId'; - -/** - * The name of the saved object reference indicating the caseId reference - */ -export const CASE_REF_NAME = `associated-${CASE_SAVED_OBJECT}`; - -/** - * The name of the saved object reference indicating the commentId reference - */ -export const COMMENT_REF_NAME = `associated-${CASE_COMMENT_SAVED_OBJECT}`; - -/** - * The name of the saved object reference indicating the subCaseId reference - */ -export const SUB_CASE_REF_NAME = `associated-${SUB_CASE_SAVED_OBJECT}`; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index 9020f65ae352c..bca12a86a544e 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -30,324 +30,322 @@ const create_7_14_0_case = ({ }, }); -describe('case migrations', () => { - describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector.id is none', () => { - const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('does not create a reference when the connector is undefined', () => { - const caseSavedObject = create_7_14_0_case(); +describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector.id is none', () => { + const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the connector is undefined', () => { + const caseSavedObject = create_7_14_0_case(); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); + it('sets the connector to the default none connector if the connector.id is undefined', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + fields: null, + name: ConnectorTypes.jira, + type: ConnectorTypes.jira, + } as ESCaseConnectorWithId, }); - it('sets the connector to the default none connector if the connector.id is undefined', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - fields: null, - name: ConnectorTypes.jira, - type: ConnectorTypes.jira, - } as ESCaseConnectorWithId, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the external_service is null', () => { - const caseSavedObject = create_7_14_0_case({ externalService: null }); + it('does not create a reference when the external_service is null', () => { + const caseSavedObject = create_7_14_0_case({ externalService: null }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); + + it('does not create a reference when the external_service is undefined and sets external_service to null', () => { + const caseSavedObject = create_7_14_0_case(); - it('does not create a reference when the external_service is undefined and sets external_service to null', () => { - const caseSavedObject = create_7_14_0_case(); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); + it('does not create a reference when the external_service.connector_id is none', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: createExternalService({ connector_id: noneConnectorId }), }); - it('does not create a reference when the external_service.connector_id is none', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: createExternalService({ connector_id: noneConnectorId }), - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('preserves the existing references when migrating', () => { + const caseSavedObject = { + ...create_7_14_0_case(), + references: [{ id: '1', name: 'awesome', type: 'hello' }], + }; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); + "id": "1", + "name": "awesome", + "type": "hello", + }, + ] + `); + }); - it('preserves the existing references when migrating', () => { - const caseSavedObject = { - ...create_7_14_0_case(), - references: [{ id: '1', name: 'awesome', type: 'hello' }], - }; - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "awesome", - "type": "hello", - }, - ] - `); + it('creates a connector reference and removes the connector.id field', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }, }); - it('creates a connector reference and removes the connector.id field', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", }, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + ] + `); + }); - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", - }, - ] - `); + it('creates a push connector reference and removes the connector_id field', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + }, }); - it('creates a push connector reference and removes the connector_id field', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", }, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: null, - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, + "id": "100", + "name": "pushConnectorId", + "type": "action", }, - }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + ] + `); + }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); + it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: null, + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + }, }); - it('migrates both connector and external_service when provided', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", }, - }); + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('migrates both connector and external_service when provided', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, + }, + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }, + }); - expect(migratedConnector.references.length).toBe(2); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(2); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + "id": "123", + "name": "connectorId", + "type": "action", + }, Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", - }, - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index 80f02fa3bf6a6..bffd4171270ef 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -14,11 +14,7 @@ import { } from '../../../../../../src/core/server'; import { ESConnectorFields } from '../../services'; import { ConnectorTypes, CaseType } from '../../../common'; -import { - transformConnectorIdToReference, - transformPushConnectorIdToReference, -} from '../../services/user_actions/transform'; -import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../../common'; +import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; interface UnsanitizedCaseConnector { connector_id: string; @@ -54,13 +50,11 @@ export const caseConnectorIdMigration = ( // removing the id field since it will be stored in the references instead const { connector, external_service, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( - CONNECTOR_ID_REFERENCE_NAME, - connector - ); + const { transformedConnector, references: connectorReferences } = + transformConnectorIdToReference(connector); const { transformedPushConnector, references: pushConnectorReferences } = - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, external_service); + transformPushConnectorIdToReference(external_service); const { references = [] } = doc; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts index 9ae0285598dbf..4467b499817a5 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts @@ -40,89 +40,87 @@ const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ }, }); -describe('configuration migrations', () => { - describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector ID is none', () => { - const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); +describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector ID is none', () => { + const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('creates a reference using the connector id', () => { - const configureSavedObject = create_7_14_0_configSchema({ - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }); + it('creates a reference using the connector id', () => { + const configureSavedObject = create_7_14_0_configSchema({ + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references).toEqual([ - { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, - ]); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references).toEqual([ + { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, + ]); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('returns the other attributes and default connector when the connector is undefined', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('returns the other attributes and default connector when the connector is undefined', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "closure_type": "close-by-pushing", - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - "created_at": "2020-04-09T09:43:51.778Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "owner": "securitySolution", - "updated_at": "2020-04-09T09:43:51.778Z", - "updated_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, + expect(migratedConnector).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "closure_type": "close-by-pushing", + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", }, - "id": "1", - "references": Array [], - "type": "cases-configure", - } - `); - }); + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + "owner": "securitySolution", + "updated_at": "2020-04-09T09:43:51.778Z", + "updated_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + "id": "1", + "references": Array [], + "type": "cases-configure", + } + `); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts index f9937253e0d2f..527d40fca2e35 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts @@ -13,8 +13,7 @@ import { } from '../../../../../../src/core/server'; import { ConnectorTypes } from '../../../common'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { transformConnectorIdToReference } from '../../services/user_actions/transform'; -import { CONNECTOR_ID_REFERENCE_NAME } from '../../common'; +import { transformConnectorIdToReference } from './utils'; interface UnsanitizedConfigureConnector { connector_id: string; @@ -35,10 +34,8 @@ export const configureConnectorIdMigration = ( ): SavedObjectSanitizedDoc => { // removing the id field since it will be stored in the references instead const { connector, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( - CONNECTOR_ID_REFERENCE_NAME, - connector - ); + const { transformedConnector, references: connectorReferences } = + transformConnectorIdToReference(connector); const { references = [] } = doc; return { diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts index a4f50fbfcde5b..a445131073d19 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts @@ -5,17 +5,24 @@ * 2.0. */ +/* eslint-disable @typescript-eslint/naming-convention */ + import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, } from '../../../../../../src/core/server'; -import { SECURITY_SOLUTION_OWNER } from '../../../common'; +import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; export { caseMigrations } from './cases'; export { configureMigrations } from './configuration'; -export { userActionsMigrations } from './user_actions'; export { createCommentsMigrations, CreateCommentsMigrationsDeps } from './comments'; +interface UserActions { + action_field: string[]; + new_value: string; + old_value: string; +} + export interface SanitizedCaseOwner { owner: string; } @@ -31,6 +38,52 @@ export const addOwnerToSO = >( references: doc.references || [], }); +export const userActionsMigrations = { + '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { + const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; + + if ( + action_field == null || + !Array.isArray(action_field) || + action_field[0] !== 'connector_id' + ) { + return { ...doc, references: doc.references || [] }; + } + + return { + ...doc, + attributes: { + ...restAttributes, + action_field: ['connector'], + new_value: + new_value != null + ? JSON.stringify({ + id: new_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : new_value, + old_value: + old_value != null + ? JSON.stringify({ + id: old_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : old_value, + }, + references: doc.references || [], + }; + }, + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc> + ): SavedObjectSanitizedDoc => { + return addOwnerToSO(doc); + }, +}; + export const connectorMappingsMigrations = { '7.14.0': ( doc: SavedObjectUnsanitizedDoc> diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts deleted file mode 100644 index e71c8db0db694..0000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { SavedObjectMigrationContext, SavedObjectSanitizedDoc } from 'kibana/server'; -import { migrationMocks } from 'src/core/server/mocks'; -import { CaseUserActionAttributes, CASE_USER_ACTION_SAVED_OBJECT } from '../../../common'; -import { - createConnectorObject, - createExternalService, - createJiraConnector, -} from '../../services/test_utils'; -import { userActionsConnectorIdMigration } from './user_actions'; - -const create_7_14_0_userAction = ( - params: { - action?: string; - action_field?: string[]; - new_value?: string | null | object; - old_value?: string | null | object; - } = {} -) => { - const { new_value, old_value, ...restParams } = params; - - return { - type: CASE_USER_ACTION_SAVED_OBJECT, - id: '1', - attributes: { - ...restParams, - new_value: new_value && typeof new_value === 'object' ? JSON.stringify(new_value) : new_value, - old_value: old_value && typeof old_value === 'object' ? JSON.stringify(old_value) : old_value, - }, - }; -}; - -describe('user action migrations', () => { - describe('7.15.0 connector ID migration', () => { - describe('userActionsConnectorIdMigration', () => { - let context: jest.Mocked; - - beforeEach(() => { - context = migrationMocks.createContext(); - }); - - describe('push user action', () => { - it('extracts the external_service connector_id to references for a new pushed user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: createExternalService(), - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedExternalService = JSON.parse(migratedUserAction.attributes.new_value!); - expect(parsedExternalService).not.toHaveProperty('connector_id'); - expect(parsedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - - expect(migratedUserAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - - expect(migratedUserAction.attributes.old_value).toBeNull(); - }); - - it('extract the external_service connector_id to references for new and old pushed user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: createExternalService(), - old_value: createExternalService({ connector_id: '5' }), - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewExternalService).not.toHaveProperty('connector_id'); - expect(parsedOldExternalService).not.toHaveProperty('connector_id'); - expect(migratedUserAction.references).toEqual([ - { id: '100', name: 'pushConnectorId', type: 'action' }, - { id: '5', name: 'oldPushConnectorId', type: 'action' }, - ]); - }); - - it('preserves the existing references after extracting the external_service connector_id field', () => { - const userAction = { - ...create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: createExternalService(), - old_value: createExternalService({ connector_id: '5' }), - }), - references: [{ id: '500', name: 'someReference', type: 'ref' }], - }; - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewExternalService).not.toHaveProperty('connector_id'); - expect(parsedOldExternalService).not.toHaveProperty('connector_id'); - expect(migratedUserAction.references).toEqual([ - { id: '500', name: 'someReference', type: 'ref' }, - { id: '100', name: 'pushConnectorId', type: 'action' }, - { id: '5', name: 'oldPushConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid push user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['invalid field'], - new_value: 'hello', - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction.attributes.old_value).toBeNull(); - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "push-to-service", - "action_field": Array [ - "invalid field", - ], - "new_value": "hello", - "old_value": null, - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('leaves the object unmodified when it new value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: '{a', - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction.attributes.old_value).toBeNull(); - expect(migratedUserAction.attributes.new_value).toEqual('{a'); - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "push-to-service", - "action_field": Array [ - "pushed", - ], - "new_value": "{a", - "old_value": null, - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('logs an error new value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'push-to-service', - action_field: ['pushed'], - new_value: '{a', - old_value: null, - }); - - userActionsConnectorIdMigration(userAction, context); - - expect(context.log.error).toHaveBeenCalled(); - }); - }); - - describe('update connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); - expect(parsedConnector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - - expect(migratedUserAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(migratedUserAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: { ...createJiraConnector(), id: '5' }, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); - - expect(parsedNewConnector).not.toHaveProperty('id'); - expect(parsedOldConnector).not.toHaveProperty('id'); - - expect(migratedUserAction.references).toEqual([ - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('preserves the existing references after extracting the connector.id field', () => { - const userAction = { - ...create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: { ...createJiraConnector(), id: '5' }, - }), - references: [{ id: '500', name: 'someReference', type: 'ref' }], - }; - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewConnectorId).not.toHaveProperty('id'); - expect(parsedOldConnectorId).not.toHaveProperty('id'); - expect(migratedUserAction.references).toEqual([ - { id: '500', name: 'someReference', type: 'ref' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['invalid action'], - new_value: 'new json value', - old_value: 'old value', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "update", - "action_field": Array [ - "invalid action", - ], - "new_value": "new json value", - "old_value": "old value", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('leaves the object unmodified when old_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: '{}', - old_value: '{b', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "update", - "action_field": Array [ - "connector", - ], - "new_value": "{}", - "old_value": "{b", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('logs an error message when old_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'update', - action_field: ['connector'], - new_value: createJiraConnector(), - old_value: '{b', - }); - - userActionsConnectorIdMigration(userAction, context); - - expect(context.log.error).toHaveBeenCalled(); - }); - }); - - describe('create connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: createConnectorObject(), - old_value: null, - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); - expect(parsedConnector.connector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - - expect(migratedUserAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(migratedUserAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: createConnectorObject(), - old_value: createConnectorObject({ id: '5' }), - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); - - expect(parsedNewConnector.connector).not.toHaveProperty('id'); - expect(parsedOldConnector.connector).not.toHaveProperty('id'); - - expect(migratedUserAction.references).toEqual([ - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('preserves the existing references after extracting the connector.id field', () => { - const userAction = { - ...create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: createConnectorObject(), - old_value: createConnectorObject({ id: '5' }), - }), - references: [{ id: '500', name: 'someReference', type: 'ref' }], - }; - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); - const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); - - expect(parsedNewConnectorId.connector).not.toHaveProperty('id'); - expect(parsedOldConnectorId.connector).not.toHaveProperty('id'); - expect(migratedUserAction.references).toEqual([ - { id: '500', name: 'someReference', type: 'ref' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['invalid action'], - new_value: 'new json value', - old_value: 'old value', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "create", - "action_field": Array [ - "invalid action", - ], - "new_value": "new json value", - "old_value": "old value", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('leaves the object unmodified when new_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: 'new json value', - old_value: 'old value', - }); - - const migratedUserAction = userActionsConnectorIdMigration( - userAction, - context - ) as SavedObjectSanitizedDoc; - - expect(migratedUserAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "create", - "action_field": Array [ - "connector", - ], - "new_value": "new json value", - "old_value": "old value", - }, - "id": "1", - "references": Array [], - "type": "cases-user-actions", - } - `); - }); - - it('logs an error message when new_value is invalid json', () => { - const userAction = create_7_14_0_userAction({ - action: 'create', - action_field: ['connector'], - new_value: 'new json value', - old_value: 'old value', - }); - - userActionsConnectorIdMigration(userAction, context); - - expect(context.log.error).toHaveBeenCalled(); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts deleted file mode 100644 index ed6b57ef647f9..0000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { - SavedObjectUnsanitizedDoc, - SavedObjectSanitizedDoc, - SavedObjectMigrationContext, - LogMeta, -} from '../../../../../../src/core/server'; -import { ConnectorTypes, isCreateConnector, isPush, isUpdateConnector } from '../../../common'; - -import { extractConnectorIdFromJson } from '../../services/user_actions/transform'; -import { UserActionFieldType } from '../../services/user_actions/types'; - -interface UserActions { - action_field: string[]; - new_value: string; - old_value: string; -} - -interface UserActionUnmigratedConnectorDocument { - action?: string; - action_field?: string[]; - new_value?: string | null; - old_value?: string | null; -} - -interface UserActionLogMeta extends LogMeta { - migrations: { userAction: { id: string } }; -} - -export function userActionsConnectorIdMigration( - doc: SavedObjectUnsanitizedDoc, - context: SavedObjectMigrationContext -): SavedObjectSanitizedDoc { - const originalDocWithReferences = { ...doc, references: doc.references ?? [] }; - - if (!isConnectorUserAction(doc.attributes.action, doc.attributes.action_field)) { - return originalDocWithReferences; - } - - try { - return formatDocumentWithConnectorReferences(doc); - } catch (error) { - logError(doc.id, context, error); - - return originalDocWithReferences; - } -} - -function isConnectorUserAction(action?: string, actionFields?: string[]): boolean { - return ( - isCreateConnector(action, actionFields) || - isUpdateConnector(action, actionFields) || - isPush(action, actionFields) - ); -} - -function formatDocumentWithConnectorReferences( - doc: SavedObjectUnsanitizedDoc -): SavedObjectSanitizedDoc { - const { new_value, old_value, action, action_field, ...restAttributes } = doc.attributes; - const { references = [] } = doc; - - const { transformedActionDetails: transformedNewValue, references: newValueConnectorRefs } = - extractConnectorIdFromJson({ - action, - actionFields: action_field, - actionDetails: new_value, - fieldType: UserActionFieldType.New, - }); - - const { transformedActionDetails: transformedOldValue, references: oldValueConnectorRefs } = - extractConnectorIdFromJson({ - action, - actionFields: action_field, - actionDetails: old_value, - fieldType: UserActionFieldType.Old, - }); - - return { - ...doc, - attributes: { - ...restAttributes, - action, - action_field, - new_value: transformedNewValue, - old_value: transformedOldValue, - }, - references: [...references, ...newValueConnectorRefs, ...oldValueConnectorRefs], - }; -} - -function logError(id: string, context: SavedObjectMigrationContext, error: Error) { - context.log.error( - `Failed to migrate user action connector doc id: ${id} version: ${context.migrationVersion} error: ${error.message}`, - { - migrations: { - userAction: { - id, - }, - }, - } - ); -} - -export const userActionsMigrations = { - '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { - const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; - - if ( - action_field == null || - !Array.isArray(action_field) || - action_field[0] !== 'connector_id' - ) { - return { ...doc, references: doc.references || [] }; - } - - return { - ...doc, - attributes: { - ...restAttributes, - action_field: ['connector'], - new_value: - new_value != null - ? JSON.stringify({ - id: new_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : new_value, - old_value: - old_value != null - ? JSON.stringify({ - id: old_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : old_value, - }, - references: doc.references || [], - }; - }, - '7.14.0': ( - doc: SavedObjectUnsanitizedDoc> - ): SavedObjectSanitizedDoc => { - return addOwnerToSO(doc); - }, - '7.16.0': userActionsConnectorIdMigration, -}; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts new file mode 100644 index 0000000000000..f591bef6b3236 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { noneConnectorId } from '../../../common'; +import { createExternalService, createJiraConnector } from '../../services/test_utils'; +import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; + +describe('migration utils', () => { + describe('transformConnectorIdToReference', () => { + it('returns the default none connector when the connector is undefined', () => { + expect(transformConnectorIdToReference().transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is undefined', () => { + expect(transformConnectorIdToReference({ id: undefined }).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none', () => { + expect(transformConnectorIdToReference({ id: noneConnectorId }).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none and other fields are defined', () => { + expect( + transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns an empty array of references when the connector is undefined', () => { + expect(transformConnectorIdToReference().references.length).toBe(0); + }); + + it('returns an empty array of references when the id is undefined', () => { + expect(transformConnectorIdToReference({ id: undefined }).references.length).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector', () => { + expect(transformConnectorIdToReference({ id: noneConnectorId }).references.length).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector and other fields are defined', () => { + expect( + transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) + .references.length + ).toBe(0); + }); + + it('returns a jira connector', () => { + const transformedFields = transformConnectorIdToReference(createJiraConnector()); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('transformPushConnectorIdToReference', () => { + it('sets external_service to null when it is undefined', () => { + expect(transformPushConnectorIdToReference().transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('sets external_service to null when it is null', () => { + expect(transformPushConnectorIdToReference(null).transformedPushConnector) + .toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference({ connector_id: undefined }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is null', () => { + expect(transformPushConnectorIdToReference({ connector_id: null }).transformedPushConnector) + .toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is none', () => { + const otherFields = { otherField: 'hi' }; + + expect( + transformPushConnectorIdToReference({ ...otherFields, connector_id: noneConnectorId }) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "otherField": "hi", + }, + } + `); + }); + + it('returns an empty array of references when the external_service is undefined', () => { + expect(transformPushConnectorIdToReference().references.length).toBe(0); + }); + + it('returns an empty array of references when the external_service is null', () => { + expect(transformPushConnectorIdToReference(null).references.length).toBe(0); + }); + + it('returns an empty array of references when the connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference({ connector_id: undefined }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is null', () => { + expect( + transformPushConnectorIdToReference({ connector_id: undefined }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector', () => { + expect( + transformPushConnectorIdToReference({ connector_id: noneConnectorId }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { + expect( + transformPushConnectorIdToReference({ + ...createExternalService(), + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns the external_service connector', () => { + const transformedFields = transformPushConnectorIdToReference(createExternalService()); + expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts new file mode 100644 index 0000000000000..0100a04cde679 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { noneConnectorId } from '../../../common'; +import { SavedObjectReference } from '../../../../../../src/core/server'; +import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; +import { + getNoneCaseConnector, + CONNECTOR_ID_REFERENCE_NAME, + PUSH_CONNECTOR_ID_REFERENCE_NAME, +} from '../../common'; + +export const transformConnectorIdToReference = (connector?: { + id?: string; +}): { transformedConnector: Record; references: SavedObjectReference[] } => { + const { id: connectorId, ...restConnector } = connector ?? {}; + + const references = createConnectorReference( + connectorId, + ACTION_SAVED_OBJECT_TYPE, + CONNECTOR_ID_REFERENCE_NAME + ); + + const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); + const connectorFieldsToReturn = + connector && references.length > 0 ? restConnector : restNoneConnector; + + return { + transformedConnector: { + connector: connectorFieldsToReturn, + }, + references, + }; +}; + +const createConnectorReference = ( + id: string | null | undefined, + type: string, + name: string +): SavedObjectReference[] => { + return id && id !== noneConnectorId + ? [ + { + id, + type, + name, + }, + ] + : []; +}; + +export const transformPushConnectorIdToReference = ( + external_service?: { connector_id?: string | null } | null +): { transformedPushConnector: Record; references: SavedObjectReference[] } => { + const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; + + const references = createConnectorReference( + pushConnectorId, + ACTION_SAVED_OBJECT_TYPE, + PUSH_CONNECTOR_ID_REFERENCE_NAME + ); + + return { + transformedPushConnector: { external_service: external_service ? restExternalService : null }, + references, + }; +}; diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 8c71abe5bff4f..18f4ff867cfa9 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -40,7 +40,6 @@ import { createSavedObjectReferences, createCaseSavedObjectResponse, basicCaseFields, - createSOFindResponse, } from '../test_utils'; import { ESCaseAttributes } from './types'; @@ -88,6 +87,13 @@ const createFindSO = ( score: 0, }); +const createSOFindResponse = (savedObjects: Array>) => ({ + saved_objects: savedObjects, + total: savedObjects.length, + per_page: savedObjects.length, + page: 1, +}); + const createCaseUpdateParams = ( connector?: CaseConnector, externalService?: CaseFullExternalService diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index 07743eda61212..b712ea07f9c71 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference, SavedObjectsFindResult } from 'kibana/server'; +import { SavedObject, SavedObjectReference } from 'kibana/server'; import { ESConnectorFields } from '.'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../common'; import { @@ -54,7 +54,7 @@ export const createESJiraConnector = ( { key: 'parent', value: '2' }, ], type: ConnectorTypes.jira, - ...overrides, + ...(overrides && { ...overrides }), }; }; @@ -94,7 +94,7 @@ export const createExternalService = ( email: 'testemail@elastic.co', username: 'elastic', }, - ...overrides, + ...(overrides && { ...overrides }), }); export const basicCaseFields = { @@ -198,14 +198,3 @@ export const createSavedObjectReferences = ({ ] : []), ]; - -export const createConnectorObject = (overrides?: Partial) => ({ - connector: { ...createJiraConnector(), ...overrides }, -}); - -export const createSOFindResponse = (savedObjects: Array>) => ({ - saved_objects: savedObjects, - total: savedObjects.length, - per_page: savedObjects.length, - page: 1, -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts deleted file mode 100644 index 7bcbaf58d0f6e..0000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { UserActionField } from '../../../common'; -import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; -import { buildCaseUserActionItem } from './helpers'; - -const defaultFields = () => ({ - actionAt: 'now', - actionBy: { - email: 'a', - full_name: 'j', - username: '1', - }, - caseId: '300', - owner: 'securitySolution', -}); - -describe('user action helpers', () => { - describe('buildCaseUserActionItem', () => { - describe('push user action', () => { - it('extracts the external_service connector_id to references for a new pushed user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'push-to-service', - fields: ['pushed'], - newValue: createExternalService(), - }); - - const parsedExternalService = JSON.parse(userAction.attributes.new_value!); - expect(parsedExternalService).not.toHaveProperty('connector_id'); - expect(parsedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - - expect(userAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - - expect(userAction.attributes.old_value).toBeNull(); - }); - - it('extract the external_service connector_id to references for new and old pushed user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'push-to-service', - fields: ['pushed'], - newValue: createExternalService(), - oldValue: createExternalService({ connector_id: '5' }), - }); - - const parsedNewExternalService = JSON.parse(userAction.attributes.new_value!); - const parsedOldExternalService = JSON.parse(userAction.attributes.old_value!); - - expect(parsedNewExternalService).not.toHaveProperty('connector_id'); - expect(parsedOldExternalService).not.toHaveProperty('connector_id'); - expect(userAction.references).toEqual([ - { id: '300', name: 'associated-cases', type: 'cases' }, - { id: '100', name: 'pushConnectorId', type: 'action' }, - { id: '5', name: 'oldPushConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid push user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'push-to-service', - fields: ['invalid field'] as unknown as UserActionField, - newValue: 'hello' as unknown as Record, - }); - - expect(userAction.attributes.old_value).toBeNull(); - expect(userAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "push-to-service", - "action_at": "now", - "action_by": Object { - "email": "a", - "full_name": "j", - "username": "1", - }, - "action_field": Array [ - "invalid field", - ], - "new_value": "hello", - "old_value": null, - "owner": "securitySolution", - }, - "references": Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - ], - } - `); - }); - }); - - describe('update connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'update', - fields: ['connector'], - newValue: createJiraConnector(), - }); - - const parsedConnector = JSON.parse(userAction.attributes.new_value!); - expect(parsedConnector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - - expect(userAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(userAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'update', - fields: ['connector'], - newValue: createJiraConnector(), - oldValue: { ...createJiraConnector(), id: '5' }, - }); - - const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); - - expect(parsedNewConnector).not.toHaveProperty('id'); - expect(parsedOldConnector).not.toHaveProperty('id'); - - expect(userAction.references).toEqual([ - { id: '300', name: 'associated-cases', type: 'cases' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'update', - fields: ['invalid field'] as unknown as UserActionField, - newValue: 'hello' as unknown as Record, - oldValue: 'old value' as unknown as Record, - }); - - expect(userAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "update", - "action_at": "now", - "action_by": Object { - "email": "a", - "full_name": "j", - "username": "1", - }, - "action_field": Array [ - "invalid field", - ], - "new_value": "hello", - "old_value": "old value", - "owner": "securitySolution", - }, - "references": Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - ], - } - `); - }); - }); - - describe('create connector user action', () => { - it('extracts the connector id to references for a new create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'create', - fields: ['connector'], - newValue: createConnectorObject(), - }); - - const parsedConnector = JSON.parse(userAction.attributes.new_value!); - expect(parsedConnector.connector).not.toHaveProperty('id'); - expect(parsedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - - expect(userAction.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - - expect(userAction.attributes.old_value).toBeNull(); - }); - - it('extracts the connector id to references for a new and old create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'create', - fields: ['connector'], - newValue: createConnectorObject(), - oldValue: createConnectorObject({ id: '5' }), - }); - - const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); - const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); - - expect(parsedNewConnector.connector).not.toHaveProperty('id'); - expect(parsedOldConnector.connector).not.toHaveProperty('id'); - - expect(userAction.references).toEqual([ - { id: '300', name: 'associated-cases', type: 'cases' }, - { id: '1', name: 'connectorId', type: 'action' }, - { id: '5', name: 'oldConnectorId', type: 'action' }, - ]); - }); - - it('leaves the object unmodified when it is not a valid create connector user action', () => { - const userAction = buildCaseUserActionItem({ - ...defaultFields(), - action: 'create', - fields: ['invalid action'] as unknown as UserActionField, - newValue: 'new json value' as unknown as Record, - oldValue: 'old value' as unknown as Record, - }); - - expect(userAction).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "action": "create", - "action_at": "now", - "action_by": Object { - "email": "a", - "full_name": "j", - "username": "1", - }, - "action_field": Array [ - "invalid action", - ], - "new_value": "new json value", - "old_value": "old value", - "owner": "securitySolution", - }, - "references": Array [ - Object { - "id": "300", - "name": "associated-cases", - "type": "cases", - }, - ], - } - `); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index e91b69f0995bd..223e731aa8d9b 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference, SavedObjectsUpdateResponse } from 'kibana/server'; +import { SavedObject, SavedObjectsUpdateResponse } from 'kibana/server'; import { get, isPlainObject, isString } from 'lodash'; import deepEqual from 'fast-deep-equal'; @@ -23,68 +23,8 @@ import { } from '../../../common'; import { isTwoArraysDifference } from '../../client/utils'; import { UserActionItem } from '.'; -import { extractConnectorId } from './transform'; -import { UserActionFieldType } from './types'; -import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -interface BuildCaseUserActionParams { - action: UserAction; - actionAt: string; - actionBy: User; - caseId: string; - owner: string; - fields: UserActionField; - newValue?: Record | string | null; - oldValue?: Record | string | null; - subCaseId?: string; -} - -export const buildCaseUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCaseUserActionParams): UserActionItem => { - const { transformedActionDetails: transformedNewValue, references: newValueReferences } = - extractConnectorId({ - action, - actionFields: fields, - actionDetails: newValue, - fieldType: UserActionFieldType.New, - }); - - const { transformedActionDetails: transformedOldValue, references: oldValueReferences } = - extractConnectorId({ - action, - actionFields: fields, - actionDetails: oldValue, - fieldType: UserActionFieldType.Old, - }); - - return { - attributes: transformNewUserAction({ - actionField: fields, - action, - actionAt, - owner, - ...actionBy, - newValue: transformedNewValue, - oldValue: transformedOldValue, - }), - references: [ - ...createCaseReferences(caseId, subCaseId), - ...newValueReferences, - ...oldValueReferences, - ], - }; -}; - -const transformNewUserAction = ({ +export const transformNewUserAction = ({ actionField, action, actionAt, @@ -115,43 +55,103 @@ const transformNewUserAction = ({ owner, }); -const createCaseReferences = (caseId: string, subCaseId?: string): SavedObjectReference[] => [ - { - type: CASE_SAVED_OBJECT, - name: CASE_REF_NAME, - id: caseId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - name: SUB_CASE_REF_NAME, - id: subCaseId, - }, - ] - : []), -]; +interface BuildCaseUserAction { + action: UserAction; + actionAt: string; + actionBy: User; + caseId: string; + owner: string; + fields: UserActionField | unknown[]; + newValue?: string | unknown; + oldValue?: string | unknown; + subCaseId?: string; +} -interface BuildCommentUserActionItem extends BuildCaseUserActionParams { +interface BuildCommentUserActionItem extends BuildCaseUserAction { commentId: string; } -export const buildCommentUserActionItem = (params: BuildCommentUserActionItem): UserActionItem => { - const { commentId } = params; - const { attributes, references } = buildCaseUserActionItem(params); +export const buildCommentUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + commentId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCommentUserActionItem): UserActionItem => ({ + attributes: transformNewUserAction({ + actionField: fields as UserActionField, + action, + actionAt, + owner, + ...actionBy, + newValue: newValue as string, + oldValue: oldValue as string, + }), + references: [ + { + type: CASE_SAVED_OBJECT, + name: `associated-${CASE_SAVED_OBJECT}`, + id: caseId, + }, + { + type: CASE_COMMENT_SAVED_OBJECT, + name: `associated-${CASE_COMMENT_SAVED_OBJECT}`, + id: commentId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + id: subCaseId, + name: `associated-${SUB_CASE_SAVED_OBJECT}`, + }, + ] + : []), + ], +}); - return { - attributes, - references: [ - ...references, - { - type: CASE_COMMENT_SAVED_OBJECT, - name: COMMENT_REF_NAME, - id: commentId, - }, - ], - }; -}; +export const buildCaseUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCaseUserAction): UserActionItem => ({ + attributes: transformNewUserAction({ + actionField: fields as UserActionField, + action, + actionAt, + owner, + ...actionBy, + newValue: newValue as string, + oldValue: oldValue as string, + }), + references: [ + { + type: CASE_SAVED_OBJECT, + name: `associated-${CASE_SAVED_OBJECT}`, + id: caseId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + name: `associated-${SUB_CASE_SAVED_OBJECT}`, + id: subCaseId, + }, + ] + : []), + ], +}); const userActionFieldsAllowed: UserActionField = [ 'comment', @@ -278,8 +278,8 @@ const buildGenericCaseUserActions = ({ caseId, subCaseId, fields: [field], - newValue: updatedValue, - oldValue: origValue, + newValue: JSON.stringify(updatedValue), + oldValue: JSON.stringify(origValue), owner: originalItem.attributes.owner, }), ]; diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts deleted file mode 100644 index c4a350f4ac015..0000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/index.test.ts +++ /dev/null @@ -1,557 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SavedObject, SavedObjectsFindResult } from 'kibana/server'; -import { transformFindResponseToExternalModel, UserActionItem } from '.'; -import { - CaseUserActionAttributes, - CASE_USER_ACTION_SAVED_OBJECT, - UserAction, - UserActionField, -} from '../../../common'; - -import { - createConnectorObject, - createExternalService, - createJiraConnector, - createSOFindResponse, -} from '../test_utils'; -import { buildCaseUserActionItem, buildCommentUserActionItem } from './helpers'; - -const createConnectorUserAction = ( - subCaseId?: string, - overrides?: Partial -): SavedObject => { - return { - ...createUserActionSO({ - action: 'create', - fields: ['connector'], - newValue: createConnectorObject(), - subCaseId, - }), - ...(overrides && { ...overrides }), - }; -}; - -const updateConnectorUserAction = ({ - subCaseId, - overrides, - oldValue, -}: { - subCaseId?: string; - overrides?: Partial; - oldValue?: string | null | Record; -} = {}): SavedObject => { - return { - ...createUserActionSO({ - action: 'update', - fields: ['connector'], - newValue: createJiraConnector(), - oldValue, - subCaseId, - }), - ...(overrides && { ...overrides }), - }; -}; - -const pushConnectorUserAction = ({ - subCaseId, - overrides, - oldValue, -}: { - subCaseId?: string; - overrides?: Partial; - oldValue?: string | null | Record; -} = {}): SavedObject => { - return { - ...createUserActionSO({ - action: 'push-to-service', - fields: ['pushed'], - newValue: createExternalService(), - oldValue, - subCaseId, - }), - ...(overrides && { ...overrides }), - }; -}; - -const createUserActionFindSO = ( - userAction: SavedObject -): SavedObjectsFindResult => ({ - ...userAction, - score: 0, -}); - -const createUserActionSO = ({ - action, - fields, - subCaseId, - newValue, - oldValue, - attributesOverrides, - commentId, -}: { - action: UserAction; - fields: UserActionField; - subCaseId?: string; - newValue?: string | null | Record; - oldValue?: string | null | Record; - attributesOverrides?: Partial; - commentId?: string; -}): SavedObject => { - const defaultParams = { - action, - actionAt: 'abc', - actionBy: { - email: 'a', - username: 'b', - full_name: 'abc', - }, - caseId: '1', - subCaseId, - fields, - newValue, - oldValue, - owner: 'securitySolution', - }; - - let userAction: UserActionItem; - - if (commentId) { - userAction = buildCommentUserActionItem({ - commentId, - ...defaultParams, - }); - } else { - userAction = buildCaseUserActionItem(defaultParams); - } - - return { - type: CASE_USER_ACTION_SAVED_OBJECT, - id: '100', - attributes: { - ...userAction.attributes, - ...(attributesOverrides && { ...attributesOverrides }), - }, - references: userAction.references, - }; -}; - -describe('CaseUserActionService', () => { - describe('transformFindResponseToExternalModel', () => { - it('does not populate the ids when the response is an empty array', () => { - expect(transformFindResponseToExternalModel(createSOFindResponse([]))).toMatchInlineSnapshot(` - Object { - "page": 1, - "per_page": 0, - "saved_objects": Array [], - "total": 0, - } - `); - }); - - it('preserves the saved object fields and attributes when inject the ids', () => { - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(createConnectorUserAction())]) - ); - - expect(transformed).toMatchInlineSnapshot(` - Object { - "page": 1, - "per_page": 1, - "saved_objects": Array [ - Object { - "attributes": Object { - "action": "create", - "action_at": "abc", - "action_by": Object { - "email": "a", - "full_name": "abc", - "username": "b", - }, - "action_field": Array [ - "connector", - ], - "action_id": "100", - "case_id": "1", - "comment_id": null, - "new_val_connector_id": "1", - "new_value": "{\\"connector\\":{\\"name\\":\\".jira\\",\\"type\\":\\".jira\\",\\"fields\\":{\\"issueType\\":\\"bug\\",\\"priority\\":\\"high\\",\\"parent\\":\\"2\\"}}}", - "old_val_connector_id": null, - "old_value": null, - "owner": "securitySolution", - "sub_case_id": "", - }, - "id": "100", - "references": Array [ - Object { - "id": "1", - "name": "associated-cases", - "type": "cases", - }, - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ], - "score": 0, - "type": "cases-user-actions", - }, - ], - "total": 1, - } - `); - }); - - it('populates the new_val_connector_id for multiple user actions', () => { - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(createConnectorUserAction()), - createUserActionFindSO(createConnectorUserAction()), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); - expect(transformed.saved_objects[1].attributes.new_val_connector_id).toEqual('1'); - }); - - it('populates the old_val_connector_id for multiple user actions', () => { - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO( - createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject(), - }) - ), - createUserActionFindSO( - createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject({ id: '10' }), - }) - ), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); - expect(transformed.saved_objects[1].attributes.old_val_connector_id).toEqual('10'); - }); - - describe('reference ids', () => { - it('sets case_id to an empty string when it cannot find the reference', () => { - const userAction = { - ...createConnectorUserAction(), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.case_id).toEqual(''); - }); - - it('sets comment_id to null when it cannot find the reference', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], commentId: '5' }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); - }); - - it('sets sub_case_id to an empty string when it cannot find the reference', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); - }); - - it('sets case_id correctly when it finds the reference', () => { - const userAction = createConnectorUserAction(); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.case_id).toEqual('1'); - }); - - it('sets comment_id correctly when it finds the reference', () => { - const userAction = createUserActionSO({ - action: 'create', - fields: ['connector'], - commentId: '5', - }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.comment_id).toEqual('5'); - }); - - it('sets sub_case_id correctly when it finds the reference', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), - }; - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.sub_case_id).toEqual('5'); - }); - - it('sets action_id correctly to the saved object id', () => { - const userAction = { - ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), - }; - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.action_id).toEqual('100'); - }); - }); - - describe('create connector', () => { - it('does not populate the new_val_connector_id when it cannot find the reference', () => { - const userAction = { ...createConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when it cannot find the reference', () => { - const userAction = { ...createConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = createConnectorUserAction(); - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject(), - }); - - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('populates the new_val_connector_id', () => { - const userAction = createConnectorUserAction(); - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); - }); - - it('populates the old_val_connector_id', () => { - const userAction = createUserActionSO({ - action: 'create', - fields: ['connector'], - oldValue: createConnectorObject(), - }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); - }); - }); - - describe('update connector', () => { - it('does not populate the new_val_connector_id when it cannot find the reference', () => { - const userAction = { ...updateConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when it cannot find the reference', () => { - const userAction = { - ...updateConnectorUserAction({ oldValue: createJiraConnector() }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = updateConnectorUserAction(); - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); - - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('populates the new_val_connector_id', () => { - const userAction = updateConnectorUserAction(); - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); - }); - - it('populates the old_val_connector_id', () => { - const userAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); - }); - }); - - describe('push connector', () => { - it('does not populate the new_val_connector_id when it cannot find the reference', () => { - const userAction = { ...pushConnectorUserAction(), references: [] }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when it cannot find the reference', () => { - const userAction = { - ...pushConnectorUserAction({ oldValue: createExternalService() }), - references: [], - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = pushConnectorUserAction(); - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); - }); - - it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { - const validUserAction = pushConnectorUserAction({ oldValue: createExternalService() }); - - const invalidUserAction = { - ...validUserAction, - attributes: { ...validUserAction.attributes, action: 'invalid' }, - }; - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([ - createUserActionFindSO(invalidUserAction as SavedObject), - ]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); - }); - - it('populates the new_val_connector_id', () => { - const userAction = pushConnectorUserAction(); - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('100'); - }); - - it('populates the old_val_connector_id', () => { - const userAction = pushConnectorUserAction({ oldValue: createExternalService() }); - - const transformed = transformFindResponseToExternalModel( - createSOFindResponse([createUserActionFindSO(userAction)]) - ); - - expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('100'); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index 4f158862e3d63..b702448165554 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -5,12 +5,7 @@ * 2.0. */ -import { - Logger, - SavedObjectReference, - SavedObjectsFindResponse, - SavedObjectsFindResult, -} from 'kibana/server'; +import { Logger, SavedObjectReference } from 'kibana/server'; import { CASE_SAVED_OBJECT, @@ -18,17 +13,8 @@ import { CaseUserActionAttributes, MAX_DOCS_PER_PAGE, SUB_CASE_SAVED_OBJECT, - CaseUserActionResponse, - CASE_COMMENT_SAVED_OBJECT, - isCreateConnector, - isPush, - isUpdateConnector, } from '../../../common'; import { ClientArgs } from '..'; -import { UserActionFieldType } from './types'; -import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -import { ConnectorIdReferenceName, PushConnectorIdReferenceName } from './transform'; -import { findConnectorIdReference } from '../transform'; interface GetCaseUserActionArgs extends ClientArgs { caseId: string; @@ -47,16 +33,12 @@ interface PostCaseUserActionArgs extends ClientArgs { export class CaseUserActionService { constructor(private readonly log: Logger) {} - public async getAll({ - unsecuredSavedObjectsClient, - caseId, - subCaseId, - }: GetCaseUserActionArgs): Promise> { + public async getAll({ unsecuredSavedObjectsClient, caseId, subCaseId }: GetCaseUserActionArgs) { try { const id = subCaseId ?? caseId; const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT; - const userActions = await unsecuredSavedObjectsClient.find({ + return await unsecuredSavedObjectsClient.find({ type: CASE_USER_ACTION_SAVED_OBJECT, hasReference: { type, id }, page: 1, @@ -64,22 +46,17 @@ export class CaseUserActionService { sortField: 'action_at', sortOrder: 'asc', }); - - return transformFindResponseToExternalModel(userActions); } catch (error) { this.log.error(`Error on GET case user action case id: ${caseId}: ${error}`); throw error; } } - public async bulkCreate({ - unsecuredSavedObjectsClient, - actions, - }: PostCaseUserActionArgs): Promise { + public async bulkCreate({ unsecuredSavedObjectsClient, actions }: PostCaseUserActionArgs) { try { this.log.debug(`Attempting to POST a new case user action`); - await unsecuredSavedObjectsClient.bulkCreate( + return await unsecuredSavedObjectsClient.bulkCreate( actions.map((action) => ({ type: CASE_USER_ACTION_SAVED_OBJECT, ...action })) ); } catch (error) { @@ -88,71 +65,3 @@ export class CaseUserActionService { } } } - -export function transformFindResponseToExternalModel( - userActions: SavedObjectsFindResponse -): SavedObjectsFindResponse { - return { - ...userActions, - saved_objects: userActions.saved_objects.map((so) => ({ - ...so, - ...transformToExternalModel(so), - })), - }; -} - -function transformToExternalModel( - userAction: SavedObjectsFindResult -): SavedObjectsFindResult { - const { references } = userAction; - - const newValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.New, userAction); - const oldValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.Old, userAction); - - const caseId = findReferenceId(CASE_REF_NAME, CASE_SAVED_OBJECT, references) ?? ''; - const commentId = - findReferenceId(COMMENT_REF_NAME, CASE_COMMENT_SAVED_OBJECT, references) ?? null; - const subCaseId = findReferenceId(SUB_CASE_REF_NAME, SUB_CASE_SAVED_OBJECT, references) ?? ''; - - return { - ...userAction, - attributes: { - ...userAction.attributes, - action_id: userAction.id, - case_id: caseId, - comment_id: commentId, - sub_case_id: subCaseId, - new_val_connector_id: newValueConnectorId, - old_val_connector_id: oldValueConnectorId, - }, - }; -} - -function getConnectorIdFromReferences( - fieldType: UserActionFieldType, - userAction: SavedObjectsFindResult -): string | null { - const { - // eslint-disable-next-line @typescript-eslint/naming-convention - attributes: { action, action_field }, - references, - } = userAction; - - if (isCreateConnector(action, action_field) || isUpdateConnector(action, action_field)) { - return findConnectorIdReference(ConnectorIdReferenceName[fieldType], references)?.id ?? null; - } else if (isPush(action, action_field)) { - return ( - findConnectorIdReference(PushConnectorIdReferenceName[fieldType], references)?.id ?? null - ); - } - - return null; -} - -function findReferenceId( - name: string, - type: string, - references: SavedObjectReference[] -): string | undefined { - return references.find((ref) => ref.name === name && ref.type === type)?.id; -} diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts deleted file mode 100644 index 2d28770617094..0000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts +++ /dev/null @@ -1,1246 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { noneConnectorId } from '../../../common'; -import { - CONNECTOR_ID_REFERENCE_NAME, - getNoneCaseConnector, - PUSH_CONNECTOR_ID_REFERENCE_NAME, - USER_ACTION_OLD_ID_REF_NAME, - USER_ACTION_OLD_PUSH_ID_REF_NAME, -} from '../../common'; -import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; -import { - extractConnectorIdHelper, - extractConnectorIdFromJson, - extractConnectorId, - transformConnectorIdToReference, - transformPushConnectorIdToReference, -} from './transform'; -import { UserActionFieldType } from './types'; - -describe('user action transform utils', () => { - describe('transformConnectorIdToReference', () => { - it('returns the default none connector when the connector is undefined', () => { - expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is undefined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none and other fields are defined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { - ...createJiraConnector(), - id: noneConnectorId, - }).transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns an empty array of references when the connector is undefined', () => { - expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).references.length).toBe( - 0 - ); - }); - - it('returns an empty array of references when the id is undefined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }).references - .length - ).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) - .references.length - ).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector and other fields are defined', () => { - expect( - transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { - ...createJiraConnector(), - id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns a jira connector', () => { - const transformedFields = transformConnectorIdToReference( - CONNECTOR_ID_REFERENCE_NAME, - createJiraConnector() - ); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('returns a jira connector with the user action reference name', () => { - const transformedFields = transformConnectorIdToReference( - USER_ACTION_OLD_ID_REF_NAME, - createJiraConnector() - ); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('transformPushConnectorIdToReference', () => { - it('sets external_service to null when it is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('sets external_service to null when it is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: undefined, - }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: null, - }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is none', () => { - const otherFields = { otherField: 'hi' }; - - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - ...otherFields, - connector_id: noneConnectorId, - }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "otherField": "hi", - }, - } - `); - }); - - it('returns an empty array of references when the external_service is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the external_service is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null).references - .length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: undefined, - }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is null', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: undefined, - }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { - expect( - transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { - ...createExternalService(), - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns the external_service connector', () => { - const transformedFields = transformPushConnectorIdToReference( - PUSH_CONNECTOR_ID_REFERENCE_NAME, - createExternalService() - ); - expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns the external_service connector with a user actions reference name', () => { - const transformedFields = transformPushConnectorIdToReference( - USER_ACTION_OLD_PUSH_ID_REF_NAME, - createExternalService() - ); - - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "oldPushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('extractConnectorIdHelper', () => { - it('throws an error when action details has a circular reference', () => { - const circularRef = { prop: {} }; - circularRef.prop = circularRef; - - expect(() => { - extractConnectorIdHelper({ - action: 'a', - actionFields: [], - actionDetails: circularRef, - fieldType: UserActionFieldType.New, - }); - }).toThrow(); - }); - - describe('create action', () => { - it('returns no references and untransformed json when actionDetails is not a valid connector', () => { - expect( - extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns no references and untransformed json when the action is create and action fields does not contain connector', () => { - expect( - extractConnectorIdHelper({ - action: 'create', - actionFields: ['', 'something', 'onnector'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns the stringified json without the id', () => { - const jiraConnector = createConnectorObject(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.New, - }); - - expect(JSON.parse(transformedActionDetails)).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - }); - - it('removes the connector.id when the connector is none', () => { - const connector = { connector: getNoneCaseConnector() }; - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - const parsedJson = JSON.parse(transformedActionDetails); - - expect(parsedJson.connector).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('does not return a reference when the connector is none', () => { - const connector = { connector: getNoneCaseConnector() }; - - const { references } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toEqual([]); - }); - - it('returns a reference to the connector.id', () => { - const connector = createConnectorObject(); - - const { references } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('returns an old reference name to the connector.id', () => { - const connector = createConnectorObject(); - - const { references } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns the transformed connector and the description', () => { - const details = { ...createConnectorObject(), description: 'a description' }; - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'create', - actionFields: ['connector'], - actionDetails: details, - fieldType: UserActionFieldType.Old, - })!; - - const parsedJson = JSON.parse(transformedActionDetails); - - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - "description": "a description", - } - `); - }); - }); - - describe('update action', () => { - it('returns no references and untransformed json when actionDetails is not a valid connector', () => { - expect( - extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns no references and untransformed json when the action is update and action fields does not contain connector', () => { - expect( - extractConnectorIdHelper({ - action: 'update', - actionFields: ['', 'something', 'onnector'], - actionDetails: 5, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "5", - } - `); - }); - - it('returns the stringified json without the id', () => { - const jiraConnector = createJiraConnector(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.New, - }); - - const transformedConnetor = JSON.parse(transformedActionDetails!); - expect(transformedConnetor).not.toHaveProperty('id'); - expect(transformedConnetor).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - }); - - it('returns the stringified json without the id when the connector is none', () => { - const connector = getNoneCaseConnector(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - }); - - const transformedConnetor = JSON.parse(transformedActionDetails); - expect(transformedConnetor).not.toHaveProperty('id'); - expect(transformedConnetor).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('returns a reference to the connector.id', () => { - const jiraConnector = createJiraConnector(); - - const { references } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('does not return a reference when the connector is none', () => { - const connector = getNoneCaseConnector(); - - const { references } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: connector, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toEqual([]); - }); - - it('returns an old reference name to the connector.id', () => { - const jiraConnector = createJiraConnector(); - - const { references } = extractConnectorIdHelper({ - action: 'update', - actionFields: ['connector'], - actionDetails: jiraConnector, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('push action', () => { - it('returns no references and untransformed json when actionDetails is not a valid external_service', () => { - expect( - extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns no references and untransformed json when the action is push-to-service and action fields does not contain pushed', () => { - expect( - extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['', 'something', 'ushed'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }) - ).toMatchInlineSnapshot(` - Object { - "references": Array [], - "transformedActionDetails": "{\\"a\\":\\"hello\\"}", - } - `); - }); - - it('returns the stringified json without the connector_id', () => { - const externalService = createExternalService(); - - const { transformedActionDetails } = extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - }); - - const transformedExternalService = JSON.parse(transformedActionDetails); - expect(transformedExternalService).not.toHaveProperty('connector_id'); - expect(transformedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('returns a reference to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns an old reference name to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorIdHelper({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "oldPushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - }); - - describe('extractConnectorId', () => { - it('returns null when the action details has a circular reference', () => { - const circularRef = { prop: {} }; - circularRef.prop = circularRef; - - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: circularRef, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails).toBeNull(); - expect(references).toEqual([]); - }); - - describe('fails to extract the id', () => { - it('returns a null transformed action details when it is initially null', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: null, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails).toBeNull(); - expect(references).toEqual([]); - }); - - it('returns an undefined transformed action details when it is initially undefined', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: undefined, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails).toBeUndefined(); - expect(references).toEqual([]); - }); - - it('returns a json encoded string and empty references when the action is not a valid connector', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: { a: 'hello' }, - fieldType: UserActionFieldType.New, - }); - - expect(JSON.parse(transformedActionDetails!)).toEqual({ a: 'hello' }); - expect(references).toEqual([]); - }); - - it('returns a json encoded string and empty references when the action details is an invalid object', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'a', - actionFields: ['a'], - actionDetails: 5 as unknown as Record, - fieldType: UserActionFieldType.New, - }); - - expect(transformedActionDetails!).toEqual('5'); - expect(references).toEqual([]); - }); - }); - - describe('create', () => { - it('extracts the connector.id from a new create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'create', - actionFields: ['connector'], - actionDetails: createConnectorObject(), - fieldType: UserActionFieldType.New, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('extracts the connector.id from an old create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'create', - actionFields: ['connector'], - actionDetails: createConnectorObject(), - fieldType: UserActionFieldType.Old, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('update', () => { - it('extracts the connector.id from a new create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'update', - actionFields: ['connector'], - actionDetails: createJiraConnector(), - fieldType: UserActionFieldType.New, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - - it('extracts the connector.id from an old create jira connector to the references', () => { - const { transformedActionDetails, references } = extractConnectorId({ - action: 'update', - actionFields: ['connector'], - actionDetails: createJiraConnector(), - fieldType: UserActionFieldType.Old, - }); - - const parsedJson = JSON.parse(transformedActionDetails!); - - expect(parsedJson).not.toHaveProperty('id'); - expect(parsedJson).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "oldConnectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('push action', () => { - it('returns the stringified json without the connector_id', () => { - const externalService = createExternalService(); - - const { transformedActionDetails } = extractConnectorId({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - }); - - const transformedExternalService = JSON.parse(transformedActionDetails!); - expect(transformedExternalService).not.toHaveProperty('connector_id'); - expect(transformedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('returns a reference to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorId({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - - it('returns a reference to the old action details connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorId({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: externalService, - fieldType: UserActionFieldType.Old, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "oldPushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - }); - - describe('extractConnectorIdFromJson', () => { - describe('fails to extract the id', () => { - it('returns no references and null transformed json when action is undefined', () => { - expect( - extractConnectorIdFromJson({ - actionFields: [], - actionDetails: undefined, - fieldType: UserActionFieldType.New, - }) - ).toEqual({ - transformedActionDetails: undefined, - references: [], - }); - }); - - it('returns no references and undefined transformed json when actionFields is undefined', () => { - expect( - extractConnectorIdFromJson({ action: 'a', fieldType: UserActionFieldType.New }) - ).toEqual({ - transformedActionDetails: undefined, - references: [], - }); - }); - - it('returns no references and undefined transformed json when actionDetails is undefined', () => { - expect( - extractConnectorIdFromJson({ - action: 'a', - actionFields: [], - fieldType: UserActionFieldType.New, - }) - ).toEqual({ - transformedActionDetails: undefined, - references: [], - }); - }); - - it('returns no references and undefined transformed json when actionDetails is null', () => { - expect( - extractConnectorIdFromJson({ - action: 'a', - actionFields: [], - actionDetails: null, - fieldType: UserActionFieldType.New, - }) - ).toEqual({ - transformedActionDetails: null, - references: [], - }); - }); - - it('throws an error when actionDetails is invalid json', () => { - expect(() => - extractConnectorIdFromJson({ - action: 'a', - actionFields: [], - actionDetails: '{a', - fieldType: UserActionFieldType.New, - }) - ).toThrow(); - }); - }); - - describe('create action', () => { - it('returns the stringified json without the id', () => { - const jiraConnector = createConnectorObject(); - - const { transformedActionDetails } = extractConnectorIdFromJson({ - action: 'create', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - }); - - expect(JSON.parse(transformedActionDetails!)).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - }); - - it('returns a reference to the connector.id', () => { - const jiraConnector = createConnectorObject(); - - const { references } = extractConnectorIdFromJson({ - action: 'create', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('update action', () => { - it('returns the stringified json without the id', () => { - const jiraConnector = createJiraConnector(); - - const { transformedActionDetails } = extractConnectorIdFromJson({ - action: 'update', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - }); - - const transformedConnetor = JSON.parse(transformedActionDetails!); - expect(transformedConnetor).not.toHaveProperty('id'); - expect(transformedConnetor).toMatchInlineSnapshot(` - Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - } - `); - }); - - it('returns a reference to the connector.id', () => { - const jiraConnector = createJiraConnector(); - - const { references } = extractConnectorIdFromJson({ - action: 'update', - actionFields: ['connector'], - actionDetails: JSON.stringify(jiraConnector), - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('push action', () => { - it('returns the stringified json without the connector_id', () => { - const externalService = createExternalService(); - - const { transformedActionDetails } = extractConnectorIdFromJson({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: JSON.stringify(externalService), - fieldType: UserActionFieldType.New, - }); - - const transformedExternalService = JSON.parse(transformedActionDetails!); - expect(transformedExternalService).not.toHaveProperty('connector_id'); - expect(transformedExternalService).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('returns a reference to the connector_id', () => { - const externalService = createExternalService(); - - const { references } = extractConnectorIdFromJson({ - action: 'push-to-service', - actionFields: ['pushed'], - actionDetails: JSON.stringify(externalService), - fieldType: UserActionFieldType.New, - })!; - - expect(references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts deleted file mode 100644 index 93595374208a3..0000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/transform.ts +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import * as rt from 'io-ts'; -import { isString } from 'lodash'; - -import { SavedObjectReference } from '../../../../../../src/core/server'; -import { - CaseAttributes, - CaseConnector, - CaseConnectorRt, - CaseExternalServiceBasicRt, - isCreateConnector, - isPush, - isUpdateConnector, - noneConnectorId, -} from '../../../common'; -import { - CONNECTOR_ID_REFERENCE_NAME, - getNoneCaseConnector, - PUSH_CONNECTOR_ID_REFERENCE_NAME, - USER_ACTION_OLD_ID_REF_NAME, - USER_ACTION_OLD_PUSH_ID_REF_NAME, -} from '../../common'; -import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { UserActionFieldType } from './types'; - -/** - * Extracts the connector id from a json encoded string and formats it as a saved object reference. This will remove - * the field it extracted the connector id from. - */ -export function extractConnectorIdFromJson({ - action, - actionFields, - actionDetails, - fieldType, -}: { - action?: string; - actionFields?: string[]; - actionDetails?: string | null; - fieldType: UserActionFieldType; -}): { transformedActionDetails?: string | null; references: SavedObjectReference[] } { - if (!action || !actionFields || !actionDetails) { - return { transformedActionDetails: actionDetails, references: [] }; - } - - const decodedJson = JSON.parse(actionDetails); - - return extractConnectorIdHelper({ - action, - actionFields, - actionDetails: decodedJson, - fieldType, - }); -} - -/** - * Extracts the connector id from an unencoded object and formats it as a saved object reference. - * This will remove the field it extracted the connector id from. - */ -export function extractConnectorId({ - action, - actionFields, - actionDetails, - fieldType, -}: { - action: string; - actionFields: string[]; - actionDetails?: Record | string | null; - fieldType: UserActionFieldType; -}): { - transformedActionDetails?: string | null; - references: SavedObjectReference[]; -} { - if (!actionDetails || isString(actionDetails)) { - // the action was null, undefined, or a regular string so just return it unmodified and not encoded - return { transformedActionDetails: actionDetails, references: [] }; - } - - try { - return extractConnectorIdHelper({ - action, - actionFields, - actionDetails, - fieldType, - }); - } catch (error) { - return { transformedActionDetails: encodeActionDetails(actionDetails), references: [] }; - } -} - -function encodeActionDetails(actionDetails: Record): string | null { - try { - return JSON.stringify(actionDetails); - } catch (error) { - return null; - } -} - -/** - * Internal helper function for extracting the connector id. This is only exported for usage in unit tests. - * This function handles encoding the transformed fields as a json string - */ -export function extractConnectorIdHelper({ - action, - actionFields, - actionDetails, - fieldType, -}: { - action: string; - actionFields: string[]; - actionDetails: unknown; - fieldType: UserActionFieldType; -}): { transformedActionDetails: string; references: SavedObjectReference[] } { - let transformedActionDetails: unknown = actionDetails; - let referencesToReturn: SavedObjectReference[] = []; - - try { - if (isCreateCaseConnector(action, actionFields, actionDetails)) { - const { transformedActionDetails: transformedConnectorPortion, references } = - transformConnectorFromCreateAndUpdateAction(actionDetails.connector, fieldType); - - // the above call only transforms the connector portion of the action details so let's add back - // the rest of the details and we'll overwrite the connector portion when the transformed one - transformedActionDetails = { - ...actionDetails, - ...transformedConnectorPortion, - }; - referencesToReturn = references; - } else if (isUpdateCaseConnector(action, actionFields, actionDetails)) { - const { - transformedActionDetails: { connector: transformedConnector }, - references, - } = transformConnectorFromCreateAndUpdateAction(actionDetails, fieldType); - - transformedActionDetails = transformedConnector; - referencesToReturn = references; - } else if (isPushConnector(action, actionFields, actionDetails)) { - ({ transformedActionDetails, references: referencesToReturn } = - transformConnectorFromPushAction(actionDetails, fieldType)); - } - } catch (error) { - // ignore any errors, we'll just return whatever was passed in for action details in that case - } - - return { - transformedActionDetails: JSON.stringify(transformedActionDetails), - references: referencesToReturn, - }; -} - -function isCreateCaseConnector( - action: string, - actionFields: string[], - actionDetails: unknown -): actionDetails is { connector: CaseConnector } { - try { - const unsafeCase = actionDetails as CaseAttributes; - - return ( - isCreateConnector(action, actionFields) && - unsafeCase.connector !== undefined && - CaseConnectorRt.is(unsafeCase.connector) - ); - } catch { - return false; - } -} - -export const ConnectorIdReferenceName: Record = { - [UserActionFieldType.New]: CONNECTOR_ID_REFERENCE_NAME, - [UserActionFieldType.Old]: USER_ACTION_OLD_ID_REF_NAME, -}; - -function transformConnectorFromCreateAndUpdateAction( - connector: CaseConnector, - fieldType: UserActionFieldType -): { - transformedActionDetails: { connector: unknown }; - references: SavedObjectReference[]; -} { - const { transformedConnector, references } = transformConnectorIdToReference( - ConnectorIdReferenceName[fieldType], - connector - ); - - return { - transformedActionDetails: transformedConnector, - references, - }; -} - -type ConnectorIdRefNameType = - | typeof CONNECTOR_ID_REFERENCE_NAME - | typeof USER_ACTION_OLD_ID_REF_NAME; - -export const transformConnectorIdToReference = ( - referenceName: ConnectorIdRefNameType, - connector?: { - id?: string; - } -): { - transformedConnector: { connector: unknown }; - references: SavedObjectReference[]; -} => { - const { id: connectorId, ...restConnector } = connector ?? {}; - - const references = createConnectorReference(connectorId, ACTION_SAVED_OBJECT_TYPE, referenceName); - - const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); - const connectorFieldsToReturn = - connector && isConnectorIdValid(connectorId) ? restConnector : restNoneConnector; - - return { - transformedConnector: { - connector: connectorFieldsToReturn, - }, - references, - }; -}; - -const createConnectorReference = ( - id: string | null | undefined, - type: string, - name: string -): SavedObjectReference[] => { - return isConnectorIdValid(id) - ? [ - { - id, - type, - name, - }, - ] - : []; -}; - -const isConnectorIdValid = (id: string | null | undefined): id is string => - id != null && id !== noneConnectorId; - -function isUpdateCaseConnector( - action: string, - actionFields: string[], - actionDetails: unknown -): actionDetails is CaseConnector { - try { - return isUpdateConnector(action, actionFields) && CaseConnectorRt.is(actionDetails); - } catch { - return false; - } -} - -type CaseExternalService = rt.TypeOf; - -function isPushConnector( - action: string, - actionFields: string[], - actionDetails: unknown -): actionDetails is CaseExternalService { - try { - return isPush(action, actionFields) && CaseExternalServiceBasicRt.is(actionDetails); - } catch { - return false; - } -} - -export const PushConnectorIdReferenceName: Record = - { - [UserActionFieldType.New]: PUSH_CONNECTOR_ID_REFERENCE_NAME, - [UserActionFieldType.Old]: USER_ACTION_OLD_PUSH_ID_REF_NAME, - }; - -function transformConnectorFromPushAction( - externalService: CaseExternalService, - fieldType: UserActionFieldType -): { - transformedActionDetails: {} | null; - references: SavedObjectReference[]; -} { - const { transformedPushConnector, references } = transformPushConnectorIdToReference( - PushConnectorIdReferenceName[fieldType], - externalService - ); - - return { - transformedActionDetails: transformedPushConnector.external_service, - references, - }; -} - -type PushConnectorIdRefNameType = - | typeof PUSH_CONNECTOR_ID_REFERENCE_NAME - | typeof USER_ACTION_OLD_PUSH_ID_REF_NAME; - -export const transformPushConnectorIdToReference = ( - referenceName: PushConnectorIdRefNameType, - external_service?: { connector_id?: string | null } | null -): { - transformedPushConnector: { external_service: {} | null }; - references: SavedObjectReference[]; -} => { - const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; - - const references = createConnectorReference( - pushConnectorId, - ACTION_SAVED_OBJECT_TYPE, - referenceName - ); - - return { - transformedPushConnector: { external_service: external_service ? restExternalService : null }, - references, - }; -}; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts deleted file mode 100644 index 3c67535255ecc..0000000000000 --- a/x-pack/plugins/cases/server/services/user_actions/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Indicates whether which user action field is being parsed, the new_value or the old_value. - */ -export enum UserActionFieldType { - New = 'New', - Old = 'Old', -} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 68f0ba43d889b..964e9135aba7b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should create a user action when deleting a case', async () => { + it('should create a user action when creating a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); await deleteCases({ supertest, caseIDs: [postedCase.id] }); const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); @@ -106,8 +106,6 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, old_value: null, new_value: null, - new_val_connector_id: null, - old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index d7c506a6b69d2..63b2f2e9b90ed 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -126,8 +126,6 @@ export default ({ getService }: FtrProviderContext): void => { action: 'update', action_by: defaultUser, new_value: CaseStatuses.closed, - new_val_connector_id: null, - old_val_connector_id: null, old_value: CaseStatuses.open, case_id: `${postedCase.id}`, comment_id: null, @@ -167,8 +165,6 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, new_value: CaseStatuses['in-progress'], old_value: CaseStatuses.open, - old_val_connector_id: null, - new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 13408c5d309d9..96709ee7c309d 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -114,8 +114,6 @@ export default ({ getService }: FtrProviderContext): void => { const { new_value, ...rest } = creationUserAction as CaseUserActionResponse; const parsedNewValue = JSON.parse(new_value!); - const { id: connectorId, ...restCaseConnector } = postedCase.connector; - expect(rest).to.eql({ action_field: [ 'description', @@ -129,9 +127,6 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, old_value: null, - old_val_connector_id: null, - // the connector id will be null here because it the connector is none - new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -143,7 +138,7 @@ export default ({ getService }: FtrProviderContext): void => { description: postedCase.description, title: postedCase.title, tags: postedCase.tags, - connector: restCaseConnector, + connector: postedCase.connector, settings: postedCase.settings, owner: postedCase.owner, }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index 942293437b03f..f4c31c052cddd 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -148,9 +148,7 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}","owner":"securitySolutionFixture"}`, - new_val_connector_id: null, old_value: null, - old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: `${patchedCase.comments![0].id}`, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 4cae10510d28e..35ebb1a4bf7b1 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -48,15 +48,6 @@ export default ({ getService }: FtrProviderContext): void => { }); it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector', 'settings, owner]`, async () => { - const { id: connectorId, ...restConnector } = userActionPostResp.connector; - - const userActionNewValueNoId = { - ...userActionPostResp, - connector: { - ...restConnector, - }, - }; - const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -82,10 +73,7 @@ export default ({ getService }: FtrProviderContext): void => { ]); expect(body[0].action).to.eql('create'); expect(body[0].old_value).to.eql(null); - expect(body[0].old_val_connector_id).to.eql(null); - // this will be null because it is for the none connector - expect(body[0].new_val_connector_id).to.eql(null); - expect(JSON.parse(body[0].new_value)).to.eql(userActionNewValueNoId); + expect(JSON.parse(body[0].new_value)).to.eql(userActionPostResp); }); it(`on close case, user action: 'update' should be called with actionFields: ['status']`, async () => { @@ -159,19 +147,18 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.length).to.eql(2); expect(body[1].action_field).to.eql(['connector']); expect(body[1].action).to.eql('update'); - // this is null because it is the none connector - expect(body[1].old_val_connector_id).to.eql(null); expect(JSON.parse(body[1].old_value)).to.eql({ + id: 'none', name: 'none', type: '.none', fields: null, }); expect(JSON.parse(body[1].new_value)).to.eql({ + id: '123', name: 'Connector', type: '.jira', fields: { issueType: 'Task', priority: 'High', parent: null }, }); - expect(body[1].new_val_connector_id).to.eql('123'); }); it(`on update tags, user action: 'add' and 'delete' should be called with actionFields: ['tags']`, async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts index f9e66880c5230..b4c2dca47bf5f 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts @@ -12,10 +12,6 @@ import { SECURITY_SOLUTION_OWNER, } from '../../../../../../plugins/cases/common/constants'; import { getCaseUserActions } from '../../../../common/lib/utils'; -import { - CaseUserActionResponse, - CaseUserActionsResponse, -} from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { @@ -45,18 +41,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(connectorUserAction.action_field.length).eql(1); expect(connectorUserAction.action_field[0]).eql('connector'); - expect(connectorUserAction.old_val_connector_id).to.eql( - 'c1900ac0-017f-11eb-93f8-d161651bf509' - ); expect(oldValue).to.eql({ + id: 'c1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, }); - expect(connectorUserAction.new_val_connector_id).to.eql( - 'b1900ac0-017f-11eb-93f8-d161651bf509' - ); expect(newValue).to.eql({ + id: 'b1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, @@ -85,142 +77,5 @@ export default function createGetTests({ getService }: FtrProviderContext) { } }); }); - - describe('7.13 connector id extraction', () => { - let userActions: CaseUserActionsResponse; - - before(async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' - ); - }); - - after(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' - ); - }); - - describe('none connector case', () => { - it('removes the connector id from the case create user action and sets the ids to null', async () => { - userActions = await getCaseUserActions({ - supertest, - caseID: 'aa8ac630-005e-11ec-91f1-6daf2ab59fb5', - }); - - const userAction = getUserActionById( - userActions, - 'ab43b5f0-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.description).to.be('a description'); - expect(newValDecoded.title).to.be('a case'); - expect(newValDecoded.connector).not.have.property('id'); - // the connector id should be none so it should be removed - expect(userAction.new_val_connector_id).to.be(null); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('sets the connector ids to null for a create user action with null new and old values', async () => { - const userAction = getUserActionById( - userActions, - 'b3094de0-005e-11ec-91f1-6daf2ab59fb5' - )!; - - expect(userAction.new_val_connector_id).to.be(null); - expect(userAction.old_val_connector_id).to.be(null); - }); - }); - - describe('case with many user actions', () => { - before(async () => { - userActions = await getCaseUserActions({ - supertest, - caseID: 'e6fa9370-005e-11ec-91f1-6daf2ab59fb5', - }); - }); - - it('removes the connector id field for a created case user action', async () => { - const userAction = getUserActionById( - userActions, - 'e7882d70-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.description).to.be('a description'); - expect(newValDecoded.title).to.be('a case'); - - expect(newValDecoded.connector).to.not.have.property('id'); - expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('removes the connector id from the external service new value', async () => { - const userAction = getUserActionById( - userActions, - 'e9471b80-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.connector_name).to.be('a jira connector'); - expect(newValDecoded).to.not.have.property('connector_id'); - expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('sets the connector ids to null for a comment user action', async () => { - const userAction = getUserActionById( - userActions, - 'efe9de50-005e-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - expect(newValDecoded.comment).to.be('a comment'); - expect(userAction.new_val_connector_id).to.be(null); - expect(userAction.old_val_connector_id).to.be(null); - }); - - it('removes the connector id for an update connector action', async () => { - const userAction = getUserActionById( - userActions, - '16cd9e30-005f-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - const oldValDecoded = JSON.parse(userAction.old_value!); - - expect(newValDecoded.name).to.be('a different jira connector'); - expect(oldValDecoded.name).to.be('a jira connector'); - - expect(newValDecoded).to.not.have.property('id'); - expect(oldValDecoded).to.not.have.property('id'); - expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); - }); - - it('removes the connector id from the external service new value for second push', async () => { - const userAction = getUserActionById( - userActions, - '1ea33bb0-005f-11ec-91f1-6daf2ab59fb5' - )!; - - const newValDecoded = JSON.parse(userAction.new_value!); - - expect(newValDecoded.connector_name).to.be('a different jira connector'); - - expect(newValDecoded).to.not.have.property('connector_id'); - expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); - expect(userAction.old_val_connector_id).to.be(null); - }); - }); - }); }); } - -function getUserActionById( - userActions: CaseUserActionsResponse, - id: string -): CaseUserActionResponse | undefined { - return userActions.find((userAction) => userAction.action_id === id); -} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 0ea66d35b63b8..94fe494fc7cc4 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -275,8 +275,6 @@ export default ({ getService }: FtrProviderContext): void => { action: 'push-to-service', action_by: defaultUser, old_value: null, - old_val_connector_id: null, - new_val_connector_id: connector.id, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -286,6 +284,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(parsedNewValue).to.eql({ pushed_at: pushedCase.external_service!.pushed_at, pushed_by: defaultUser, + connector_id: connector.id, connector_name: connector.name, external_id: '123', external_title: 'INC01', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 255a2a4ce28b5..79af6bb279a3d 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -108,10 +108,8 @@ export default ({ getService }: FtrProviderContext): void => { expect(body[1].action_field).to.eql(['pushed']); expect(body[1].action).to.eql('push-to-service'); expect(body[1].old_value).to.eql(null); - expect(body[1].old_val_connector_id).to.eql(null); - expect(body[1].new_val_connector_id).to.eql(configure.connector.id); const newValue = JSON.parse(body[1].new_value); - expect(newValue).to.not.have.property('connector_id'); + expect(newValue.connector_id).to.eql(configure.connector.id); expect(newValue.pushed_by).to.eql(defaultUser); }); }); diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz deleted file mode 100644 index 5f73dfd89d1660ef207e6ec26ce7198f2a722616..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2078 zcmV+(2;ui1iwFpikRD+G17u-zVJ>QOZ*BnXTuXDKI1s+)S5TY-LE>SvxgZy+IT5F>ahZaIhWE-j?DT+?5t5lioJNZyKPi$UR$bs&%<`)2h3okV(a3a zL#Oy&DHWJDNxNT|jw-rg7~^}oGPm^>`SOTBQ4}aL)$g~xMn9kcdl7$V`cR|B4O~BP zqt_0h|8$>z;4rF*UHSD`?VD}BV?wB@n?Bo~_4Cj^nGOj*-2e&YwlJ-7)stzXQl$6l zh6Wf#xgLm6fk0B05?J0t#ZnXtXtJuQ4`NG?L_HWCza&<@-~_JN!GM!|m2&)MuBmhx9q}HCvC}gmQ#_ z+fhiAP6!kL7MvE58|{)2@BjUW$f9tX#Q|M!@kbOLLhJ4>F>@&*`saF2GK$DLhc5DD z4@Y;Kz<&wxUF&#wWg|CggBy49oDxweD&-< zb~uP=O%%kqy?2D7n6?o817_4HLQi#A6T|p`gHoi&YV&;Kh!&9DU`EQUkpQ>*>^d>p zRcqse!#BjXO(=RyYKS+rwIfCl;J@9c08#t<4+mjpmkSxFvQ~^M?wf6@-Y9cWv>TKa zu`x}6x0rysjjNkcEPS!H={QAkk41(0LpobFS25I@I_w+z$nQ5dZ){!e%NyRD_2&{@ zND0~Wv59lv5O~qI{Q!pV-rF3!&^BRRE7IabxCFx4Jx^+cnue!l zJ|JK(w>}RR!qB%}icb;?Te-?HnAYk5A6wi@%Om%3Gnt)hGE~fmCjQn@F_nTKW4MH^O>YEbb(vmNHhKZMDpBhSK34k%}} z16_HGrVj!F1sK!E{n*qNV6(h{xStZ5QshfHTiojakdfp|8LD-HbS4LYl*jGQQxsHj z!VB=0BB2Tjia4bPa*30UROFOT$R9W+8NjqyjU)w({mj!hArW} zQJfE0UYX=hCb>5jcO!7gI@3y)b*7W$sRJ22D+)~AP4dz|^A+@W_Od={mc5oI0DI-E zzZ?qdvI>F<+l@p02 zaX5v>QMt(~d?G+WQ&tye2or%}J0?!wI&9AhQ0W5o2Pj~v#CY_Xo~g@Vt~W$w5tM@T z1zZ}PJ64aN9Cw?bKr~e>^_Txb<0w8>igODIIjNCXWIszp2WS?80jGJr$O0xts=C=+ z0AP-0BLRIkyO?XL)=AmOVkxF$BYWd_D;wE~z}Nxr4lAb1^Z{+a&N!<1|<$U5Iw zRxBaS99|>5;$A8+ zg$Yj)35^VZxS__-2xA(;gQaD+1~znb0krJUJOG&Z8TzH+JZagnc>ptcZO)~I=bF+m z$x-a9QQ19?V Date: Mon, 20 Sep 2021 23:55:06 +0100 Subject: [PATCH 14/69] [Archive Migration] xpack..reporting-api-integ/reporting-security (#112432) * [Archive Migration] xpack..reporting-api-integ/reporting-security Found another spot where the es archive for discover is still in use. Use the kbn archive instead. * Switch to unload, per cr. * unload logstash Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../reporting_and_security/bwc_existing_indexes.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts b/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts index 0da51901f9086..168390bc6fc28 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/bwc_existing_indexes.ts @@ -19,6 +19,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const reportingAPI = getService('reportingAPI'); + const kibanaServer = getService('kibanaServer'); describe('BWC report generation into existing indexes', () => { let cleanupIndexAlias: () => Promise; @@ -28,7 +29,8 @@ export default function ({ getService }: FtrProviderContext) { await reportingAPI.deleteAllReports(); // data to report on await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.load('test/functional/fixtures/es_archiver/discover'); // includes index pattern for logstash_functional + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + // archive with reporting index mappings v6.2 await esArchiver.load('x-pack/test/functional/es_archives/reporting/bwc/6_2'); @@ -41,8 +43,8 @@ export default function ({ getService }: FtrProviderContext) { }); after('remove index alias', async () => { - await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); - await esArchiver.load('test/functional/fixtures/es_archiver/discover'); + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await cleanupIndexAlias(); await esArchiver.unload('x-pack/test/functional/es_archives/reporting/bwc/6_2'); From 6980064e5e52b2333de05c55081ff8bf85e22b65 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Mon, 20 Sep 2021 20:35:59 -0700 Subject: [PATCH 15/69] [Reporting] Address Canvas PDF test failing in Cloud (#112623) --- x-pack/test/functional/apps/canvas/reports.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/canvas/reports.ts b/x-pack/test/functional/apps/canvas/reports.ts index 468430c390030..e21ec5b404d1f 100644 --- a/x-pack/test/functional/apps/canvas/reports.ts +++ b/x-pack/test/functional/apps/canvas/reports.ts @@ -198,7 +198,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { " `); - expect(res.get('content-length')).to.be('20725'); + const contentLength = parseInt(res.get('content-length'), 10); + expect(contentLength >= 20725 && contentLength <= 20726).to.be(true); // contentLength can be between 20725 and 20726 }); it('downloaded PDF base64 string is correct without borders and logo', async function () { From 0af821aaf9b2c2371369b0b455852364e0841c24 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 21 Sep 2021 10:16:29 +0200 Subject: [PATCH 16/69] [kibana_react] Clean up `any` in public non-test files (#111261) * first pass at any cleanup * fix types on TableListView * change generic constraint * fix lint * fix TS in no data card Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../application/listing/dashboard_listing.tsx | 4 +- src/plugins/kibana_react/README.md | 14 -- .../code_editor/code_editor.test.helpers.tsx | 11 +- .../public/code_editor/code_editor.tsx | 4 +- .../kibana_react/public/context/context.tsx | 8 +- .../kibana_react/public/context/index.ts | 1 - .../no_data_card/elastic_agent_card.tsx | 4 +- .../no_data_card/elastic_beats_card.tsx | 4 +- .../no_data_card/no_data_card.tsx | 12 +- .../react_router_navigate.tsx | 11 +- .../containers/panel_container.tsx | 2 +- .../public/split_panel/context.tsx | 2 +- .../table_list_view/table_list_view.tsx | 37 +++--- .../public/util/mount_point_portal.tsx | 4 +- .../test_helpers/react_mount_serializer.ts | 7 +- .../application/utils/get_table_columns.tsx | 121 ++++++++++-------- 16 files changed, 129 insertions(+), 117 deletions(-) diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx index 6508bd9f355e1..827e5abf2bd6a 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx @@ -7,7 +7,7 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiLink, EuiButton, EuiEmptyPrompt, EuiBasicTableColumn } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { attemptLoadDashboardByTitle } from '../lib'; import { DashboardAppServices, DashboardRedirect } from '../../types'; @@ -231,7 +231,7 @@ const getTableColumns = ( sortable: true, }, ...(savedObjectsTagging ? [savedObjectsTagging.ui.getTableColumnDefinition()] : []), - ]; + ] as unknown as Array>>; }; const getNoItemsMessage = ( diff --git a/src/plugins/kibana_react/README.md b/src/plugins/kibana_react/README.md index adbdb628ea9dd..5071c6c93f152 100644 --- a/src/plugins/kibana_react/README.md +++ b/src/plugins/kibana_react/README.md @@ -75,20 +75,6 @@ const Demo = ({ kibana }) => { export default withKibana(Demo); ``` -Using `` render prop. - -```tsx -import { UseKibana } from 'kibana-react'; - -const Demo = () => { - return ( - - {(kibana) =>
{kibana.services.uiSettings.get('theme:darkMode') ? 'dark' : 'light'}
} -
- ); -}; -``` - ## `uiSettings` service Wrappers around Core's `uiSettings` service. diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx index cee1ecad2196a..e0b868f3a8b08 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.test.helpers.tsx @@ -9,9 +9,9 @@ import React, { useEffect, KeyboardEventHandler } from 'react'; import { monaco } from '@kbn/monaco'; function createEditorInstance() { - const keyDownListeners: any[] = []; - const didShowListeners: any[] = []; - const didHideListeners: any[] = []; + const keyDownListeners: Array<(e?: unknown) => void> = []; + const didShowListeners: Array<(e?: unknown) => void> = []; + const didHideListeners: Array<(e?: unknown) => void> = []; let areSuggestionsVisible = false; const editorInstance = { @@ -69,7 +69,10 @@ type MockedEditor = ReturnType; export const mockedEditorInstance: MockedEditor = createEditorInstance(); // mock -const mockMonacoEditor = ({ editorWillMount, editorDidMount }: any) => { +const mockMonacoEditor = ({ + editorWillMount, + editorDidMount, +}: Record void>) => { editorWillMount(monaco); useEffect(() => { diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index 29b905c511d4c..07bda19bf6d14 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -135,7 +135,9 @@ export const CodeEditor: React.FC = ({ const MonacoEditor: typeof ReactMonacoEditor = useMemo(() => { const isMockedComponent = typeof ReactMonacoEditor === 'function' && ReactMonacoEditor.name === 'JestMockEditor'; - return isMockedComponent ? (ReactMonacoEditor as any)() : ReactMonacoEditor; + return isMockedComponent + ? (ReactMonacoEditor as unknown as () => typeof ReactMonacoEditor)() + : ReactMonacoEditor; }, []); const isReadOnly = options?.readOnly ?? false; diff --git a/src/plugins/kibana_react/public/context/context.tsx b/src/plugins/kibana_react/public/context/context.tsx index 974bd4e894791..c9dc3df90864d 100644 --- a/src/plugins/kibana_react/public/context/context.tsx +++ b/src/plugins/kibana_react/public/context/context.tsx @@ -26,7 +26,7 @@ export const useKibana = (): KibanaReactContextValue< > => useContext(context as unknown as React.Context>); -export const withKibana = }>( +export const withKibana = }>( type: React.ComponentType ): React.FC> => { const EnhancedType: React.FC> = (props: Omit) => { @@ -36,10 +36,6 @@ export const withKibana = return EnhancedType; }; -export const UseKibana: React.FC<{ - children: (kibana: KibanaReactContextValue) => React.ReactNode; -}> = ({ children }) => <>{children(useKibana())}; - export const createKibanaReactContext = ( services: Services ): KibanaReactContext => { @@ -58,7 +54,7 @@ export const createKibanaReactContext = ( () => createKibanaReactContext({ ...services, ...oldValue.services, ...newServices }), [services, oldValue, newServices] ); - return createElement(context.Provider as React.ComponentType, { + return createElement(context.Provider, { value: newValue, children, }); diff --git a/src/plugins/kibana_react/public/context/index.ts b/src/plugins/kibana_react/public/context/index.ts index 12f8bfccbb592..b34951b298836 100644 --- a/src/plugins/kibana_react/public/context/index.ts +++ b/src/plugins/kibana_react/public/context/index.ts @@ -12,6 +12,5 @@ export { KibanaContextProvider, useKibana, withKibana, - UseKibana, } from './context'; export { KibanaReactContext, KibanaReactContextValue, KibanaServices } from './types'; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index ad3fb0e2e6396..f071bd9fab25a 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -26,6 +26,7 @@ export const ElasticAgentCard: FunctionComponent = ({ title, href, button, + layout, ...cardRest }) => { const { @@ -58,7 +59,8 @@ export const ElasticAgentCard: FunctionComponent = ({ image={addBasePath(`${basePathUrl}elastic_agent_card.svg`)} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} footer={footer} - {...(cardRest as any)} + layout={layout as 'vertical' | undefined} + {...cardRest} /> ); }; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx index d0a8ceddb8dc5..0372d12096489 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx @@ -23,6 +23,7 @@ export const ElasticBeatsCard: FunctionComponent = ({ button, href, solution, // unused for now + layout, ...cardRest }) => { const { @@ -58,7 +59,8 @@ export const ElasticBeatsCard: FunctionComponent = ({ )} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} footer={footer} - {...(cardRest as any)} + layout={layout as 'vertical' | undefined} + {...cardRest} /> ); }; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx index 682d0267e1a7e..9cc38cc5f6038 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; import { EuiButton, EuiCard, EuiCardProps } from '@elastic/eui'; import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page'; @@ -17,6 +18,7 @@ export const NoDataCard: FunctionComponent = ({ recommended, title, button, + layout, ...cardRest }) => { const footer = @@ -25,10 +27,16 @@ export const NoDataCard: FunctionComponent = ({ return ( ); }; diff --git a/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx b/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx index db122d034371d..5abd77c9dc9b6 100644 --- a/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx +++ b/src/plugins/kibana_react/public/react_router_navigate/react_router_navigate.tsx @@ -7,6 +7,7 @@ */ import { ScopedHistory } from 'kibana/public'; +import { MouseEvent } from 'react'; import { History, parsePath } from 'history'; interface LocationObject { @@ -15,10 +16,10 @@ interface LocationObject { hash?: string; } -const isModifiedEvent = (event: any) => +const isModifiedEvent = (event: MouseEvent) => !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); -const isLeftClickEvent = (event: any) => event.button === 0; +const isLeftClickEvent = (event: MouseEvent) => event.button === 0; export const toLocationObject = (to: string | LocationObject) => typeof to === 'string' ? parsePath(to) : to; @@ -34,7 +35,7 @@ export const reactRouterNavigate = ( export const reactRouterOnClickHandler = (history: ScopedHistory | History, to: string | LocationObject, onClickCallback?: Function) => - (event: any) => { + (event: MouseEvent) => { if (onClickCallback) { onClickCallback(event); } @@ -43,7 +44,9 @@ export const reactRouterOnClickHandler = return; } - if (event.target.getAttribute('target')) { + if ( + (event.target as unknown as { getAttribute: (a: string) => unknown })?.getAttribute('target') + ) { return; } diff --git a/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx b/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx index 86f73f9331881..69beb565ad857 100644 --- a/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx +++ b/src/plugins/kibana_react/public/split_panel/containers/panel_container.tsx @@ -17,7 +17,7 @@ export interface Props { children: ReactNode; className?: string; resizerClassName?: string; - onPanelWidthChange?: (arrayOfPanelWidths: number[]) => any; + onPanelWidthChange?: (arrayOfPanelWidths: number[]) => void; } interface State { diff --git a/src/plugins/kibana_react/public/split_panel/context.tsx b/src/plugins/kibana_react/public/split_panel/context.tsx index bad39f8bd5ef9..e236b5037e7f3 100644 --- a/src/plugins/kibana_react/public/split_panel/context.tsx +++ b/src/plugins/kibana_react/public/split_panel/context.tsx @@ -12,7 +12,7 @@ import { PanelRegistry } from './registry'; const PanelContext = createContext({ registry: new PanelRegistry() }); interface ContextProps { - children: any; + children: JSX.Element; registry: PanelRegistry; } diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 30d09f4bf8657..18a88790b425a 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -26,17 +26,13 @@ import React from 'react'; import { KibanaPageTemplate } from '../page_template'; import { toMountPoint } from '../util'; -interface Item { - id?: string; -} - -export interface TableListViewProps { +export interface TableListViewProps { createItem?(): void; - deleteItems?(items: object[]): Promise; - editItem?(item: object): void; + deleteItems?(items: V[]): Promise; + editItem?(item: V): void; entityName: string; entityNamePlural: string; - findItems(query: string): Promise<{ total: number; hits: object[] }>; + findItems(query: string): Promise<{ total: number; hits: V[] }>; listingLimit: number; initialFilter: string; initialPageSize: number; @@ -44,7 +40,7 @@ export interface TableListViewProps { * Should be an EuiEmptyPrompt (but TS doesn't support this typing) */ emptyPrompt?: JSX.Element; - tableColumns: Array>; + tableColumns: Array>; tableListTitle: string; toastNotifications: ToastsStart; /** @@ -63,8 +59,8 @@ export interface TableListViewProps { searchFilters?: SearchFilterConfig[]; } -export interface TableListViewState { - items: object[]; +export interface TableListViewState { + items: V[]; hasInitialFetchReturned: boolean; isFetchingItems: boolean; isDeletingItems: boolean; @@ -81,11 +77,14 @@ export interface TableListViewState { // and not supporting server-side paging. // This component does not try to tackle these problems (yet) and is just feature matching the legacy component // TODO support server side sorting/paging once title and description are sortable on the server. -class TableListView extends React.Component { +class TableListView extends React.Component< + TableListViewProps, + TableListViewState +> { private pagination = {}; private _isMounted = false; - constructor(props: TableListViewProps) { + constructor(props: TableListViewProps) { super(props); this.pagination = { @@ -134,7 +133,7 @@ class TableListView extends React.Component(response.hits, 'title') : response.hits, totalItems: response.total, showLimitError: response.total > this.props.listingLimit, }); @@ -404,17 +403,17 @@ class TableListView extends React.Component { + onSelectionChange: (obj: V[]) => { this.setState({ selectedIds: obj - .map((item) => item.id) - .filter((id: undefined | string): id is string => Boolean(id)), + .map((item) => (item as Record)?.id) + .filter((id): id is string => Boolean(id)), }); }, } : undefined; - const actions: EuiTableActionsColumnType['actions'] = [ + const actions: EuiTableActionsColumnType['actions'] = [ { name: i18n.translate('kibana-react.tableListView.listing.table.editActionName', { defaultMessage: 'Edit', @@ -427,7 +426,7 @@ class TableListView extends React.Component !error, + enabled: (v) => !(v as unknown as { error: string })?.error, onClick: this.props.editItem, }, ]; diff --git a/src/plugins/kibana_react/public/util/mount_point_portal.tsx b/src/plugins/kibana_react/public/util/mount_point_portal.tsx index 07d2edfa717b9..d0753c68d1f2d 100644 --- a/src/plugins/kibana_react/public/util/mount_point_portal.tsx +++ b/src/plugins/kibana_react/public/util/mount_point_portal.tsx @@ -60,12 +60,12 @@ export const MountPointPortal: React.FC = ({ children, se } }; -class MountPointPortalErrorBoundary extends Component<{}, { error?: any }> { +class MountPointPortalErrorBoundary extends Component<{}, { error?: unknown }> { state = { error: undefined, }; - static getDerivedStateFromError(error: any) { + static getDerivedStateFromError(error: unknown) { return { error }; } diff --git a/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts b/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts index 6438ff0b8d98f..4d1575bf16184 100644 --- a/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts +++ b/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts @@ -6,11 +6,14 @@ * Side Public License, v 1. */ -export function test(value: any) { +export function test(value?: Record) { return value && value.__reactMount__; } -export function print(value: any, serialize: any) { +export function print( + value: Record, + serialize: (args: Record) => { replace: (s1: string, s2: string) => unknown } +) { // there is no proper way to correctly indent multiline values // so the trick here is to use the Object representation and rewriting the root object name return serialize({ diff --git a/src/plugins/visualize/public/application/utils/get_table_columns.tsx b/src/plugins/visualize/public/application/utils/get_table_columns.tsx index 8c8ecaf9a448a..69383005deb07 100644 --- a/src/plugins/visualize/public/application/utils/get_table_columns.tsx +++ b/src/plugins/visualize/public/application/utils/get_table_columns.tsx @@ -8,7 +8,15 @@ import React from 'react'; import { METRIC_TYPE } from '@kbn/analytics'; -import { EuiBetaBadge, EuiButton, EuiEmptyPrompt, EuiIcon, EuiLink, EuiBadge } from '@elastic/eui'; +import { + EuiBetaBadge, + EuiButton, + EuiEmptyPrompt, + EuiIcon, + EuiLink, + EuiBadge, + EuiBasicTableColumn, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart } from 'kibana/public'; @@ -86,61 +94,62 @@ export const getTableColumns = ( application: ApplicationStart, kbnUrlStateStorage: IKbnUrlStateStorage, taggingApi?: SavedObjectsTaggingApi -) => [ - { - field: 'title', - name: i18n.translate('visualize.listing.table.titleColumnName', { - defaultMessage: 'Title', - }), - sortable: true, - render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) => - // In case an error occurs i.e. the vis has wrong type, we render the vis but without the link - !error ? ( - - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - { - doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name); - }} - data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`} - > - {field} - - - ) : ( - field - ), - }, - { - field: 'typeTitle', - name: i18n.translate('visualize.listing.table.typeColumnName', { - defaultMessage: 'Type', - }), - sortable: true, - render: (field: string, record: VisualizationListItem) => - !record.error ? ( - - {renderItemTypeIcon(record)} - {record.typeTitle} - {getBadge(record)} - - ) : ( - - {record.error} - - ), - }, - { - field: 'description', - name: i18n.translate('visualize.listing.table.descriptionColumnName', { - defaultMessage: 'Description', - }), - sortable: true, - render: (field: string, record: VisualizationListItem) => {record.description}, - }, - ...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []), -]; +) => + [ + { + field: 'title', + name: i18n.translate('visualize.listing.table.titleColumnName', { + defaultMessage: 'Title', + }), + sortable: true, + render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) => + // In case an error occurs i.e. the vis has wrong type, we render the vis but without the link + !error ? ( + + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + { + doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name); + }} + data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`} + > + {field} + + + ) : ( + field + ), + }, + { + field: 'typeTitle', + name: i18n.translate('visualize.listing.table.typeColumnName', { + defaultMessage: 'Type', + }), + sortable: true, + render: (field: string, record: VisualizationListItem) => + !record.error ? ( + + {renderItemTypeIcon(record)} + {record.typeTitle} + {getBadge(record)} + + ) : ( + + {record.error} + + ), + }, + { + field: 'description', + name: i18n.translate('visualize.listing.table.descriptionColumnName', { + defaultMessage: 'Description', + }), + sortable: true, + render: (field: string, record: VisualizationListItem) => {record.description}, + }, + ...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []), + ] as unknown as Array>>; export const getNoItemsMessage = (createItem: () => void) => ( Date: Tue, 21 Sep 2021 10:30:30 +0200 Subject: [PATCH 17/69] [SOM] Add `visibleInManagement` option to management metadata (#112073) * implement SavedObjectsTypeManagementDefinition.visibleInManagement * update generated doc * improve FTR tests * fix FTR test --- ...in-core-server.deprecationsservicesetup.md | 1 + ...er.savedobjectstypemanagementdefinition.md | 1 + ...anagementdefinition.visibleinmanagement.md | 18 + .../saved_objects_type_registry.test.ts | 53 ++ .../saved_objects_type_registry.ts | 5 + src/core/server/saved_objects/types.ts | 9 + src/core/server/server.api.md | 1 + .../server/routes/get_allowed_types.ts | 1 + .../visible_in_management/data.json | 20 + .../visible_in_management/mappings.json | 477 ++++++++++++++++++ .../server/plugin.ts | 36 ++ .../_import_non_visible_in_management.ndjson | 1 + .../saved_objects_management/index.ts | 1 + .../visible_in_management.ts | 113 +++++ 14 files changed, 737 insertions(+) create mode 100644 docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md create mode 100644 test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json create mode 100644 test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json create mode 100644 test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson create mode 100644 test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md index 7b2cbdecd146a..2bc7f6cba594d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md @@ -27,6 +27,7 @@ async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecations const deprecations: DeprecationsDetails[] = []; const count = await getFooCount(savedObjectsClient); if (count > 0) { + // Example of a manual correctiveAction deprecations.push({ title: i18n.translate('xpack.foo.deprecations.title', { defaultMessage: `Foo's are deprecated` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md index 8c42884eb0b31..2c3dd9f3f8434 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md @@ -25,4 +25,5 @@ export interface SavedObjectsTypeManagementDefinition | [isExportable](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.isexportable.md) | SavedObjectsExportablePredicate<Attributes> | Optional hook to specify whether an object should be exportable.If specified, isExportable will be called during export for each of this type's objects in the export, and the ones not matching the predicate will be excluded from the export.When implementing both isExportable and onExport, it is mandatory that isExportable returns the same value for an object before and after going though the export transform. E.g isExportable(objectBeforeTransform) === isExportable(objectAfterTransform) | | [onExport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md) | SavedObjectsExportTransform<Attributes> | An optional export transform function that can be used transform the objects of the registered type during the export process.It can be used to either mutate the exported objects, or add additional objects (of any type) to the export list.See [the transform type documentation](./kibana-plugin-core-server.savedobjectsexporttransform.md) for more info and examples.When implementing both isExportable and onExport, it is mandatory that isExportable returns the same value for an object before and after going though the export transform. E.g isExportable(objectBeforeTransform) === isExportable(objectAfterTransform) | | [onImport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md) | SavedObjectsImportHook<Attributes> | An optional [import hook](./kibana-plugin-core-server.savedobjectsimporthook.md) to use when importing given type.Import hooks are executed during the savedObjects import process and allow to interact with the imported objects. See the [hook documentation](./kibana-plugin-core-server.savedobjectsimporthook.md) for more info. | +| [visibleInManagement](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md) | boolean | When set to false, the type will not be listed or searchable in the SO management section. Main usage of setting this property to false for a type is when objects from the type should be included in the export via references or export hooks, but should not directly appear in the SOM. Defaults to true. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md new file mode 100644 index 0000000000000..33ddc8e8c8307 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) > [visibleInManagement](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.visibleinmanagement.md) + +## SavedObjectsTypeManagementDefinition.visibleInManagement property + +When set to false, the type will not be listed or searchable in the SO management section. Main usage of setting this property to false for a type is when objects from the type should be included in the export via references or export hooks, but should not directly appear in the SOM. Defaults to `true`. + +Signature: + +```typescript +visibleInManagement?: boolean; +``` + +## Remarks + +`importableAndExportable` must be `true` to specify this property. + diff --git a/src/core/server/saved_objects/saved_objects_type_registry.test.ts b/src/core/server/saved_objects/saved_objects_type_registry.test.ts index 872b61706c526..c86a1a54f14a8 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.test.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.test.ts @@ -47,6 +47,59 @@ describe('SavedObjectTypeRegistry', () => { }).toThrowErrorMatchingInlineSnapshot(`"Type 'typeA' is already registered"`); }); + it('throws when `management.visibleInManagement` is specified but `management.importableAndExportable` is undefined or false', () => { + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + visibleInManagement: true, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'"` + ); + + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + visibleInManagement: false, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'"` + ); + + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + importableAndExportable: false, + visibleInManagement: false, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'"` + ); + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + importableAndExportable: true, + visibleInManagement: false, + }, + }) + ); + }).not.toThrow(); + }); + it('throws when `management.onExport` is specified but `management.importableAndExportable` is undefined or false', () => { expect(() => { registry.registerType( diff --git a/src/core/server/saved_objects/saved_objects_type_registry.ts b/src/core/server/saved_objects/saved_objects_type_registry.ts index ba5960c59239d..444af58eee801 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.ts @@ -134,5 +134,10 @@ const validateType = ({ name, management }: SavedObjectsType) => { `Type ${name}: 'management.importableAndExportable' must be 'true' when specifying 'management.onExport'` ); } + if (management.visibleInManagement !== undefined && !management.importableAndExportable) { + throw new Error( + `Type ${name}: 'management.importableAndExportable' must be 'true' when specifying 'management.visibleInManagement'` + ); + } } }; diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 83f46c521a836..b2a6be38bb526 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -356,6 +356,15 @@ export interface SavedObjectsTypeManagementDefinition { * Is the type importable or exportable. Defaults to `false`. */ importableAndExportable?: boolean; + /** + * When set to false, the type will not be listed or searchable in the SO management section. + * Main usage of setting this property to false for a type is when objects from the type should + * be included in the export via references or export hooks, but should not directly appear in the SOM. + * Defaults to `true`. + * + * @remarks `importableAndExportable` must be `true` to specify this property. + */ + visibleInManagement?: boolean; /** * The default search field to use for this type. Defaults to `id`. */ diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index ba7973fa1ce06..3c7aa8b992688 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2751,6 +2751,7 @@ export interface SavedObjectsTypeManagementDefinition { isExportable?: SavedObjectsExportablePredicate; onExport?: SavedObjectsExportTransform; onImport?: SavedObjectsImportHook; + visibleInManagement?: boolean; } // @public diff --git a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts index 13adf2764b555..ce371ea590230 100644 --- a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts +++ b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts @@ -17,6 +17,7 @@ export const registerGetAllowedTypesRoute = (router: IRouter) => { async (context, req, res) => { const allowedTypes = context.core.savedObjects.typeRegistry .getImportableAndExportableTypes() + .filter((type) => type.management!.visibleInManagement ?? true) .map((type) => type.name); return res.ok({ diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json new file mode 100644 index 0000000000000..236d7bbff7215 --- /dev/null +++ b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/data.json @@ -0,0 +1,20 @@ +{ + "type": "doc", + "value": { + "id": "test-not-visible-in-management:vim-1", + "index": ".kibana", + "source": { + "coreMigrationVersion": "7.14.0", + "references": [ + ], + "test-not-visible-in-management": { + "enabled": true, + "title": "vim-1" + }, + "type": "test-not-visible-in-management", + "updated_at": "2018-12-21T00:43:07.096Z" + }, + "type": "_doc" + } +} + diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json new file mode 100644 index 0000000000000..3c0a975a30be0 --- /dev/null +++ b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json @@ -0,0 +1,477 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana_$KIBANA_PACKAGE_VERSION": {}, + ".kibana": {} + }, + "index": ".kibana_$KIBANA_PACKAGE_VERSION_001", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", + "dashboard": "40554caf09725935e2c02e02563a2d07", + "index-pattern": "45915a1ad866812242df474eb0479052", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "legacy-url-alias": "6155300fd11a00e23d5cbaa39f0fce0a", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "db2c00e39b36f40930a3b9fc71c823e1", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "usage-counters": "8cc260bdceffec4ffc3ad165c97dc1b4", + "visualization": "f819cf6636b75c9e76ba733a0c6ef355" + } + }, + "dynamic": "strict", + "properties": { + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_daily": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "type": "object" + }, + "canvas-workpad": { + "dynamic": "false", + "type": "object" + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "core-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "coreMigrationVersion": { + "type": "keyword" + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "graph-workspace": { + "dynamic": "false", + "type": "object" + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "legacy-url-alias": { + "dynamic": "false", + "properties": { + "disabled": { + "type": "boolean" + }, + "sourceId": { + "type": "keyword" + }, + "targetType": { + "type": "keyword" + } + } + }, + "map": { + "dynamic": "false", + "type": "object" + }, + "migrationVersion": { + "dynamic": "true", + "type": "object" + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "grid": { + "enabled": false, + "type": "object" + }, + "hideChart": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "server": { + "dynamic": "false", + "type": "object" + }, + "space": { + "dynamic": "false", + "type": "object" + }, + "spaceId": { + "type": "keyword" + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "test-export-add": { + "dynamic": "false", + "type": "object" + }, + "test-export-add-dep": { + "dynamic": "false", + "type": "object" + }, + "test-export-invalid-transform": { + "dynamic": "false", + "type": "object" + }, + "test-export-transform": { + "dynamic": "false", + "type": "object" + }, + "test-export-transform-error": { + "dynamic": "false", + "type": "object" + }, + "test-visible-in-management": { + "dynamic": "false", + "type": "object" + }, + "test-not-visible-in-management": { + "dynamic": "false", + "type": "object" + }, + "type": { + "type": "keyword" + }, + "ui-counter": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "usage-counters": { + "dynamic": "false", + "properties": { + "domainId": { + "type": "keyword" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "savedSearchRefName": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "index": false, + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "index": false, + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1", + "priority": "10", + "refresh_interval": "1s", + "routing_partition_size": "1" + } + } + } +} diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts b/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts index 15afdb229b1fd..0cb6a5ba8eb5d 100644 --- a/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts +++ b/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts @@ -176,6 +176,42 @@ export class SavedObjectExportTransformsPlugin implements Plugin { }, }, }); + + // example of a SO type with `visibleInManagement: false` + savedObjects.registerType<{ enabled: boolean; title: string }>({ + name: 'test-not-visible-in-management', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + enabled: { type: 'boolean' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + visibleInManagement: false, + }, + }); + + // example of a SO type with `visibleInManagement: true` + savedObjects.registerType<{ enabled: boolean; title: string }>({ + name: 'test-visible-in-management', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + enabled: { type: 'boolean' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + visibleInManagement: true, + }, + }); } public start() {} diff --git a/test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson new file mode 100644 index 0000000000000..754848a99d03d --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_non_visible_in_management.ndjson @@ -0,0 +1 @@ +{"attributes": { "title": "Saved object type that is not visible in management" }, "id":"ff3773b0-9ate-11e7-ahb3-3dcb94193fab", "references":[], "type":"test-not-visible-in-management", "version":1} diff --git a/test/plugin_functional/test_suites/saved_objects_management/index.ts b/test/plugin_functional/test_suites/saved_objects_management/index.ts index 03ac96b9a11f6..b7730a95d3c71 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/index.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/index.ts @@ -16,5 +16,6 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) { loadTestFile(require.resolve('./export_transform')); loadTestFile(require.resolve('./import_warnings')); loadTestFile(require.resolve('./hidden_types')); + loadTestFile(require.resolve('./visible_in_management')); }); } diff --git a/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts b/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts new file mode 100644 index 0000000000000..dd43ba80a8e8e --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/visible_in_management.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { join } from 'path'; +import expect from '@kbn/expect'; +import type { Response } from 'supertest'; +import type { PluginFunctionalProviderContext } from '../../services'; +import { SavedObject } from '../../../../src/core/types'; + +function parseNdJson(input: string): Array> { + return input.split('\n').map((str) => JSON.parse(str)); +} + +export default function ({ getService }: PluginFunctionalProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('types with `visibleInManagement` ', () => { + before(async () => { + await esArchiver.load( + 'test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management' + ); + }); + + after(async () => { + await esArchiver.unload( + 'test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management' + ); + }); + + describe('export', () => { + it('allows to export them directly by id', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + objects: [ + { + type: 'test-not-visible-in-management', + id: 'vim-1', + }, + ], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql(['vim-1']); + }); + }); + + it('allows to export them directly by type', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + type: ['test-not-visible-in-management'], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql(['vim-1']); + }); + }); + }); + + describe('import', () => { + it('allows to import them', async () => { + await supertest + .post('/api/saved_objects/_import') + .set('kbn-xsrf', 'true') + .attach('file', join(__dirname, './exports/_import_non_visible_in_management.ndjson')) + .expect(200) + .then((resp) => { + expect(resp.body).to.eql({ + success: true, + successCount: 1, + successResults: [ + { + id: 'ff3773b0-9ate-11e7-ahb3-3dcb94193fab', + meta: { + title: 'Saved object type that is not visible in management', + }, + type: 'test-not-visible-in-management', + }, + ], + warnings: [], + }); + }); + }); + }); + + describe('savedObjects management APIS', () => { + it('GET /api/kibana/management/saved_objects/_allowed_types should only return types that are `visibleInManagement: true`', async () => + await supertest + .get('/api/kibana/management/saved_objects/_allowed_types') + .set('kbn-xsrf', 'true') + .expect(200) + .then((response: Response) => { + const { types } = response.body; + expect(types.includes('test-is-exportable')).to.eql(true); + expect(types.includes('test-visible-in-management')).to.eql(true); + expect(types.includes('test-not-visible-in-management')).to.eql(false); + })); + }); + }); +} From bb4f1360a8a7b986393e8cf3e7165352707df71e Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 21 Sep 2021 10:34:57 +0200 Subject: [PATCH 18/69] remove last usages of plugin async lifecycles (#112111) * remove last usages of plugin async lifecycles * fix contract type * fix types. again. * remove unused import --- src/plugins/presentation_util/README.mdx | 2 +- x-pack/plugins/lists/server/plugin.ts | 8 +++----- x-pack/plugins/monitoring/server/plugin.ts | 2 +- .../plugins/kibana_cors_test/server/plugin.ts | 14 ++++++-------- .../application_usage_test/public/plugin.ts | 10 +++++++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/plugins/presentation_util/README.mdx b/src/plugins/presentation_util/README.mdx index 35b80e3634534..575e8002e6eb8 100755 --- a/src/plugins/presentation_util/README.mdx +++ b/src/plugins/presentation_util/README.mdx @@ -162,7 +162,7 @@ Once your services and providers are defined, and you have at least one set of f import { pluginServices } from './services'; import { registry } from './services/kibana'; - public async start( + public start( coreStart: CoreStart, startPlugins: StartDeps ): Promise { diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts index 6a1b0733c1867..7fa1bc460723a 100644 --- a/x-pack/plugins/lists/server/plugin.ts +++ b/x-pack/plugins/lists/server/plugin.ts @@ -27,9 +27,7 @@ import { getUser } from './get_user'; import { initSavedObjects } from './saved_objects'; import { ExceptionListClient } from './services/exception_lists/exception_list_client'; -export class ListPlugin - implements Plugin, ListsPluginStart, {}, PluginsStart> -{ +export class ListPlugin implements Plugin { private readonly logger: Logger; private readonly config: ConfigType; private spaces: SpacesServiceStart | undefined | null; @@ -40,7 +38,7 @@ export class ListPlugin this.config = this.initializerContext.config.get(); } - public async setup(core: CoreSetup): Promise { + public setup(core: CoreSetup): ListPluginSetup { const { config } = this; initSavedObjects(core.savedObjects); @@ -70,7 +68,7 @@ export class ListPlugin }; } - public start(core: CoreStart, plugins: PluginsStart): void { + public start(core: CoreStart, plugins: PluginsStart): ListsPluginStart { this.logger.debug('Starting plugin'); this.security = plugins.security; this.spaces = plugins.spaces?.spacesService; diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 3b939e7361652..18eb8fd4d4ddb 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -207,7 +207,7 @@ export class MonitoringPlugin } } - async start(coreStart: CoreStart, { licensing }: PluginsStart) { + start(coreStart: CoreStart, { licensing }: PluginsStart) { const config = this.config!; this.cluster = instantiateClient( config.ui.elasticsearch, diff --git a/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts b/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts index e6c3f4b05aabd..9ba373c02fafa 100644 --- a/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts +++ b/x-pack/test/functional_cors/plugins/kibana_cors_test/server/plugin.ts @@ -7,7 +7,6 @@ import Hapi from '@hapi/hapi'; import { kbnTestConfig } from '@kbn/test'; -import { take } from 'rxjs/operators'; import Url from 'url'; import abab from 'abab'; @@ -47,20 +46,18 @@ fetch('${url}', { export class CorsTestPlugin implements Plugin { private server?: Hapi.Server; + constructor(private readonly initializerContext: PluginInitializerContext) {} - async setup(core: CoreSetup) { + setup(core: CoreSetup) { const router = core.http.createRouter(); router.post({ path: '/cors-test', validate: false }, (context, req, res) => res.ok({ body: 'content from kibana' }) ); } - async start(core: CoreStart) { - const config = await this.initializerContext.config - .create() - .pipe(take(1)) - .toPromise(); + start(core: CoreStart) { + const config = this.initializerContext.config.get(); const server = new Hapi.Server({ port: config.port, @@ -78,8 +75,9 @@ export class CorsTestPlugin implements Plugin { return h.response(renderBody(kibanaUrl)); }, }); - await server.start(); + server.start(); } + public stop() { if (this.server) { this.server.stop(); diff --git a/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts b/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts index b0c777593b021..ff1e89b58c7e8 100644 --- a/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts +++ b/x-pack/test/usage_collection/plugins/application_usage_test/public/plugin.ts @@ -12,8 +12,12 @@ import './types'; export class ApplicationUsageTest implements Plugin { public setup(core: CoreSetup) {} - public async start(core: CoreStart) { - const applications = await core.application.applications$.pipe(first()).toPromise(); - window.__applicationIds__ = [...applications.keys()]; + public start(core: CoreStart) { + core.application.applications$ + .pipe(first()) + .toPromise() + .then((applications) => { + window.__applicationIds__ = [...applications.keys()]; + }); } } From 60500c20afbaa1707f52015c4722ae83f4a47473 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 21 Sep 2021 13:45:49 +0300 Subject: [PATCH 19/69] Move timeseries to vis_types folder (#112228) * Move timeseries to vis_types folder * Fix jest * Fix types * Fix more types * Fix types * fix jest tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .eslintrc.js | 2 +- .github/CODEOWNERS | 2 +- .i18nrc.json | 2 +- api_docs/data.json | 320 +++++++++--------- api_docs/data_index_patterns.json | 116 +++---- api_docs/deprecations_by_plugin.mdx | 36 +- api_docs/vis_type_timeseries.json | 20 +- docs/developer/plugin-list.asciidoc | 2 +- src/plugins/vis_type_timeseries/tsconfig.json | 26 -- .../common/__mocks__/index_patterns_utils.ts | 0 .../timeseries}/common/agg_utils.test.ts | 2 +- .../timeseries}/common/agg_utils.ts | 2 +- .../timeseries}/common/basic_aggs.ts | 0 .../common/calculate_label.test.ts | 0 .../timeseries}/common/calculate_label.ts | 2 +- .../timeseries}/common/constants.ts | 0 .../timeseries}/common/empty_label.ts | 0 .../timeseries}/common/enums/index.ts | 0 .../timeseries}/common/enums/metric_types.ts | 0 .../timeseries}/common/enums/model_types.ts | 0 .../timeseries}/common/enums/panel_types.ts | 0 .../common/enums/timerange_data_modes.ts | 0 .../timeseries}/common/errors.ts | 0 .../timeseries}/common/fields_utils.test.ts | 2 +- .../timeseries}/common/fields_utils.ts | 4 +- .../common/index_patterns_utils.test.ts | 2 +- .../common/index_patterns_utils.ts | 2 +- .../common/interval_regexp.test.ts | 0 .../timeseries}/common/interval_regexp.ts | 0 .../common/last_value_utils.test.ts | 0 .../timeseries}/common/last_value_utils.ts | 0 .../common/operators_utils.test.ts | 0 .../timeseries}/common/operators_utils.ts | 0 .../common/to_percentile_number.ts | 0 .../timeseries}/common/types/color_rules.ts | 0 .../timeseries}/common/types/index.ts | 2 +- .../timeseries}/common/types/panel_model.ts | 2 +- .../timeseries}/common/types/vis_data.ts | 0 .../timeseries}/common/ui_restrictions.ts | 0 .../timeseries}/common/validate_interval.ts | 2 +- .../timeseries}/common/vis_data_utils.ts | 0 .../timeseries}/jest.config.js | 8 +- .../timeseries}/kibana.json | 0 .../public/application/_mixins.scss | 0 .../public/application/_tvb_editor.scss | 0 .../public/application/_variables.scss | 0 .../components/_annotations_editor.scss | 0 .../application/components/_color_picker.scss | 0 .../application/components/_color_rules.scss | 0 .../public/application/components/_error.scss | 0 .../public/application/components/_index.scss | 0 .../components/_markdown_editor.scss | 0 .../application/components/_no_data.scss | 0 .../components/_series_editor.scss | 0 .../application/components/_vis_editor.scss | 0 .../components/_vis_editor_visualization.scss | 0 .../application/components/_vis_picker.scss | 0 .../components/_vis_with_splits.scss | 0 .../components/add_delete_buttons.test.tsx | 0 .../components/add_delete_buttons.tsx | 0 .../application/components/aggs/_agg_row.scss | 0 .../application/components/aggs/_index.scss | 0 .../application/components/aggs/agg.tsx | 0 .../application/components/aggs/agg_row.tsx | 0 .../components/aggs/agg_select.test.tsx | 0 .../components/aggs/agg_select.tsx | 0 .../application/components/aggs/aggs.tsx | 0 .../components/aggs/calculation.js | 0 .../components/aggs/cumulative_sum.js | 0 .../application/components/aggs/derivative.js | 0 .../components/aggs/field_select.tsx | 0 .../components/aggs/filter_ratio.js | 2 +- .../components/aggs/filter_ratio.test.js | 2 +- .../components/aggs/histogram_support.test.js | 2 +- .../components/aggs/invalid_agg.tsx | 0 .../application/components/aggs/math.js | 0 .../components/aggs/metric_select.js | 0 .../components/aggs/moving_average.js | 0 .../application/components/aggs/percentile.js | 2 +- .../components/aggs/percentile_hdr.tsx | 0 .../components/aggs/percentile_rank/index.js | 0 .../percentile_rank/multi_value_row.test.tsx | 0 .../aggs/percentile_rank/multi_value_row.tsx | 0 .../aggs/percentile_rank/percentile_rank.tsx | 2 +- .../percentile_rank_values.tsx | 0 .../components/aggs/percentile_ui.js | 0 .../components/aggs/percentile_ui.test.tsx | 0 .../components/aggs/positive_only.js | 0 .../components/aggs/positive_rate.js | 2 +- .../components/aggs/serial_diff.js | 0 .../application/components/aggs/series_agg.js | 0 .../application/components/aggs/static.js | 0 .../application/components/aggs/std_agg.js | 0 .../components/aggs/std_deviation.js | 2 +- .../components/aggs/std_sibling.js | 0 .../application/components/aggs/top_hit.js | 2 +- .../application/components/aggs/vars.js | 0 .../application/components/annotation_row.tsx | 2 +- .../components/annotations_editor.tsx | 0 .../components/color_picker.test.tsx | 0 .../application/components/color_picker.tsx | 0 .../components/color_rules.test.tsx | 0 .../application/components/color_rules.tsx | 0 .../components/data_format_picker.tsx | 0 .../public/application/components/error.js | 0 .../__snapshots__/icon_select.test.js.snap | 0 .../components/icon_select/icon_select.js | 0 .../icon_select/icon_select.test.js | 0 .../application/components/index_pattern.js | 4 +- .../components/last_value_mode_indicator.tsx | 0 .../components/last_value_mode_popover.scss | 0 .../components/last_value_mode_popover.tsx | 0 .../components/lib/agg_to_component.js | 0 .../components/lib/calculate_siblings.js | 0 .../components/lib/calculate_siblings.test.js | 0 .../lib/check_if_numeric_metric.test.ts | 2 +- .../components/lib/check_if_numeric_metric.ts | 2 +- ...eck_if_series_have_same_formatters.test.ts | 0 .../check_if_series_have_same_formatters.ts | 2 +- .../components/lib/collection_actions.test.ts | 0 .../components/lib/collection_actions.ts | 0 .../lib/convert_series_to_datatable.test.ts | 0 .../lib/convert_series_to_datatable.ts | 0 .../components/lib/convert_series_to_vars.js | 0 .../lib/convert_series_to_vars.test.js | 0 .../components/lib/create_change_handler.js | 0 .../lib/create_field_formatter.test.ts | 2 +- .../components/lib/create_field_formatter.ts | 6 +- .../lib/create_interval_based_formatter.ts | 0 .../lib/create_number_handler.test.ts | 0 .../components/lib/create_number_handler.ts | 0 .../lib/create_select_handler.test.ts | 0 .../components/lib/create_select_handler.ts | 0 .../lib/create_text_handler.test.ts | 0 .../components/lib/create_text_handler.ts | 0 .../components/lib/durations.test.ts | 0 .../application/components/lib/durations.ts | 0 .../components/lib/get_axis_label_string.js | 0 .../lib/get_axis_label_string.test.js | 0 .../lib/get_click_filter_data.test.ts | 0 .../components/lib/get_click_filter_data.ts | 0 .../lib/get_default_query_language.ts | 2 +- .../components/lib/get_display_name.js | 0 .../components/lib/get_formatter_type.test.ts | 0 .../components/lib/get_formatter_type.ts | 0 .../components/lib/get_interval.ts | 2 +- .../components/lib/get_metrics_field.test.ts | 0 .../components/lib/get_metrics_field.ts | 0 .../get_supported_fields_by_metric_type.js | 4 +- ...et_supported_fields_by_metric_type.test.js | 2 +- .../index_pattern_select/combo_box_select.tsx | 2 +- .../field_text_select.tsx | 0 .../lib/index_pattern_select/index.ts | 0 .../index_pattern_select.tsx | 2 +- .../switch_mode_popover.tsx | 0 .../lib/index_pattern_select/types.ts | 2 +- .../lib/label_date_formatter.test.ts | 0 .../components/lib/label_date_formatter.ts | 0 .../components/lib/new_metric_agg_fn.ts | 2 +- .../components/lib/new_series_fn.js | 0 .../components/lib/re_id_series.js | 0 .../components/lib/re_id_series.test.js | 0 .../application/components/lib/reorder.ts | 0 .../components/lib/replace_vars.test.ts | 0 .../components/lib/replace_vars.ts | 0 .../components/lib/series_change_handler.js | 0 .../application/components/lib/stacked.js | 0 .../components/lib/tick_formatter.js | 0 .../components/lib/tick_formatter.test.js | 6 +- .../application/components/lib/types.ts | 0 .../application/components/markdown_editor.js | 2 +- .../components/palette_picker.test.tsx | 2 +- .../application/components/palette_picker.tsx | 0 .../components/panel_config/_index.scss | 0 .../panel_config/_panel_config.scss | 0 .../components/panel_config/gauge.test.tsx | 0 .../components/panel_config/gauge.tsx | 0 .../components/panel_config/index.ts | 0 .../components/panel_config/markdown.tsx | 2 +- .../components/panel_config/metric.tsx | 0 .../components/panel_config/panel_config.tsx | 0 .../components/panel_config/table.tsx | 2 +- .../panel_config/timeseries.test.tsx | 0 .../components/panel_config/timeseries.tsx | 0 .../components/panel_config/top_n.tsx | 0 .../components/panel_config/types.ts | 0 .../components/query_bar_wrapper.tsx | 2 +- .../public/application/components/series.js | 0 .../application/components/series_config.js | 0 ...fig_query_bar_with_ignore_global_filter.js | 0 .../components/series_drag_handler.tsx | 0 .../application/components/series_editor.js | 0 .../public/application/components/split.js | 0 .../splits/__snapshots__/terms.test.js.snap | 0 .../components/splits/everything.js | 0 .../application/components/splits/filter.js | 0 .../components/splits/filter_items.js | 0 .../application/components/splits/filters.js | 0 .../components/splits/group_by_select.js | 0 .../application/components/splits/terms.js | 2 +- .../components/splits/terms.test.js | 0 .../components/splits/unsupported_split.js | 0 .../application/components/svg/bomb_icon.js | 0 .../application/components/svg/fire_icon.js | 0 .../components/timeseries_visualization.scss | 0 .../components/timeseries_visualization.tsx | 2 +- .../use_index_patter_mode_callout.tsx | 0 .../application/components/vis_editor.tsx | 10 +- .../components/vis_editor_lazy.tsx | 0 .../components/vis_editor_visualization.js | 0 .../application/components/vis_picker.tsx | 0 .../components/vis_types/_index.scss | 0 .../components/vis_types/_vis_types.scss | 0 .../components/vis_types/gauge/series.js | 0 .../components/vis_types/gauge/series.test.js | 0 .../components/vis_types/gauge/vis.js | 0 .../application/components/vis_types/index.ts | 2 +- .../vis_types/markdown/_markdown.scss | 0 .../components/vis_types/markdown/series.js | 0 .../components/vis_types/markdown/vis.js | 2 +- .../components/vis_types/metric/series.js | 0 .../vis_types/metric/series.test.js | 0 .../components/vis_types/metric/vis.js | 0 .../components/vis_types/table/config.js | 0 .../components/vis_types/table/is_sortable.js | 0 .../components/vis_types/table/series.js | 0 .../components/vis_types/table/vis.js | 4 +- .../components/vis_types/timeseries/config.js | 0 .../components/vis_types/timeseries/series.js | 0 .../components/vis_types/timeseries/vis.js | 2 +- .../vis_types/timeseries/vis.test.js | 8 +- .../components/vis_types/top_n/series.js | 0 .../components/vis_types/top_n/vis.js | 0 .../application/components/vis_with_splits.js | 0 .../application/components/yes_no.test.tsx | 0 .../public/application/components/yes_no.tsx | 0 .../contexts/form_validation_context.ts | 0 .../contexts/panel_model_context.ts | 0 .../contexts/query_input_bar_context.ts | 0 .../application/contexts/vis_data_context.ts | 0 .../public/application/editor_controller.tsx | 0 .../timeseries}/public/application/index.scss | 0 .../application/lib/check_ui_restrictions.js | 0 .../lib/compute_gradient_final_color.test.ts | 0 .../lib/compute_gradient_final_color.ts | 0 .../public/application/lib/fetch_fields.ts | 0 .../lib/get_split_by_terms_color.test.ts | 2 +- .../lib/get_split_by_terms_color.ts | 0 .../public/application/lib/get_timezone.ts | 0 .../public/application/lib/index.ts | 0 .../public/application/lib/rainbow_colors.ts | 0 .../public/application/lib/set_is_reversed.js | 0 .../visualizations/constants/chart.ts | 0 .../visualizations/constants/icons.ts | 0 .../visualizations/constants/index.ts | 0 .../visualizations/lib/calc_dimensions.js | 0 .../lib/calculate_coordinates.js | 0 .../visualizations/lib/get_value_by.js | 0 .../visualizations/views/_annotation.scss | 0 .../visualizations/views/_gauge.scss | 0 .../visualizations/views/_index.scss | 0 .../visualizations/views/_metric.scss | 0 .../visualizations/views/_top_n.scss | 0 .../visualizations/views/annotation.js | 0 .../application/visualizations/views/gauge.js | 0 .../visualizations/views/gauge_vis.js | 0 .../visualizations/views/metric.js | 0 .../__snapshots__/area_decorator.test.js.snap | 0 .../__snapshots__/bar_decorator.test.js.snap | 0 .../timeseries/decorators/area_decorator.js | 0 .../decorators/area_decorator.test.js | 0 .../timeseries/decorators/bar_decorator.js | 0 .../decorators/bar_decorator.test.js | 0 .../visualizations/views/timeseries/index.js | 2 +- .../model/__snapshots__/charts.test.js.snap | 0 .../views/timeseries/model/charts.js | 0 .../views/timeseries/model/charts.test.js | 0 .../__snapshots__/series_styles.test.js.snap | 0 .../utils/series_domain_calculation.ts | 0 .../utils/series_domain_calculations.test.ts | 2 +- .../views/timeseries/utils/series_styles.js | 0 .../timeseries/utils/series_styles.test.js | 0 .../views/timeseries/utils/stack_format.js | 0 .../timeseries/utils/stack_format.test.js | 0 .../views/timeseries/utils/theme.test.ts | 0 .../views/timeseries/utils/theme.ts | 0 .../application/visualizations/views/top_n.js | 0 .../timeseries}/public/index.ts | 2 +- .../timeseries}/public/metrics_fn.ts | 4 +- .../timeseries}/public/metrics_type.ts | 2 +- .../timeseries}/public/plugin.ts | 10 +- .../timeseries}/public/request_handler.ts | 2 +- .../timeseries}/public/services.ts | 6 +- .../timeseries}/public/test_utils/index.ts | 0 .../public/timeseries_vis_renderer.tsx | 4 +- .../timeseries}/public/to_ast.ts | 4 +- .../timeseries}/public/types.ts | 0 .../timeseries}/server/config.ts | 0 .../timeseries}/server/index.ts | 0 .../timeseries}/server/lib/get_fields.ts | 0 .../timeseries}/server/lib/get_vis_data.ts | 0 .../default_search_capabilities.test.ts | 0 .../default_search_capabilities.ts | 0 .../rollup_search_capabilities.test.ts | 0 .../rollup_search_capabilities.ts | 0 .../server/lib/search_strategies/index.ts | 0 .../lib/cached_index_pattern_fetcher.test.ts | 0 .../lib/cached_index_pattern_fetcher.ts | 2 +- .../search_strategies/lib/fields_fetcher.ts | 2 +- .../lib/interval_helper.test.ts | 0 .../search_strategies/lib/interval_helper.ts | 0 .../search_strategies_registry.test.ts | 0 .../search_strategy_registry.ts | 0 .../abstract_search_strategy.test.ts | 4 +- .../strategies/abstract_search_strategy.ts | 2 +- .../default_search_strategy.test.ts | 0 .../strategies/default_search_strategy.ts | 2 +- .../lib/search_strategies/strategies/index.ts | 0 .../strategies/rollup_search_strategy.test.ts | 2 +- .../strategies/rollup_search_strategy.ts | 5 +- .../annotations/build_request_body.ts | 0 .../annotations/get_request_params.ts | 0 .../vis_data/build_processor_function.test.ts | 0 .../lib/vis_data/build_processor_function.ts | 0 .../server/lib/vis_data/get_annotations.ts | 0 .../get_interval_and_timefield.test.ts | 0 .../vis_data/get_interval_and_timefield.ts | 0 .../server/lib/vis_data/get_series_data.ts | 0 .../server/lib/vis_data/get_table_data.ts | 0 .../vis_data/handle_error_response.test.ts | 0 .../lib/vis_data/handle_error_response.ts | 0 .../bucket_transform.test.js.snap | 0 .../lib/vis_data/helpers/bucket_transform.js | 0 .../vis_data/helpers/bucket_transform.test.js | 0 .../server/lib/vis_data/helpers/check_aggs.ts | 0 .../server/lib/vis_data/helpers/format_key.ts | 0 .../lib/vis_data/helpers/get_active_series.ts | 0 .../lib/vis_data/helpers/get_agg_value.js | 2 +- .../vis_data/helpers/get_agg_value.test.js | 0 .../vis_data/helpers/get_bucket_size.test.ts | 0 .../lib/vis_data/helpers/get_bucket_size.ts | 2 +- .../vis_data/helpers/get_buckets_path.test.ts | 0 .../lib/vis_data/helpers/get_buckets_path.ts | 2 +- .../helpers/get_default_decoration.js | 0 .../helpers/get_default_decoration.test.js | 0 .../helpers/get_es_query_uisettings.ts | 2 +- .../vis_data/helpers/get_last_metric.test.ts | 0 .../lib/vis_data/helpers/get_last_metric.ts | 0 .../vis_data/helpers/get_sibling_agg_value.js | 0 .../helpers/get_sibling_agg_value.test.js | 0 .../lib/vis_data/helpers/get_splits.test.js | 0 .../server/lib/vis_data/helpers/get_splits.ts | 0 .../vis_data/helpers/get_timerange.test.ts | 0 .../lib/vis_data/helpers/get_timerange.ts | 0 .../vis_data/helpers/get_timerange_mode.ts | 0 .../server/lib/vis_data/helpers/index.ts | 0 .../helpers/map_empty_to_zero.test.ts | 2 +- .../lib/vis_data/helpers/map_empty_to_zero.ts | 2 +- .../lib/vis_data/helpers/moving_fn_scripts.js | 0 .../helpers/moving_fn_scripts.test.js | 0 .../server/lib/vis_data/helpers/overwrite.ts | 0 .../lib/vis_data/helpers/parse_interval.js | 0 .../lib/vis_data/helpers/timestamp.test.ts | 0 .../server/lib/vis_data/helpers/timestamp.ts | 0 .../vis_data/helpers/unit_to_seconds.test.ts | 0 .../lib/vis_data/helpers/unit_to_seconds.ts | 0 .../server/lib/vis_data/offset_time.js | 0 .../server/lib/vis_data/offset_time.test.js | 0 .../annotations/date_histogram.ts | 2 +- .../request_processors/annotations/index.ts | 0 .../request_processors/annotations/query.ts | 2 +- .../annotations/top_hits.ts | 0 .../request_processors/annotations/types.ts | 0 .../series/date_histogram.js | 2 +- .../series/date_histogram.test.js | 2 +- .../series/filter_ratios.js | 2 +- .../series/filter_ratios.test.js | 0 .../request_processors/series/index.js | 0 .../series/metric_buckets.js | 0 .../series/metric_buckets.test.js | 0 .../series/normalize_query.js | 0 .../series/normalize_query.test.js | 0 .../series/positive_rate.js | 2 +- .../series/positive_rate.test.js | 0 .../request_processors/series/query.js | 2 +- .../request_processors/series/query.test.js | 0 .../series/sibling_buckets.js | 0 .../series/sibling_buckets.test.js | 0 .../series/split_by_everything.js | 0 .../series/split_by_everything.test.js | 0 .../series/split_by_filter.js | 2 +- .../series/split_by_filter.test.js | 0 .../series/split_by_filters.js | 2 +- .../series/split_by_filters.test.js | 0 .../series/split_by_terms.js | 0 .../series/split_by_terms.test.js | 0 .../table/calculate_agg_root.ts | 0 .../table/date_histogram.ts | 2 +- .../request_processors/table/filter_ratios.ts | 2 +- .../request_processors/table/index.ts | 0 .../table/metric_buckets.ts | 0 .../table/normalize_query.test.ts | 0 .../table/normalize_query.ts | 0 .../request_processors/table/pivot.ts | 0 .../request_processors/table/positive_rate.ts | 2 +- .../request_processors/table/query.ts | 2 +- .../table/sibling_buckets.ts | 0 .../table/split_by_everything.ts | 2 +- .../table/split_by_terms.ts | 2 +- .../request_processors/table/types.ts | 2 +- .../lib/vis_data/request_processors/types.ts | 0 .../annotations/buckets.ts | 0 .../annotations/filter.test.ts | 0 .../response_processors/annotations/filter.ts | 0 .../response_processors/annotations/index.ts | 0 .../response_processors/series/_series_agg.js | 0 .../series/_series_agg.test.js | 0 .../series/drop_last_bucket.js | 0 .../series/format_label.ts | 2 +- .../response_processors/series/index.js | 0 .../response_processors/series/math.js | 0 .../response_processors/series/math.test.js | 0 .../response_processors/series/percentile.js | 0 .../series/percentile.test.js | 0 .../series/percentile_rank.js | 0 .../series/percentile_rank.test.ts | 0 .../response_processors/series/series_agg.js | 0 .../series/series_agg.test.js | 0 .../series/std_deviation_bands.js | 0 .../series/std_deviation_bands.test.js | 0 .../series/std_deviation_sibling.js | 0 .../series/std_deviation_sibling.test.js | 0 .../response_processors/series/std_metric.js | 0 .../series/std_metric.test.js | 0 .../response_processors/series/std_sibling.js | 0 .../series/std_sibling.test.js | 0 .../response_processors/series/time_shift.js | 0 .../series/time_shift.test.js | 0 .../response_processors/table/_series_agg.js | 0 .../table/drop_last_bucket.ts | 0 .../response_processors/table/index.ts | 0 .../response_processors/table/math.ts | 0 .../response_processors/table/percentile.ts | 0 .../table/percentile_rank.ts | 0 .../response_processors/table/series_agg.ts | 0 .../response_processors/table/std_metric.ts | 0 .../response_processors/table/std_sibling.ts | 0 .../response_processors/table/types.ts | 0 .../series/build_request_body.test.ts | 0 .../lib/vis_data/series/build_request_body.ts | 0 .../lib/vis_data/series/get_request_params.ts | 0 .../vis_data/series/handle_response_body.ts | 2 +- .../lib/vis_data/table/build_request_body.ts | 0 .../lib/vis_data/table/build_response_body.ts | 0 .../lib/vis_data/table/process_bucket.test.ts | 0 .../lib/vis_data/table/process_bucket.ts | 0 .../timeseries}/server/plugin.ts | 8 +- .../timeseries}/server/routes/fields.ts | 0 .../timeseries}/server/routes/vis.ts | 0 .../timeseries}/server/types.ts | 4 +- .../timeseries}/server/ui_settings.ts | 0 .../get_usage_collector.mock.ts | 0 .../get_usage_collector.test.ts | 7 +- .../usage_collector/get_usage_collector.ts | 6 +- .../server/usage_collector/index.ts | 0 .../register_timeseries_collector.test.ts | 4 +- .../register_timeseries_collector.ts | 2 +- .../vis_types/timeseries/tsconfig.json | 26 ++ .../lib/adapters/framework/adapter_types.ts | 2 +- .../framework/kibana_framework_adapter.ts | 2 +- .../metrics/kibana_metrics_adapter.ts | 2 +- x-pack/plugins/infra/tsconfig.json | 2 +- x-pack/plugins/rollup/server/types.ts | 2 +- x-pack/plugins/rollup/tsconfig.json | 2 +- 474 files changed, 420 insertions(+), 414 deletions(-) delete mode 100644 src/plugins/vis_type_timeseries/tsconfig.json rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/__mocks__/index_patterns_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/agg_utils.test.ts (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/agg_utils.ts (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/basic_aggs.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/calculate_label.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/calculate_label.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/constants.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/empty_label.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/metric_types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/model_types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/panel_types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/enums/timerange_data_modes.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/errors.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/fields_utils.test.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/fields_utils.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/index_patterns_utils.test.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/index_patterns_utils.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/interval_regexp.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/interval_regexp.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/last_value_utils.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/last_value_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/operators_utils.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/operators_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/to_percentile_number.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/color_rules.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/index.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/panel_model.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/types/vis_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/ui_restrictions.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/validate_interval.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/common/vis_data_utils.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/jest.config.js (72%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/kibana.json (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/_mixins.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/_tvb_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/_variables.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_annotations_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_color_picker.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_color_rules.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_error.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_markdown_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_no_data.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_series_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_editor.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_editor_visualization.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_picker.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/_vis_with_splits.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/add_delete_buttons.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/add_delete_buttons.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/_agg_row.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg_row.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg_select.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/agg_select.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/aggs.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/calculation.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/cumulative_sum.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/derivative.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/field_select.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/filter_ratio.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/filter_ratio.test.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/histogram_support.test.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/invalid_agg.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/math.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/metric_select.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/moving_average.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_hdr.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/index.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/multi_value_row.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/percentile_rank.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_ui.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/percentile_ui.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/positive_only.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/positive_rate.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/serial_diff.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/static.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/std_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/std_deviation.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/std_sibling.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/top_hit.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/aggs/vars.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/annotation_row.tsx (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/annotations_editor.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_picker.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_rules.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/color_rules.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/data_format_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/error.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/icon_select/icon_select.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/icon_select/icon_select.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/index_pattern.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/last_value_mode_indicator.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/last_value_mode_popover.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/last_value_mode_popover.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/agg_to_component.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/calculate_siblings.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/calculate_siblings.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_numeric_metric.test.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_numeric_metric.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_series_have_same_formatters.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/check_if_series_have_same_formatters.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/collection_actions.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/collection_actions.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_datatable.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_datatable.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_vars.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/convert_series_to_vars.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_change_handler.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_field_formatter.test.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_field_formatter.ts (86%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_interval_based_formatter.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_number_handler.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_number_handler.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_select_handler.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_select_handler.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_text_handler.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/create_text_handler.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/durations.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/durations.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_axis_label_string.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_axis_label_string.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_click_filter_data.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_click_filter_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_default_query_language.ts (89%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_display_name.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_formatter_type.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_formatter_type.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_interval.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_metrics_field.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_metrics_field.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_supported_fields_by_metric_type.js (87%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/get_supported_fields_by_metric_type.test.js (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/combo_box_select.tsx (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/field_text_select.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/index_pattern_select.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/index_pattern_select/types.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/label_date_formatter.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/label_date_formatter.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/new_metric_agg_fn.ts (89%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/new_series_fn.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/re_id_series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/re_id_series.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/reorder.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/replace_vars.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/replace_vars.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/series_change_handler.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/stacked.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/tick_formatter.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/tick_formatter.test.js (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/lib/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/markdown_editor.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/palette_picker.test.tsx (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/palette_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/_panel_config.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/gauge.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/gauge.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/markdown.tsx (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/metric.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/panel_config.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/table.tsx (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/timeseries.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/timeseries.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/top_n.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/panel_config/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/query_bar_wrapper.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_config.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_config_query_bar_with_ignore_global_filter.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_drag_handler.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/series_editor.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/split.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/__snapshots__/terms.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/everything.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/filter.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/filter_items.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/filters.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/group_by_select.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/terms.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/terms.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/splits/unsupported_split.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/svg/bomb_icon.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/svg/fire_icon.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/timeseries_visualization.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/timeseries_visualization.tsx (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/use_index_patter_mode_callout.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_editor.tsx (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_editor_lazy.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_editor_visualization.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_picker.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/_vis_types.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/gauge/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/gauge/series.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/gauge/vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/index.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/markdown/_markdown.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/markdown/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/markdown/vis.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/metric/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/metric/series.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/metric/vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/config.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/is_sortable.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/table/vis.js (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/config.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/vis.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/timeseries/vis.test.js (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/top_n/series.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_types/top_n/vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/vis_with_splits.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/yes_no.test.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/components/yes_no.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/form_validation_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/panel_model_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/query_input_bar_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/contexts/vis_data_context.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/editor_controller.tsx (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/check_ui_restrictions.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/compute_gradient_final_color.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/compute_gradient_final_color.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/fetch_fields.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/get_split_by_terms_color.test.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/get_split_by_terms_color.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/get_timezone.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/rainbow_colors.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/lib/set_is_reversed.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/constants/chart.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/constants/icons.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/constants/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/lib/calc_dimensions.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/lib/calculate_coordinates.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/lib/get_value_by.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_annotation.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_gauge.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_index.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_metric.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/_top_n.scss (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/annotation.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/gauge.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/gauge_vis.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/metric.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/area_decorator.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/bar_decorator.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/index.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/model/charts.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/model/charts.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_styles.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/series_styles.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/stack_format.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/stack_format.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/theme.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/timeseries/utils/theme.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/application/visualizations/views/top_n.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/index.ts (88%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/metrics_fn.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/metrics_type.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/plugin.ts (86%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/request_handler.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/services.ts (84%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/test_utils/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/timeseries_vis_renderer.tsx (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/to_ast.ts (91%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/public/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/config.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/get_fields.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/get_vis_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/default_search_capabilities.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/fields_fetcher.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/interval_helper.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/lib/interval_helper.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/search_strategies_registry.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/search_strategy_registry.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/abstract_search_strategy.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/default_search_strategy.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/default_search_strategy.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/search_strategies/strategies/rollup_search_strategy.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/annotations/build_request_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/annotations/get_request_params.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/build_processor_function.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/build_processor_function.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_annotations.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_interval_and_timefield.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_interval_and_timefield.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_series_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/get_table_data.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/handle_error_response.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/handle_error_response.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/bucket_transform.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/bucket_transform.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/check_aggs.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/format_key.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_active_series.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_agg_value.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_agg_value.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_bucket_size.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_bucket_size.ts (98%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_buckets_path.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_buckets_path.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_default_decoration.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_default_decoration.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_es_query_uisettings.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_last_metric.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_last_metric.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_sibling_agg_value.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_sibling_agg_value.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_splits.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_splits.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_timerange.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_timerange.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/get_timerange_mode.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/map_empty_to_zero.test.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/map_empty_to_zero.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/moving_fn_scripts.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/moving_fn_scripts.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/overwrite.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/parse_interval.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/timestamp.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/timestamp.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/unit_to_seconds.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/helpers/unit_to_seconds.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/offset_time.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/offset_time.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/date_histogram.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/query.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/top_hits.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/annotations/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/date_histogram.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/date_histogram.test.js (99%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/filter_ratios.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/filter_ratios.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/index.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/metric_buckets.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/metric_buckets.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/normalize_query.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/normalize_query.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/positive_rate.js (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/positive_rate.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/query.js (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/query.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/sibling_buckets.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/sibling_buckets.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_everything.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_everything.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filter.js (92%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filter.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filters.js (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_filters.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_terms.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/series/split_by_terms.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/calculate_agg_root.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/date_histogram.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/filter_ratios.ts (97%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/metric_buckets.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/normalize_query.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/normalize_query.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/pivot.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/positive_rate.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/query.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/sibling_buckets.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/split_by_everything.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/split_by_terms.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/table/types.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/request_processors/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/buckets.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/filter.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/filter.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/annotations/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/_series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/_series_agg.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/drop_last_bucket.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/format_label.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/index.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/math.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/math.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile_rank.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/percentile_rank.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/series_agg.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_bands.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_sibling.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_metric.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_metric.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_sibling.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/std_sibling.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/time_shift.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/series/time_shift.test.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/_series_agg.js (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/drop_last_bucket.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/math.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/percentile.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/percentile_rank.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/series_agg.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/std_metric.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/std_sibling.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/response_processors/table/types.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/build_request_body.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/build_request_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/get_request_params.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/series/handle_response_body.ts (96%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/build_request_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/build_response_body.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/process_bucket.test.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/lib/vis_data/table/process_bucket.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/plugin.ts (94%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/routes/fields.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/routes/vis.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/types.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/ui_settings.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/get_usage_collector.mock.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/get_usage_collector.test.ts (95%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/get_usage_collector.ts (93%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/index.ts (100%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/register_timeseries_collector.test.ts (92%) rename src/plugins/{vis_type_timeseries => vis_types/timeseries}/server/usage_collector/register_timeseries_collector.ts (92%) create mode 100644 src/plugins/vis_types/timeseries/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 30bc9d7322673..83afc27263248 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1502,7 +1502,7 @@ module.exports = { * TSVB overrides */ { - files: ['src/plugins/vis_type_timeseries/**/*.{js,mjs,ts,tsx}'], + files: ['src/plugins/vis_types/timeseries/**/*.{js,mjs,ts,tsx}'], rules: { 'import/no-default-export': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7831981569225..6ae834b58fc89 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,7 @@ /src/plugins/vis_types/table/ @elastic/kibana-vis-editors /src/plugins/vis_types/tagcloud/ @elastic/kibana-vis-editors /src/plugins/vis_types/timelion/ @elastic/kibana-vis-editors -/src/plugins/vis_type_timeseries/ @elastic/kibana-vis-editors +/src/plugins/vis_types/timeseries/ @elastic/kibana-vis-editors /src/plugins/vis_types/vega/ @elastic/kibana-vis-editors /src/plugins/vis_types/vislib/ @elastic/kibana-vis-editors /src/plugins/vis_types/xy/ @elastic/kibana-vis-editors diff --git a/.i18nrc.json b/.i18nrc.json index 90afa274c6e6d..4107772e421ca 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -63,7 +63,7 @@ "visTypeMetric": "src/plugins/vis_types/metric", "visTypeTable": "src/plugins/vis_types/table", "visTypeTagCloud": "src/plugins/vis_types/tagcloud", - "visTypeTimeseries": "src/plugins/vis_type_timeseries", + "visTypeTimeseries": "src/plugins/vis_types/timeseries", "visTypeVega": "src/plugins/vis_types/vega", "visTypeVislib": "src/plugins/vis_types/vislib", "visTypeXy": "src/plugins/vis_types/xy", diff --git a/api_docs/data.json b/api_docs/data.json index e62ddb5e30f12..3012f68b4c0ff 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -2716,19 +2716,19 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "reporting", @@ -2924,11 +2924,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "osquery", @@ -3076,35 +3076,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "discover", @@ -4972,15 +4972,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "inputControlVis", @@ -5144,15 +5144,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -5176,15 +5176,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "discover", @@ -6581,27 +6581,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -6874,51 +6874,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -6946,39 +6946,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -6990,27 +6990,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", @@ -14937,11 +14937,11 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" } ], "initialIsOpen": false @@ -16879,11 +16879,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "securitySolution", @@ -24265,35 +24265,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts" + "path": "src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/metrics_type.ts" + "path": "src/plugins/vis_types/timeseries/public/metrics_type.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx" }, { "plugin": "visTypeVega", @@ -24483,7 +24483,7 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/plugin.ts" + "path": "src/plugins/vis_types/timeseries/public/plugin.ts" }, { "plugin": "visTypeMetric", @@ -25018,19 +25018,19 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "reporting", @@ -25226,11 +25226,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "osquery", @@ -25378,35 +25378,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "discover", @@ -27274,15 +27274,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "inputControlVis", @@ -27446,15 +27446,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -27478,15 +27478,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "discover", @@ -28883,27 +28883,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -29176,51 +29176,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -29248,39 +29248,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -29292,27 +29292,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", @@ -29373,51 +29373,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -29445,39 +29445,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -29489,27 +29489,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", @@ -30753,11 +30753,11 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" } ], "initialIsOpen": false @@ -32456,11 +32456,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "securitySolution", @@ -37439,11 +37439,11 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts" } ], "initialIsOpen": false @@ -39172,11 +39172,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "securitySolution", diff --git a/api_docs/data_index_patterns.json b/api_docs/data_index_patterns.json index cf621b2413975..3ebbf2429948a 100644 --- a/api_docs/data_index_patterns.json +++ b/api_docs/data_index_patterns.json @@ -1111,7 +1111,7 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "graph", @@ -1147,7 +1147,7 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts" + "path": "src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts" } ], "children": [], @@ -3639,19 +3639,19 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "reporting", @@ -3847,11 +3847,11 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/types/index.ts" + "path": "src/plugins/vis_types/timeseries/common/types/index.ts" }, { "plugin": "osquery", @@ -3999,35 +3999,35 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx" }, { "plugin": "discover", @@ -5895,15 +5895,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts" }, { "plugin": "inputControlVis", @@ -6067,15 +6067,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -6099,15 +6099,15 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" + "path": "src/plugins/vis_types/timeseries/target/types/public/application/components/lib/convert_series_to_datatable.d.ts" }, { "plugin": "discover", @@ -7504,27 +7504,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts" }, { "plugin": "discover", @@ -7797,51 +7797,51 @@ "references": [ { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/types.ts" + "path": "src/plugins/vis_types/timeseries/server/types.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts" }, { "plugin": "maps", @@ -7869,39 +7869,39 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/plugin.ts" + "path": "src/plugins/vis_types/timeseries/server/plugin.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts" + "path": "src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts" }, { "plugin": "discover", @@ -7913,27 +7913,27 @@ }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" + "path": "src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "visTypeTimeseries", - "path": "src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" + "path": "src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx" }, { "plugin": "discover", diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 99f241e81384d..67a9e90c74606 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -926,24 +926,24 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | -| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | -| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | -| | [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts#:~:text=indexPatterns), [combo_box_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx#:~:text=indexPatterns), [query_bar_wrapper.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx#:~:text=indexPatterns), [annotation_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx#:~:text=indexPatterns), [metrics_type.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/metrics_type.ts#:~:text=indexPatterns), [convert_series_to_datatable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns) | - | -| | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/plugin.ts#:~:text=fieldFormats) | - | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | -| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=getNonScriptedFields), [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts#:~:text=getNonScriptedFields) | 8.1 | -| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | -| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | -| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | -| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/types.ts#:~:text=IndexPatternsService)+ 44 more | - | -| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | +| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | +| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | +| | [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts#:~:text=indexPatterns), [combo_box_select.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx#:~:text=indexPatterns), [query_bar_wrapper.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx#:~:text=indexPatterns), [annotation_row.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx#:~:text=indexPatterns), [metrics_type.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/metrics_type.ts#:~:text=indexPatterns), [convert_series_to_datatable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns), [timeseries_visualization.tsx](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx#:~:text=indexPatterns) | - | +| | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/plugin.ts#:~:text=fieldFormats) | - | +| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | +| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService)+ 17 more | - | +| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=getNonScriptedFields), [fetch_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts#:~:text=getNonScriptedFields) | 8.1 | +| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | +| | [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField), [convert_series_to_datatable.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts#:~:text=IndexPatternField) | - | +| | [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [cached_index_pattern_fetcher.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern), [index_patterns_utils.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts#:~:text=IndexPattern)+ 13 more | - | +| | [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [abstract_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [default_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [cached_index_pattern_fetcher.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [rollup_search_strategy.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/types.ts#:~:text=IndexPatternsService)+ 44 more | - | +| | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/common/types/index.ts#:~:text=Filter) | 8.1 | +| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts#:~:text=EsQueryConfig) | 8.1 | diff --git a/api_docs/vis_type_timeseries.json b/api_docs/vis_type_timeseries.json index 01b211e3da90b..885f037b00a25 100644 --- a/api_docs/vis_type_timeseries.json +++ b/api_docs/vis_type_timeseries.json @@ -24,7 +24,7 @@ ") => data is ", "SeriesData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "children": [ { @@ -37,7 +37,7 @@ "signature": [ "TimeseriesVisData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "isRequired": true } @@ -58,7 +58,7 @@ ") => data is ", "TableData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "children": [ { @@ -71,7 +71,7 @@ "signature": [ "TimeseriesVisData" ], - "path": "src/plugins/vis_type_timeseries/common/vis_data_utils.ts", + "path": "src/plugins/vis_types/timeseries/common/vis_data_utils.ts", "deprecated": false, "isRequired": true } @@ -95,7 +95,7 @@ " | ", "TableData" ], - "path": "src/plugins/vis_type_timeseries/common/types/vis_data.ts", + "path": "src/plugins/vis_types/timeseries/common/types/vis_data.ts", "deprecated": false, "initialIsOpen": false } @@ -108,7 +108,7 @@ "tags": [], "label": "VisTypeTimeseriesSetup", "description": [], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "children": [ { @@ -133,7 +133,7 @@ "TimeseriesVisData", ">" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "children": [ { @@ -146,7 +146,7 @@ "signature": [ "DataRequestHandlerContext" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "isRequired": true }, @@ -167,7 +167,7 @@ }, "" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "isRequired": true }, @@ -181,7 +181,7 @@ "signature": [ "any" ], - "path": "src/plugins/vis_type_timeseries/server/plugin.ts", + "path": "src/plugins/vis_types/timeseries/server/plugin.ts", "deprecated": false, "isRequired": true } diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 850b80c20e09f..1ab7ab60d4a8e 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -297,7 +297,7 @@ The plugin exposes the static DefaultEditorController class to consume. |Contains the timelion visualization and the timelion backend. -|{kib-repo}blob/{branch}/src/plugins/vis_type_timeseries[visTypeTimeseries] +|{kib-repo}blob/{branch}/src/plugins/vis_types/timeseries[visTypeTimeseries] |WARNING: Missing README. diff --git a/src/plugins/vis_type_timeseries/tsconfig.json b/src/plugins/vis_type_timeseries/tsconfig.json deleted file mode 100644 index 68097d8cff786..0000000000000 --- a/src/plugins/vis_type_timeseries/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "*.ts" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../charts/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, - { "path": "../expressions/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" }, - { "path": "../visualize/tsconfig.json" }, - { "path": "../kibana_utils/tsconfig.json" }, - { "path": "../kibana_react/tsconfig.json" }, - { "path": "../usage_collection/tsconfig.json" }, - ] -} diff --git a/src/plugins/vis_type_timeseries/common/__mocks__/index_patterns_utils.ts b/src/plugins/vis_types/timeseries/common/__mocks__/index_patterns_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/__mocks__/index_patterns_utils.ts rename to src/plugins/vis_types/timeseries/common/__mocks__/index_patterns_utils.ts diff --git a/src/plugins/vis_type_timeseries/common/agg_utils.test.ts b/src/plugins/vis_types/timeseries/common/agg_utils.test.ts similarity index 99% rename from src/plugins/vis_type_timeseries/common/agg_utils.test.ts rename to src/plugins/vis_types/timeseries/common/agg_utils.test.ts index 63d81e2c43d40..21fa870c2ca04 100644 --- a/src/plugins/vis_type_timeseries/common/agg_utils.test.ts +++ b/src/plugins/vis_types/timeseries/common/agg_utils.test.ts @@ -13,7 +13,7 @@ import { getAggsByPredicate, getAggsByType, } from './agg_utils'; -import { METRIC_TYPES } from '../../data/common'; +import { METRIC_TYPES } from '../../../data/common'; import { TSVB_METRIC_TYPES } from './enums'; import type { Metric } from './types'; diff --git a/src/plugins/vis_type_timeseries/common/agg_utils.ts b/src/plugins/vis_types/timeseries/common/agg_utils.ts similarity index 99% rename from src/plugins/vis_type_timeseries/common/agg_utils.ts rename to src/plugins/vis_types/timeseries/common/agg_utils.ts index 2f0488bdc4dbe..71f13542b10f4 100644 --- a/src/plugins/vis_type_timeseries/common/agg_utils.ts +++ b/src/plugins/vis_types/timeseries/common/agg_utils.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { filter } from 'lodash'; import { Assign } from 'utility-types'; -import { METRIC_TYPES } from '../../data/common'; +import { METRIC_TYPES } from '../../../data/common'; import { TSVB_METRIC_TYPES } from './enums'; import type { Metric, MetricType } from './types'; diff --git a/src/plugins/vis_type_timeseries/common/basic_aggs.ts b/src/plugins/vis_types/timeseries/common/basic_aggs.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/basic_aggs.ts rename to src/plugins/vis_types/timeseries/common/basic_aggs.ts diff --git a/src/plugins/vis_type_timeseries/common/calculate_label.test.ts b/src/plugins/vis_types/timeseries/common/calculate_label.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/calculate_label.test.ts rename to src/plugins/vis_types/timeseries/common/calculate_label.test.ts diff --git a/src/plugins/vis_type_timeseries/common/calculate_label.ts b/src/plugins/vis_types/timeseries/common/calculate_label.ts similarity index 98% rename from src/plugins/vis_type_timeseries/common/calculate_label.ts rename to src/plugins/vis_types/timeseries/common/calculate_label.ts index d054698536b5b..e5cf053d310aa 100644 --- a/src/plugins/vis_type_timeseries/common/calculate_label.ts +++ b/src/plugins/vis_types/timeseries/common/calculate_label.ts @@ -10,7 +10,7 @@ import { includes, startsWith } from 'lodash'; import { i18n } from '@kbn/i18n'; import { getMetricLabel } from './agg_utils'; import { extractFieldLabel } from './fields_utils'; -import { METRIC_TYPES } from '../../data/common'; +import { METRIC_TYPES } from '../../../data/common'; import { TSVB_METRIC_TYPES } from './enums'; import type { Metric, SanitizedFieldType } from './types'; diff --git a/src/plugins/vis_type_timeseries/common/constants.ts b/src/plugins/vis_types/timeseries/common/constants.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/constants.ts rename to src/plugins/vis_types/timeseries/common/constants.ts diff --git a/src/plugins/vis_type_timeseries/common/empty_label.ts b/src/plugins/vis_types/timeseries/common/empty_label.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/empty_label.ts rename to src/plugins/vis_types/timeseries/common/empty_label.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/index.ts b/src/plugins/vis_types/timeseries/common/enums/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/index.ts rename to src/plugins/vis_types/timeseries/common/enums/index.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/metric_types.ts b/src/plugins/vis_types/timeseries/common/enums/metric_types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/metric_types.ts rename to src/plugins/vis_types/timeseries/common/enums/metric_types.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/model_types.ts b/src/plugins/vis_types/timeseries/common/enums/model_types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/model_types.ts rename to src/plugins/vis_types/timeseries/common/enums/model_types.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/panel_types.ts b/src/plugins/vis_types/timeseries/common/enums/panel_types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/panel_types.ts rename to src/plugins/vis_types/timeseries/common/enums/panel_types.ts diff --git a/src/plugins/vis_type_timeseries/common/enums/timerange_data_modes.ts b/src/plugins/vis_types/timeseries/common/enums/timerange_data_modes.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/enums/timerange_data_modes.ts rename to src/plugins/vis_types/timeseries/common/enums/timerange_data_modes.ts diff --git a/src/plugins/vis_type_timeseries/common/errors.ts b/src/plugins/vis_types/timeseries/common/errors.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/errors.ts rename to src/plugins/vis_types/timeseries/common/errors.ts diff --git a/src/plugins/vis_type_timeseries/common/fields_utils.test.ts b/src/plugins/vis_types/timeseries/common/fields_utils.test.ts similarity index 96% rename from src/plugins/vis_type_timeseries/common/fields_utils.test.ts rename to src/plugins/vis_types/timeseries/common/fields_utils.test.ts index f056c38b0c0c3..228dfbfd2db9d 100644 --- a/src/plugins/vis_type_timeseries/common/fields_utils.test.ts +++ b/src/plugins/vis_types/timeseries/common/fields_utils.test.ts @@ -7,7 +7,7 @@ */ import { toSanitizedFieldType } from './fields_utils'; -import type { FieldSpec } from '../../data/common'; +import type { FieldSpec } from '../../../data/common'; describe('fields_utils', () => { describe('toSanitizedFieldType', () => { diff --git a/src/plugins/vis_type_timeseries/common/fields_utils.ts b/src/plugins/vis_types/timeseries/common/fields_utils.ts similarity index 93% rename from src/plugins/vis_type_timeseries/common/fields_utils.ts rename to src/plugins/vis_types/timeseries/common/fields_utils.ts index 1af0340dfa525..d6987b9cdae9c 100644 --- a/src/plugins/vis_type_timeseries/common/fields_utils.ts +++ b/src/plugins/vis_types/timeseries/common/fields_utils.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { FieldSpec } from '../../data/common'; -import { isNestedField } from '../../data/common'; +import { FieldSpec } from '../../../data/common'; +import { isNestedField } from '../../../data/common'; import { FetchedIndexPattern, SanitizedFieldType } from './types'; import { FieldNotFoundError } from './errors'; diff --git a/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts b/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts similarity index 98% rename from src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts rename to src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts index cac607f7c0f90..e9f3be64079ac 100644 --- a/src/plugins/vis_type_timeseries/common/index_patterns_utils.test.ts +++ b/src/plugins/vis_types/timeseries/common/index_patterns_utils.test.ts @@ -12,7 +12,7 @@ import { fetchIndexPattern, } from './index_patterns_utils'; import { Panel } from './types'; -import { IndexPattern, IndexPatternsService } from '../../data/common'; +import { IndexPattern, IndexPatternsService } from '../../../data/common'; describe('isStringTypeIndexPattern', () => { test('should returns true on string-based index', () => { diff --git a/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts b/src/plugins/vis_types/timeseries/common/index_patterns_utils.ts similarity index 97% rename from src/plugins/vis_type_timeseries/common/index_patterns_utils.ts rename to src/plugins/vis_types/timeseries/common/index_patterns_utils.ts index 1a8c277efbf7c..0a65e9e16d130 100644 --- a/src/plugins/vis_type_timeseries/common/index_patterns_utils.ts +++ b/src/plugins/vis_types/timeseries/common/index_patterns_utils.ts @@ -8,7 +8,7 @@ import { uniq } from 'lodash'; import type { Panel, IndexPatternValue, FetchedIndexPattern } from '../common/types'; -import { IndexPatternsService } from '../../data/common'; +import { IndexPatternsService } from '../../../data/common'; export const isStringTypeIndexPattern = ( indexPatternValue: IndexPatternValue diff --git a/src/plugins/vis_type_timeseries/common/interval_regexp.test.ts b/src/plugins/vis_types/timeseries/common/interval_regexp.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/interval_regexp.test.ts rename to src/plugins/vis_types/timeseries/common/interval_regexp.test.ts diff --git a/src/plugins/vis_type_timeseries/common/interval_regexp.ts b/src/plugins/vis_types/timeseries/common/interval_regexp.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/interval_regexp.ts rename to src/plugins/vis_types/timeseries/common/interval_regexp.ts diff --git a/src/plugins/vis_type_timeseries/common/last_value_utils.test.ts b/src/plugins/vis_types/timeseries/common/last_value_utils.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/last_value_utils.test.ts rename to src/plugins/vis_types/timeseries/common/last_value_utils.test.ts diff --git a/src/plugins/vis_type_timeseries/common/last_value_utils.ts b/src/plugins/vis_types/timeseries/common/last_value_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/last_value_utils.ts rename to src/plugins/vis_types/timeseries/common/last_value_utils.ts diff --git a/src/plugins/vis_type_timeseries/common/operators_utils.test.ts b/src/plugins/vis_types/timeseries/common/operators_utils.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/operators_utils.test.ts rename to src/plugins/vis_types/timeseries/common/operators_utils.test.ts diff --git a/src/plugins/vis_type_timeseries/common/operators_utils.ts b/src/plugins/vis_types/timeseries/common/operators_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/operators_utils.ts rename to src/plugins/vis_types/timeseries/common/operators_utils.ts diff --git a/src/plugins/vis_type_timeseries/common/to_percentile_number.ts b/src/plugins/vis_types/timeseries/common/to_percentile_number.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/to_percentile_number.ts rename to src/plugins/vis_types/timeseries/common/to_percentile_number.ts diff --git a/src/plugins/vis_type_timeseries/common/types/color_rules.ts b/src/plugins/vis_types/timeseries/common/types/color_rules.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/types/color_rules.ts rename to src/plugins/vis_types/timeseries/common/types/color_rules.ts diff --git a/src/plugins/vis_type_timeseries/common/types/index.ts b/src/plugins/vis_types/timeseries/common/types/index.ts similarity index 95% rename from src/plugins/vis_type_timeseries/common/types/index.ts rename to src/plugins/vis_types/timeseries/common/types/index.ts index fb8e217fe704c..123b6723d8ccd 100644 --- a/src/plugins/vis_type_timeseries/common/types/index.ts +++ b/src/plugins/vis_types/timeseries/common/types/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, IndexPattern, Query } from '../../../data/common'; +import { Filter, IndexPattern, Query } from '../../../../data/common'; import { Panel } from './panel_model'; export { Metric, Series, Panel, MetricType } from './panel_model'; diff --git a/src/plugins/vis_type_timeseries/common/types/panel_model.ts b/src/plugins/vis_types/timeseries/common/types/panel_model.ts similarity index 98% rename from src/plugins/vis_type_timeseries/common/types/panel_model.ts rename to src/plugins/vis_types/timeseries/common/types/panel_model.ts index 6fd2e727ade32..f71602fdf0443 100644 --- a/src/plugins/vis_type_timeseries/common/types/panel_model.ts +++ b/src/plugins/vis_types/timeseries/common/types/panel_model.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { METRIC_TYPES, Query } from '../../../data/common'; +import { METRIC_TYPES, Query } from '../../../../data/common'; import { PANEL_TYPES, TOOLTIP_MODES, TSVB_METRIC_TYPES } from '../enums'; import { IndexPatternValue, Annotation } from './index'; import { ColorRules, BackgroundColorRules, BarColorRules, GaugeColorRules } from './color_rules'; diff --git a/src/plugins/vis_type_timeseries/common/types/vis_data.ts b/src/plugins/vis_types/timeseries/common/types/vis_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/types/vis_data.ts rename to src/plugins/vis_types/timeseries/common/types/vis_data.ts diff --git a/src/plugins/vis_type_timeseries/common/ui_restrictions.ts b/src/plugins/vis_types/timeseries/common/ui_restrictions.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/ui_restrictions.ts rename to src/plugins/vis_types/timeseries/common/ui_restrictions.ts diff --git a/src/plugins/vis_type_timeseries/common/validate_interval.ts b/src/plugins/vis_types/timeseries/common/validate_interval.ts similarity index 93% rename from src/plugins/vis_type_timeseries/common/validate_interval.ts rename to src/plugins/vis_types/timeseries/common/validate_interval.ts index 7c7a4e7badfc0..78a2410f905c9 100644 --- a/src/plugins/vis_type_timeseries/common/validate_interval.ts +++ b/src/plugins/vis_types/timeseries/common/validate_interval.ts @@ -7,7 +7,7 @@ */ import { GTE_INTERVAL_RE } from './interval_regexp'; -import { parseInterval, TimeRangeBounds } from '../../data/common'; +import { parseInterval, TimeRangeBounds } from '../../../data/common'; import { ValidateIntervalError } from './errors'; export function validateInterval(bounds: TimeRangeBounds, interval: string, maxBuckets: number) { diff --git a/src/plugins/vis_type_timeseries/common/vis_data_utils.ts b/src/plugins/vis_types/timeseries/common/vis_data_utils.ts similarity index 100% rename from src/plugins/vis_type_timeseries/common/vis_data_utils.ts rename to src/plugins/vis_types/timeseries/common/vis_data_utils.ts diff --git a/src/plugins/vis_type_timeseries/jest.config.js b/src/plugins/vis_types/timeseries/jest.config.js similarity index 72% rename from src/plugins/vis_type_timeseries/jest.config.js rename to src/plugins/vis_types/timeseries/jest.config.js index 3d4333675f7d7..d6ddcaa3344b0 100644 --- a/src/plugins/vis_type_timeseries/jest.config.js +++ b/src/plugins/vis_types/timeseries/jest.config.js @@ -8,11 +8,11 @@ module.exports = { preset: '@kbn/test', - rootDir: '../../..', - roots: ['/src/plugins/vis_type_timeseries'], - coverageDirectory: '/target/kibana-coverage/jest/src/plugins/vis_type_timeseries', + rootDir: '../../../..', + roots: ['/src/plugins/vis_types/timeseries'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/vis_types/timeseries', coverageReporters: ['text', 'html'], collectCoverageFrom: [ - '/src/plugins/vis_type_timeseries/{common,public,server}/**/*.{js,ts,tsx}', + '/src/plugins/vis_types/timeseries/{common,public,server}/**/*.{js,ts,tsx}', ], }; diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_types/timeseries/kibana.json similarity index 100% rename from src/plugins/vis_type_timeseries/kibana.json rename to src/plugins/vis_types/timeseries/kibana.json diff --git a/src/plugins/vis_type_timeseries/public/application/_mixins.scss b/src/plugins/vis_types/timeseries/public/application/_mixins.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/_mixins.scss rename to src/plugins/vis_types/timeseries/public/application/_mixins.scss diff --git a/src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss b/src/plugins/vis_types/timeseries/public/application/_tvb_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/_tvb_editor.scss rename to src/plugins/vis_types/timeseries/public/application/_tvb_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/_variables.scss b/src/plugins/vis_types/timeseries/public/application/_variables.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/_variables.scss rename to src/plugins/vis_types/timeseries/public/application/_variables.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_annotations_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_annotations_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_annotations_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_annotations_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_color_picker.scss b/src/plugins/vis_types/timeseries/public/application/components/_color_picker.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_color_picker.scss rename to src/plugins/vis_types/timeseries/public/application/components/_color_picker.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_color_rules.scss b/src/plugins/vis_types/timeseries/public/application/components/_color_rules.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_color_rules.scss rename to src/plugins/vis_types/timeseries/public/application/components/_color_rules.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_error.scss b/src/plugins/vis_types/timeseries/public/application/components/_error.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_error.scss rename to src/plugins/vis_types/timeseries/public/application/components/_error.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_markdown_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_markdown_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_markdown_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_markdown_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_no_data.scss b/src/plugins/vis_types/timeseries/public/application/components/_no_data.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_no_data.scss rename to src/plugins/vis_types/timeseries/public/application/components/_no_data.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_series_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_series_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_series_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_series_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_editor.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_editor.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_editor.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_editor.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_editor_visualization.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_editor_visualization.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_editor_visualization.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_editor_visualization.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_picker.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_picker.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_picker.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_picker.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/_vis_with_splits.scss b/src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/_vis_with_splits.scss rename to src/plugins/vis_types/timeseries/public/application/components/_vis_with_splits.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.tsx b/src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/add_delete_buttons.tsx rename to src/plugins/vis_types/timeseries/public/application/components/add_delete_buttons.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/_agg_row.scss b/src/plugins/vis_types/timeseries/public/application/components/aggs/_agg_row.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/_agg_row.scss rename to src/plugins/vis_types/timeseries/public/application/components/aggs/_agg_row.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/aggs/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/aggs/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_row.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg_row.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg_row.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg_row.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/agg_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/agg_select.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/aggs.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/aggs.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/calculation.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/calculation.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/calculation.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/calculation.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/cumulative_sum.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/cumulative_sum.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/cumulative_sum.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/cumulative_sum.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/derivative.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/derivative.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/derivative.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/derivative.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/field_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/field_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.js index 6a3f57e502796..59a372ccc1107 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.js @@ -24,7 +24,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { getSupportedFieldsByMetricType } from '../lib/get_supported_fields_by_metric_type'; import { getDataStart } from '../../../services'; import { QueryBarWrapper } from '../query_bar_wrapper'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.test.js similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.test.js index 5648a8d3e7133..bd9ceeeb74028 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/filter_ratio.test.js @@ -11,7 +11,7 @@ import { mountWithIntl } from '@kbn/test/jest'; import { FilterRatioAgg } from './filter_ratio'; import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils'; import { EuiComboBox } from '@elastic/eui'; -import { dataPluginMock } from '../../../../../data/public/mocks'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; import { setDataStart } from '../../../services'; jest.mock('../query_bar_wrapper', () => ({ diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/histogram_support.test.js similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/histogram_support.test.js index c4a49a393acd6..c131ba2ae804c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/histogram_support.test.js @@ -12,7 +12,7 @@ import { Agg } from './agg'; import { FieldSelect } from './field_select'; import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils'; import { setDataStart } from '../../../services'; -import { dataPluginMock } from '../../../../../data/public/mocks'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; jest.mock('../query_bar_wrapper', () => ({ QueryBarWrapper: jest.fn(() => null), diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/invalid_agg.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/invalid_agg.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/invalid_agg.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/invalid_agg.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/math.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/math.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/math.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/math.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/metric_select.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/metric_select.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/metric_select.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/metric_select.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/moving_average.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/moving_average.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/moving_average.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/moving_average.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile.js index 94adb37de156b..3e4159cdf42dc 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile.js @@ -23,7 +23,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { Percentiles, newPercentile } from './percentile_ui'; import { PercentileHdr } from './percentile_hdr'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_hdr.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_hdr.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/index.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/index.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/index.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/index.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx index 7f9634ff15844..8b71dec15f17c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx @@ -26,7 +26,7 @@ import { createNumberHandler } from '../../lib/create_number_handler'; import { AggRow } from '../agg_row'; import { PercentileRankValues } from './percentile_rank_values'; -import { KBN_FIELD_TYPES } from '../../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../data/public'; import type { Metric, Panel, SanitizedFieldType, Series } from '../../../../../common/types'; import { TSVB_DEFAULT_COLOR } from '../../../../../common/constants'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/aggs/percentile_ui.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_only.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/positive_only.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/positive_only.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/positive_only.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/positive_rate.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/positive_rate.js index 09d9f2f1a62f2..ea3f8542e8595 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/positive_rate.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/positive_rate.js @@ -26,7 +26,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const UNIT_OPTIONS = [ { diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/serial_diff.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/serial_diff.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/serial_diff.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/serial_diff.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/series_agg.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/series_agg.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/series_agg.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/static.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/static.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/static.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/static.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/std_agg.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/std_agg.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_deviation.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/std_deviation.js index d4caa8a94652f..728cda8720268 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_deviation.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_deviation.js @@ -25,7 +25,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const RESTRICT_FIELDS = KBN_FIELD_TYPES.NUMBER; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/std_sibling.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/std_sibling.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/std_sibling.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/std_sibling.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/top_hit.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/top_hit.js index ade64fc3db8c5..caf601dfab83e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/top_hit.js +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/top_hit.js @@ -24,7 +24,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { PANEL_TYPES } from '../../../../common/enums'; import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/vars.js b/src/plugins/vis_types/timeseries/public/application/components/aggs/vars.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/aggs/vars.js rename to src/plugins/vis_types/timeseries/public/application/components/aggs/vars.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx b/src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx rename to src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx index 379c74d0d4bba..734bdfecac673 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/annotation_row.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/annotation_row.tsx @@ -21,7 +21,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { getDataStart } from '../../services'; -import { KBN_FIELD_TYPES, Query } from '../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES, Query } from '../../../../../../plugins/data/public'; import { AddDeleteButtons } from './add_delete_buttons'; import { ColorPicker } from './color_picker'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx b/src/plugins/vis_types/timeseries/public/application/components/annotations_editor.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/annotations_editor.tsx rename to src/plugins/vis_types/timeseries/public/application/components/annotations_editor.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_picker.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_picker.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_rules.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_rules.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_rules.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_rules.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_rules.tsx b/src/plugins/vis_types/timeseries/public/application/components/color_rules.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/color_rules.tsx rename to src/plugins/vis_types/timeseries/public/application/components/color_rules.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/data_format_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/data_format_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/error.js b/src/plugins/vis_types/timeseries/public/application/components/error.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/error.js rename to src/plugins/vis_types/timeseries/public/application/components/error.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap b/src/plugins/vis_types/timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/components/icon_select/__snapshots__/icon_select.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.js b/src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.js rename to src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.test.js b/src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/icon_select/icon_select.test.js rename to src/plugins/vis_types/timeseries/public/application/components/icon_select/icon_select.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js b/src/plugins/vis_types/timeseries/public/application/components/index_pattern.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/index_pattern.js rename to src/plugins/vis_types/timeseries/public/application/components/index_pattern.js index 1810424625022..e5d09c745b522 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/index_pattern.js +++ b/src/plugins/vis_types/timeseries/public/application/components/index_pattern.js @@ -26,7 +26,7 @@ import { createTextHandler } from './lib/create_text_handler'; import { IndexPatternSelect } from './lib/index_pattern_select'; import { YesNo } from './yes_no'; import { LastValueModePopover } from './last_value_mode_popover'; -import { KBN_FIELD_TYPES } from '../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../data/public'; import { FormValidationContext } from '../contexts/form_validation_context'; import { isGteInterval, validateReInterval, isAutoInterval } from './lib/get_interval'; import { i18n } from '@kbn/i18n'; @@ -36,7 +36,7 @@ import { AUTO_INTERVAL } from '../../../common/constants'; import { isTimerangeModeEnabled } from '../lib/check_ui_restrictions'; import { VisDataContext } from '../contexts/vis_data_context'; import { getDataStart, getUISettings } from '../../services'; -import { UI_SETTINGS } from '../../../../data/common'; +import { UI_SETTINGS } from '../../../../../data/common'; import { fetchIndexPattern } from '../../../common/index_patterns_utils'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.DATE]; diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx b/src/plugins/vis_types/timeseries/public/application/components/last_value_mode_indicator.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/last_value_mode_indicator.tsx rename to src/plugins/vis_types/timeseries/public/application/components/last_value_mode_indicator.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.scss b/src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.scss rename to src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.tsx b/src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/last_value_mode_popover.tsx rename to src/plugins/vis_types/timeseries/public/application/components/last_value_mode_popover.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/agg_to_component.js b/src/plugins/vis_types/timeseries/public/application/components/lib/agg_to_component.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/agg_to_component.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/agg_to_component.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.js b/src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/calculate_siblings.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/calculate_siblings.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.test.ts similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.test.ts index 17827275f86d8..eb6ea561fec84 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import { checkIfNumericMetric } from './check_if_numeric_metric'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.ts similarity index 94% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.ts index a70abaeac9f82..139c13d7ddbda 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_numeric_metric.ts @@ -8,7 +8,7 @@ import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; -import { KBN_FIELD_TYPES } from '../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../data/public'; import type { Metric, IndexPatternValue } from '../../../../common/types'; import type { VisFields } from '../../lib/fetch_fields'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts similarity index 94% rename from src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts index afa1216406ab0..44715d1262d05 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts @@ -9,7 +9,7 @@ import { last, isEqual } from 'lodash'; import { DATA_FORMATTERS } from '../../../../common/enums'; import type { Series } from '../../../../common/types'; -import type { FieldFormatMap } from '../../../../../data/common'; +import type { FieldFormatMap } from '../../../../../../data/common'; export const checkIfSeriesHaveSameFormatters = ( seriesModel: Series[], diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/collection_actions.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/collection_actions.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_datatable.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_datatable.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/convert_series_to_vars.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_change_handler.js b/src/plugins/vis_types/timeseries/public/application/components/lib/create_change_handler.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_change_handler.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_change_handler.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.test.ts similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.test.ts index c56c6820fff48..5a6b6a18f67e0 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.test.ts @@ -7,7 +7,7 @@ */ import { createFieldFormatter } from './create_field_formatter'; -import { getFieldFormatsRegistry } from '../../../../../data/public/test_utils'; +import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; import { setFieldFormats } from '../../../services'; import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common'; import type { CoreSetup } from 'kibana/public'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.ts similarity index 86% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.ts index 5cba549220f2c..a7606895e84aa 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/create_field_formatter.ts @@ -9,9 +9,9 @@ import { isNumber } from 'lodash'; import { getFieldFormats } from '../../../services'; import { isEmptyValue, DISPLAY_EMPTY_VALUE } from '../../../../common/last_value_utils'; -import { FIELD_FORMAT_IDS } from '../../../../../field_formats/common'; -import type { FieldFormatMap } from '../../../../../data/common'; -import type { FieldFormatsContentType } from '../../../../../field_formats/common'; +import { FIELD_FORMAT_IDS } from '../../../../../../field_formats/common'; +import type { FieldFormatMap } from '../../../../../../data/common'; +import type { FieldFormatsContentType } from '../../../../../../field_formats/common'; const DEFAULT_FIELD_FORMAT = { id: 'number' }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_interval_based_formatter.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_interval_based_formatter.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_number_handler.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_number_handler.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_select_handler.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_select_handler.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_text_handler.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/create_text_handler.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/durations.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/durations.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/durations.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/durations.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_axis_label_string.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_click_filter_data.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_click_filter_data.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_default_query_language.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_default_query_language.ts similarity index 89% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_default_query_language.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_default_query_language.ts index e6f65e71043c7..3cb7ee0388f94 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_default_query_language.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_default_query_language.ts @@ -7,7 +7,7 @@ */ import { getUISettings } from '../../../services'; -import { UI_SETTINGS } from '../../../../../data/public'; +import { UI_SETTINGS } from '../../../../../../data/public'; export function getDefaultQueryLanguage() { return getUISettings().get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_display_name.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_display_name.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_display_name.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_display_name.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_formatter_type.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_interval.ts similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_interval.ts index 4b232af299a19..ea86ef6dc7ae9 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_interval.ts @@ -9,7 +9,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { search } from '../../../../../../plugins/data/public'; +import { search } from '../../../../../../../plugins/data/public'; import { GTE_INTERVAL_RE } from '../../../../common/interval_regexp'; import { AUTO_INTERVAL } from '../../../../common/constants'; import { isVisTableData } from '../../../../common/vis_data_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_metrics_field.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js similarity index 87% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js index 7aa40d48994b8..2909e7804b1bf 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.js @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; export function getSupportedFieldsByMetricType(type) { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js similarity index 95% rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js index c009146abb7bd..878b4f7655f3e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/get_supported_fields_by_metric_type.test.js @@ -7,7 +7,7 @@ */ import { getSupportedFieldsByMetricType } from './get_supported_fields_by_metric_type'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; describe('getSupportedFieldsByMetricType', () => { const shouldHaveHistogramAndNumbers = (type) => diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx index 7111a63244c7f..ad60aee290640 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/combo_box_select.tsx @@ -15,7 +15,7 @@ import { SwitchModePopover } from './switch_mode_popover'; import type { SelectIndexComponentProps } from './types'; import type { IndexPatternValue } from '../../../../../common/types'; -import type { IndexPatternsService } from '../../../../../../data/public'; +import type { IndexPatternsService } from '../../../../../../../data/public'; /** @internal **/ type IdsWithTitle = UnwrapPromise>; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/field_text_select.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx index 927b3c608c16c..1029ac67cc43c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx @@ -19,7 +19,7 @@ import { ComboBoxSelect } from './combo_box_select'; import type { IndexPatternValue, FetchedIndexPattern } from '../../../../../common/types'; import { USE_KIBANA_INDEXES_KEY } from '../../../../../common/constants'; -import { IndexPattern } from '../../../../../../data/common'; +import { IndexPattern } from '../../../../../../../data/common'; export interface IndexPatternSelectProps { indexPatternName: string; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts similarity index 93% rename from src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts index 18288f75d4c90..244e95e8db9dd 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/index_pattern_select/types.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/types.ts @@ -7,7 +7,7 @@ */ import type { Assign } from '@kbn/utility-types'; import type { FetchedIndexPattern, IndexPatternValue } from '../../../../../common/types'; -import type { IndexPattern } from '../../../../../../data/common'; +import type { IndexPattern } from '../../../../../../../data/common'; /** @internal **/ export interface SelectIndexComponentProps { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/label_date_formatter.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/label_date_formatter.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/new_metric_agg_fn.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/new_metric_agg_fn.ts similarity index 89% rename from src/plugins/vis_type_timeseries/public/application/components/lib/new_metric_agg_fn.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/new_metric_agg_fn.ts index 28dd4c81510fc..9c7cc1225c5ac 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/new_metric_agg_fn.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/new_metric_agg_fn.ts @@ -7,7 +7,7 @@ */ import uuid from 'uuid'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import type { Metric } from '../../../../common/types'; export const newMetricAggFn = (): Metric => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js b/src/plugins/vis_types/timeseries/public/application/components/lib/new_series_fn.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/new_series_fn.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.js b/src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/re_id_series.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/re_id_series.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/reorder.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/reorder.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/reorder.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.test.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/replace_vars.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/replace_vars.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/series_change_handler.js b/src/plugins/vis_types/timeseries/public/application/components/lib/series_change_handler.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/series_change_handler.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/series_change_handler.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/stacked.js b/src/plugins/vis_types/timeseries/public/application/components/lib/stacked.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/stacked.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/stacked.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js b/src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js b/src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.test.js similarity index 93% rename from src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js rename to src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.test.js index 9b9beae67e44f..8053c066114a3 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/tick_formatter.test.js @@ -7,10 +7,10 @@ */ import { createTickFormatter } from './tick_formatter'; -import { getFieldFormatsRegistry } from '../../../../../data/public/test_utils'; +import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; import { setFieldFormats } from '../../../services'; -import { UI_SETTINGS } from '../../../../../data/public'; -import { FORMATS_UI_SETTINGS } from '../../../../../field_formats/common'; +import { UI_SETTINGS } from '../../../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; const mockUiSettings = { get: (item) => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/types.ts b/src/plugins/vis_types/timeseries/public/application/components/lib/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/types.ts rename to src/plugins/vis_types/timeseries/public/application/components/lib/types.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js b/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js rename to src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js index 046b1c5799836..adee297fe0119 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js +++ b/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js @@ -15,7 +15,7 @@ import React, { Component } from 'react'; import { createTickFormatter } from './lib/tick_formatter'; import { convertSeriesToVars } from './lib/convert_series_to_vars'; import _ from 'lodash'; -import { CodeEditor, MarkdownLang } from '../../../../kibana_react/public'; +import { CodeEditor, MarkdownLang } from '../../../../../kibana_react/public'; import { EuiText, EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/palette_picker.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.test.tsx similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/palette_picker.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/palette_picker.test.tsx index ae7fa5c59c73d..81b33943f8b04 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/palette_picker.test.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { ReactWrapper } from 'enzyme'; import { PalettePicker, PalettePickerProps } from './palette_picker'; -import { chartPluginMock } from '../../../../charts/public/mocks'; +import { chartPluginMock } from '../../../../../charts/public/mocks'; import { EuiColorPalettePicker } from '@elastic/eui'; import { PALETTES } from '../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/palette_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/palette_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/palette_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/panel_config/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/_panel_config.scss b/src/plugins/vis_types/timeseries/public/application/components/panel_config/_panel_config.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/_panel_config.scss rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/_panel_config.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/gauge.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/gauge.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts b/src/plugins/vis_types/timeseries/public/application/components/panel_config/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/index.ts rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/markdown.tsx similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/markdown.tsx index 7f82f95d250ea..b099209af4348 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/markdown.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/panel_config/markdown.tsx @@ -42,7 +42,7 @@ import { getDefaultQueryLanguage } from '../lib/get_default_query_language'; import { VisDataContext } from '../../contexts/vis_data_context'; import { PanelConfigProps, PANEL_CONFIG_TABS } from './types'; import { TimeseriesVisParams } from '../../../types'; -import { CodeEditor, CssLang } from '../../../../../kibana_react/public'; +import { CodeEditor, CssLang } from '../../../../../../kibana_react/public'; const lessC = less(window, { env: 'production' }); diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/metric.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/metric.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/metric.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/metric.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/panel_config.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/panel_config.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/panel_config.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/panel_config.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/table.tsx similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/table.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/table.tsx index 38cbd57b0b517..ecbd9767f34a6 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/panel_config/table.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/panel_config/table.tsx @@ -42,7 +42,7 @@ import { BUCKET_TYPES } from '../../../../common/enums'; import { PanelConfigProps, PANEL_CONFIG_TABS } from './types'; import { TimeseriesVisParams } from '../../../types'; import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; -import { KBN_FIELD_TYPES } from '../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../data/public'; export class TablePanelConfig extends Component< PanelConfigProps, diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/timeseries.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/timeseries.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/top_n.tsx b/src/plugins/vis_types/timeseries/public/application/components/panel_config/top_n.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/top_n.tsx rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/top_n.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/panel_config/types.ts b/src/plugins/vis_types/timeseries/public/application/components/panel_config/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/panel_config/types.ts rename to src/plugins/vis_types/timeseries/public/application/components/panel_config/types.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx b/src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx rename to src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx index d3b249f54fe34..e0c66ea8d70a7 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/query_bar_wrapper.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/query_bar_wrapper.tsx @@ -11,7 +11,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { CoreStartContext } from '../contexts/query_input_bar_context'; import type { IndexPatternValue } from '../../../common/types'; -import { QueryStringInput, QueryStringInputProps } from '../../../../../plugins/data/public'; +import { QueryStringInput, QueryStringInputProps } from '../../../../../../plugins/data/public'; import { getDataStart } from '../../services'; import { fetchIndexPattern, isStringTypeIndexPattern } from '../../../common/index_patterns_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/series.js b/src/plugins/vis_types/timeseries/public/application/components/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series.js rename to src/plugins/vis_types/timeseries/public/application/components/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config.js b/src/plugins/vis_types/timeseries/public/application/components/series_config.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_config.js rename to src/plugins/vis_types/timeseries/public/application/components/series_config.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js b/src/plugins/vis_types/timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js rename to src/plugins/vis_types/timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_drag_handler.tsx b/src/plugins/vis_types/timeseries/public/application/components/series_drag_handler.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_drag_handler.tsx rename to src/plugins/vis_types/timeseries/public/application/components/series_drag_handler.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_editor.js b/src/plugins/vis_types/timeseries/public/application/components/series_editor.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/series_editor.js rename to src/plugins/vis_types/timeseries/public/application/components/series_editor.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/split.js b/src/plugins/vis_types/timeseries/public/application/components/split.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/split.js rename to src/plugins/vis_types/timeseries/public/application/components/split.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap b/src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/components/splits/__snapshots__/terms.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/everything.js b/src/plugins/vis_types/timeseries/public/application/components/splits/everything.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/everything.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/everything.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filter.js b/src/plugins/vis_types/timeseries/public/application/components/splits/filter.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/filter.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/filter.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js b/src/plugins/vis_types/timeseries/public/application/components/splits/filter_items.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/filter_items.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/filter_items.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/filters.js b/src/plugins/vis_types/timeseries/public/application/components/splits/filters.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/filters.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/filters.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js b/src/plugins/vis_types/timeseries/public/application/components/splits/group_by_select.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/group_by_select.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/group_by_select.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.js b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/splits/terms.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/terms.js index a668e5b727b48..b32af037533f7 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.js +++ b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.js @@ -25,7 +25,7 @@ import { EuiFieldText, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../data/public'; +import { KBN_FIELD_TYPES } from '../../../../../../data/public'; import { STACKED_OPTIONS } from '../../visualizations/constants'; import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js b/src/plugins/vis_types/timeseries/public/application/components/splits/terms.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/terms.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/unsupported_split.js b/src/plugins/vis_types/timeseries/public/application/components/splits/unsupported_split.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/splits/unsupported_split.js rename to src/plugins/vis_types/timeseries/public/application/components/splits/unsupported_split.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/svg/bomb_icon.js b/src/plugins/vis_types/timeseries/public/application/components/svg/bomb_icon.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/svg/bomb_icon.js rename to src/plugins/vis_types/timeseries/public/application/components/svg/bomb_icon.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/svg/fire_icon.js b/src/plugins/vis_types/timeseries/public/application/components/svg/fire_icon.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/svg/fire_icon.js rename to src/plugins/vis_types/timeseries/public/application/components/svg/fire_icon.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.scss b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.scss rename to src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx rename to src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx index b1722d4098587..a73f9c6a5e092 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/timeseries_visualization.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/timeseries_visualization.tsx @@ -27,7 +27,7 @@ import { LastValueModeIndicator } from './last_value_mode_indicator'; import { getInterval } from './lib/get_interval'; import { AUTO_INTERVAL } from '../../../common/constants'; import { TIME_RANGE_DATA_MODES, PANEL_TYPES } from '../../../common/enums'; -import type { IndexPattern } from '../../../../data/common'; +import type { IndexPattern } from '../../../../../data/common'; interface TimeseriesVisualizationProps { className?: string; diff --git a/src/plugins/vis_type_timeseries/public/application/components/use_index_patter_mode_callout.tsx b/src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/use_index_patter_mode_callout.tsx rename to src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx b/src/plugins/vis_types/timeseries/public/application/components/vis_editor.tsx similarity index 95% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx rename to src/plugins/vis_types/timeseries/public/application/components/vis_editor.tsx index 5e4ff436ff1e6..9e46427e33c2e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_editor.tsx @@ -15,11 +15,11 @@ import type { IUiSettingsClient } from 'kibana/public'; import type { Vis, VisualizeEmbeddableContract, -} from '../../../../../plugins/visualizations/public'; -import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; -import { Storage } from '../../../../../plugins/kibana_utils/public'; +} from '../../../../../../plugins/visualizations/public'; +import { KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; +import { Storage } from '../../../../../../plugins/kibana_utils/public'; -import type { TimeRange } from '../../../../../plugins/data/public'; +import type { TimeRange } from '../../../../../../plugins/data/public'; import type { IndexPatternValue, TimeseriesVisData } from '../../../common/types'; // @ts-expect-error @@ -32,7 +32,7 @@ import { fetchFields, VisFields } from '../lib/fetch_fields'; import { getDataStart, getCoreStart } from '../../services'; import type { TimeseriesVisParams } from '../../types'; import { UseIndexPatternModeCallout } from './use_index_patter_mode_callout'; -import type { EditorRenderProps } from '../../../../visualize/public'; +import type { EditorRenderProps } from '../../../../../visualize/public'; const VIS_STATE_DEBOUNCE_DELAY = 200; const APP_NAME = 'VisEditor'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx b/src/plugins/vis_types/timeseries/public/application/components/vis_editor_lazy.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx rename to src/plugins/vis_types/timeseries/public/application/components/vis_editor_lazy.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js b/src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx b/src/plugins/vis_types/timeseries/public/application/components/vis_picker.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_picker.tsx rename to src/plugins/vis_types/timeseries/public/application/components/vis_picker.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/_index.scss b/src/plugins/vis_types/timeseries/public/application/components/vis_types/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/_index.scss rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss b/src/plugins/vis_types/timeseries/public/application/components/vis_types/_vis_types.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/_vis_types.scss rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/_vis_types.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.test.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/series.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/gauge/vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts similarity index 96% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts index b2e40940b8001..653b2985ed1ae 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/index.ts @@ -14,7 +14,7 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { TimeseriesVisParams } from '../../../types'; import type { TimeseriesVisData, PanelData } from '../../../../common/types'; -import type { FieldFormatMap } from '../../../../../data/common'; +import type { FieldFormatMap } from '../../../../../../data/common'; /** * Lazy load each visualization type, since the only one is presented on the screen at the same time. diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/_markdown.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/_markdown.scss rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/_markdown.scss diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/vis.js similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/vis.js index fc7019bd38293..49fdbcd98501c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/markdown/vis.js @@ -11,7 +11,7 @@ import React from 'react'; import classNames from 'classnames'; import uuid from 'uuid'; import { get } from 'lodash'; -import { Markdown } from '../../../../../../../plugins/kibana_react/public'; +import { Markdown } from '../../../../../../../../plugins/kibana_react/public'; import { ErrorComponent } from '../../error'; import { replaceVars } from '../../lib/replace_vars'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/series.test.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/series.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/metric/vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/config.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/is_sortable.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/is_sortable.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/is_sortable.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/is_sortable.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js similarity index 98% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js index 21d7de9f1d880..7b1db4b362647 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js @@ -10,14 +10,14 @@ import _, { isArray, last, get } from 'lodash'; import React, { Component } from 'react'; import { parse as parseUrl } from 'url'; import PropTypes from 'prop-types'; -import { RedirectAppLinks } from '../../../../../../kibana_react/public'; +import { RedirectAppLinks } from '../../../../../../../kibana_react/public'; import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; import { createFieldFormatter } from '../../lib/create_field_formatter'; import { isSortable } from './is_sortable'; import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; -import { FIELD_FORMAT_IDS } from '../../../../../../../plugins/field_formats/common'; +import { FIELD_FORMAT_IDS } from '../../../../../../../../plugins/field_formats/common'; import { FormattedMessage } from '@kbn/i18n/react'; import { getFieldFormats, getCoreStart } from '../../../../services'; import { DATA_FORMATTERS } from '../../../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index fed295fef9d30..75a8f11e640df 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -18,7 +18,7 @@ import { createTickFormatter } from '../../lib/tick_formatter'; import { createFieldFormatter } from '../../lib/create_field_formatter'; import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; -import { MarkdownSimple } from '../../../../../../../plugins/kibana_react/public'; +import { MarkdownSimple } from '../../../../../../../../plugins/kibana_react/public'; import { replaceVars } from '../../lib/replace_vars'; import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js similarity index 96% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js index d6e7484e903bf..cf4c327df3d77 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js @@ -12,11 +12,11 @@ import { TimeSeries } from '../../../visualizations/views/timeseries'; import TimeseriesVisualization from './vis'; import { setFieldFormats } from '../../../../services'; import { createFieldFormatter } from '../../lib/create_field_formatter'; -import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; -import { METRIC_TYPES } from '../../../../../../data/common'; -import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; +import { FORMATS_UI_SETTINGS } from '../../../../../../../field_formats/common'; +import { METRIC_TYPES } from '../../../../../../../data/common'; +import { getFieldFormatsRegistry } from '../../../../../../../data/public/test_utils'; -jest.mock('../../../../../../data/public/services', () => ({ +jest.mock('../../../../../../../data/public/services', () => ({ getUiSettings: () => ({ get: jest.fn() }), })); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/series.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/series.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/series.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/series.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js b/src/plugins/vis_types/timeseries/public/application/components/vis_with_splits.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js rename to src/plugins/vis_types/timeseries/public/application/components/vis_with_splits.js diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.test.tsx b/src/plugins/vis_types/timeseries/public/application/components/yes_no.test.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/yes_no.test.tsx rename to src/plugins/vis_types/timeseries/public/application/components/yes_no.test.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx b/src/plugins/vis_types/timeseries/public/application/components/yes_no.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/yes_no.tsx rename to src/plugins/vis_types/timeseries/public/application/components/yes_no.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/form_validation_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/form_validation_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/form_validation_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/panel_model_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/panel_model_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/panel_model_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/panel_model_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/query_input_bar_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/query_input_bar_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/query_input_bar_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/query_input_bar_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts b/src/plugins/vis_types/timeseries/public/application/contexts/vis_data_context.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/contexts/vis_data_context.ts rename to src/plugins/vis_types/timeseries/public/application/contexts/vis_data_context.ts diff --git a/src/plugins/vis_type_timeseries/public/application/editor_controller.tsx b/src/plugins/vis_types/timeseries/public/application/editor_controller.tsx similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/editor_controller.tsx rename to src/plugins/vis_types/timeseries/public/application/editor_controller.tsx diff --git a/src/plugins/vis_type_timeseries/public/application/index.scss b/src/plugins/vis_types/timeseries/public/application/index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/index.scss rename to src/plugins/vis_types/timeseries/public/application/index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/lib/check_ui_restrictions.js b/src/plugins/vis_types/timeseries/public/application/lib/check_ui_restrictions.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/check_ui_restrictions.js rename to src/plugins/vis_types/timeseries/public/application/lib/check_ui_restrictions.js diff --git a/src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.test.ts b/src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.test.ts rename to src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.ts b/src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/compute_gradient_final_color.ts rename to src/plugins/vis_types/timeseries/public/application/lib/compute_gradient_final_color.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts b/src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/fetch_fields.ts rename to src/plugins/vis_types/timeseries/public/application/lib/fetch_fields.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.test.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.test.ts similarity index 97% rename from src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.test.ts rename to src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.test.ts index 0a78e525796ff..c447acb74dc37 100644 --- a/src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.test.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { chartPluginMock } from '../../../../charts/public/mocks'; +import { chartPluginMock } from '../../../../../charts/public/mocks'; import { getSplitByTermsColor, SplitByTermsColorProps } from './get_split_by_terms_color'; const chartsRegistry = chartPluginMock.createPaletteRegistry(); diff --git a/src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/get_split_by_terms_color.ts rename to src/plugins/vis_types/timeseries/public/application/lib/get_split_by_terms_color.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/get_timezone.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/get_timezone.ts rename to src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/index.ts b/src/plugins/vis_types/timeseries/public/application/lib/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/index.ts rename to src/plugins/vis_types/timeseries/public/application/lib/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/rainbow_colors.ts b/src/plugins/vis_types/timeseries/public/application/lib/rainbow_colors.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/rainbow_colors.ts rename to src/plugins/vis_types/timeseries/public/application/lib/rainbow_colors.ts diff --git a/src/plugins/vis_type_timeseries/public/application/lib/set_is_reversed.js b/src/plugins/vis_types/timeseries/public/application/lib/set_is_reversed.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/lib/set_is_reversed.js rename to src/plugins/vis_types/timeseries/public/application/lib/set_is_reversed.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/constants/chart.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/constants/chart.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/constants/chart.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/constants/chart.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/constants/icons.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/constants/icons.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/constants/icons.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/constants/icons.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/constants/index.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/constants/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/constants/index.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/constants/index.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/calc_dimensions.js b/src/plugins/vis_types/timeseries/public/application/visualizations/lib/calc_dimensions.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/calc_dimensions.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/lib/calc_dimensions.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/calculate_coordinates.js b/src/plugins/vis_types/timeseries/public/application/visualizations/lib/calculate_coordinates.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/calculate_coordinates.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/lib/calculate_coordinates.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/get_value_by.js b/src/plugins/vis_types/timeseries/public/application/visualizations/lib/get_value_by.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/get_value_by.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/lib/get_value_by.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_annotation.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_annotation.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_annotation.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_annotation.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_gauge.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_gauge.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_index.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_index.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_index.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_index.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_metric.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_metric.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_metric.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss b/src/plugins/vis_types/timeseries/public/application/visualizations/views/_top_n.scss similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/_top_n.scss rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/_top_n.scss diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/annotation.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/annotation.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/annotation.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/annotation.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge_vis.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge_vis.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge_vis.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge_vis.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/metric.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/metric.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/area_decorator.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/decorators/bar_decorator.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js similarity index 99% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 1edaf38ef403c..6f6ddbbb7c414 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -32,7 +32,7 @@ import { getBaseTheme, getChartClasses } from './utils/theme'; import { TOOLTIP_MODES } from '../../../../../common/enums'; import { getValueOrEmpty } from '../../../../../common/empty_label'; import { getSplitByTermsColor } from '../../../lib/get_split_by_terms_color'; -import { renderEndzoneTooltip, useActiveCursor } from '../../../../../../charts/public'; +import { renderEndzoneTooltip, useActiveCursor } from '../../../../../../../charts/public'; import { getAxisLabelString } from '../../../components/lib/get_axis_label_string'; import { calculateDomainForSeries } from './utils/series_domain_calculation'; diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/__snapshots__/charts.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/model/charts.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/model/charts.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/__snapshots__/series_styles.test.js.snap diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculation.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts similarity index 93% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts index 5b502636003f0..53157286328e2 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_domain_calculations.test.ts @@ -7,7 +7,7 @@ */ import { calculateDomainForSeries } from './series_domain_calculation'; -import { PanelData } from 'src/plugins/vis_type_timeseries/common/types'; +import { PanelData } from 'src/plugins/vis_types/timeseries/common/types'; describe('calculateDomainForSeries', () => { it('should return 0 for domainStart and 3 for domainEnd', () => { diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/series_styles.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/stack_format.test.js diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/utils/theme.ts rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/top_n.js similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js rename to src/plugins/vis_types/timeseries/public/application/visualizations/views/top_n.js diff --git a/src/plugins/vis_type_timeseries/public/index.ts b/src/plugins/vis_types/timeseries/public/index.ts similarity index 88% rename from src/plugins/vis_type_timeseries/public/index.ts rename to src/plugins/vis_types/timeseries/public/index.ts index 0ab10581c48c2..a3180678b53a0 100644 --- a/src/plugins/vis_type_timeseries/public/index.ts +++ b/src/plugins/vis_types/timeseries/public/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { PluginInitializerContext } from '../../../core/public'; +import { PluginInitializerContext } from '../../../../core/public'; import { MetricsPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/vis_type_timeseries/public/metrics_fn.ts b/src/plugins/vis_types/timeseries/public/metrics_fn.ts similarity index 93% rename from src/plugins/vis_type_timeseries/public/metrics_fn.ts rename to src/plugins/vis_types/timeseries/public/metrics_fn.ts index fe1c4722762ae..23c196ebe1149 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_fn.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_fn.ts @@ -7,8 +7,8 @@ */ import { i18n } from '@kbn/i18n'; -import { KibanaContext } from '../../data/public'; -import { ExpressionFunctionDefinition, Render } from '../../expressions/public'; +import { KibanaContext } from '../../../data/public'; +import { ExpressionFunctionDefinition, Render } from '../../../expressions/public'; import type { TimeseriesVisData } from '../common/types'; import { metricsRequestHandler } from './request_handler'; diff --git a/src/plugins/vis_type_timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts similarity index 98% rename from src/plugins/vis_type_timeseries/public/metrics_type.ts rename to src/plugins/vis_types/timeseries/public/metrics_type.ts index 5d4a61c1edb82..64970d9730eee 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -19,7 +19,7 @@ import { VisGroups, VisParams, VisTypeDefinition, -} from '../../visualizations/public'; +} from '../../../visualizations/public'; import { getDataStart } from './services'; import type { TimeseriesVisDefaultParams, TimeseriesVisParams } from './types'; diff --git a/src/plugins/vis_type_timeseries/public/plugin.ts b/src/plugins/vis_types/timeseries/public/plugin.ts similarity index 86% rename from src/plugins/vis_type_timeseries/public/plugin.ts rename to src/plugins/vis_types/timeseries/public/plugin.ts index 3cd090c7da829..d6d83caa6eb0f 100644 --- a/src/plugins/vis_type_timeseries/public/plugin.ts +++ b/src/plugins/vis_types/timeseries/public/plugin.ts @@ -7,9 +7,9 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; -import { VisualizationsSetup } from '../../visualizations/public'; -import { VisualizePluginSetup } from '../../visualize/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; +import { VisualizationsSetup } from '../../../visualizations/public'; +import { VisualizePluginSetup } from '../../../visualize/public'; import { EditorController, TSVB_EDITOR_NAME } from './application/editor_controller'; import { createMetricsFn } from './metrics_fn'; @@ -22,8 +22,8 @@ import { setDataStart, setCharts, } from './services'; -import { DataPublicPluginStart } from '../../data/public'; -import { ChartsPluginStart } from '../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { ChartsPluginStart } from '../../../charts/public'; import { getTimeseriesVisRenderer } from './timeseries_vis_renderer'; /** @internal */ diff --git a/src/plugins/vis_type_timeseries/public/request_handler.ts b/src/plugins/vis_types/timeseries/public/request_handler.ts similarity index 97% rename from src/plugins/vis_type_timeseries/public/request_handler.ts rename to src/plugins/vis_types/timeseries/public/request_handler.ts index 0a110dd65d5e9..e9037c0b84a5e 100644 --- a/src/plugins/vis_type_timeseries/public/request_handler.ts +++ b/src/plugins/vis_types/timeseries/public/request_handler.ts @@ -12,7 +12,7 @@ import { ROUTES } from '../common/constants'; import type { TimeseriesVisParams } from './types'; import type { TimeseriesVisData } from '../common/types'; -import type { KibanaContext } from '../../data/public'; +import type { KibanaContext } from '../../../data/public'; interface MetricsRequestHandlerParams { input: KibanaContext | null; diff --git a/src/plugins/vis_type_timeseries/public/services.ts b/src/plugins/vis_types/timeseries/public/services.ts similarity index 84% rename from src/plugins/vis_type_timeseries/public/services.ts rename to src/plugins/vis_types/timeseries/public/services.ts index ba7ab4a25c8a2..f76a9ed7c6389 100644 --- a/src/plugins/vis_type_timeseries/public/services.ts +++ b/src/plugins/vis_types/timeseries/public/services.ts @@ -7,9 +7,9 @@ */ import { I18nStart, IUiSettingsClient, CoreStart } from 'src/core/public'; -import { createGetterSetter } from '../../kibana_utils/public'; -import { ChartsPluginStart } from '../../charts/public'; -import { DataPublicPluginStart } from '../../data/public'; +import { createGetterSetter } from '../../../kibana_utils/public'; +import { ChartsPluginStart } from '../../../charts/public'; +import { DataPublicPluginStart } from '../../../data/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); diff --git a/src/plugins/vis_type_timeseries/public/test_utils/index.ts b/src/plugins/vis_types/timeseries/public/test_utils/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/test_utils/index.ts rename to src/plugins/vis_types/timeseries/public/test_utils/index.ts diff --git a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx similarity index 94% rename from src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx rename to src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx index 9a19ddc285ebb..34cc1dc347ef8 100644 --- a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_types/timeseries/public/timeseries_vis_renderer.tsx @@ -14,14 +14,14 @@ import { I18nProvider } from '@kbn/i18n/react'; import { IUiSettingsClient } from 'kibana/public'; import { fetchIndexPattern } from '../common/index_patterns_utils'; -import { VisualizationContainer, PersistedState } from '../../visualizations/public'; +import { VisualizationContainer, PersistedState } from '../../../visualizations/public'; import type { TimeseriesVisData } from '../common/types'; import { isVisTableData } from '../common/vis_data_utils'; import { getCharts, getDataStart } from './services'; import type { TimeseriesVisParams } from './types'; -import type { ExpressionRenderDefinition } from '../../expressions/common'; +import type { ExpressionRenderDefinition } from '../../../expressions/common'; import type { TimeseriesRenderValue } from './metrics_fn'; const TimeseriesVisualization = lazy( diff --git a/src/plugins/vis_type_timeseries/public/to_ast.ts b/src/plugins/vis_types/timeseries/public/to_ast.ts similarity index 91% rename from src/plugins/vis_type_timeseries/public/to_ast.ts rename to src/plugins/vis_types/timeseries/public/to_ast.ts index c0c0a5b1546a9..91fc7465f122f 100644 --- a/src/plugins/vis_type_timeseries/public/to_ast.ts +++ b/src/plugins/vis_types/timeseries/public/to_ast.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { buildExpression, buildExpressionFunction } from '../../expressions/public'; -import type { Vis } from '../../visualizations/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; +import type { Vis } from '../../../visualizations/public'; import type { TimeseriesExpressionFunctionDefinition } from './metrics_fn'; import type { TimeseriesVisParams } from './types'; diff --git a/src/plugins/vis_type_timeseries/public/types.ts b/src/plugins/vis_types/timeseries/public/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/types.ts rename to src/plugins/vis_types/timeseries/public/types.ts diff --git a/src/plugins/vis_type_timeseries/server/config.ts b/src/plugins/vis_types/timeseries/server/config.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/config.ts rename to src/plugins/vis_types/timeseries/server/config.ts diff --git a/src/plugins/vis_type_timeseries/server/index.ts b/src/plugins/vis_types/timeseries/server/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/index.ts rename to src/plugins/vis_types/timeseries/server/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts b/src/plugins/vis_types/timeseries/server/lib/get_fields.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/get_fields.ts rename to src/plugins/vis_types/timeseries/server/lib/get_fields.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts rename to src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/default_search_capabilities.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/capabilities/rollup_search_capabilities.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/index.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/index.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts index 5f989a50ca639..2a3738878c97a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/cached_index_pattern_fetcher.ts @@ -8,7 +8,7 @@ import { getIndexPatternKey, fetchIndexPattern } from '../../../../common/index_patterns_utils'; -import type { IndexPatternsService } from '../../../../../data/server'; +import type { IndexPatternsService } from '../../../../../../data/server'; import type { IndexPatternValue, FetchedIndexPattern } from '../../../../common/types'; export const getCachedIndexPatternFetcher = ( diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts index 9563a8fbece23..cf7bc42fc6db3 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/fields_fetcher.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/fields_fetcher.ts @@ -10,7 +10,7 @@ import { getIndexPatternKey } from '../../../../common/index_patterns_utils'; import type { VisTypeTimeseriesVisDataRequest } from '../../../types'; import type { SearchStrategy, SearchCapabilities } from '../index'; -import type { IndexPatternsService } from '../../../../../data/common'; +import type { IndexPatternsService } from '../../../../../../data/common'; import type { CachedIndexPatternFetcher } from './cached_index_pattern_fetcher'; import type { IndexPatternValue } from '../../../../common/types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/lib/interval_helper.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/lib/interval_helper.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategies_registry.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategies_registry.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategies_registry.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategies_registry.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategy_registry.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategy_registry.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/search_strategy_registry.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/search_strategy_registry.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts index 0a9b3d0047c80..6216bce00fc7d 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { IndexPatternsService } from '../../../../../data/common'; +import { IndexPatternsService } from '../../../../../../data/common'; import { from } from 'rxjs'; import { AbstractSearchStrategy } from './abstract_search_strategy'; -import type { FieldSpec } from '../../../../../data/common'; +import type { FieldSpec } from '../../../../../../data/common'; import type { CachedIndexPatternFetcher } from '../lib/cached_index_pattern_fetcher'; import type { VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index 26c3a6c7c8bf7..bce07d2cdb300 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IndexPatternsService } from '../../../../../data/server'; +import { IndexPatternsService } from '../../../../../../data/server'; import { toSanitizedFieldType } from '../../../../common/fields_utils'; import type { FetchedIndexPattern } from '../../../../common/types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts index 34892ec797c0b..0fa92b5f061fa 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts @@ -9,7 +9,7 @@ import { AbstractSearchStrategy } from './abstract_search_strategy'; import { DefaultSearchCapabilities } from '../capabilities/default_search_capabilities'; -import type { IndexPatternsService } from '../../../../../data/server'; +import type { IndexPatternsService } from '../../../../../../data/server'; import type { FetchedIndexPattern } from '../../../../common/types'; import type { VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/index.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/index.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts similarity index 98% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts index 85b8bcdf57b93..4d2608e3519ed 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.test.ts @@ -8,7 +8,7 @@ import { RollupSearchStrategy } from './rollup_search_strategy'; -import type { IndexPatternsService } from '../../../../../data/common'; +import type { IndexPatternsService } from '../../../../../../data/common'; import type { CachedIndexPatternFetcher } from '../lib/cached_index_pattern_fetcher'; import type { VisTypeTimeseriesRequestHandlerContext, diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts rename to src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts index f68c877cf7a3f..903e7f239f824 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -import { getCapabilitiesForRollupIndices, IndexPatternsService } from '../../../../../data/server'; +import { + getCapabilitiesForRollupIndices, + IndexPatternsService, +} from '../../../../../../data/server'; import { AbstractSearchStrategy } from './abstract_search_strategy'; import { RollupSearchCapabilities } from '../capabilities/rollup_search_capabilities'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/build_request_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/build_request_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/build_request_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/build_request_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/get_request_params.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/annotations/get_request_params.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/annotations/get_request_params.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/build_processor_function.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_annotations.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_annotations.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_annotations.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_annotations.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_interval_and_timefield.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_series_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_series_data.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/get_table_data.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/get_table_data.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/get_table_data.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/handle_error_response.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/handle_error_response.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/__snapshots__/bucket_transform.test.js.snap diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/bucket_transform.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/check_aggs.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/check_aggs.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/check_aggs.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/check_aggs.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/format_key.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/format_key.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/format_key.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/format_key.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_active_series.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_active_series.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_active_series.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_active_series.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.js index 6756a8f7fc85a..eac2c4af65291 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.js @@ -8,7 +8,7 @@ import { get, max, min, sum, noop } from 'lodash'; import { toPercentileNumber } from '../../../../common/to_percentile_number'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import { getAggByPredicate } from '../../../../common/agg_utils'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_agg_value.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts similarity index 98% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts index 7f5874c0763f5..e02b403c8ba13 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_bucket_size.ts @@ -15,7 +15,7 @@ import { } from './unit_to_seconds'; import { getTimerange } from './get_timerange'; import { INTERVAL_STRING_RE, GTE_INTERVAL_RE } from '../../../../common/interval_regexp'; -import { search } from '../../../../../data/server'; +import { search } from '../../../../../../data/server'; import type { SearchCapabilities } from '../../search_strategies'; import type { VisTypeTimeseriesVisDataRequest } from '../../../types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.ts index be8755584e2c7..06df19ab73856 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_buckets_path.ts @@ -8,7 +8,7 @@ import { startsWith } from 'lodash'; import { toPercentileNumber } from '../../../../common/to_percentile_number'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; import type { Metric } from '../../../../common/types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts similarity index 93% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts index c5ceac5cd7dd5..07b030782d6d7 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_es_query_uisettings.ts @@ -7,7 +7,7 @@ */ import { IUiSettingsClient } from 'kibana/server'; -import { UI_SETTINGS } from '../../../../../data/server'; +import { UI_SETTINGS } from '../../../../../../data/server'; export async function getEsQueryConfig(uiSettings: IUiSettingsClient) { const allowLeadingWildcards = await uiSettings.get(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_last_metric.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_splits.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/get_timerange_mode.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts index d52b6b38a7bd7..fd061cd363695 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.test.ts @@ -7,7 +7,7 @@ */ import { mapEmptyToZero } from './map_empty_to_zero'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { TSVB_METRIC_TYPES } from '../../../../common/enums'; describe('mapEmptyToZero(metric, buckets)', () => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts similarity index 94% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts index a035d566d130e..26014b229bae9 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/map_empty_to_zero.ts @@ -8,7 +8,7 @@ // @ts-expect-error not typed yet import { getAggValue } from './get_agg_value'; -import { METRIC_TYPES } from '../../../../../data/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import type { Metric } from '../../../../common/types'; import type { PanelDataArray } from '../../../../common/types/vis_data'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/moving_fn_scripts.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/overwrite.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/overwrite.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/overwrite.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/overwrite.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_interval.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_interval.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/parse_interval.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/timestamp.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/timestamp.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/helpers/unit_to_seconds.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/offset_time.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts index 258dc2d541376..a52e15eb90fee 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/date_histogram.ts @@ -10,7 +10,7 @@ import { overwrite } from '../../helpers'; import { getBucketSize, getTimerange } from '../../helpers'; import { validateField } from '../../../../../common/fields_utils'; -import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; +import { search, UI_SETTINGS } from '../../../../../../../../plugins/data/server'; import type { AnnotationsRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/query.ts similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/query.ts index 53fe51329acb3..f1b11a780fec8 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/query.ts @@ -8,7 +8,7 @@ import { getBucketSize, getTimerange, overwrite } from '../../helpers'; import { validateField } from '../../../../../common/fields_utils'; -import { esQuery, UI_SETTINGS } from '../../../../../../data/server'; +import { esQuery, UI_SETTINGS } from '../../../../../../../data/server'; import type { AnnotationsRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/top_hits.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/annotations/types.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.js index 6349a75993aa8..696bea7d6421b 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.js @@ -10,7 +10,7 @@ import { overwrite } from '../../helpers'; import { getBucketSize } from '../../helpers/get_bucket_size'; import { offsetTime } from '../../offset_time'; import { isLastValueTimerangeMode } from '../../helpers/get_timerange_mode'; -import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; +import { search, UI_SETTINGS } from '../../../../../../../../plugins/data/server'; import { AGG_TYPE, getAggsByType } from '../../../../../common/agg_utils'; import { TSVB_METRIC_TYPES } from '../../../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js similarity index 99% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js index b09b2c28d77e3..6a7f09a49f26a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js @@ -9,7 +9,7 @@ import { DefaultSearchCapabilities } from '../../../search_strategies/capabilities/default_search_capabilities'; import { dateHistogram } from './date_histogram'; import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; -import { UI_SETTINGS } from '../../../../../../data/common'; +import { UI_SETTINGS } from '../../../../../../../data/common'; describe('dateHistogram(req, panel, series)', () => { let panel; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js index d45943f6f21ac..32e9a1ea0aa90 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js @@ -8,7 +8,7 @@ import { bucketTransform } from '../../helpers/bucket_transform'; import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; const filter = (metric) => metric.type === 'filter_ratio'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/index.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/index.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/index.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/index.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/normalize_query.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.js similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.js index 91016384794c4..86c5311f51dcb 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.js @@ -9,7 +9,7 @@ import { getBucketSize } from '../../helpers/get_bucket_size'; import { bucketTransform } from '../../helpers/bucket_transform'; import { overwrite } from '../../helpers'; -import { UI_SETTINGS } from '../../../../../../data/common'; +import { UI_SETTINGS } from '../../../../../../../data/common'; export const filter = (metric) => metric.type === 'positive_rate'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/positive_rate.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.js similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.js index 5031a0f2ec185..f745a7bf7ad74 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.js @@ -7,7 +7,7 @@ */ import { offsetTime } from '../../offset_time'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; export function query( req, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/query.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js similarity index 92% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js index 01e1b9f8d1dce..b7e7754d05f81 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; export function splitByFilter(req, panel, series, esQueryConfig, seriesIndex) { return (next) => (doc) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js similarity index 93% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js index 77b9ccc5880fe..5e8def5db771e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; export function splitByFilters(req, panel, series, esQueryConfig, seriesIndex) { return (next) => (doc) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/calculate_agg_root.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts index 5dee812dd4c56..c02f661c3aedc 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/date_histogram.ts @@ -8,7 +8,7 @@ import { overwrite, getBucketSize, isLastValueTimerangeMode, getTimerange } from '../../helpers'; import { calculateAggRoot } from './calculate_agg_root'; -import { search, UI_SETTINGS } from '../../../../../../../plugins/data/server'; +import { search, UI_SETTINGS } from '../../../../../../../../plugins/data/server'; import { AGG_TYPE, getAggsByType } from '../../../../../common/agg_utils'; import { TSVB_METRIC_TYPES } from '../../../../../common/enums'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts similarity index 97% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts index e8fb684ef4b6c..b6036a03ba464 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/filter_ratios.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import { overwrite, bucketTransform } from '../../helpers'; import { calculateAggRoot } from './calculate_agg_root'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/metric_buckets.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/normalize_query.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/pivot.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/pivot.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/pivot.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/pivot.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts index 85a817e124aa1..e03a65ffebbd1 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/positive_rate.ts @@ -8,7 +8,7 @@ import { getBucketSize } from '../../helpers/get_bucket_size'; import { calculateAggRoot } from './calculate_agg_root'; -import { UI_SETTINGS } from '../../../../../../data/common'; +import { UI_SETTINGS } from '../../../../../../../data/common'; import type { TableRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/query.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/query.ts index 5ddf83e1134b3..76c4649ee5738 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/query.ts @@ -7,7 +7,7 @@ */ import { getTimerange, overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import type { TableRequestProcessorsFunction } from './types'; export const query: TableRequestProcessorsFunction = diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/sibling_buckets.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts similarity index 94% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts index cca86a1960fcd..e0e1485f6a8ff 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_everything.ts @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import type { TableRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts index 35b0ba50a05b9..1a4e0f6ceb1b5 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/split_by_terms.ts @@ -7,7 +7,7 @@ */ import { overwrite } from '../../helpers'; -import { esQuery } from '../../../../../../data/server'; +import { esQuery } from '../../../../../../../data/server'; import type { TableRequestProcessorsFunction } from './types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts index d47d3fe34c9b1..6bb4dfa55426a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/types.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/table/types.ts @@ -8,7 +8,7 @@ import type { IUiSettingsClient } from 'kibana/server'; import type { FetchedIndexPattern, Panel } from '../../../../../common/types'; -import type { EsQueryConfig } from '../../../../../../data/common'; +import type { EsQueryConfig } from '../../../../../../../data/common'; import type { SearchCapabilities } from '../../../search_strategies'; import type { VisTypeTimeseriesVisDataRequest } from '../../../../types'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/request_processors/types.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/buckets.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/filter.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/filter.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/annotations/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/annotations/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/drop_last_bucket.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/format_label.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/format_label.ts index 7908cbccb9845..6d824c1c7f43e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/format_label.ts @@ -9,7 +9,7 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { BUCKET_TYPES, PANEL_TYPES } from '../../../../../common/enums'; import type { Panel, PanelData, Series } from '../../../../../common/types'; -import type { FieldFormatsRegistry } from '../../../../../../field_formats/common'; +import type { FieldFormatsRegistry } from '../../../../../../../field_formats/common'; import type { createFieldsFetcher } from '../../../search_strategies/lib/fields_fetcher'; import type { CachedIndexPatternFetcher } from '../../../search_strategies/lib/cached_index_pattern_fetcher'; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/index.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/index.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/math.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/math.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/percentile_rank.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/_series_agg.js b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/_series_agg.js similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/_series_agg.js rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/_series_agg.js diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/drop_last_bucket.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/index.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/index.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/index.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/math.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/math.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/math.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/math.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/percentile_rank.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/series_agg.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/series_agg.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/series_agg.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/series_agg.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_metric.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_metric.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_metric.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_metric.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/std_sibling.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/types.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/types.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/table/types.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/response_processors/table/types.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/build_request_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/get_request_params.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/get_request_params.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/get_request_params.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/handle_response_body.ts similarity index 96% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/series/handle_response_body.ts index 78e9f971a61dd..415844abeedaf 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts +++ b/src/plugins/vis_types/timeseries/server/lib/vis_data/series/handle_response_body.ts @@ -17,7 +17,7 @@ import { FieldsFetcherServices, } from '../../search_strategies/lib/fields_fetcher'; import { VisTypeTimeseriesVisDataRequest } from '../../../types'; -import type { FieldFormatsRegistry } from '../../../../../field_formats/common'; +import type { FieldFormatsRegistry } from '../../../../../../field_formats/common'; export function handleResponseBody( panel: Panel, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_request_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_request_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_request_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_request_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_response_body.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_response_body.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/build_response_body.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/build_response_body.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.test.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.test.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.test.ts diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.ts b/src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/lib/vis_data/table/process_bucket.ts rename to src/plugins/vis_types/timeseries/server/lib/vis_data/table/process_bucket.ts diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_types/timeseries/server/plugin.ts similarity index 94% rename from src/plugins/vis_type_timeseries/server/plugin.ts rename to src/plugins/vis_types/timeseries/server/plugin.ts index d2ecb07c0273d..5347d9ab7bfbc 100644 --- a/src/plugins/vis_type_timeseries/server/plugin.ts +++ b/src/plugins/vis_types/timeseries/server/plugin.ts @@ -20,9 +20,9 @@ import { Server } from '@hapi/hapi'; import { first, map } from 'rxjs/operators'; import { VisTypeTimeseriesConfig } from './config'; import { getVisData } from './lib/get_vis_data'; -import { UsageCollectionSetup } from '../../usage_collection/server'; -import { PluginStart } from '../../data/server'; -import { IndexPatternsService } from '../../data/common'; +import { UsageCollectionSetup } from '../../../usage_collection/server'; +import { PluginStart } from '../../../data/server'; +import { IndexPatternsService } from '../../../data/common'; import { visDataRoutes } from './routes/vis'; import { fieldsRoutes } from './routes/fields'; import { getUiSettings } from './ui_settings'; @@ -30,7 +30,7 @@ import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesVisDataRequest, } from './types'; -import type { FieldFormatsRegistry } from '../../field_formats/common'; +import type { FieldFormatsRegistry } from '../../../field_formats/common'; import { SearchStrategyRegistry, diff --git a/src/plugins/vis_type_timeseries/server/routes/fields.ts b/src/plugins/vis_types/timeseries/server/routes/fields.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/routes/fields.ts rename to src/plugins/vis_types/timeseries/server/routes/fields.ts diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_types/timeseries/server/routes/vis.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/routes/vis.ts rename to src/plugins/vis_types/timeseries/server/routes/vis.ts diff --git a/src/plugins/vis_type_timeseries/server/types.ts b/src/plugins/vis_types/timeseries/server/types.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/types.ts rename to src/plugins/vis_types/timeseries/server/types.ts index 40ced72933012..ab01f09c75f1e 100644 --- a/src/plugins/vis_type_timeseries/server/types.ts +++ b/src/plugins/vis_types/timeseries/server/types.ts @@ -10,8 +10,8 @@ import { Observable } from 'rxjs'; import { EsQueryConfig } from '@kbn/es-query'; import { SharedGlobalConfig } from 'kibana/server'; import type { IRouter, IUiSettingsClient, KibanaRequest } from 'src/core/server'; -import type { DataRequestHandlerContext, IndexPatternsService } from '../../data/server'; -import type { FieldFormatsRegistry } from '../../field_formats/common'; +import type { DataRequestHandlerContext, IndexPatternsService } from '../../../data/server'; +import type { FieldFormatsRegistry } from '../../../field_formats/common'; import type { Series, VisPayload } from '../common/types'; import type { SearchStrategyRegistry } from './lib/search_strategies'; import type { CachedIndexPatternFetcher } from './lib/search_strategies/lib/cached_index_pattern_fetcher'; diff --git a/src/plugins/vis_type_timeseries/server/ui_settings.ts b/src/plugins/vis_types/timeseries/server/ui_settings.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/ui_settings.ts rename to src/plugins/vis_types/timeseries/server/ui_settings.ts diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.mock.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.mock.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.mock.ts diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.test.ts similarity index 95% rename from src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.test.ts index 91daf09121f18..aac6d879f48fd 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.test.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.test.ts @@ -7,8 +7,11 @@ */ import { getStats } from './get_usage_collector'; -import { createCollectorFetchContextMock } from '../../../usage_collection/server/mocks'; -import type { SavedObjectsClientContract, SavedObjectsFindResponse } from '../../../../core/server'; +import { createCollectorFetchContextMock } from '../../../../usage_collection/server/mocks'; +import type { + SavedObjectsClientContract, + SavedObjectsFindResponse, +} from '../../../../../core/server'; import { TIME_RANGE_DATA_MODES } from '../../common/enums'; const mockedSavedObject = { diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.ts similarity index 93% rename from src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.ts index a8d64afaf8cee..8309d51a9d56d 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/get_usage_collector.ts @@ -7,14 +7,14 @@ */ import { TIME_RANGE_DATA_MODES } from '../../common/enums'; -import { findByValueEmbeddables } from '../../../dashboard/server'; +import { findByValueEmbeddables } from '../../../../dashboard/server'; import type { SavedObjectsClientContract, ISavedObjectsRepository, SavedObjectsFindResult, -} from '../../../../core/server'; -import type { SavedVisState } from '../../../visualizations/common'; +} from '../../../../../core/server'; +import type { SavedVisState } from '../../../../visualizations/common'; export interface TimeseriesUsage { timeseries_use_last_value_mode_total: number; diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/index.ts b/src/plugins/vis_types/timeseries/server/usage_collector/index.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/usage_collector/index.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/index.ts diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.test.ts similarity index 92% rename from src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.test.ts index 0dfe5ae5f9351..26a74821fe5ae 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.test.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.test.ts @@ -7,8 +7,8 @@ */ import { mockStats, mockGetStats } from './get_usage_collector.mock'; -import { createUsageCollectionSetupMock } from '../../../usage_collection/server/mocks'; -import { createCollectorFetchContextMock } from '../../../usage_collection/server/mocks'; +import { createUsageCollectionSetupMock } from '../../../../usage_collection/server/mocks'; +import { createCollectorFetchContextMock } from '../../../../usage_collection/server/mocks'; import { registerTimeseriesUsageCollector } from './register_timeseries_collector'; describe('registerTimeseriesUsageCollector', () => { diff --git a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.ts similarity index 92% rename from src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts rename to src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.ts index 7e9294f03ba1c..6fccd7ef30171 100644 --- a/src/plugins/vis_type_timeseries/server/usage_collector/register_timeseries_collector.ts +++ b/src/plugins/vis_types/timeseries/server/usage_collector/register_timeseries_collector.ts @@ -7,7 +7,7 @@ */ import { getStats, TimeseriesUsage } from './get_usage_collector'; -import type { UsageCollectionSetup } from '../../../usage_collection/server'; +import type { UsageCollectionSetup } from '../../../../usage_collection/server'; export function registerTimeseriesUsageCollector(collectorSet: UsageCollectionSetup) { const collector = collectorSet.makeUsageCollector({ diff --git a/src/plugins/vis_types/timeseries/tsconfig.json b/src/plugins/vis_types/timeseries/tsconfig.json new file mode 100644 index 0000000000000..f336a467e0c81 --- /dev/null +++ b/src/plugins/vis_types/timeseries/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "*.ts" + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../data/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + { "path": "../../visualize/tsconfig.json" }, + { "path": "../../kibana_utils/tsconfig.json" }, + { "path": "../../kibana_react/tsconfig.json" }, + { "path": "../../usage_collection/tsconfig.json" }, + ] +} diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 3cd435ab0f6e8..4d4a0ff6320bd 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -15,7 +15,7 @@ import { PluginStart as DataPluginStart, } from '../../../../../../../src/plugins/data/server'; import { HomeServerPluginSetup } from '../../../../../../../src/plugins/home/server'; -import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_types/timeseries/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../plugins/features/server'; import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerting/server'; 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 2aede2f6aad16..4576a2e8452ac 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 @@ -34,7 +34,7 @@ import { RequestHandler } from '../../../../../../../src/core/server'; import { InfraConfig } from '../../../plugin'; import type { InfraPluginRequestHandlerContext } from '../../../types'; import { UI_SETTINGS } from '../../../../../../../src/plugins/data/server'; -import { TimeseriesVisData } from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { TimeseriesVisData } from '../../../../../../../src/plugins/vis_types/timeseries/server'; import { InfraServerPluginStartDeps } from './adapter_types'; export class KibanaFramework { diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index a9ddcc8d3d4c1..730da9511dc38 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -21,7 +21,7 @@ import { import { calculateMetricInterval } from '../../../utils/calculate_metric_interval'; import { CallWithRequestParams, InfraDatabaseSearchResponse } from '../framework'; import type { InfraPluginRequestHandlerContext } from '../../../types'; -import { isVisSeriesData } from '../../../../../../../src/plugins/vis_type_timeseries/server'; +import { isVisSeriesData } from '../../../../../../../src/plugins/vis_types/timeseries/server'; export class KibanaMetricsAdapter implements InfraMetricsAdapter { private framework: KibanaFramework; diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index a9739bdfdedc7..a2d1d2b63655a 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -22,7 +22,7 @@ { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, - { "path": "../../../src/plugins/vis_type_timeseries/tsconfig.json" }, + { "path": "../../../src/plugins/vis_types/timeseries/tsconfig.json" }, { "path": "../data_enhanced/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, { "path": "../features/tsconfig.json" }, diff --git a/x-pack/plugins/rollup/server/types.ts b/x-pack/plugins/rollup/server/types.ts index c774644da46ce..a3e826fefa0bf 100644 --- a/x-pack/plugins/rollup/server/types.ts +++ b/x-pack/plugins/rollup/server/types.ts @@ -7,7 +7,7 @@ import { IRouter } from 'src/core/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; +import { VisTypeTimeseriesSetup } from 'src/plugins/vis_types/timeseries/server'; import { getCapabilitiesForRollupIndices } from 'src/plugins/data/server'; import { IndexManagementPluginSetup } from '../../index_management/server'; diff --git a/x-pack/plugins/rollup/tsconfig.json b/x-pack/plugins/rollup/tsconfig.json index fbe323b2549ea..252c27a66fba2 100644 --- a/x-pack/plugins/rollup/tsconfig.json +++ b/x-pack/plugins/rollup/tsconfig.json @@ -22,7 +22,7 @@ { "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../index_management/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, - { "path": "../../../src/plugins/vis_type_timeseries/tsconfig.json" }, + { "path": "../../../src/plugins/vis_types/timeseries/tsconfig.json" }, // required bundles { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, From dd56ac3bcdcacc9951c0e455cf9b894634ffd525 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Sep 2021 07:05:01 -0400 Subject: [PATCH 20/69] Update dependency @types/node-forge to ^0.10.5 (master) (#112625) Co-authored-by: Renovate Bot --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f3af0ba80cfc6..c24f61e556556 100644 --- a/package.json +++ b/package.json @@ -573,7 +573,7 @@ "@types/nock": "^10.0.3", "@types/node": "14.14.44", "@types/node-fetch": "^2.5.7", - "@types/node-forge": "^0.10.4", + "@types/node-forge": "^0.10.5", "@types/nodemailer": "^6.4.0", "@types/normalize-path": "^3.0.0", "@types/object-hash": "^1.3.0", diff --git a/yarn.lock b/yarn.lock index ef179cc8105ed..a69450d4b628d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5631,10 +5631,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node-forge@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.10.4.tgz#f30025cc2da0177393b9deaefbf3b9edd55b807b" - integrity sha512-RpP7JCxlPA32n8FE0kjOpCsCrsX6VjiD0fjOCo4NwIn8IdcicHi4B2e+votWuOpOmwzUjMwRLqVIF95epGd5nA== +"@types/node-forge@^0.10.5": + version "0.10.5" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.10.5.tgz#f79925c88202817a7ec0958c3a9d3a915d362b4f" + integrity sha512-P+Q+MPSDr0RgIzv5h0gJuJDCm1e4RaSu/EMJZTUS4ZzboWH2uX/T7TiqAAcEFTHzCKtgMRqCgTVTX9SD72fMTQ== dependencies: "@types/node" "*" From 1ecb42db2ac5067b2470a0939907546862aa5f8f Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 21 Sep 2021 14:05:55 +0300 Subject: [PATCH 21/69] [i18n] fix type (#112642) --- .../document_fields/field_parameters/path_parameter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx index aeb4debddcd65..7307dd0e41eda 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx @@ -86,7 +86,7 @@ export const PathParameter = ({ field, allFields }: Props) => { 'xpack.idxMgmt.mappingsEditor.aliasType.aliasTargetFieldDescription', { defaultMessage: - 'Select the field you want your alias to point to. You will then be able to use the alias instead of the target field in search requests, and selected other APIs like field capabilities.', + 'Select the field you want your alias to point to. You will then be able to use the alias instead of the target field in search requests and select other APIs like field capabilities.', } )} withToggle={false} From b2bc5a592ddd58daa435bc6725cfdfbe1c81ef0a Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 21 Sep 2021 06:10:00 -0500 Subject: [PATCH 22/69] [storybook] Fix Shared UI Dep paths (#112631) --- packages/kbn-storybook/templates/index.ejs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/kbn-storybook/templates/index.ejs b/packages/kbn-storybook/templates/index.ejs index 1fca20b1ff8e8..3473f73ca131f 100644 --- a/packages/kbn-storybook/templates/index.ejs +++ b/packages/kbn-storybook/templates/index.ejs @@ -16,12 +16,11 @@ - - + - + From 322c5e26f09e74aa129ae8fd5f25842ae86767d3 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 21 Sep 2021 13:15:05 +0200 Subject: [PATCH 23/69] [Uptime] Update no data available state (#112403) * wip * update component * update paths * fix i18n * fix tests * revert uneeded --- .../translations/translations/ja-JP.json | 6 -- .../translations/translations/zh-CN.json | 6 -- .../public/apps/uptime_page_template.tsx | 64 ++++++++++++ .../uptime/public/apps/use_no_data_config.ts | 46 +++++++++ .../data_or_index_missing.test.tsx | 26 ----- .../empty_state/data_or_index_missing.tsx | 87 ---------------- .../overview/empty_state/empty_state.test.tsx | 99 ------------------- .../overview/empty_state/empty_state.tsx | 73 -------------- .../empty_state/empty_state_container.tsx | 54 ---------- .../components/overview/empty_state/index.ts | 9 -- .../overview/empty_state/use_has_data.tsx | 36 +++++++ .../public/components/overview/index.ts | 1 - .../plugins/uptime/public/pages/overview.tsx | 5 +- x-pack/plugins/uptime/public/routes.tsx | 19 +--- 14 files changed, 151 insertions(+), 380 deletions(-) create mode 100644 x-pack/plugins/uptime/public/apps/uptime_page_template.tsx create mode 100644 x-pack/plugins/uptime/public/apps/use_no_data_config.ts delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx delete mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/index.ts create mode 100644 x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f49d2d674330f..9c69e4fa612f5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -26137,13 +26137,7 @@ "xpack.uptime.createPackagePolicy.stepConfigure.tlsSettings.label": "TLS設定", "xpack.uptime.durationChart.emptyPrompt.description": "このモニターは選択された時間範囲で一度も{emphasizedText}していません。", "xpack.uptime.durationChart.emptyPrompt.title": "利用可能な期間データがありません", - "xpack.uptime.emptyState.configureHeartbeatIndexSettings": "Heartbeatがすでに設定されている場合は、データがElasticsearchに送信されていることを確認してから、Heartbeat構成に合わせてインデックスパターン設定を更新します。", - "xpack.uptime.emptyState.configureHeartbeatToGetStartedMessage": "サービスの監視を開始するには、Heartbeatを設定します。", "xpack.uptime.emptyState.loadingMessage": "読み込み中…", - "xpack.uptime.emptyState.noDataMessage": "インデックス{indexName}にはアップタイムデータが見つかりません", - "xpack.uptime.emptyState.noIndexTitle": "パターン{indexName}のインデックスが見つかりません", - "xpack.uptime.emptyState.updateIndexPattern": "インデックスパターン設定を更新", - "xpack.uptime.emptyState.viewSetupInstructions": "セットアップの手順を表示", "xpack.uptime.emptyStateError.notAuthorized": "アップタイムデータの表示が承認されていません。システム管理者にお問い合わせください。", "xpack.uptime.emptyStateError.notFoundPage": "ページが見つかりません", "xpack.uptime.emptyStateError.title": "エラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 85e11905e5eb7..1c91700a74e81 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -26572,13 +26572,7 @@ "xpack.uptime.createPackagePolicy.stepConfigure.tlsSettings.label": "TLS 设置", "xpack.uptime.durationChart.emptyPrompt.description": "在选定时间范围内此监测从未{emphasizedText}。", "xpack.uptime.durationChart.emptyPrompt.title": "没有持续时间数据", - "xpack.uptime.emptyState.configureHeartbeatIndexSettings": "如果已设置 Heartbeat,请确认其正向 Elasticsearch 发送数据,然后更新索引模式设置以匹配 Heartbeat 配置。", - "xpack.uptime.emptyState.configureHeartbeatToGetStartedMessage": "设置 Heartbeat 以开始监测您的服务。", "xpack.uptime.emptyState.loadingMessage": "正在加载……", - "xpack.uptime.emptyState.noDataMessage": "在索引 {indexName} 中找不到运行时间数据", - "xpack.uptime.emptyState.noIndexTitle": "找不到模式 {indexName} 的索引", - "xpack.uptime.emptyState.updateIndexPattern": "更新索引模式设置", - "xpack.uptime.emptyState.viewSetupInstructions": "查看设置说明", "xpack.uptime.emptyStateError.notAuthorized": "您无权查看 Uptime 数据,请联系系统管理员。", "xpack.uptime.emptyStateError.notFoundPage": "未找到页面", "xpack.uptime.emptyStateError.title": "错误", diff --git a/x-pack/plugins/uptime/public/apps/uptime_page_template.tsx b/x-pack/plugins/uptime/public/apps/uptime_page_template.tsx new file mode 100644 index 0000000000000..829f587e248e7 --- /dev/null +++ b/x-pack/plugins/uptime/public/apps/uptime_page_template.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import styled from 'styled-components'; +import { EuiPageHeaderProps } from '@elastic/eui'; +import { OVERVIEW_ROUTE } from '../../common/constants'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { ClientPluginsStart } from './plugin'; +import { useNoDataConfig } from './use_no_data_config'; +import { EmptyStateLoading } from '../components/overview/empty_state/empty_state_loading'; +import { EmptyStateError } from '../components/overview/empty_state/empty_state_error'; +import { useHasData } from '../components/overview/empty_state/use_has_data'; + +interface Props { + path: string; + pageHeader?: EuiPageHeaderProps; +} + +export const UptimePageTemplateComponent: React.FC = ({ path, pageHeader, children }) => { + const { + services: { observability }, + } = useKibana(); + + const PageTemplateComponent = observability.navigation.PageTemplate; + + const StyledPageTemplateComponent = useMemo(() => { + return styled(PageTemplateComponent)` + .euiPageHeaderContent > .euiFlexGroup { + flex-wrap: wrap; + } + `; + }, [PageTemplateComponent]); + + const noDataConfig = useNoDataConfig(); + + const { loading, error } = useHasData(); + + if (error) { + return ; + } + + return ( + <> +
+ + {loading && path === OVERVIEW_ROUTE && } +
+ {children} +
+
+ + ); +}; diff --git a/x-pack/plugins/uptime/public/apps/use_no_data_config.ts b/x-pack/plugins/uptime/public/apps/use_no_data_config.ts new file mode 100644 index 0000000000000..dc00a25e3a111 --- /dev/null +++ b/x-pack/plugins/uptime/public/apps/use_no_data_config.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useContext } from 'react'; +import { useSelector } from 'react-redux'; +import { KibanaPageTemplateProps, useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { UptimeSettingsContext } from '../contexts'; +import { ClientPluginsStart } from './plugin'; +import { indexStatusSelector } from '../state/selectors'; + +export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { + const { basePath } = useContext(UptimeSettingsContext); + + const { + services: { docLinks }, + } = useKibana(); + + const { data } = useSelector(indexStatusSelector); + + // Returns no data config when there is no historical data + if (data && !data.indexExists) { + return { + solution: i18n.translate('xpack.uptime.noDataConfig.solutionName', { + defaultMessage: 'Observability', + }), + actions: { + beats: { + title: i18n.translate('xpack.uptime.noDataConfig.beatsCard.title', { + defaultMessage: 'Add monitors with Heartbeat', + }), + description: i18n.translate('xpack.uptime.noDataConfig.beatsCard.description', { + defaultMessage: + 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', + }), + href: basePath + `/app/home#/tutorial/uptimeMonitors`, + }, + }, + docsLink: docLinks!.links.observability.guide, + }; + } +} diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx deleted file mode 100644 index caff055ce987c..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.test.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { render } from '../../../lib/helper/rtl_helpers'; -import { DataOrIndexMissing } from './data_or_index_missing'; - -describe('DataOrIndexMissing component', () => { - it('renders headingMessage', () => { - const headingMessage = ( - heartbeat-* }} - /> - ); - render(); - expect(screen.getByText(/heartbeat-*/)).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx deleted file mode 100644 index 44e55de990bbf..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiFlexGroup, - EuiEmptyPrompt, - EuiFlexItem, - EuiSpacer, - EuiPanel, - EuiTitle, - EuiButton, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useContext } from 'react'; -import { UptimeSettingsContext } from '../../../contexts'; -import { DynamicSettings } from '../../../../common/runtime_types'; - -interface DataMissingProps { - headingMessage: JSX.Element; - settings?: DynamicSettings; -} - -export const DataOrIndexMissing = ({ headingMessage, settings }: DataMissingProps) => { - const { basePath } = useContext(UptimeSettingsContext); - return ( - - - - - -

{headingMessage}

- - } - body={ - <> -

- -

-

- -

- - } - actions={ - - - - - - - - - - - - - } - /> -
-
-
- ); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx deleted file mode 100644 index 45b107928d79a..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { EmptyStateComponent } from './empty_state'; -import { StatesIndexStatus } from '../../../../common/runtime_types'; -import { HttpFetchError, IHttpFetchError } from 'src/core/public'; -import { render } from '../../../lib/helper/rtl_helpers'; - -describe('EmptyState component', () => { - let statesIndexStatus: StatesIndexStatus; - - beforeEach(() => { - statesIndexStatus = { - indexExists: true, - docCount: 1, - indices: 'heartbeat-*,synthetics-*', - }; - }); - - it('renders child components when count is truthy', () => { - render( - -
Foo
-
Bar
-
Baz
-
- ); - - expect(screen.getByText('Foo')).toBeInTheDocument(); - expect(screen.getByText('Bar')).toBeInTheDocument(); - expect(screen.getByText('Baz')).toBeInTheDocument(); - }); - - it(`doesn't render child components when count is falsy`, () => { - render( - -
Should not be rendered
-
- ); - expect(screen.queryByText('Should not be rendered')).toBeNull(); - }); - - it(`renders error message when an error occurs`, () => { - const errors: IHttpFetchError[] = [ - new HttpFetchError('There was an error fetching your data.', 'error', {} as any, {} as any, { - body: { message: 'There was an error fetching your data.' }, - }), - ]; - render( - -
Should not appear...
-
- ); - expect(screen.queryByText('Should not appear...')).toBeNull(); - }); - - it('renders loading state if no errors or doc count', () => { - render( - -
Should appear even while loading...
-
- ); - expect(screen.queryByText('Should appear even while loading...')).toBeInTheDocument(); - }); - - it('does not render empty state with appropriate base path and no docs', () => { - statesIndexStatus = { - docCount: 0, - indexExists: true, - indices: 'heartbeat-*,synthetics-*', - }; - const text = 'If this is in the snapshot the test should fail'; - render( - -
{text}
-
- ); - expect(screen.queryByText(text)).toBeNull(); - }); - - it('notifies when index does not exist', () => { - statesIndexStatus.indexExists = false; - - const text = 'This text should not render'; - - render( - -
{text}
-
- ); - expect(screen.queryByText(text)).toBeNull(); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx deleted file mode 100644 index a6fd6579c49fa..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { Fragment } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { IHttpFetchError } from 'src/core/public'; -import { EmptyStateError } from './empty_state_error'; -import { EmptyStateLoading } from './empty_state_loading'; -import { DataOrIndexMissing } from './data_or_index_missing'; -import { DynamicSettings, StatesIndexStatus } from '../../../../common/runtime_types'; - -interface EmptyStateProps { - children: JSX.Element[] | JSX.Element; - statesIndexStatus: StatesIndexStatus | null; - loading: boolean; - errors?: IHttpFetchError[]; - settings?: DynamicSettings; -} - -export const EmptyStateComponent = ({ - children, - statesIndexStatus, - loading, - errors, - settings, -}: EmptyStateProps) => { - if (errors?.length) { - return ; - } - const { indexExists, docCount } = statesIndexStatus ?? {}; - - const isLoading = loading && (!indexExists || docCount === 0 || !statesIndexStatus); - - const noIndicesMessage = ( - {settings?.heartbeatIndices} }} - /> - ); - - const noUptimeDataMessage = ( - {settings?.heartbeatIndices} }} - /> - ); - - if (!indexExists && !isLoading) { - return ; - } else if (indexExists && docCount === 0 && !isLoading) { - return ; - } - /** - * We choose to render the children any time the count > 0, even if - * the component is loading. If we render the loading state for this component, - * it will blow away the state of child components and trigger an ugly - * jittery UX any time the components refresh. This way we'll keep the stale - * state displayed during the fetching process. - */ - return ( - - {isLoading && } -
{children}
-
- ); - // } -}; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx deleted file mode 100644 index 562e45727dda7..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useContext, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { indexStatusAction } from '../../../state/actions'; -import { indexStatusSelector, selectDynamicSettings } from '../../../state/selectors'; -import { EmptyStateComponent } from './index'; -import { UptimeRefreshContext } from '../../../contexts'; -import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; - -export const EmptyState: React.FC = ({ children }) => { - const { data, loading, error } = useSelector(indexStatusSelector); - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { settings } = useSelector(selectDynamicSettings); - - const heartbeatIndices = settings?.heartbeatIndices || ''; - - const dispatch = useDispatch(); - - const noDataInfo = !data || data?.docCount === 0 || data?.indexExists === false; - - useEffect(() => { - if (noDataInfo) { - // only call when we haven't fetched it already - dispatch(indexStatusAction.get()); - } - }, [dispatch, lastRefresh, noDataInfo]); - - useEffect(() => { - // using separate side effect, we want to call index status, - // every statue indices setting changes - dispatch(indexStatusAction.get()); - }, [dispatch, heartbeatIndices]); - - useEffect(() => { - dispatch(getDynamicSettings()); - }, [dispatch]); - - return ( - - ); -}; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/index.ts b/x-pack/plugins/uptime/public/components/overview/empty_state/index.ts deleted file mode 100644 index 5ffcc15ed404b..0000000000000 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/index.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { EmptyStateComponent } from './empty_state'; -export { EmptyState } from './empty_state_container'; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx new file mode 100644 index 0000000000000..66c68834f285f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/use_has_data.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { indexStatusAction } from '../../../state/actions'; +import { indexStatusSelector, selectDynamicSettings } from '../../../state/selectors'; +import { UptimeRefreshContext } from '../../../contexts'; +import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; + +export const useHasData = () => { + const { loading, error } = useSelector(indexStatusSelector); + const { lastRefresh } = useContext(UptimeRefreshContext); + + const { settings } = useSelector(selectDynamicSettings); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(indexStatusAction.get()); + }, [dispatch, lastRefresh]); + + useEffect(() => { + dispatch(getDynamicSettings()); + }, [dispatch]); + + return { + error, + loading, + settings, + }; +}; diff --git a/x-pack/plugins/uptime/public/components/overview/index.ts b/x-pack/plugins/uptime/public/components/overview/index.ts index d647c38cee1ca..6ff4524df5277 100644 --- a/x-pack/plugins/uptime/public/components/overview/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/index.ts @@ -6,6 +6,5 @@ */ export * from './monitor_list'; -export * from './empty_state'; export * from './alerts'; export * from './snapshot'; diff --git a/x-pack/plugins/uptime/public/pages/overview.tsx b/x-pack/plugins/uptime/public/pages/overview.tsx index c350c8e1c962f..5199eab00f09f 100644 --- a/x-pack/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/plugins/uptime/public/pages/overview.tsx @@ -12,7 +12,6 @@ import styled from 'styled-components'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { useTrackPageview } from '../../../observability/public'; import { MonitorList } from '../components/overview/monitor_list/monitor_list_container'; -import { EmptyState } from '../components/overview'; import { StatusPanel } from '../components/overview/status_panel'; import { QueryBar } from '../components/overview/query_bar/query_bar'; import { MONITORING_OVERVIEW_LABEL } from '../routes'; @@ -37,7 +36,7 @@ export const OverviewPageComponent = () => { useBreadcrumbs([{ text: MONITORING_OVERVIEW_LABEL }]); // No extra breadcrumbs on overview return ( - + <> @@ -48,6 +47,6 @@ export const OverviewPageComponent = () => { - + ); }; diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index d111d44f08c2d..e151e19180dd4 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useEffect } from 'react'; -import styled from 'styled-components'; import { Route, Switch } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -27,10 +26,8 @@ import { SyntheticsCheckStepsPageHeader, SyntheticsCheckStepsPageRightSideItem, } from './pages/synthetics/synthetics_checks'; -import { ClientPluginsStart } from './apps/plugin'; import { MonitorPageTitle, MonitorPageTitleContent } from './components/monitor/monitor_title'; import { UptimeDatePicker } from './components/common/uptime_date_picker'; -import { useKibana } from '../../../../src/plugins/kibana_react/public'; import { CertRefreshBtn } from './components/certificates/cert_refresh_btn'; import { CertificateTitle } from './components/certificates/certificate_title'; import { SyntheticsCallout } from './components/overview/synthetics_callout'; @@ -40,6 +37,7 @@ import { StepDetailPageHeader, StepDetailPageRightSideItem, } from './pages/synthetics/step_detail_page'; +import { UptimePageTemplateComponent } from './apps/uptime_page_template'; interface RouteProps { path: string; @@ -159,17 +157,6 @@ const RouteInit: React.FC> = }; export const PageRouter: FC = () => { - const { - services: { observability }, - } = useKibana(); - const PageTemplateComponent = observability.navigation.PageTemplate; - - const StyledPageTemplateComponent = styled(PageTemplateComponent)` - .euiPageHeaderContent > .euiFlexGroup { - flex-wrap: wrap; - } - `; - return ( {Routes.map( @@ -178,9 +165,9 @@ export const PageRouter: FC = () => {
- + - +
) From b1d6779d43cd8616bb8be88765da5aaa50e87afb Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 21 Sep 2021 08:23:13 -0500 Subject: [PATCH 24/69] [fleet] Introduce Storybook to Fleet (#112611) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../steps/storybooks/build_and_upload.js | 1 + src/dev/storybook/aliases.ts | 1 + .../components/assets_facet_group.stories.tsx | 55 +++++++ .../epm/components/package_card.stories.tsx | 79 ++++++++++ .../sections/epm/components/package_card.tsx | 2 +- .../components/package_list_grid.stories.tsx | 138 ++++++++++++++++++ .../epm/components/package_list_grid.tsx | 2 +- .../epm/components/requirements.stories.tsx | 39 +++++ x-pack/plugins/fleet/storybook/decorator.tsx | 79 ++++++++++ x-pack/plugins/fleet/storybook/main.ts | 20 +++ x-pack/plugins/fleet/storybook/manager.ts | 20 +++ x-pack/plugins/fleet/storybook/preview.tsx | 28 ++++ x-pack/plugins/fleet/tsconfig.json | 1 + 13 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx create mode 100644 x-pack/plugins/fleet/storybook/decorator.tsx create mode 100644 x-pack/plugins/fleet/storybook/main.ts create mode 100644 x-pack/plugins/fleet/storybook/manager.ts create mode 100644 x-pack/plugins/fleet/storybook/preview.tsx diff --git a/.buildkite/scripts/steps/storybooks/build_and_upload.js b/.buildkite/scripts/steps/storybooks/build_and_upload.js index 9d72f518837e9..c1032575ae4a6 100644 --- a/.buildkite/scripts/steps/storybooks/build_and_upload.js +++ b/.buildkite/scripts/steps/storybooks/build_and_upload.js @@ -13,6 +13,7 @@ const STORYBOOKS = [ 'dashboard_enhanced', 'data_enhanced', 'embeddable', + 'fleet', 'infra', 'security_solution', 'ui_actions_enhanced', diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 9395c5fdf8834..a61a2618d6428 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -24,6 +24,7 @@ export const storybookAliases = { expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', expression_tagcloud: 'src/plugins/chart_expressions/expression_tagcloud/.storybook', + fleet: 'x-pack/plugins/fleet/storybook', infra: 'x-pack/plugins/infra/.storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/.storybook', diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx new file mode 100644 index 0000000000000..d98f2b2408d56 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { AssetsFacetGroup as Component } from './assets_facet_group'; + +export default { + component: Component, + title: 'Sections/EPM/Assets Facet Group', +}; + +interface Args { + width: number; +} + +const args: Args = { + width: 250, +}; + +export const AssetsFacetGroup = ({ width }: Args) => { + return ( +
+ +
+ ); +}; + +AssetsFacetGroup.args = args; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx new file mode 100644 index 0000000000000..e8814b8b8c877 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import type { SavedObject } from 'src/core/public'; + +import type { Installation } from '../../../../../../common'; + +import type { PackageCardProps } from './package_card'; +import { PackageCard } from './package_card'; + +export default { + title: 'Sections/EPM/Package Card', + description: 'A card representing a package available in Fleet', +}; + +type Args = Omit & { width: number }; + +const args: Args = { + width: 250, + title: 'Title', + description: 'Description', + name: 'beats', + release: 'ga', + id: 'id', + version: '1.0.0', + download: '/', + path: 'path', +}; + +const argTypes = { + release: { + control: { + type: 'radio', + options: ['ga', 'beta', 'experimental'], + }, + }, +}; + +export const NotInstalled = ({ width, ...props }: Args) => ( +
+ +
+); + +export const Installed = ({ width, ...props }: Args) => { + const savedObject: SavedObject = { + id: props.id, + type: props.type || '', + attributes: { + name: props.name, + version: props.version, + install_version: props.version, + es_index_patterns: {}, + installed_kibana: [], + installed_es: [], + install_status: 'installed', + install_source: 'registry', + install_started_at: '2020-01-01T00:00:00.000Z', + }, + references: [], + }; + + return ( +
+ +
+ ); +}; + +NotInstalled.args = args; +NotInstalled.argTypes = argTypes; +Installed.args = args; +Installed.argTypes = argTypes; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx index c12e67fdb5718..c2d6d0f1e028b 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx @@ -15,7 +15,7 @@ import { PackageIcon } from '../../../components'; import { RELEASE_BADGE_LABEL, RELEASE_BADGE_DESCRIPTION } from './release_badge'; -type PackageCardProps = PackageListItem; +export type PackageCardProps = PackageListItem; // adding the `href` causes EuiCard to use a `a` instead of a `button` // `a` tags use `euiLinkColor` which results in blueish Badge text diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx new file mode 100644 index 0000000000000..d84e286b6f560 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.stories.tsx @@ -0,0 +1,138 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { action } from '@storybook/addon-actions'; + +import type { SavedObject } from 'src/core/public'; + +import type { Installation } from '../../../../../../common'; + +import type { ListProps } from './package_list_grid'; +import { PackageListGrid } from './package_list_grid'; + +export default { + component: PackageListGrid, + title: 'Sections/EPM/Package List Grid', +}; + +type Args = Pick; + +const args: Args = { + title: 'Installed integrations', + isLoading: false, + showMissingIntegrationMessage: false, +}; + +const savedObject: SavedObject = { + id: 'id', + type: 'integration', + attributes: { + name: 'savedObject', + version: '1.2.3', + install_version: '1.2.3', + es_index_patterns: {}, + installed_kibana: [], + installed_es: [], + install_status: 'installed', + install_source: 'registry', + install_started_at: '2020-01-01T00:00:00.000Z', + }, + references: [], +}; + +export const EmptyList = (props: Args) => ( + +); + +export const List = (props: Args) => ( + +); + +EmptyList.args = args; +List.args = args; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx index 6bbd479c5c2ba..db63c5c7dd832 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx @@ -28,7 +28,7 @@ import { useLocalSearch, searchIdField } from '../../../hooks'; import { PackageCard } from './package_card'; -interface ListProps { +export interface ListProps { isLoading?: boolean; controls?: ReactNode; title: string; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx new file mode 100644 index 0000000000000..205d739d48696 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/requirements.stories.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { Requirements as Component } from './requirements'; + +export default { + component: Component, + title: 'Sections/EPM/Requirements', +}; + +interface Args { + width: number; +} + +const args: Args = { + width: 250, +}; + +export const Requirements = ({ width }: Args) => { + return ( +
+ +
+ ); +}; + +Requirements.args = args; diff --git a/x-pack/plugins/fleet/storybook/decorator.tsx b/x-pack/plugins/fleet/storybook/decorator.tsx new file mode 100644 index 0000000000000..499b01c2bfba0 --- /dev/null +++ b/x-pack/plugins/fleet/storybook/decorator.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { of } from 'rxjs'; +import type { DecoratorFn } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { createMemoryHistory } from 'history'; + +import { I18nProvider } from '@kbn/i18n/react'; + +import { ScopedHistory } from '../../../../src/core/public'; +import { IntegrationsAppContext } from '../public/applications/integrations/app'; +import type { FleetConfigType, FleetStartServices } from '../public/plugin'; + +// TODO: clintandrewhall - this is not ideal, or complete. The root context of Fleet applications +// requires full start contracts of its dependencies. As a result, we have to mock all of those contracts +// with Storybook equivalents. This is a temporary solution, and should be replaced with a more complete +// mock later, (or, ideally, Fleet starts to use a service abstraction). +// +// Expect this to grow as components that are given Stories need access to mocked services. +export const contextDecorator: DecoratorFn = (story: Function) => { + const basepath = '/'; + const memoryHistory = createMemoryHistory({ initialEntries: [basepath] }); + const history = new ScopedHistory(memoryHistory, basepath); + + const startServices = { + application: { + currentAppId$: of('home'), + navigateToUrl: (url: string) => action(`Navigate to: ${url}`), + getUrlForApp: (url: string) => url, + }, + http: { + basePath: { + prepend: () => basepath, + }, + }, + notifications: {}, + history, + uiSettings: { + get$: (key: string) => { + switch (key) { + case 'theme:darkMode': + return of(false); + default: + return of(); + } + }, + }, + i18n: { + Context: I18nProvider, + }, + } as unknown as FleetStartServices; + + const config = { + enabled: true, + agents: { + enabled: true, + elasticsearch: {}, + }, + } as unknown as FleetConfigType; + + const extensions = {}; + + const kibanaVersion = '1.2.3'; + + return ( + + {story()} + + ); +}; diff --git a/x-pack/plugins/fleet/storybook/main.ts b/x-pack/plugins/fleet/storybook/main.ts new file mode 100644 index 0000000000000..2cf4124c61783 --- /dev/null +++ b/x-pack/plugins/fleet/storybook/main.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Configuration } from 'webpack'; +import { defaultConfig, WebpackConfig } from '@kbn/storybook'; + +module.exports = { + ...defaultConfig, + addons: ['@storybook/addon-essentials'], + babel: () => ({ + presets: [require.resolve('@kbn/babel-preset/webpack_preset')], + }), + webpackFinal: (config: Configuration) => { + return WebpackConfig({ config }); + }, +}; diff --git a/x-pack/plugins/fleet/storybook/manager.ts b/x-pack/plugins/fleet/storybook/manager.ts new file mode 100644 index 0000000000000..471a735ed370f --- /dev/null +++ b/x-pack/plugins/fleet/storybook/manager.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { addons } from '@storybook/addons'; +import { create } from '@storybook/theming'; +import { PANEL_ID } from '@storybook/addon-actions'; + +addons.setConfig({ + theme: create({ + base: 'light', + brandTitle: 'Kibana Fleet Storybook', + brandUrl: 'https://github.com/elastic/kibana/tree/master/x-pack/plugins/fleet', + }), + showPanel: true.valueOf, + selectedPanel: PANEL_ID, +}); diff --git a/x-pack/plugins/fleet/storybook/preview.tsx b/x-pack/plugins/fleet/storybook/preview.tsx new file mode 100644 index 0000000000000..a50ff2faaff56 --- /dev/null +++ b/x-pack/plugins/fleet/storybook/preview.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { addDecorator } from '@storybook/react'; +import { Title, Subtitle, Description, Primary, Stories } from '@storybook/addon-docs/blocks'; + +import { contextDecorator } from './decorator'; + +addDecorator(contextDecorator); + +export const parameters = { + docs: { + page: () => ( + <> + + <Subtitle /> + <Description /> + <Primary /> + <Stories /> + </> + ), + }, +}; diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 5002bf2893872..a9dd66ce503a9 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -14,6 +14,7 @@ "server/**/*.json", "scripts/**/*", "package.json", + "storybook/**/*", "../../../typings/**/*" ], "references": [ From 119c742185a5649e17efbdbaa67185da7aabb5c1 Mon Sep 17 00:00:00 2001 From: Matthew Kime <matt@mattki.me> Date: Tue, 21 Sep 2021 09:41:47 -0500 Subject: [PATCH 25/69] Data views - saved object client use `resolve` instead of `get` (#108637) * so client - use resolve instead of get Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../errors/data_view_saved_object_conflict.ts | 14 +++++ .../data/common/data_views/errors/index.ts | 1 + .../saved_objects_client_wrapper.test.ts | 55 +++++++++++++++++++ .../saved_objects_client_wrapper.ts | 10 +++- .../saved_objects_client_wrapper.test.ts | 55 +++++++++++++++++++ .../saved_objects_client_wrapper.ts | 7 ++- src/plugins/data/server/index.ts | 1 + 7 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.ts create mode 100644 src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts create mode 100644 src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts diff --git a/src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.ts b/src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.ts new file mode 100644 index 0000000000000..3fcb281655727 --- /dev/null +++ b/src/plugins/data/common/data_views/errors/data_view_saved_object_conflict.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export class DataViewSavedObjectConflictError extends Error { + constructor(savedObjectId: string) { + super(`Conflict loading DataView saved object, id: ${savedObjectId}`); + this.name = 'DataViewSavedObjectConflictError'; + } +} diff --git a/src/plugins/data/common/data_views/errors/index.ts b/src/plugins/data/common/data_views/errors/index.ts index 63bd1ac5f5848..20ff90d3fd6cf 100644 --- a/src/plugins/data/common/data_views/errors/index.ts +++ b/src/plugins/data/common/data_views/errors/index.ts @@ -7,3 +7,4 @@ */ export * from './duplicate_index_pattern'; +export * from './data_view_saved_object_conflict'; diff --git a/src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts b/src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts new file mode 100644 index 0000000000000..221a18ac7fab7 --- /dev/null +++ b/src/plugins/data/public/data_views/saved_objects_client_wrapper.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper'; +import { savedObjectsServiceMock } from 'src/core/public/mocks'; + +import { DataViewSavedObjectConflictError } from '../../common/data_views'; + +describe('SavedObjectsClientPublicToCommon', () => { + const soClient = savedObjectsServiceMock.createStartContract().client; + + test('get saved object - exactMatch', async () => { + const mockedSavedObject = { + version: 'abc', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientPublicToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - aliasMatch', async () => { + const mockedSavedObject = { + version: 'def', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientPublicToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - conflict', async () => { + const mockedSavedObject = { + version: 'ghi', + }; + + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientPublicToCommon(soClient); + + await expect(service.get('index-pattern', '1')).rejects.toThrow( + DataViewSavedObjectConflictError + ); + }); +}); diff --git a/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts b/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts index f55ca3896df33..1db4e3b1ccd24 100644 --- a/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts +++ b/src/plugins/data/public/data_views/saved_objects_client_wrapper.ts @@ -12,9 +12,10 @@ import { SavedObjectsClientCommon, SavedObjectsClientCommonFindArgs, SavedObject, + DataViewSavedObjectConflictError, } from '../../common/data_views'; -type SOClient = Pick<SavedObjectsClient, 'find' | 'get' | 'update' | 'create' | 'delete'>; +type SOClient = Pick<SavedObjectsClient, 'find' | 'resolve' | 'update' | 'create' | 'delete'>; const simpleSavedObjectToSavedObject = <T>(simpleSavedObject: SimpleSavedObject): SavedObject<T> => ({ @@ -33,8 +34,11 @@ export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommo } async get<T = unknown>(type: string, id: string) { - const response = await this.savedObjectClient.get<T>(type, id); - return simpleSavedObjectToSavedObject<T>(response); + const response = await this.savedObjectClient.resolve<T>(type, id); + if (response.outcome === 'conflict') { + throw new DataViewSavedObjectConflictError(id); + } + return simpleSavedObjectToSavedObject<T>(response.saved_object); } async update( type: string, diff --git a/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts new file mode 100644 index 0000000000000..39cf74b906106 --- /dev/null +++ b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper'; +import { SavedObjectsClientContract } from 'src/core/server'; + +import { DataViewSavedObjectConflictError } from '../../common/data_views'; + +describe('SavedObjectsClientPublicToCommon', () => { + const soClient = ({ resolve: jest.fn() } as unknown) as SavedObjectsClientContract; + + test('get saved object - exactMatch', async () => { + const mockedSavedObject = { + version: 'abc', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientServerToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - aliasMatch', async () => { + const mockedSavedObject = { + version: 'def', + }; + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientServerToCommon(soClient); + const result = await service.get('index-pattern', '1'); + expect(result).toStrictEqual(mockedSavedObject); + }); + + test('get saved object - conflict', async () => { + const mockedSavedObject = { + version: 'ghi', + }; + + soClient.resolve = jest + .fn() + .mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject }); + const service = new SavedObjectsClientServerToCommon(soClient); + + await expect(service.get('index-pattern', '1')).rejects.toThrow( + DataViewSavedObjectConflictError + ); + }); +}); diff --git a/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts b/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts index 45ad8d85f6086..b37648a3f038e 100644 --- a/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts +++ b/src/plugins/data/server/data_views/saved_objects_client_wrapper.ts @@ -10,6 +10,7 @@ import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; import { SavedObjectsClientCommon, SavedObjectsClientCommonFindArgs, + DataViewSavedObjectConflictError, } from '../../common/data_views'; export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommon { @@ -23,7 +24,11 @@ export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommo } async get<T = unknown>(type: string, id: string) { - return await this.savedObjectClient.get<T>(type, id); + const response = await this.savedObjectClient.resolve<T>(type, id); + if (response.outcome === 'conflict') { + throw new DataViewSavedObjectConflictError(id); + } + return response.saved_object; } async update( type: string, diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 9d2e94bcf15c0..a17c66c694b2d 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -54,6 +54,7 @@ export { IndexPattern, IndexPatternsService, IndexPatternsService as IndexPatternsCommonService, + DataView, } from '../common'; /** From 9bd61a21fe4a1f69e218b60f8b5ce14dd686c5c4 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 21 Sep 2021 10:58:24 -0400 Subject: [PATCH 26/69] [Security Solution][Endpoint] Policy Details store/routing common changes for Trusted Apps by Policy feature (#112567) The changes were picked from the current draft PR #112239 so that they can be shared with other changes taking place in parallel: - Routing methods for parsing/retrieving url params for policy details page - store selectors/reducers in support of url location management for policy details --- .../public/management/common/routing.ts | 48 +++++++++++++++++++ .../policy/store/policy_details/reducer.ts | 23 +++++++++ .../policy/store/policy_details/selectors.ts | 9 +++- .../public/management/pages/policy/types.ts | 21 ++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index b4776f328cf15..58fbd64faf8a6 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -26,6 +26,7 @@ import { appendSearch } from '../../common/components/link_to/helpers'; import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types'; import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; import { EventFiltersPageLocation } from '../pages/event_filters/types'; +import { PolicyDetailsArtifactsPageLocation } from '../pages/policy/types'; // Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150 type ExactKeys<T1, T2> = Exclude<keyof T1, keyof T2> extends never ? T1 : never; @@ -160,6 +161,25 @@ const normalizeTrustedAppsPageLocation = ( } }; +const normalizePolicyDetailsArtifactsListPageLocation = ( + location?: Partial<PolicyDetailsArtifactsPageLocation> +): Partial<PolicyDetailsArtifactsPageLocation> => { + if (location) { + return { + ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE) + ? { page_index: location.page_index } + : {}), + ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) + ? { page_size: location.page_size } + : {}), + ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), + ...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''), + }; + } else { + return {}; + } +}; + const normalizeEventFiltersPageLocation = ( location?: Partial<EventFiltersPageLocation> ): Partial<EventFiltersPageLocation> => { @@ -257,6 +277,34 @@ export const getTrustedAppsListPath = (location?: Partial<TrustedAppsListPageLoc )}`; }; +export const extractPolicyDetailsArtifactsListPageLocation = ( + query: querystring.ParsedUrlQuery +): PolicyDetailsArtifactsPageLocation => { + const showParamValue = extractFirstParamValue( + query, + 'show' + ) as PolicyDetailsArtifactsPageLocation['show']; + + return { + ...extractListPaginationParams(query), + show: showParamValue && 'list' === showParamValue ? showParamValue : undefined, + }; +}; + +export const getPolicyDetailsArtifactsListPath = ( + policyId: string, + location?: Partial<PolicyDetailsArtifactsPageLocation> +): string => { + const path = generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH, { + tabName: AdministrationSubTab.policies, + policyId, + }); + + return `${path}${appendSearch( + querystring.stringify(normalizePolicyDetailsArtifactsListPageLocation(location)) + )}`; +}; + export const extractEventFiltetrsPageLocation = ( query: querystring.ParsedUrlQuery ): EventFiltersPageLocation => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts index 512059e9c3aab..cfd808fb9b621 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts @@ -5,6 +5,8 @@ * 2.0. */ +// eslint-disable-next-line import/no-nodejs-modules +import { parse } from 'querystring'; import { fullPolicy, isOnPolicyDetailsPage, license } from './selectors'; import { Immutable, @@ -15,6 +17,12 @@ import { import { ImmutableReducer } from '../../../../../common/store'; import { AppAction } from '../../../../../common/store/actions'; import { PolicyDetailsState } from '../../types'; +import { + MANAGEMENT_DEFAULT_PAGE, + MANAGEMENT_DEFAULT_PAGE_SIZE, +} from '../../../../common/constants'; +import { extractPolicyDetailsArtifactsListPageLocation } from '../../../../common/routing'; +import { createUninitialisedResourceState } from '../../../../state'; const updatePolicyConfigInPolicyData = ( policyData: Immutable<PolicyData>, @@ -47,6 +55,15 @@ export const initialPolicyDetailsState: () => Immutable<PolicyDetailsState> = () total: 0, other: 0, }, + artifacts: { + location: { + page_index: MANAGEMENT_DEFAULT_PAGE, + page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: undefined, + filter: '', + }, + availableList: createUninitialisedResourceState(), + }, }); export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = ( @@ -106,6 +123,12 @@ export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppActio const newState: Immutable<PolicyDetailsState> = { ...state, location: action.payload, + artifacts: { + ...state.artifacts, + location: extractPolicyDetailsArtifactsListPageLocation( + parse(action.payload.search.slice(1)) + ), + }, }; const isCurrentlyOnDetailsPage = isOnPolicyDetailsPage(newState); const wasPreviouslyOnDetailsPage = isOnPolicyDetailsPage(state); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts index 017111da8d884..1aae538cbf47e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts @@ -9,7 +9,7 @@ import { createSelector } from 'reselect'; import { matchPath } from 'react-router-dom'; import { ILicense } from '../../../../../../../licensing/common/types'; import { unsetPolicyFeaturesAccordingToLicenseLevel } from '../../../../../../common/license/policy_config'; -import { PolicyDetailsState } from '../../types'; +import { PolicyDetailsArtifactsPageLocation, PolicyDetailsState } from '../../types'; import { Immutable, NewPolicyData, @@ -80,6 +80,13 @@ export const needsToRefresh = (state: Immutable<PolicyDetailsState>): boolean => return !state.policyItem && !state.apiError; }; +/** + * Returns current artifacts location + */ +export const getCurrentArtifactsLocation = ( + state: Immutable<PolicyDetailsState> +): Immutable<PolicyDetailsArtifactsPageLocation> => state.artifacts.location; + /** Returns a boolean of whether the user is on the policy form page or not */ export const isOnPolicyFormPage = (state: Immutable<PolicyDetailsState>) => { return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts index 6d767df73cd1c..14d740510d251 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts @@ -21,6 +21,8 @@ import { GetPackagesResponse, UpdatePackagePolicyResponse, } from '../../../../../fleet/common'; +import { AsyncResourceState } from '../../state'; +import { TrustedAppsListData } from '../trusted_apps/state'; /** * Policy list store state @@ -61,6 +63,8 @@ export interface PolicyDetailsState { isLoading: boolean; /** current location of the application */ location?: Immutable<AppLocation>; + /** artifacts namespace inside policy details page */ + artifacts: PolicyArtifactsState; /** A summary of stats for the agents associated with a given Fleet Agent Policy */ agentStatusSummary?: Omit<GetAgentStatusResponse['results'], 'updating'>; /** Status of an update to the policy */ @@ -72,12 +76,29 @@ export interface PolicyDetailsState { license?: ILicense; } +/** + * Policy artifacts store state + */ +export interface PolicyArtifactsState { + /** artifacts location params */ + location: PolicyDetailsArtifactsPageLocation; + /** A list of artifacts can be linked to the policy */ + availableList: AsyncResourceState<TrustedAppsListData>; +} + export enum OS { windows = 'windows', mac = 'mac', linux = 'linux', } +export interface PolicyDetailsArtifactsPageLocation { + page_index: number; + page_size: number; + show?: 'list'; + filter: string; +} + /** * Returns the keys of an object whose values meet a criteria. * Ex) interface largeNestedObject = { From 2e563510969cefd21cd3b614b08364a52418219e Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 21 Sep 2021 11:24:25 -0400 Subject: [PATCH 27/69] [Security solution][endpoint] Fix Endpoint List KQL bar not updating the list when the filter is removed (#112595) * Fix search bar `onQuerySubmit` to drop prior `admin_query` param when pushing a new route change * enhance current test case to ensure existing data is also changed when its blank --- .../pages/endpoint_hosts/view/components/search_bar.test.tsx | 2 +- .../pages/endpoint_hosts/view/components/search_bar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx index 624a3c265c4c0..1cb21c7da1703 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.test.tsx @@ -92,7 +92,7 @@ describe('when rendering the endpoint list `AdminSearchBar`', () => { ])( 'should update the url and exclude the `admin_query` param when %s was entered', async (_, value) => { - await render(); + await render({ admin_query: "(language:kuery,query:'foo')" }); await submitQuery(value); expect(getQueryParamsFromStore().admin_query).toBe(undefined); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx index 2f2a1666b6f52..18d22e0cd1b15 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -24,7 +24,7 @@ const AdminQueryBar = styled.div` export const AdminSearchBar = memo(() => { const history = useHistory(); - const queryParams = useEndpointSelector(selectors.uiQueryParams); + const { admin_query: _, ...queryParams } = useEndpointSelector(selectors.uiQueryParams); const searchBarIndexPatterns = useEndpointSelector(selectors.patterns); const searchBarQuery = useEndpointSelector(selectors.searchBarQuery); const clonedIndexPatterns = useMemo( From 924ad664507a153025fb0d73c27e35d5495a4bbb Mon Sep 17 00:00:00 2001 From: Brian Seeders <brian.seeders@elastic.co> Date: Tue, 21 Sep 2021 11:30:20 -0400 Subject: [PATCH 28/69] [CI] Bump agent size for jest integration tests in Buildkite (#112672) --- .buildkite/pipelines/es_snapshots/verify.yml | 2 +- .buildkite/pipelines/hourly.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index 10c996c5aceca..9af2e938db49d 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -64,7 +64,7 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' agents: - queue: jest + queue: n2-4 timeout_in_minutes: 120 key: jest-integration retry: diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index b78db4698c01b..e8480b2d3b75d 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -118,14 +118,14 @@ steps: - command: .buildkite/scripts/steps/test/jest_integration.sh label: 'Jest Integration Tests' agents: - queue: jest + queue: n2-4 timeout_in_minutes: 120 key: jest-integration - command: .buildkite/scripts/steps/test/api_integration.sh label: 'API Integration Tests' agents: - queue: jest + queue: n2-2 timeout_in_minutes: 120 key: api-integration From 14b2157990df733568032eff98ce6d7119382592 Mon Sep 17 00:00:00 2001 From: Brian Seeders <brian.seeders@elastic.co> Date: Tue, 21 Sep 2021 11:31:15 -0400 Subject: [PATCH 29/69] [CI] Disable tracked branch jobs in Jenkins, enable reporting in Buildkite (#112604) --- .buildkite/scripts/lifecycle/post_command.sh | 3 +-- Jenkinsfile | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.buildkite/scripts/lifecycle/post_command.sh b/.buildkite/scripts/lifecycle/post_command.sh index 14391fdd1e698..23f44a586e978 100755 --- a/.buildkite/scripts/lifecycle/post_command.sh +++ b/.buildkite/scripts/lifecycle/post_command.sh @@ -22,6 +22,5 @@ if [[ "$IS_TEST_EXECUTION_STEP" == "true" ]]; then buildkite-agent artifact upload 'x-pack/test/functional/failure_debug/html/*.html' buildkite-agent artifact upload '.es/**/*.hprof' - # TODO - re-enable when Jenkins is disabled - # node scripts/report_failed_tests --build-url="${BUILDKITE_BUILD_URL}#${BUILDKITE_JOB_ID}" 'target/junit/**/*.xml' + node scripts/report_failed_tests --build-url="${BUILDKITE_BUILD_URL}#${BUILDKITE_JOB_ID}" 'target/junit/**/*.xml' fi diff --git a/Jenkinsfile b/Jenkinsfile index db5ae306e6e2e..7dd3d0f41d27a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,10 @@ #!/bin/groovy +if (!env.ghprbPullId) { + print "Non-PR builds are now in Buildkite." + return +} + library 'kibana-pipeline-library' kibanaLibrary.load() From 04e5a154642e12b31831ffade5ee633fa15fe1d4 Mon Sep 17 00:00:00 2001 From: Lisa Cawley <lcawley@elastic.co> Date: Tue, 21 Sep 2021 08:33:48 -0700 Subject: [PATCH 30/69] [DOCS] Add machine learning sync API (#112033) --- docs/api/machine-learning.asciidoc | 11 ++++ docs/api/machine-learning/sync.asciidoc | 79 +++++++++++++++++++++++++ docs/user/api.asciidoc | 1 + 3 files changed, 91 insertions(+) create mode 100644 docs/api/machine-learning.asciidoc create mode 100644 docs/api/machine-learning/sync.asciidoc diff --git a/docs/api/machine-learning.asciidoc b/docs/api/machine-learning.asciidoc new file mode 100644 index 0000000000000..265896e6340df --- /dev/null +++ b/docs/api/machine-learning.asciidoc @@ -0,0 +1,11 @@ +[[machine-learning-api]] +== {ml-cap} APIs + +//Manage {kib} saved objects, including dashboards, visualizations, and more. + +The following {ml} API is available: + +* <<machine-learning-api-sync, Sync API>> +//to retrieve a single {kib} saved object by ID + +include::machine-learning/sync.asciidoc[] diff --git a/docs/api/machine-learning/sync.asciidoc b/docs/api/machine-learning/sync.asciidoc new file mode 100644 index 0000000000000..5f19bc17ab2fb --- /dev/null +++ b/docs/api/machine-learning/sync.asciidoc @@ -0,0 +1,79 @@ +[[machine-learning-api-sync]] +=== Sync {ml} saved objects API +++++ +<titleabbrev>Sync {ml} saved objects</titleabbrev> +++++ + +Synchronizes {kib} saved objects for {ml} jobs. + +[[machine-learning-api-sync-request]] +==== Request + +`GET <kibana host>:<port>/api/ml/saved_objects/sync` + +`GET <kibana host>:<port>/s/<space_id>/api/ml/saved_objects/sync` + + +[[machine-learning-api-sync-path-params]] +==== Path parameters + +`space_id`:: +(Optional, string) An identifier for the space. If `space_id` is not provided in +the URL the default space is used. + +[[machine-learning-api-sync-query-params]] +==== Query parameters + +`simulate`:: +(Optional, boolean) When `true`, simulates the synchronization by only returning +the list actions that _would_ be performed. + +[[machine-learning-api-sync-response-body]] +==== Response body + +`datafeedsAdded`:: +(array) If a saved object for an {anomaly-job} is missing a {dfeed} identifier, +it is added. This list contains the {dfeed} identifiers and indicates whether +the synchronization was successful. + +`datafeedsRemoved`:: +(array) If saved objects exist for {dfeeds} that no longer exist, they are +deleted. This list contains the {dfeed} identifiers and indicates whether the +synchronization was successful. + +`savedObjectsCreated`:: +(array) If saved objects are missing for {ml} jobs, they are created. This +list contains the job identifiers and indicates whether the synchronization was +successful. + +`savedObjectsDeleted`:: +(array) If saved objects exist for jobs that no longer exist, they are deleted. +This list contains the job identifiers and indicates whether the synchronization +was successful. + +[[machine-learning-api-sync-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[machine-learning-api-sync-example]] +==== Example + +Retrieve the list of {ml} saved objects that require synchronization: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/ml/saved_objects/sync?simulate=true +-------------------------------------------------- +// KIBANA + +If there are two jobs and a {dfeed} that need to be synchronized, for example, +the API returns the following: + +[source,sh] +-------------------------------------------------- +{{"savedObjectsCreated":{"myjob1":{"success":true},"myjob2":{"success":true}},"savedObjectsDeleted":{},"datafeedsAdded":{},"datafeedsRemoved":{"myfeed3":{"success":true}}} +-------------------------------------------------- + +To perform the synchronization, re-run the API and omit the `simulate` parameter. \ No newline at end of file diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc index 00aa3c545df69..12e200bb0ba27 100644 --- a/docs/user/api.asciidoc +++ b/docs/user/api.asciidoc @@ -96,6 +96,7 @@ include::{kib-repo-dir}/api/alerting.asciidoc[] include::{kib-repo-dir}/api/actions-and-connectors.asciidoc[] include::{kib-repo-dir}/api/dashboard-api.asciidoc[] include::{kib-repo-dir}/api/logstash-configuration-management.asciidoc[] +include::{kib-repo-dir}/api/machine-learning.asciidoc[] include::{kib-repo-dir}/api/url-shortening.asciidoc[] include::{kib-repo-dir}/api/task-manager/health.asciidoc[] include::{kib-repo-dir}/api/upgrade-assistant.asciidoc[] From fd0f42386f9815082b79e74a47afde05cbc91641 Mon Sep 17 00:00:00 2001 From: James Rodewig <40268737+jrodewig@users.noreply.github.com> Date: Tue, 21 Sep 2021 12:03:23 -0400 Subject: [PATCH 31/69] Update index settings link for Snapshot and Restore (#112597) Updates the docs help link for index settings in the Snapshot and Restore feature. Currently, the link points to https://www.elastic.co/guide/en/elasticsearch/reference/7.14/snapshots-restore-snapshot.html#change-index-settings-during-restore, which primarily documents related restore snapshot API parameters. With elastic/elasticsearch#76929, this section was removed. The new link covers all available index settings, which is a bit more relevant. --- src/core/public/doc_links/doc_links_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 72fa6c5553f77..c86a856577c37 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -387,7 +387,7 @@ export class DocLinksService { }, snapshotRestore: { guide: `${KIBANA_DOCS}snapshot-repositories.html`, - changeIndexSettings: `${ELASTICSEARCH_DOCS}snapshots-restore-snapshot.html#change-index-settings-during-restore`, + changeIndexSettings: `${ELASTICSEARCH_DOCS}index-modules.html`, createSnapshot: `${ELASTICSEARCH_DOCS}snapshots-take-snapshot.html`, getSnapshot: `${ELASTICSEARCH_DOCS}get-snapshot-api.html`, registerSharedFileSystem: `${ELASTICSEARCH_DOCS}snapshots-register-repository.html#snapshots-filesystem-repository`, From d37ad90e53e727adcb208b1fad3a253e69596843 Mon Sep 17 00:00:00 2001 From: Mat Schaffer <mat@elastic.co> Date: Wed, 22 Sep 2021 01:31:02 +0900 Subject: [PATCH 32/69] [Stack Monitoring][Angular removal] No-data page (#110432) * Basic no-data page * Rename to NoDataPage * Add getData property to pass into EuiSuperDatePicker from pages * Wire getData and redirect for no data page * Draft port of isLoading & model updating * Add todo on handling checkers * Switch to model as state object * Add checkers * Porting enabler * Fix build checks * Attempting to smooth out enablement * Clean up CI errors * Fix breadcrumbs * Fix linter warning * Fix checkers dependency (I hope) * Hook up catchReason * Add a stub for react setup mode * Clean warnings * Fix toggleSetupMode by calling initSetupModeState first * Translating checker strings * typo on "xpack" * Move isCollection/reason check in NoData This replicates how the angular app did selective re-rendering of the react component, but while still being able to render the component. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../monitoring/public/application/index.tsx | 7 +- .../application/pages/no_data/enabler.ts | 57 +++++ .../public/application/pages/no_data/index.ts | 8 + .../pages/no_data/no_data_page.tsx | 240 ++++++++++++++++++ .../public/components/no_data/no_data.js | 4 +- .../monitoring/public/lib/setup_mode.tsx | 2 + 6 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts create mode 100644 x-pack/plugins/monitoring/public/application/pages/no_data/index.ts create mode 100644 x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index 6db9343035237..8cd5bc3088acc 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -18,6 +18,7 @@ import { GlobalStateProvider } from './global_state_context'; import { ExternalConfigContext, ExternalConfig } from './external_config_context'; import { createPreserveQueryHistory } from './preserve_query_history'; import { RouteInit } from './route_init'; +import { NoDataPage } from './pages/no_data'; import { ElasticsearchOverviewPage } from './pages/elasticsearch/overview'; import { CODE_PATH_ELASTICSEARCH } from '../../common/constants'; import { MonitoringTimeContainer } from './hooks/use_monitoring_time'; @@ -54,7 +55,7 @@ const MonitoringApp: React.FC<{ <BreadcrumbContainer.Provider history={history}> <Router history={history}> <Switch> - <Route path="/no-data" component={NoData} /> + <Route path="/no-data" component={NoDataPage} /> <Route path="/loading" component={LoadingPage} /> <RouteInit path="/license" @@ -98,10 +99,6 @@ const MonitoringApp: React.FC<{ ); }; -const NoData: React.FC<{}> = () => { - return <div>No data page</div>; -}; - const Home: React.FC<{}> = () => { return <div>Home page (Cluster listing)</div>; }; diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts b/x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts new file mode 100644 index 0000000000000..6225e3863b2f7 --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/enabler.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/enabler.js +export class Enabler { + http: any; + updateModel: any; + + constructor(http: any, updateModel: (properties: any) => void) { + this.http = http; + this.updateModel = updateModel; + } + + async enableCollectionInterval() { + try { + this.updateModel({ isCollectionIntervalUpdating: true }); + + await this.http.fetch('../api/monitoring/v1/elasticsearch_settings/set/collection_interval', { + method: 'PUT', + }); + this.updateModel({ + isCollectionIntervalUpdated: true, + isCollectionIntervalUpdating: false, + }); + } catch (err) { + this.updateModel({ + errors: (err as any).data, + isCollectionIntervalUpdated: false, + isCollectionIntervalUpdating: false, + }); + } + } + + async enableCollectionEnabled() { + try { + this.updateModel({ isCollectionEnabledUpdating: true }); + await this.http.fetch('../api/monitoring/v1/elasticsearch_settings/set/collection_enabled', { + method: 'PUT', + }); + + this.updateModel({ + isCollectionEnabledUpdated: true, + isCollectionEnabledUpdating: false, + }); + } catch (err) { + this.updateModel({ + errors: (err as any).data, + isCollectionEnabledUpdated: false, + isCollectionEnabledUpdating: false, + }); + } + } +} diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/index.ts b/x-pack/plugins/monitoring/public/application/pages/no_data/index.ts new file mode 100644 index 0000000000000..7fa176d0e6adf --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { NoDataPage } from './no_data_page'; diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx new file mode 100644 index 0000000000000..b05bd783b2ff2 --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx @@ -0,0 +1,240 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useContext, useState } from 'react'; +import { Redirect } from 'react-router-dom'; + +import { i18n } from '@kbn/i18n'; +// @ts-ignore +import { NoData } from '../../../components/no_data'; +import { PageTemplate } from '../page_template'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { CODE_PATH_LICENSE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; +import { Legacy } from '../../../legacy_shims'; +import { Enabler } from './enabler'; +import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { initSetupModeState } from '../../setup_mode/setup_mode'; +import { GlobalStateContext } from '../../global_state_context'; + +const CODE_PATHS = [CODE_PATH_LICENSE]; + +interface NoDataPageSetupDeps { + http: any; + data: any; +} + +interface SettingsChecker { + message: string; + api: string; + next?: SettingsChecker; +} + +const clusterCheckers: SettingsChecker[] = [ + { + message: i18n.translate('xpack.monitoring.noData.checker.clusterSettings', { + defaultMessage: 'Checking cluster settings API on production cluster', + }), + api: '../api/monitoring/v1/elasticsearch_settings/check/cluster', + }, + { + message: i18n.translate('xpack.monitoring.noData.checker.nodesSettings', { + defaultMessage: 'Checking nodes settings API on production cluster', + }), + api: '../api/monitoring/v1/elasticsearch_settings/check/nodes', + }, +]; + +export const NoDataPage = () => { + const title = i18n.translate('xpack.monitoring.noData.routeTitle', { + defaultMessage: 'Setup Monitoring', + }); + + const { services } = useKibana<NoDataPageSetupDeps>(); + const [shouldRedirect, setShouldRedirect] = useState(false); + + const [model, setModel] = useState({ + errors: [], // errors can happen from trying to check or set ES settings + checkMessage: null, // message to show while waiting for api response + isLoading: true, // flag for in-progress state of checking for no data reason + isCollectionEnabledUpdating: false, // flags to indicate whether to show a spinner while waiting for ajax + isCollectionEnabledUpdated: false, + isCollectionIntervalUpdating: false, + isCollectionIntervalUpdated: false, + } as any); + + const { update: updateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + updateBreadcrumbs([ + { + 'data-test-subj': 'breadcrumbClusters', + text: 'Clusters', + href: '#/home', + ignoreGlobalState: true, + }, + ]); + + const globalState = useContext(GlobalStateContext); + initSetupModeState(globalState, services.http); + + // From x-pack/plugins/monitoring/public/views/no_data/model_updater.js + const updateModel = useCallback( + (properties: any) => { + setModel((previousModel: any) => { + const updated = { ...previousModel }; + const keys = Object.keys(properties); + + keys.forEach((key) => { + if (Array.isArray(updated[key])) { + updated[key].push(properties[key]); + } else { + updated[key] = properties[key]; + } + }); + + return updated; + }); + }, + [setModel] + ); + + const getPageData = useCallback(async () => { + let catchReason; + try { + const clusters = await getClusters(services); + + if (clusters && clusters.length) { + setShouldRedirect(true); + return; + } + } catch (err) { + if (err && err.status === 503) { + catchReason = { + property: 'custom', + message: err.data.message, + }; + } + } + + if (catchReason) { + updateModel({ reason: catchReason }); + } else { + await startChecks(clusterCheckers, services.http, updateModel); + } + }, [services, updateModel]); + + const enabler = new Enabler(services.http, updateModel); + + return ( + <PageTemplate title={title} getPageData={getPageData}> + {shouldRedirect ? ( + <Redirect to="/home" /> + ) : ( + <NoData {...model} enabler={enabler} isCloudEnabled={Legacy.shims.isCloud} /> + )} + </PageTemplate> + ); +}; + +async function getClusters(services: NoDataPageSetupDeps): Promise<any[]> { + const url = '../api/monitoring/v1/clusters'; + const bounds = services.data?.query.timefilter.timefilter.getBounds(); + const min = bounds.min.toISOString(); + const max = bounds.max.toISOString(); + + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + css: undefined, + timeRange: { + min, + max, + }, + codePaths: CODE_PATHS, + }), + }); + + return formatClusters(response); +} + +// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js +const mapCheckers = (_checkers: SettingsChecker[]) => { + return _checkers.map((current, checkerIndex) => { + const next = _checkers[checkerIndex + 1]; + if (next !== undefined) { + current.next = next; + } + + return current; + }); +}; + +// From x-pack/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js +function startChecks( + checkers: SettingsChecker[], + http: { fetch: any }, + updateModel: (properties: any) => void +) { + const runCheck = async (currentChecker: SettingsChecker): Promise<any> => { + updateModel({ checkMessage: currentChecker.message }); + + const { found, reason, error, errorReason } = await executeCheck(currentChecker, http); + + if (error) { + updateModel({ errors: errorReason }); + if (currentChecker.next) { + return runCheck(currentChecker.next); + } + } else if (found) { + return updateModel({ + reason, + isLoading: false, + checkMessage: null, + }); + } else if (currentChecker.next) { + return runCheck(currentChecker.next); + } + + // dead end + updateModel({ + reason: null, + isLoading: false, + checkMessage: null, + }); + }; + + const _checkers = mapCheckers(checkers); + return runCheck(_checkers[0]); +} + +async function executeCheck(checker: SettingsChecker, http: { fetch: any }): Promise<any> { + try { + const response = await http.fetch(checker.api, { + method: 'GET', + }); + const { found, reason } = response; + + return { found, reason }; + } catch (err: any) { + const { data } = err; + + return { + error: true, + found: false, + errorReason: data, + }; + } +} + +function formatClusters(clusters: any): any[] { + return clusters.map(formatCluster); +} + +function formatCluster(cluster: any) { + if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) { + cluster.cluster_name = 'Standalone Cluster'; + } + return cluster; +} diff --git a/x-pack/plugins/monitoring/public/components/no_data/no_data.js b/x-pack/plugins/monitoring/public/components/no_data/no_data.js index 1714ace7ceff9..97bf7cacf53e7 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/no_data.js +++ b/x-pack/plugins/monitoring/public/components/no_data/no_data.js @@ -32,9 +32,9 @@ import { CloudDeployment } from './blurbs'; import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link'; function NoDataMessage(props) { - const { isLoading, reason, checkMessage } = props; + const { isLoading, reason, checkMessage, isCollectionEnabledUpdated } = props; - if (isLoading) { + if ((isCollectionEnabledUpdated && !reason) || isLoading) { return <CheckingSettings checkMessage={checkMessage} />; } diff --git a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx index f622f2944a31a..fca7f94731bc5 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx @@ -159,6 +159,8 @@ export const disableElasticsearchInternalCollection = async () => { }; export const toggleSetupMode = (inSetupMode: boolean) => { + if (isReactMigrationEnabled()) return setupModeReact.toggleSetupMode(inSetupMode); + checkAngularState(); const globalState = angularState.injector.get('globalState'); From c76082e00699a651d1494978fc3df994be1b59b0 Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tyler.smalley@elastic.co> Date: Tue, 21 Sep 2021 09:44:56 -0700 Subject: [PATCH 33/69] Fix eslint error Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co> --- .../data/server/data_views/saved_objects_client_wrapper.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts index 39cf74b906106..bbe857894b3f0 100644 --- a/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts +++ b/src/plugins/data/server/data_views/saved_objects_client_wrapper.test.ts @@ -12,7 +12,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { DataViewSavedObjectConflictError } from '../../common/data_views'; describe('SavedObjectsClientPublicToCommon', () => { - const soClient = ({ resolve: jest.fn() } as unknown) as SavedObjectsClientContract; + const soClient = { resolve: jest.fn() } as unknown as SavedObjectsClientContract; test('get saved object - exactMatch', async () => { const mockedSavedObject = { From 9574b4b86ba336eb78473bd300170a95e2602e6e Mon Sep 17 00:00:00 2001 From: ymao1 <ying.mao@elastic.co> Date: Tue, 21 Sep 2021 12:48:05 -0400 Subject: [PATCH 34/69] Add indicator to rule details page if broken connector (#112017) * Add indicator to rule details page if broken connector * Adding unit test * Updating disabled state and adding warning callout * Adding back the tooltip * eslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/alert_details.test.tsx | 653 ++++++++---------- .../components/alert_details.tsx | 146 +++- 2 files changed, 413 insertions(+), 386 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index d198c82366fbf..c7c41ac4e8171 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -39,6 +39,10 @@ jest.mock('react-router-dom', () => ({ }), })); +jest.mock('../../../lib/action_connector_api', () => ({ + loadAllActions: jest.fn().mockResolvedValue([]), +})); + jest.mock('../../../lib/capabilities', () => ({ hasAllPrivilege: jest.fn(() => true), hasSaveAlertsCapability: jest.fn(() => true), @@ -60,24 +64,22 @@ const authorizedConsumers = { }; const recoveryActionGroup: ActionGroup<'recovered'> = { id: 'recovered', name: 'Recovered' }; -describe('alert_details', () => { - // mock Api handlers +const alertType: AlertType = { + id: '.noop', + name: 'No Op', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup, + actionVariables: { context: [], state: [], params: [] }, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + producer: ALERTS_FEATURE_ID, + authorizedConsumers, + enabledInLicense: true, +}; +describe('alert_details', () => { it('renders the alert name as a title', () => { const alert = mockAlert(); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -87,19 +89,6 @@ describe('alert_details', () => { it('renders the alert type badge', () => { const alert = mockAlert(); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -118,19 +107,6 @@ describe('alert_details', () => { }, }, }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -155,19 +131,6 @@ describe('alert_details', () => { ], }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - enabledInLicense: true, - }; - const actionTypes: ActionType[] = [ { id: '.server-log', @@ -212,18 +175,6 @@ describe('alert_details', () => { }, ], }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - minimumLicenseRequired: 'basic', - authorizedConsumers, - enabledInLicense: true, - }; const actionTypes: ActionType[] = [ { id: '.server-log', @@ -273,20 +224,6 @@ describe('alert_details', () => { describe('links', () => { it('links to the app that created the alert', () => { const alert = mockAlert(); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - expect( shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> @@ -296,19 +233,6 @@ describe('alert_details', () => { it('links to the Edit flyout', () => { const alert = mockAlert(); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; const pageHeaderProps = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) @@ -316,22 +240,22 @@ describe('alert_details', () => { .props() as EuiPageHeaderProps; const rightSideItems = pageHeaderProps.rightSideItems; expect(!!rightSideItems && rightSideItems[2]!).toMatchInlineSnapshot(` - <React.Fragment> - <EuiButtonEmpty - data-test-subj="openEditAlertFlyoutButton" - disabled={false} - iconType="pencil" - name="edit" - onClick={[Function]} - > - <FormattedMessage - defaultMessage="Edit" - id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" - values={Object {}} - /> - </EuiButtonEmpty> - </React.Fragment> - `); + <React.Fragment> + <EuiButtonEmpty + data-test-subj="openEditAlertFlyoutButton" + disabled={false} + iconType="pencil" + name="edit" + onClick={[Function]} + > + <FormattedMessage + defaultMessage="Edit" + id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" + values={Object {}} + /> + </EuiButtonEmpty> + </React.Fragment> + `); }); }); }); @@ -341,20 +265,6 @@ describe('disable button', () => { const alert = mockAlert({ enabled: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) @@ -368,55 +278,43 @@ describe('disable button', () => { }); }); - it('should render a enable button when alert is disabled', () => { + it('should render a enable button and empty state when alert is disabled', async () => { const alert = mockAlert({ enabled: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - - const enableButton = shallow( + const wrapper = mountWithIntl( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> - ) - .find(EuiSwitch) - .find('[name="enable"]') - .first(); + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const enableButton = wrapper.find(EuiSwitch).find('[name="enable"]').first(); + const disabledEmptyPrompt = wrapper.find('[data-test-subj="disabledEmptyPrompt"]'); + const disabledEmptyPromptAction = wrapper.find('[data-test-subj="disabledEmptyPromptAction"]'); expect(enableButton.props()).toMatchObject({ checked: false, disabled: false, }); + expect(disabledEmptyPrompt.exists()).toBeTruthy(); + expect(disabledEmptyPromptAction.exists()).toBeTruthy(); + + disabledEmptyPromptAction.first().simulate('click'); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(mockAlertApis.enableAlert).toHaveBeenCalledTimes(1); }); - it('should enable the alert when alert is disabled and button is clicked', () => { + it('should disable the alert when alert is enabled and button is clicked', () => { const alert = mockAlert({ enabled: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const disableAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -439,24 +337,10 @@ describe('disable button', () => { expect(disableAlert).toHaveBeenCalledTimes(1); }); - it('should disable the alert when alert is enabled and button is clicked', () => { + it('should enable the alert when alert is disabled and button is clicked', () => { const alert = mockAlert({ enabled: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -492,19 +376,6 @@ describe('disable button', () => { }, }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const disableAlert = jest.fn(); const enableAlert = jest.fn(); const wrapper = mountWithIntl( @@ -565,19 +436,6 @@ describe('disable button', () => { }, }); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const disableAlert = jest.fn(async () => { await new Promise((resolve) => setTimeout(resolve, 6000)); }); @@ -630,27 +488,12 @@ describe('mute button', () => { enabled: true, muteAll: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) .find(EuiSwitch) .find('[name="mute"]') .first(); - expect(enableButton.props()).toMatchObject({ checked: false, disabled: false, @@ -662,27 +505,12 @@ describe('mute button', () => { enabled: true, muteAll: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) .find(EuiSwitch) .find('[name="mute"]') .first(); - expect(enableButton.props()).toMatchObject({ checked: true, disabled: false, @@ -694,20 +522,6 @@ describe('mute button', () => { enabled: true, muteAll: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const muteAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -721,7 +535,6 @@ describe('mute button', () => { .find(EuiSwitch) .find('[name="mute"]') .first(); - enableButton.simulate('click'); const handler = enableButton.prop('onChange'); expect(typeof handler).toEqual('function'); @@ -735,20 +548,6 @@ describe('mute button', () => { enabled: true, muteAll: true, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const unmuteAlert = jest.fn(); const enableButton = shallow( <AlertDetails @@ -762,7 +561,6 @@ describe('mute button', () => { .find(EuiSwitch) .find('[name="mute"]') .first(); - enableButton.simulate('click'); const handler = enableButton.prop('onChange'); expect(typeof handler).toEqual('function'); @@ -776,27 +574,12 @@ describe('mute button', () => { enabled: false, muteAll: false, }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const enableButton = shallow( <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> ) .find(EuiSwitch) .find('[name="mute"]') .first(); - expect(enableButton.props()).toMatchObject({ checked: false, disabled: true, @@ -843,20 +626,6 @@ describe('edit button', () => { }, ], }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: 'alerting', - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const pageHeaderProps = shallow( <AlertDetails alert={alert} @@ -869,27 +638,27 @@ describe('edit button', () => { .props() as EuiPageHeaderProps; const rightSideItems = pageHeaderProps.rightSideItems; expect(!!rightSideItems && rightSideItems[2]!).toMatchInlineSnapshot(` - <React.Fragment> - <EuiButtonEmpty - data-test-subj="openEditAlertFlyoutButton" - disabled={false} - iconType="pencil" - name="edit" - onClick={[Function]} - > - <FormattedMessage - defaultMessage="Edit" - id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" - values={Object {}} - /> - </EuiButtonEmpty> - </React.Fragment> - `); + <React.Fragment> + <EuiButtonEmpty + data-test-subj="openEditAlertFlyoutButton" + disabled={false} + iconType="pencil" + name="edit" + onClick={[Function]} + > + <FormattedMessage + defaultMessage="Edit" + id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" + values={Object {}} + /> + </EuiButtonEmpty> + </React.Fragment> + `); }); it('should not render an edit button when alert editable but actions arent', () => { const { hasExecuteActionsCapability } = jest.requireMock('../../../lib/capabilities'); - hasExecuteActionsCapability.mockReturnValue(false); + hasExecuteActionsCapability.mockReturnValueOnce(false); const alert = mockAlert({ enabled: true, muteAll: false, @@ -902,20 +671,6 @@ describe('edit button', () => { }, ], }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: 'alerting', - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - expect( shallow( <AlertDetails @@ -934,26 +689,12 @@ describe('edit button', () => { it('should render an edit button when alert editable but actions arent when there are no actions on the alert', async () => { const { hasExecuteActionsCapability } = jest.requireMock('../../../lib/capabilities'); - hasExecuteActionsCapability.mockReturnValue(false); + hasExecuteActionsCapability.mockReturnValueOnce(false); const alert = mockAlert({ enabled: true, muteAll: false, actions: [], }); - - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - producer: 'alerting', - authorizedConsumers, - minimumLicenseRequired: 'basic', - enabledInLicense: true, - }; - const pageHeaderProps = shallow( <AlertDetails alert={alert} @@ -966,41 +707,221 @@ describe('edit button', () => { .props() as EuiPageHeaderProps; const rightSideItems = pageHeaderProps.rightSideItems; expect(!!rightSideItems && rightSideItems[2]!).toMatchInlineSnapshot(` - <React.Fragment> - <EuiButtonEmpty - data-test-subj="openEditAlertFlyoutButton" - disabled={false} - iconType="pencil" - name="edit" - onClick={[Function]} - > - <FormattedMessage - defaultMessage="Edit" - id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" - values={Object {}} - /> - </EuiButtonEmpty> - </React.Fragment> - `); + <React.Fragment> + <EuiButtonEmpty + data-test-subj="openEditAlertFlyoutButton" + disabled={false} + iconType="pencil" + name="edit" + onClick={[Function]} + > + <FormattedMessage + defaultMessage="Edit" + id="xpack.triggersActionsUI.sections.alertDetails.editAlertButtonLabel" + values={Object {}} + /> + </EuiButtonEmpty> + </React.Fragment> + `); }); }); -describe('refresh button', () => { - it('should call requestRefresh when clicked', () => { - const alert = mockAlert(); - const alertType: AlertType = { - id: '.noop', - name: 'No Op', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup, - actionVariables: { context: [], state: [], params: [] }, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - producer: ALERTS_FEATURE_ID, - authorizedConsumers, +describe('broken connector indicator', () => { + const actionTypes: ActionType[] = [ + { + id: '.server-log', + name: 'Server log', + enabled: true, + enabledInConfig: true, enabledInLicense: true, - }; + minimumLicenseRequired: 'basic', + }, + ]; + ruleTypeRegistry.has.mockReturnValue(true); + const alertTypeR: AlertTypeModel = { + id: 'my-alert-type', + iconClass: 'test', + description: 'Alert when testing', + documentationUrl: 'https://localhost.local/docs', + validate: () => { + return { errors: {} }; + }, + alertParamsExpression: jest.fn(), + requiresAppContext: false, + }; + ruleTypeRegistry.get.mockReturnValue(alertTypeR); + useKibanaMock().services.ruleTypeRegistry = ruleTypeRegistry; + const { loadAllActions } = jest.requireMock('../../../lib/action_connector_api'); + loadAllActions.mockResolvedValue([ + { + secrets: {}, + isMissingSecrets: false, + id: 'connector-id-1', + actionTypeId: '.server-log', + name: 'Test connector', + config: {}, + isPreconfigured: false, + }, + { + secrets: {}, + isMissingSecrets: false, + id: 'connector-id-2', + actionTypeId: '.server-log', + name: 'Test connector 2', + config: {}, + isPreconfigured: false, + }, + ]); + it('should not render broken connector indicator or warning if all rule actions connectors exist', async () => { + const alert = mockAlert({ + enabled: true, + muteAll: false, + actions: [ + { + group: 'default', + id: 'connector-id-1', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-2', + params: {}, + actionTypeId: '.server-log', + }, + ], + }); + const wrapper = mountWithIntl( + <AlertDetails + alert={alert} + alertType={alertType} + actionTypes={actionTypes} + {...mockAlertApis} + /> + ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const brokenConnectorIndicator = wrapper + .find('[data-test-subj="actionWithBrokenConnector"]') + .first(); + const brokenConnectorWarningBanner = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]') + .first(); + expect(brokenConnectorIndicator.exists()).toBeFalsy(); + expect(brokenConnectorWarningBanner.exists()).toBeFalsy(); + }); + + it('should render broken connector indicator and warning if any rule actions connector does not exist', async () => { + const alert = mockAlert({ + enabled: true, + muteAll: false, + actions: [ + { + group: 'default', + id: 'connector-id-1', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-2', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-doesnt-exist', + params: {}, + actionTypeId: '.server-log', + }, + ], + }); + const wrapper = mountWithIntl( + <AlertDetails + alert={alert} + alertType={alertType} + actionTypes={actionTypes} + {...mockAlertApis} + /> + ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const brokenConnectorIndicator = wrapper + .find('[data-test-subj="actionWithBrokenConnector"]') + .first(); + const brokenConnectorWarningBanner = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]') + .first(); + const brokenConnectorWarningBannerAction = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBannerEdit"]') + .first(); + expect(brokenConnectorIndicator.exists()).toBeTruthy(); + expect(brokenConnectorWarningBanner.exists()).toBeTruthy(); + expect(brokenConnectorWarningBannerAction.exists()).toBeTruthy(); + }); + + it('should render broken connector indicator and warning with no edit button if any rule actions connector does not exist and user has no edit access', async () => { + const alert = mockAlert({ + enabled: true, + muteAll: false, + actions: [ + { + group: 'default', + id: 'connector-id-1', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-2', + params: {}, + actionTypeId: '.server-log', + }, + { + group: 'default', + id: 'connector-id-doesnt-exist', + params: {}, + actionTypeId: '.server-log', + }, + ], + }); + const { hasExecuteActionsCapability } = jest.requireMock('../../../lib/capabilities'); + hasExecuteActionsCapability.mockReturnValue(false); + const wrapper = mountWithIntl( + <AlertDetails + alert={alert} + alertType={alertType} + actionTypes={actionTypes} + {...mockAlertApis} + /> + ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + const brokenConnectorIndicator = wrapper + .find('[data-test-subj="actionWithBrokenConnector"]') + .first(); + const brokenConnectorWarningBanner = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]') + .first(); + const brokenConnectorWarningBannerAction = wrapper + .find('[data-test-subj="actionWithBrokenConnectorWarningBannerEdit"]') + .first(); + expect(brokenConnectorIndicator.exists()).toBeTruthy(); + expect(brokenConnectorWarningBanner.exists()).toBeTruthy(); + expect(brokenConnectorWarningBannerAction.exists()).toBeFalsy(); + }); +}); + +describe('refresh button', () => { + it('should call requestRefresh when clicked', async () => { + const alert = mockAlert(); const requestRefresh = jest.fn(); const wrapper = mountWithIntl( <AlertDetails @@ -1012,6 +933,10 @@ describe('refresh button', () => { /> ); + await act(async () => { + await nextTick(); + wrapper.update(); + }); const refreshButton = wrapper.find('[data-test-subj="refreshAlertsButton"]').first(); expect(refreshButton.exists()).toBeTruthy(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 2558993a13fe6..2b13bdf613d96 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -22,13 +22,16 @@ import { EuiButtonEmpty, EuiButton, EuiLoadingSpinner, + EuiIconTip, + EuiEmptyPrompt, + EuiPageTemplate, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AlertExecutionStatusErrorReasons } from '../../../../../../alerting/common'; import { hasAllPrivilege, hasExecuteActionsCapability } from '../../../lib/capabilities'; import { getAlertingSectionBreadcrumb, getAlertDetailsBreadcrumb } from '../../../lib/breadcrumb'; import { getCurrentDocTitle } from '../../../lib/doc_title'; -import { Alert, AlertType, ActionType } from '../../../../types'; +import { Alert, AlertType, ActionType, ActionConnector } from '../../../../types'; import { ComponentOpts as BulkOperationsComponentOpts, withBulkAlertOperations, @@ -40,6 +43,7 @@ import { routeToRuleDetails } from '../../../constants'; import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; import { alertReducer } from '../../alert_form/alert_reducer'; +import { loadAllActions as loadConnectors } from '../../../lib/action_connector_api'; export type AlertDetailsProps = { alert: Alert; @@ -72,6 +76,9 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; + const [hasActionsWithBrokenConnector, setHasActionsWithBrokenConnector] = + useState<boolean>(false); + // Set breadcrumb and page title useEffect(() => { setBreadcrumbs([ @@ -82,6 +89,28 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // Determine if any attached action has an issue with its connector + useEffect(() => { + (async () => { + let loadedConnectors: ActionConnector[] = []; + try { + loadedConnectors = await loadConnectors({ http }); + } catch (err) { + loadedConnectors = []; + } + + if (loadedConnectors.length > 0) { + const hasActionWithBrokenConnector = alert.actions.some( + (action) => !loadedConnectors.find((connector) => connector.id === action.id) + ); + if (setHasActionsWithBrokenConnector) { + setHasActionsWithBrokenConnector(hasActionWithBrokenConnector); + } + } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const canExecuteActions = hasExecuteActionsCapability(capabilities); const canSaveAlert = hasAllPrivilege(alert, alertType) && @@ -197,13 +226,27 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ {uniqueActions && uniqueActions.length ? ( <> <EuiText size="s"> - <p> - <FormattedMessage - id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex" - defaultMessage="Actions" + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex" + defaultMessage="Actions" + />{' '} + {hasActionsWithBrokenConnector && ( + <EuiIconTip + data-test-subj="actionWithBrokenConnector" + type="alert" + color="danger" + content={i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsWarningTooltip', + { + defaultMessage: + 'Unable to load one of the connectors associated with this rule. Edit the rule to select a new connector.', + } + )} + position="right" /> - </p> + )} </EuiText> + <EuiSpacer size="xs" /> <EuiFlexGroup wrap gutterSize="s"> {uniqueActions.map((action, index) => ( @@ -358,6 +401,42 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ </EuiFlexItem> </EuiFlexGroup> ) : null} + {hasActionsWithBrokenConnector && ( + <EuiFlexGroup> + <EuiFlexItem> + <EuiSpacer size="s" /> + <EuiCallOut + color="warning" + data-test-subj="actionWithBrokenConnectorWarningBanner" + size="s" + title={i18n.translate( + 'xpack.triggersActionsUI.sections.alertDetails.actionWithBrokenConnectorWarningBannerTitle', + { + defaultMessage: + 'There is an issue with one of the connectors associated with this rule.', + } + )} + > + {hasEditButton && ( + <EuiFlexGroup gutterSize="s" wrap={true}> + <EuiFlexItem grow={false}> + <EuiButton + data-test-subj="actionWithBrokenConnectorWarningBannerEdit" + color="warning" + onClick={() => setEditFlyoutVisibility(true)} + > + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.actionWithBrokenConnectorWarningBannerEditText" + defaultMessage="Edit rule" + /> + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + )} + </EuiCallOut> + </EuiFlexItem> + </EuiFlexGroup> + )} <EuiFlexGroup> <EuiFlexItem> {alert.enabled ? ( @@ -370,23 +449,46 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ ) : ( <> <EuiSpacer /> - <EuiCallOut - title={i18n.translate( - 'xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle', - { - defaultMessage: 'Disabled Rule', + <EuiPageTemplate template="empty"> + <EuiEmptyPrompt + data-test-subj="disabledEmptyPrompt" + title={ + <h2> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.alerts.disabledRuleTitle" + defaultMessage="Disabled Rule" + /> + </h2> } - )} - color="warning" - iconType="help" - > - <p> - <FormattedMessage - id="xpack.triggersActionsUI.sections.alertDetails.alertInstances.disabledRule" - defaultMessage="This rule is disabled and cannot be displayed. Toggle Disable ↑ to activate it." - /> - </p> - </EuiCallOut> + body={ + <> + <p> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.alertInstances.disabledRule" + defaultMessage="This rule is disabled and cannot be displayed." + /> + </p> + </> + } + actions={[ + <EuiButton + data-test-subj="disabledEmptyPromptAction" + color="primary" + fill + disabled={isEnabledUpdating} + onClick={async () => { + setIsEnabledUpdating(true); + setIsEnabled(true); + await enableAlert(alert); + requestRefresh(); + setIsEnabledUpdating(false); + }} + > + Enable + </EuiButton>, + ]} + /> + </EuiPageTemplate> </> )} </EuiFlexItem> From 126b87bd551c1669e665a7a2efa6a0662a85a32a Mon Sep 17 00:00:00 2001 From: Devon Thomson <devon.thomson@hotmail.com> Date: Tue, 21 Sep 2021 11:39:23 -0600 Subject: [PATCH 35/69] [Dashboard] [Visualize] Add Search to Legacy Redirect (#112365) added search to legacy redirect URL --- .../listing/dashboard_no_match.tsx | 3 +-- .../components/visualize_no_match.tsx | 2 +- .../apps/dashboard/bwc_shared_urls.ts | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx b/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx index 03f7b0e162229..91361836f59d4 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_no_match.tsx @@ -23,9 +23,8 @@ export const DashboardNoMatch = ({ history }: { history: RouteComponentProps['hi useEffect(() => { services.restorePreviousUrl(); - const { navigated } = services.urlForwarding.navigateToLegacyKibanaUrl( - history.location.pathname + history.location.pathname + history.location.search ); if (!navigated) { diff --git a/src/plugins/visualize/public/application/components/visualize_no_match.tsx b/src/plugins/visualize/public/application/components/visualize_no_match.tsx index 3b735eb23671c..ad993af430086 100644 --- a/src/plugins/visualize/public/application/components/visualize_no_match.tsx +++ b/src/plugins/visualize/public/application/components/visualize_no_match.tsx @@ -24,7 +24,7 @@ export const VisualizeNoMatch = () => { services.restorePreviousUrl(); const { navigated } = services.urlForwarding.navigateToLegacyKibanaUrl( - services.history.location.pathname + services.history.location.pathname + services.history.location.search ); if (!navigated) { diff --git a/test/functional/apps/dashboard/bwc_shared_urls.ts b/test/functional/apps/dashboard/bwc_shared_urls.ts index d40cf03327fd3..569cd8e2a67d5 100644 --- a/test/functional/apps/dashboard/bwc_shared_urls.ts +++ b/test/functional/apps/dashboard/bwc_shared_urls.ts @@ -86,6 +86,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('6.0 urls', () => { + let savedDashboardId: string; + it('loads an unsaved dashboard', async function () { const url = `${kibanaLegacyBaseUrl}#/dashboard?${urlQuery}`; log.debug(`Navigating to ${url}`); @@ -106,8 +108,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { storeTimeWithDashboard: true, }); - const id = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); - const url = `${kibanaLegacyBaseUrl}#/dashboard/${id}`; + savedDashboardId = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); + const url = `${kibanaLegacyBaseUrl}#/dashboard/${savedDashboardId}`; log.debug(`Navigating to ${url}`); await browser.get(url, true); await PageObjects.header.waitUntilLoadingHasFinished(); @@ -121,6 +123,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardExpect.selectedLegendColorCount('#F9D9F9', 5); }); + it('loads a saved dashboard with query via dashboard_no_match', async function () { + await PageObjects.dashboard.gotoDashboardLandingPage(); + const currentUrl = await browser.getCurrentUrl(); + const dashboardBaseUrl = currentUrl.substring(0, currentUrl.indexOf('/app/dashboards')); + const url = `${dashboardBaseUrl}/app/dashboards#/dashboard/${savedDashboardId}?_a=(query:(language:kuery,query:'boop'))`; + log.debug(`Navigating to ${url}`); + await browser.get(url); + await PageObjects.header.waitUntilLoadingHasFinished(); + + const query = await queryBar.getQueryString(); + expect(query).to.equal('boop'); + + await dashboardExpect.panelCount(2); + await PageObjects.dashboard.waitForRenderComplete(); + }); + it('uiState in url takes precedence over saved dashboard state', async function () { const id = await PageObjects.dashboard.getDashboardIdFromCurrentUrl(); const updatedQuery = urlQuery.replace(/F9D9F9/g, '000000'); From 5408a3e301564e1d3bc3460e2a65ac965106ea14 Mon Sep 17 00:00:00 2001 From: Devon Thomson <devon.thomson@hotmail.com> Date: Tue, 21 Sep 2021 11:45:56 -0600 Subject: [PATCH 36/69] [Dashboard] Retain Viewmode State in Session (#112302) * Made dashboard retain viewmode state in session. This means filters and query will be kept over reloads and navigations --- .../hooks/use_dashboard_app_state.ts | 32 ++-- .../lib/dashboard_session_storage.ts | 2 + .../apps/dashboard/dashboard_unsaved_state.ts | 169 +++++++++++------- 3 files changed, 125 insertions(+), 78 deletions(-) diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts index 1b24062ccd9b5..be600cb802146 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.ts @@ -237,27 +237,25 @@ export const useDashboardAppState = ({ .pipe(debounceTime(DashboardConstants.CHANGE_CHECK_DEBOUNCE)) .subscribe((states) => { const [lastSaved, current] = states; - const unsavedChanges = - current.viewMode === ViewMode.EDIT ? diffDashboardState(lastSaved, current) : {}; - - let savedTimeChanged = false; + const unsavedChanges = diffDashboardState(lastSaved, current); + + const savedTimeChanged = + lastSaved.timeRestore && + !areTimeRangesEqual( + { + from: savedDashboard?.timeFrom, + to: savedDashboard?.timeTo, + }, + timefilter.getTime() + ); /** - * changes to the time filter should only be considered 'unsaved changes' when + * changes to the dashboard should only be considered 'unsaved changes' when * editing the dashboard */ - if (current.viewMode === ViewMode.EDIT) { - savedTimeChanged = - lastSaved.timeRestore && - !areTimeRangesEqual( - { - from: savedDashboard?.timeFrom, - to: savedDashboard?.timeTo, - }, - timefilter.getTime() - ); - } - const hasUnsavedChanges = Object.keys(unsavedChanges).length > 0 || savedTimeChanged; + const hasUnsavedChanges = + current.viewMode === ViewMode.EDIT && + (Object.keys(unsavedChanges).length > 0 || savedTimeChanged); setDashboardAppState((s) => ({ ...s, hasUnsavedChanges })); unsavedChanges.viewMode = current.viewMode; // always push view mode into session store. diff --git a/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts b/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts index 7d0e60c0609a8..a696c8bc15b83 100644 --- a/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts +++ b/src/plugins/dashboard/public/application/lib/dashboard_session_storage.ts @@ -11,6 +11,7 @@ import { Storage } from '../../services/kibana_utils'; import { NotificationsStart } from '../../services/core'; import { panelStorageErrorStrings } from '../../dashboard_strings'; import { DashboardState } from '../../types'; +import { ViewMode } from '../../services/embeddable'; export const DASHBOARD_PANELS_UNSAVED_ID = 'unsavedDashboard'; const DASHBOARD_PANELS_SESSION_KEY = 'dashboardStateManagerPanels'; @@ -69,6 +70,7 @@ export class DashboardSessionStorage { const dashboardsWithUnsavedChanges: string[] = []; Object.keys(dashboardStatesInSpace).map((dashboardId) => { if ( + dashboardStatesInSpace[dashboardId].viewMode === ViewMode.EDIT && Object.keys(dashboardStatesInSpace[dashboardId]).some( (stateKey) => stateKey !== 'viewMode' ) diff --git a/test/functional/apps/dashboard/dashboard_unsaved_state.ts b/test/functional/apps/dashboard/dashboard_unsaved_state.ts index 6b71dd34b76f8..8043c8bf8cc37 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_state.ts +++ b/test/functional/apps/dashboard/dashboard_unsaved_state.ts @@ -12,6 +12,9 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']); + const browser = getService('browser'); + const queryBar = getService('queryBar'); + const filterBar = getService('filterBar'); const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); @@ -19,9 +22,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let originalPanelCount = 0; let unsavedPanelCount = 0; + const testQuery = 'Test Query'; - // FLAKY: https://github.com/elastic/kibana/issues/91191 - describe.skip('dashboard unsaved panels', () => { + describe('dashboard unsaved state', () => { before(async () => { await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); await kibanaServer.uiSettings.replace({ @@ -31,79 +34,123 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.preserveCrossAppState(); await PageObjects.dashboard.loadSavedDashboard('few panels'); await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.waitForRenderComplete(); originalPanelCount = await PageObjects.dashboard.getPanelCount(); }); - it('does not show unsaved changes badge when there are no unsaved changes', async () => { - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); - }); + describe('view mode state', () => { + before(async () => { + await queryBar.setQuery(testQuery); + await filterBar.addFilter('bytes', 'exists'); + await queryBar.submitQuery(); + }); - it('shows the unsaved changes badge after adding panels', async () => { - await PageObjects.dashboard.switchToEditMode(); - // add an area chart by value - await dashboardAddPanel.clickEditorMenuButton(); - await dashboardAddPanel.clickAggBasedVisualizations(); - await PageObjects.visualize.clickAreaChart(); - await PageObjects.visualize.clickNewSearch(); - await PageObjects.visualize.saveVisualizationAndReturn(); + const validateQueryAndFilter = async () => { + const query = await queryBar.getQueryString(); + expect(query).to.eql(testQuery); + const filterCount = await filterBar.getFilterCount(); + expect(filterCount).to.eql(1); + }; + + it('persists after navigating to the listing page and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + await PageObjects.dashboard.waitForRenderComplete(); + await validateQueryAndFilter(); + }); - // add a metric by reference - await dashboardAddPanel.addVisualization('Rendering-Test: metric'); + it('persists after navigating to Visualize and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.common.navigateToApp('dashboards'); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + await PageObjects.dashboard.waitForRenderComplete(); + await validateQueryAndFilter(); + }); - await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); - }); + it('persists after a hard refresh', async () => { + await browser.refresh(); + const alert = await browser.getAlert(); + await alert?.accept(); + await PageObjects.dashboard.waitForRenderComplete(); + await validateQueryAndFilter(); + }); - it('has correct number of panels', async () => { - unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + after(async () => { + // discard changes made in view mode + await PageObjects.dashboard.switchToEditMode(); + await PageObjects.dashboard.clickCancelOutOfEditMode(); + }); }); - it('retains unsaved panel count after navigating to listing page and back', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.gotoDashboardLandingPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.loadSavedDashboard('few panels'); - await PageObjects.dashboard.switchToEditMode(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(unsavedPanelCount); - }); + describe('edit mode state', () => { + const addPanels = async () => { + // add an area chart by value + await dashboardAddPanel.clickEditorMenuButton(); + await dashboardAddPanel.clickAggBasedVisualizations(); + await PageObjects.visualize.clickAreaChart(); + await PageObjects.visualize.clickNewSearch(); + await PageObjects.visualize.saveVisualizationAndReturn(); + + // add a metric by reference + await dashboardAddPanel.addVisualization('Rendering-Test: metric'); + }; + + it('does not show unsaved changes badge when there are no unsaved changes', async () => { + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); - it('retains unsaved panel count after navigating to another app and back', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.gotoVisualizationLandingPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.common.navigateToApp('dashboards'); - await PageObjects.dashboard.loadSavedDashboard('few panels'); - await PageObjects.dashboard.switchToEditMode(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(unsavedPanelCount); - }); + it('shows the unsaved changes badge after adding panels', async () => { + await PageObjects.dashboard.switchToEditMode(); + await addPanels(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); + }); - it('resets to original panel count upon entering view mode', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.clickCancelOutOfEditMode(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(originalPanelCount); - }); + it('has correct number of panels', async () => { + unsavedPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(unsavedPanelCount).to.eql(originalPanelCount + 2); + }); - it('shows unsaved changes badge in view mode if changes have not been discarded', async () => { - await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); - }); + it('retains unsaved panel count after navigating to listing page and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(unsavedPanelCount); + }); - it('retains unsaved panel count after returning to edit mode', async () => { - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.dashboard.switchToEditMode(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const currentPanelCount = await PageObjects.dashboard.getPanelCount(); - expect(currentPanelCount).to.eql(unsavedPanelCount); - }); + it('retains unsaved panel count after navigating to another app and back', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.visualize.gotoVisualizationLandingPage(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.common.navigateToApp('dashboards'); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(unsavedPanelCount); + }); - it('does not show unsaved changes badge after saving', async () => { - await PageObjects.dashboard.saveDashboard('Unsaved State Test'); - await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + it('resets to original panel count after discarding changes', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.clickCancelOutOfEditMode(); + await PageObjects.header.waitUntilLoadingHasFinished(); + const currentPanelCount = await PageObjects.dashboard.getPanelCount(); + expect(currentPanelCount).to.eql(originalPanelCount); + expect(PageObjects.dashboard.getIsInViewMode()).to.eql(true); + }); + + it('does not show unsaved changes badge after saving', async () => { + await PageObjects.dashboard.switchToEditMode(); + await addPanels(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.saveDashboard('Unsaved State Test'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); }); }); } From 659b295391a001f77a8f6ed65f4b4b1249214a61 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet <nicolas.chaulet@elastic.co> Date: Tue, 21 Sep 2021 13:52:21 -0400 Subject: [PATCH 37/69] [Fleet] Allow to preconfigure alternative ES outputs (on the same cluster) (#111002) --- docs/settings/fleet-settings.asciidoc | 16 + .../plugins/fleet/common/constants/output.ts | 4 +- .../package_policies_to_agent_inputs.ts | 5 +- x-pack/plugins/fleet/common/types/index.ts | 7 +- .../fleet/common/types/models/agent_policy.ts | 10 +- .../fleet/common/types/models/output.ts | 6 +- .../common/types/models/preconfiguration.ts | 5 + x-pack/plugins/fleet/server/errors/utils.ts | 4 +- x-pack/plugins/fleet/server/index.ts | 7 +- .../fleet/server/saved_objects/index.ts | 4 + .../full_agent_policy.test.ts.snap | 292 ++++++++++++++++++ .../agent_policies/full_agent_policy.test.ts | 256 +++++++++++++++ .../agent_policies/full_agent_policy.ts | 229 ++++++++++++++ .../server/services/agent_policies/index.ts | 8 + .../server/services/agent_policy.test.ts | 121 +------- .../fleet/server/services/agent_policy.ts | 197 +++--------- .../fleet/server/services/output.test.ts | 94 +++++- .../plugins/fleet/server/services/output.ts | 116 +++++-- .../server/services/preconfiguration.test.ts | 178 ++++++++++- .../fleet/server/services/preconfiguration.ts | 92 +++++- x-pack/plugins/fleet/server/services/setup.ts | 30 +- x-pack/plugins/fleet/server/types/index.tsx | 1 + .../types/models/preconfiguration.test.ts | 81 +++++ .../server/types/models/preconfiguration.ts | 127 +++++--- 24 files changed, 1517 insertions(+), 373 deletions(-) create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/index.ts create mode 100644 x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts diff --git a/docs/settings/fleet-settings.asciidoc b/docs/settings/fleet-settings.asciidoc index 3ae1c9df616b0..3411f39309709 100644 --- a/docs/settings/fleet-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -86,6 +86,8 @@ Optional properties are: be changed by updating the {kib} config. `is_default`:: If `true`, this policy is the default agent policy. `is_default_fleet_server`:: If `true`, this policy is the default {fleet-server} agent policy. + `data_output_id`:: ID of the output to send data (Need to be identical to `monitoring_output_id`) + `monitoring_output_id`:: ID of the output to send monitoring data. (Need to be identical to `data_output_id`) `package_policies`:: List of integration policies to add to this policy. `name`::: (required) Name of the integration policy. `package`::: (required) Integration that this policy configures @@ -96,6 +98,20 @@ Optional properties are: integration. Follows the same schema as integration inputs, with the exception that any object in `vars` can be passed `frozen: true` in order to prevent that specific `var` from being edited by the user. + +| `xpack.fleet.outputs` + | List of ouputs that are configured when the {fleet} app starts. +Required properties are: + + `id`:: Unique ID for this output. The ID should be a string. + `name`:: Output name. + `type`:: Type of Output. Currently we only support "elasticsearch". + `hosts`:: Array that contains the list of host for that output. + `config`:: Extra config for that output. + +Optional properties are: + + `is_default`:: If `true`, this output is the default output. |=== Example configuration: diff --git a/x-pack/plugins/fleet/common/constants/output.ts b/x-pack/plugins/fleet/common/constants/output.ts index 80c7e56dbb52f..9a236001aca25 100644 --- a/x-pack/plugins/fleet/common/constants/output.ts +++ b/x-pack/plugins/fleet/common/constants/output.ts @@ -13,8 +13,10 @@ export const outputType = { Elasticsearch: 'elasticsearch', } as const; +export const DEFAULT_OUTPUT_ID = 'default'; + export const DEFAULT_OUTPUT: NewOutput = { - name: 'default', + name: DEFAULT_OUTPUT_ID, is_default: true, type: outputType.Elasticsearch, hosts: [''], diff --git a/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts b/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts index f262521461b98..119bb04af5ca8 100644 --- a/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts +++ b/x-pack/plugins/fleet/common/services/package_policies_to_agent_inputs.ts @@ -11,7 +11,8 @@ import type { PackagePolicy, FullAgentPolicyInput, FullAgentPolicyInputStream } import { DEFAULT_OUTPUT } from '../constants'; export const storedPackagePoliciesToAgentInputs = ( - packagePolicies: PackagePolicy[] + packagePolicies: PackagePolicy[], + outputId: string = DEFAULT_OUTPUT.name ): FullAgentPolicyInput[] => { const fullInputs: FullAgentPolicyInput[] = []; @@ -32,7 +33,7 @@ export const storedPackagePoliciesToAgentInputs = ( data_stream: { namespace: packagePolicy.namespace || 'default', }, - use_output: DEFAULT_OUTPUT.name, + use_output: outputId, ...(input.compiled_input || {}), ...(input.streams.length ? { diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index 0deda3bf32657..bd970fc2cd83e 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -8,7 +8,11 @@ export * from './models'; export * from './rest_spec'; -import type { PreconfiguredAgentPolicy, PreconfiguredPackage } from './models/preconfiguration'; +import type { + PreconfiguredAgentPolicy, + PreconfiguredPackage, + PreconfiguredOutput, +} from './models/preconfiguration'; export interface FleetConfigType { enabled: boolean; @@ -26,6 +30,7 @@ export interface FleetConfigType { }; agentPolicies?: PreconfiguredAgentPolicy[]; packages?: PreconfiguredPackage[]; + outputs?: PreconfiguredOutput[]; agentIdVerificationEnabled?: boolean; } diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index f64467ca674fb..3f9e43e72c51d 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -23,6 +23,8 @@ export interface NewAgentPolicy { monitoring_enabled?: MonitoringType; unenroll_timeout?: number; is_preconfigured?: boolean; + data_output_id?: string; + monitoring_output_id?: string; } export interface AgentPolicy extends NewAgentPolicy { @@ -71,12 +73,14 @@ export interface FullAgentPolicyOutputPermissions { }; } +export type FullAgentPolicyOutput = Pick<Output, 'type' | 'hosts' | 'ca_sha256' | 'api_key'> & { + [key: string]: any; +}; + export interface FullAgentPolicy { id: string; outputs: { - [key: string]: Pick<Output, 'type' | 'hosts' | 'ca_sha256' | 'api_key'> & { - [key: string]: any; - }; + [key: string]: FullAgentPolicyOutput; }; output_permissions?: { [output: string]: FullAgentPolicyOutputPermissions; diff --git a/x-pack/plugins/fleet/common/types/models/output.ts b/x-pack/plugins/fleet/common/types/models/output.ts index c1dc2a4b4e058..4f70460e89ff8 100644 --- a/x-pack/plugins/fleet/common/types/models/output.ts +++ b/x-pack/plugins/fleet/common/types/models/output.ts @@ -17,11 +17,13 @@ export interface NewOutput { hosts?: string[]; ca_sha256?: string; api_key?: string; - config?: Record<string, any>; config_yaml?: string; + is_preconfigured?: boolean; } -export type OutputSOAttributes = NewOutput; +export type OutputSOAttributes = NewOutput & { + output_id?: string; +}; export type Output = NewOutput & { id: string; diff --git a/x-pack/plugins/fleet/common/types/models/preconfiguration.ts b/x-pack/plugins/fleet/common/types/models/preconfiguration.ts index 6087c910510cc..17f9b946885b1 100644 --- a/x-pack/plugins/fleet/common/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/common/types/models/preconfiguration.ts @@ -11,6 +11,7 @@ import type { NewPackagePolicyInput, } from './package_policy'; import type { NewAgentPolicy } from './agent_policy'; +import type { Output } from './output'; export type InputsOverride = Partial<NewPackagePolicyInput> & { vars?: Array<NewPackagePolicyInput['vars'] & { name: string }>; @@ -29,3 +30,7 @@ export interface PreconfiguredAgentPolicy extends Omit<NewAgentPolicy, 'namespac } export type PreconfiguredPackage = Omit<PackagePolicyPackage, 'title'>; + +export interface PreconfiguredOutput extends Omit<Output, 'config_yaml'> { + config?: Record<string, unknown>; +} diff --git a/x-pack/plugins/fleet/server/errors/utils.ts b/x-pack/plugins/fleet/server/errors/utils.ts index 2eae04e05bd6b..d58f82b94fcd7 100644 --- a/x-pack/plugins/fleet/server/errors/utils.ts +++ b/x-pack/plugins/fleet/server/errors/utils.ts @@ -11,6 +11,6 @@ export function isESClientError(error: unknown): error is ResponseError { return error instanceof ResponseError; } -export const isElasticsearchVersionConflictError = (error: Error): boolean => { +export function isElasticsearchVersionConflictError(error: Error): boolean { return isESClientError(error) && error.meta.statusCode === 409; -}; +} diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 21cdf659f2f5a..05ad8a9a9c83f 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -9,7 +9,11 @@ import { schema } from '@kbn/config-schema'; import type { TypeOf } from '@kbn/config-schema'; import type { PluginConfigDescriptor, PluginInitializerContext } from 'src/core/server'; -import { PreconfiguredPackagesSchema, PreconfiguredAgentPoliciesSchema } from './types'; +import { + PreconfiguredPackagesSchema, + PreconfiguredAgentPoliciesSchema, + PreconfiguredOutputsSchema, +} from './types'; import { FleetPlugin } from './plugin'; @@ -113,6 +117,7 @@ export const config: PluginConfigDescriptor = { }), packages: PreconfiguredPackagesSchema, agentPolicies: PreconfiguredAgentPoliciesSchema, + outputs: PreconfiguredOutputsSchema, agentIdVerificationEnabled: schema.boolean({ defaultValue: true }), }), }; diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 5c117909432b0..83188e0047044 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -156,6 +156,8 @@ const getSavedObjectTypes = ( revision: { type: 'integer' }, monitoring_enabled: { type: 'keyword', index: false }, is_preconfigured: { type: 'keyword' }, + data_output_id: { type: 'keyword' }, + monitoring_output_id: { type: 'keyword' }, }, }, migrations: { @@ -196,6 +198,7 @@ const getSavedObjectTypes = ( }, mappings: { properties: { + output_id: { type: 'keyword', index: false }, name: { type: 'keyword' }, type: { type: 'keyword' }, is_default: { type: 'boolean' }, @@ -203,6 +206,7 @@ const getSavedObjectTypes = ( ca_sha256: { type: 'keyword', index: false }, config: { type: 'flattened' }, config_yaml: { type: 'text' }, + is_preconfigured: { type: 'boolean', index: false }, }, }, migrations: { diff --git a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap new file mode 100644 index 0000000000000..970bccbafa634 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -0,0 +1,292 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getFullAgentPolicy should support a different data output 1`] = ` +Object { + "agent": Object { + "monitoring": Object { + "enabled": true, + "logs": false, + "metrics": true, + "namespace": "default", + "use_output": "default", + }, + }, + "fleet": Object { + "hosts": Array [ + "http://fleetserver:8220", + ], + }, + "id": "agent-policy", + "inputs": Array [], + "output_permissions": Object { + "data-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + }, + "_fallback": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "logs-*", + "metrics-*", + "traces-*", + "synthetics-*", + ".logs-endpoint.diagnostic.collection-*", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + "default": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "metrics-elastic_agent-default", + "metrics-elastic_agent.elastic_agent-default", + "metrics-elastic_agent.apm_server-default", + "metrics-elastic_agent.filebeat-default", + "metrics-elastic_agent.fleet_server-default", + "metrics-elastic_agent.metricbeat-default", + "metrics-elastic_agent.osquerybeat-default", + "metrics-elastic_agent.packetbeat-default", + "metrics-elastic_agent.endpoint_security-default", + "metrics-elastic_agent.auditbeat-default", + "metrics-elastic_agent.heartbeat-default", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + }, + "outputs": Object { + "data-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-data.co:9201", + ], + "type": "elasticsearch", + }, + "default": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://127.0.0.1:9201", + ], + "type": "elasticsearch", + }, + }, + "revision": 1, +} +`; + +exports[`getFullAgentPolicy should support a different monitoring output 1`] = ` +Object { + "agent": Object { + "monitoring": Object { + "enabled": true, + "logs": false, + "metrics": true, + "namespace": "default", + "use_output": "monitoring-output-id", + }, + }, + "fleet": Object { + "hosts": Array [ + "http://fleetserver:8220", + ], + }, + "id": "agent-policy", + "inputs": Array [], + "output_permissions": Object { + "default": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + }, + "_fallback": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "logs-*", + "metrics-*", + "traces-*", + "synthetics-*", + ".logs-endpoint.diagnostic.collection-*", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + "monitoring-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "metrics-elastic_agent-default", + "metrics-elastic_agent.elastic_agent-default", + "metrics-elastic_agent.apm_server-default", + "metrics-elastic_agent.filebeat-default", + "metrics-elastic_agent.fleet_server-default", + "metrics-elastic_agent.metricbeat-default", + "metrics-elastic_agent.osquerybeat-default", + "metrics-elastic_agent.packetbeat-default", + "metrics-elastic_agent.endpoint_security-default", + "metrics-elastic_agent.auditbeat-default", + "metrics-elastic_agent.heartbeat-default", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + }, + "outputs": Object { + "default": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://127.0.0.1:9201", + ], + "type": "elasticsearch", + }, + "monitoring-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-monitoring.co:9201", + ], + "type": "elasticsearch", + }, + }, + "revision": 1, +} +`; + +exports[`getFullAgentPolicy should support both different outputs for data and monitoring 1`] = ` +Object { + "agent": Object { + "monitoring": Object { + "enabled": true, + "logs": false, + "metrics": true, + "namespace": "default", + "use_output": "monitoring-output-id", + }, + }, + "fleet": Object { + "hosts": Array [ + "http://fleetserver:8220", + ], + }, + "id": "agent-policy", + "inputs": Array [], + "output_permissions": Object { + "data-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + }, + "_fallback": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "logs-*", + "metrics-*", + "traces-*", + "synthetics-*", + ".logs-endpoint.diagnostic.collection-*", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + "monitoring-output-id": Object { + "_elastic_agent_checks": Object { + "cluster": Array [ + "monitor", + ], + "indices": Array [ + Object { + "names": Array [ + "metrics-elastic_agent-default", + "metrics-elastic_agent.elastic_agent-default", + "metrics-elastic_agent.apm_server-default", + "metrics-elastic_agent.filebeat-default", + "metrics-elastic_agent.fleet_server-default", + "metrics-elastic_agent.metricbeat-default", + "metrics-elastic_agent.osquerybeat-default", + "metrics-elastic_agent.packetbeat-default", + "metrics-elastic_agent.endpoint_security-default", + "metrics-elastic_agent.auditbeat-default", + "metrics-elastic_agent.heartbeat-default", + ], + "privileges": Array [ + "auto_configure", + "create_doc", + ], + }, + ], + }, + }, + }, + "outputs": Object { + "data-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-data.co:9201", + ], + "type": "elasticsearch", + }, + "monitoring-output-id": Object { + "api_key": undefined, + "ca_sha256": undefined, + "hosts": Array [ + "http://es-monitoring.co:9201", + ], + "type": "elasticsearch", + }, + }, + "revision": 1, +} +`; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts new file mode 100644 index 0000000000000..8df1234982ee6 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -0,0 +1,256 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsClientMock } from 'src/core/server/mocks'; + +import type { AgentPolicy, Output } from '../../types'; + +import { agentPolicyService } from '../agent_policy'; +import { agentPolicyUpdateEventHandler } from '../agent_policy_update'; + +import { getFullAgentPolicy } from './full_agent_policy'; + +const mockedAgentPolicyService = agentPolicyService as jest.Mocked<typeof agentPolicyService>; + +function mockAgentPolicy(data: Partial<AgentPolicy>) { + mockedAgentPolicyService.get.mockResolvedValue({ + id: 'agent-policy', + status: 'active', + package_policies: [], + is_managed: false, + namespace: 'default', + revision: 1, + name: 'Policy', + updated_at: '2020-01-01', + updated_by: 'qwerty', + ...data, + }); +} + +jest.mock('../settings', () => { + return { + getSettings: () => { + return { + id: '93f74c0-e876-11ea-b7d3-8b2acec6f75c', + fleet_server_hosts: ['http://fleetserver:8220'], + }; + }, + }; +}); + +jest.mock('../agent_policy'); + +jest.mock('../output', () => { + return { + outputService: { + getDefaultOutputId: () => 'test-id', + get: (soClient: any, id: string): Output => { + switch (id) { + case 'data-output-id': + return { + id: 'data-output-id', + is_default: false, + name: 'Data output', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://es-data.co:9201'], + }; + case 'monitoring-output-id': + return { + id: 'monitoring-output-id', + is_default: false, + name: 'Monitoring output', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://es-monitoring.co:9201'], + }; + default: + return { + id: 'test-id', + is_default: true, + name: 'default', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + }; + } + }, + }, + }; +}); + +jest.mock('../agent_policy_update'); +jest.mock('../agents'); +jest.mock('../package_policy'); + +function getAgentPolicyUpdateMock() { + return agentPolicyUpdateEventHandler as unknown as jest.Mock< + typeof agentPolicyUpdateEventHandler + >; +} + +describe('getFullAgentPolicy', () => { + beforeEach(() => { + getAgentPolicyUpdateMock().mockClear(); + mockedAgentPolicyService.get.mockReset(); + }); + + it('should return a policy without monitoring if monitoring is not enabled', async () => { + mockAgentPolicy({ + revision: 1, + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + monitoring: { + enabled: false, + logs: false, + metrics: false, + }, + }, + }); + }); + + it('should return a policy with monitoring if monitoring is enabled for logs', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['logs'], + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + monitoring: { + namespace: 'default', + use_output: 'default', + enabled: true, + logs: true, + metrics: false, + }, + }, + }); + }); + + it('should return a policy with monitoring if monitoring is enabled for metrics', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + monitoring: { + namespace: 'default', + use_output: 'default', + enabled: true, + logs: false, + metrics: true, + }, + }, + }); + }); + + it('should support a different monitoring output', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + monitoring_output_id: 'monitoring-output-id', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchSnapshot(); + }); + + it('should support a different data output', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + data_output_id: 'data-output-id', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchSnapshot(); + }); + + it('should support both different outputs for data and monitoring ', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + data_output_id: 'data-output-id', + monitoring_output_id: 'monitoring-output-id', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchSnapshot(); + }); + + it('should use "default" as the default policy id', async () => { + mockAgentPolicy({ + id: 'policy', + status: 'active', + package_policies: [], + is_managed: false, + namespace: 'default', + revision: 1, + data_output_id: 'test-id', + monitoring_output_id: 'test-id', + }); + + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy?.outputs.default).toBeDefined(); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts new file mode 100644 index 0000000000000..4e8b3a2c1952e --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectsClientContract } from 'kibana/server'; +import { safeLoad } from 'js-yaml'; + +import type { + FullAgentPolicy, + PackagePolicy, + Settings, + Output, + FullAgentPolicyOutput, +} from '../../types'; +import { agentPolicyService } from '../agent_policy'; +import { outputService } from '../output'; +import { + storedPackagePoliciesToAgentPermissions, + DEFAULT_PERMISSIONS, +} from '../package_policies_to_agent_permissions'; +import { storedPackagePoliciesToAgentInputs, dataTypes, outputType } from '../../../common'; +import type { FullAgentPolicyOutputPermissions } from '../../../common'; +import { getSettings } from '../settings'; +import { PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, DEFAULT_OUTPUT } from '../../constants'; + +const MONITORING_DATASETS = [ + 'elastic_agent', + 'elastic_agent.elastic_agent', + 'elastic_agent.apm_server', + 'elastic_agent.filebeat', + 'elastic_agent.fleet_server', + 'elastic_agent.metricbeat', + 'elastic_agent.osquerybeat', + 'elastic_agent.packetbeat', + 'elastic_agent.endpoint_security', + 'elastic_agent.auditbeat', + 'elastic_agent.heartbeat', +]; + +export async function getFullAgentPolicy( + soClient: SavedObjectsClientContract, + id: string, + options?: { standalone: boolean } +): Promise<FullAgentPolicy | null> { + let agentPolicy; + const standalone = options?.standalone; + + try { + agentPolicy = await agentPolicyService.get(soClient, id); + } catch (err) { + if (!err.isBoom || err.output.statusCode !== 404) { + throw err; + } + } + + if (!agentPolicy) { + return null; + } + + const defaultOutputId = await outputService.getDefaultOutputId(soClient); + if (!defaultOutputId) { + throw new Error('Default output is not setup'); + } + + const dataOutputId = agentPolicy.data_output_id || defaultOutputId; + const monitoringOutputId = agentPolicy.monitoring_output_id || defaultOutputId; + + const outputs = await Promise.all( + Array.from(new Set([dataOutputId, monitoringOutputId])).map((outputId) => + outputService.get(soClient, outputId) + ) + ); + + const dataOutput = outputs.find((output) => output.id === dataOutputId); + if (!dataOutput) { + throw new Error(`Data output not found ${dataOutputId}`); + } + const monitoringOutput = outputs.find((output) => output.id === monitoringOutputId); + if (!monitoringOutput) { + throw new Error(`Monitoring output not found ${monitoringOutputId}`); + } + + const fullAgentPolicy: FullAgentPolicy = { + id: agentPolicy.id, + outputs: { + ...outputs.reduce<FullAgentPolicy['outputs']>((acc, output) => { + acc[getOutputIdForAgentPolicy(output)] = transformOutputToFullPolicyOutput(output); + + return acc; + }, {}), + }, + inputs: storedPackagePoliciesToAgentInputs( + agentPolicy.package_policies as PackagePolicy[], + getOutputIdForAgentPolicy(dataOutput) + ), + revision: agentPolicy.revision, + ...(agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 + ? { + agent: { + monitoring: { + namespace: agentPolicy.namespace, + use_output: getOutputIdForAgentPolicy(monitoringOutput), + enabled: true, + logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), + metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), + }, + }, + } + : { + agent: { + monitoring: { enabled: false, logs: false, metrics: false }, + }, + }), + }; + + const dataPermissions = (await storedPackagePoliciesToAgentPermissions( + soClient, + agentPolicy.package_policies + )) || { _fallback: DEFAULT_PERMISSIONS }; + + dataPermissions._elastic_agent_checks = { + cluster: DEFAULT_PERMISSIONS.cluster, + }; + + // TODO: fetch this from the elastic agent package https://github.com/elastic/kibana/issues/107738 + const monitoringNamespace = fullAgentPolicy.agent?.monitoring.namespace; + const monitoringPermissions: FullAgentPolicyOutputPermissions = + monitoringOutputId === dataOutputId + ? dataPermissions + : { + _elastic_agent_checks: { + cluster: DEFAULT_PERMISSIONS.cluster, + }, + }; + if ( + fullAgentPolicy.agent?.monitoring.enabled && + monitoringNamespace && + monitoringOutput && + monitoringOutput.type === outputType.Elasticsearch + ) { + let names: string[] = []; + if (fullAgentPolicy.agent.monitoring.logs) { + names = names.concat( + MONITORING_DATASETS.map((dataset) => `logs-${dataset}-${monitoringNamespace}`) + ); + } + if (fullAgentPolicy.agent.monitoring.metrics) { + names = names.concat( + MONITORING_DATASETS.map((dataset) => `metrics-${dataset}-${monitoringNamespace}`) + ); + } + + monitoringPermissions._elastic_agent_checks.indices = [ + { + names, + privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, + }, + ]; + } + + // Only add permissions if output.type is "elasticsearch" + fullAgentPolicy.output_permissions = Object.keys(fullAgentPolicy.outputs).reduce< + NonNullable<FullAgentPolicy['output_permissions']> + >((outputPermissions, outputId) => { + const output = fullAgentPolicy.outputs[outputId]; + if (output && output.type === outputType.Elasticsearch) { + outputPermissions[outputId] = + outputId === getOutputIdForAgentPolicy(dataOutput) + ? dataPermissions + : monitoringPermissions; + } + return outputPermissions; + }, {}); + + // only add settings if not in standalone + if (!standalone) { + let settings: Settings; + try { + settings = await getSettings(soClient); + } catch (error) { + throw new Error('Default settings is not setup'); + } + if (settings.fleet_server_hosts && settings.fleet_server_hosts.length) { + fullAgentPolicy.fleet = { + hosts: settings.fleet_server_hosts, + }; + } + } + return fullAgentPolicy; +} + +function transformOutputToFullPolicyOutput( + output: Output, + standalone = false +): FullAgentPolicyOutput { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { config_yaml, type, hosts, ca_sha256, api_key } = output; + const configJs = config_yaml ? safeLoad(config_yaml) : {}; + const newOutput: FullAgentPolicyOutput = { + type, + hosts, + ca_sha256, + api_key, + ...configJs, + }; + + if (standalone) { + delete newOutput.api_key; + newOutput.username = 'ES_USERNAME'; + newOutput.password = 'ES_PASSWORD'; + } + + return newOutput; +} + +/** + * Get id used in full agent policy (sent to the agents) + * we use "default" for the default policy to avoid breaking changes + */ +function getOutputIdForAgentPolicy(output: Output) { + if (output.is_default) { + return DEFAULT_OUTPUT.name; + } + + return output.id; +} diff --git a/x-pack/plugins/fleet/server/services/agent_policies/index.ts b/x-pack/plugins/fleet/server/services/agent_policies/index.ts new file mode 100644 index 0000000000000..b793ed26a08b5 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getFullAgentPolicy } from './full_agent_policy'; diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index 59e0f6fd7840e..6a5cb28dbaa0a 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -7,7 +7,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; -import type { AgentPolicy, NewAgentPolicy, Output } from '../types'; +import type { AgentPolicy, NewAgentPolicy } from '../types'; import { agentPolicyService } from './agent_policy'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; @@ -47,24 +47,6 @@ function getSavedObjectMock(agentPolicyAttributes: any) { return mock; } -jest.mock('./output', () => { - return { - outputService: { - getDefaultOutputId: () => 'test-id', - get: (): Output => { - return { - id: 'test-id', - is_default: true, - name: 'default', - // @ts-ignore - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - }; - }, - }, - }; -}); - jest.mock('./agent_policy_update'); jest.mock('./agents'); jest.mock('./package_policy'); @@ -186,106 +168,17 @@ describe('agent policy', () => { }); }); - describe('getFullAgentPolicy', () => { - it('should return a policy without monitoring if monitoring is not enabled', async () => { - const soClient = getSavedObjectMock({ - revision: 1, - }); - const agentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, 'agent-policy'); - - expect(agentPolicy).toMatchObject({ - id: 'agent-policy', - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - ca_sha256: undefined, - api_key: undefined, - }, - }, - inputs: [], - revision: 1, - fleet: { - hosts: ['http://fleetserver:8220'], - }, - agent: { - monitoring: { - enabled: false, - logs: false, - metrics: false, - }, - }, - }); - }); - - it('should return a policy with monitoring if monitoring is enabled for logs', async () => { - const soClient = getSavedObjectMock({ - namespace: 'default', - revision: 1, - monitoring_enabled: ['logs'], - }); - const agentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, 'agent-policy'); - - expect(agentPolicy).toMatchObject({ - id: 'agent-policy', - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - ca_sha256: undefined, - api_key: undefined, - }, - }, - inputs: [], - revision: 1, - fleet: { - hosts: ['http://fleetserver:8220'], - }, - agent: { - monitoring: { - namespace: 'default', - use_output: 'default', - enabled: true, - logs: true, - metrics: false, - }, - }, - }); - }); - - it('should return a policy with monitoring if monitoring is enabled for metrics', async () => { + describe('bumpAllAgentPoliciesForOutput', () => { + it('should call agentPolicyUpdateEventHandler with updated event once', async () => { const soClient = getSavedObjectMock({ - namespace: 'default', revision: 1, monitoring_enabled: ['metrics'], }); - const agentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, 'agent-policy'); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - expect(agentPolicy).toMatchObject({ - id: 'agent-policy', - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://127.0.0.1:9201'], - ca_sha256: undefined, - api_key: undefined, - }, - }, - inputs: [], - revision: 1, - fleet: { - hosts: ['http://fleetserver:8220'], - }, - agent: { - monitoring: { - namespace: 'default', - use_output: 'default', - enabled: true, - logs: false, - metrics: true, - }, - }, - }); + await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, 'output-id-123'); + + expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 38fb07754bdd3..751e981cb8085 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -6,7 +6,6 @@ */ import { uniq, omit } from 'lodash'; -import { safeLoad } from 'js-yaml'; import uuid from 'uuid/v4'; import type { ElasticsearchClient, @@ -21,7 +20,6 @@ import { AGENT_POLICY_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, - PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, } from '../constants'; import type { PackagePolicy, @@ -33,52 +31,27 @@ import type { ListWithKuery, NewPackagePolicy, } from '../types'; -import { - agentPolicyStatuses, - storedPackagePoliciesToAgentInputs, - dataTypes, - packageToPackagePolicy, - AGENT_POLICY_INDEX, -} from '../../common'; +import { agentPolicyStatuses, packageToPackagePolicy, AGENT_POLICY_INDEX } from '../../common'; import type { DeleteAgentPolicyResponse, - Settings, FleetServerPolicy, Installation, Output, DeletePackagePoliciesResponse, } from '../../common'; import { AgentPolicyNameExistsError, HostedAgentPolicyRestrictionRelatedError } from '../errors'; -import { - storedPackagePoliciesToAgentPermissions, - DEFAULT_PERMISSIONS, -} from '../services/package_policies_to_agent_permissions'; import { getPackageInfo } from './epm/packages'; import { getAgentsByKuery } from './agents'; import { packagePolicyService } from './package_policy'; import { outputService } from './output'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; -import { getSettings } from './settings'; import { normalizeKuery, escapeSearchQueryPhrase } from './saved_object'; import { appContextService } from './app_context'; +import { getFullAgentPolicy } from './agent_policies'; const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; -const MONITORING_DATASETS = [ - 'elastic_agent', - 'elastic_agent.elastic_agent', - 'elastic_agent.apm_server', - 'elastic_agent.filebeat', - 'elastic_agent.fleet_server', - 'elastic_agent.metricbeat', - 'elastic_agent.osquerybeat', - 'elastic_agent.packetbeat', - 'elastic_agent.endpoint_security', - 'elastic_agent.auditbeat', - 'elastic_agent.heartbeat', -]; - class AgentPolicyService { private triggerAgentPolicyUpdatedEvent = async ( soClient: SavedObjectsClientContract, @@ -472,6 +445,38 @@ class AgentPolicyService { return res; } + public async bumpAllAgentPoliciesForOutput( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + outputId: string, + options?: { user?: AuthenticatedUser } + ): Promise<SavedObjectsBulkUpdateResponse<AgentPolicy>> { + const currentPolicies = await soClient.find<AgentPolicySOAttributes>({ + type: SAVED_OBJECT_TYPE, + fields: ['revision', 'data_output_id', 'monitoring_output_id'], + searchFields: ['data_output_id', 'monitoring_output_id'], + search: escapeSearchQueryPhrase(outputId), + }); + const bumpedPolicies = currentPolicies.saved_objects.map((policy) => { + policy.attributes = { + ...policy.attributes, + revision: policy.attributes.revision + 1, + updated_at: new Date().toISOString(), + updated_by: options?.user ? options.user.username : 'system', + }; + return policy; + }); + const res = await soClient.bulkUpdate<AgentPolicySOAttributes>(bumpedPolicies); + + await Promise.all( + currentPolicies.saved_objects.map((policy) => + this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'updated', policy.id) + ) + ); + + return res; + } + public async bumpAllAgentPolicies( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -724,139 +729,7 @@ class AgentPolicyService { id: string, options?: { standalone: boolean } ): Promise<FullAgentPolicy | null> { - let agentPolicy; - const standalone = options?.standalone; - - try { - agentPolicy = await this.get(soClient, id); - } catch (err) { - if (!err.isBoom || err.output.statusCode !== 404) { - throw err; - } - } - - if (!agentPolicy) { - return null; - } - - const defaultOutputId = await outputService.getDefaultOutputId(soClient); - if (!defaultOutputId) { - throw new Error('Default output is not setup'); - } - const defaultOutput = await outputService.get(soClient, defaultOutputId); - - const fullAgentPolicy: FullAgentPolicy = { - id: agentPolicy.id, - outputs: { - // TEMPORARY as we only support a default output - ...[defaultOutput].reduce<FullAgentPolicy['outputs']>( - // eslint-disable-next-line @typescript-eslint/naming-convention - (outputs, { config_yaml, name, type, hosts, ca_sha256, api_key }) => { - const configJs = config_yaml ? safeLoad(config_yaml) : {}; - outputs[name] = { - type, - hosts, - ca_sha256, - api_key, - ...configJs, - }; - - if (options?.standalone) { - delete outputs[name].api_key; - outputs[name].username = 'ES_USERNAME'; - outputs[name].password = 'ES_PASSWORD'; - } - - return outputs; - }, - {} - ), - }, - inputs: storedPackagePoliciesToAgentInputs(agentPolicy.package_policies as PackagePolicy[]), - revision: agentPolicy.revision, - ...(agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 - ? { - agent: { - monitoring: { - namespace: agentPolicy.namespace, - use_output: defaultOutput.name, - enabled: true, - logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), - metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), - }, - }, - } - : { - agent: { - monitoring: { enabled: false, logs: false, metrics: false }, - }, - }), - }; - - const permissions = (await storedPackagePoliciesToAgentPermissions( - soClient, - agentPolicy.package_policies - )) || { _fallback: DEFAULT_PERMISSIONS }; - - permissions._elastic_agent_checks = { - cluster: DEFAULT_PERMISSIONS.cluster, - }; - - // TODO: fetch this from the elastic agent package - const monitoringOutput = fullAgentPolicy.agent?.monitoring.use_output; - const monitoringNamespace = fullAgentPolicy.agent?.monitoring.namespace; - if ( - fullAgentPolicy.agent?.monitoring.enabled && - monitoringNamespace && - monitoringOutput && - fullAgentPolicy.outputs[monitoringOutput]?.type === 'elasticsearch' - ) { - let names: string[] = []; - if (fullAgentPolicy.agent.monitoring.logs) { - names = names.concat( - MONITORING_DATASETS.map((dataset) => `logs-${dataset}-${monitoringNamespace}`) - ); - } - if (fullAgentPolicy.agent.monitoring.metrics) { - names = names.concat( - MONITORING_DATASETS.map((dataset) => `metrics-${dataset}-${monitoringNamespace}`) - ); - } - - permissions._elastic_agent_checks.indices = [ - { - names, - privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES, - }, - ]; - } - - // Only add permissions if output.type is "elasticsearch" - fullAgentPolicy.output_permissions = Object.keys(fullAgentPolicy.outputs).reduce< - NonNullable<FullAgentPolicy['output_permissions']> - >((outputPermissions, outputName) => { - const output = fullAgentPolicy.outputs[outputName]; - if (output && output.type === 'elasticsearch') { - outputPermissions[outputName] = permissions; - } - return outputPermissions; - }, {}); - - // only add settings if not in standalone - if (!standalone) { - let settings: Settings; - try { - settings = await getSettings(soClient); - } catch (error) { - throw new Error('Default settings is not setup'); - } - if (settings.fleet_server_hosts && settings.fleet_server_hosts.length) { - fullAgentPolicy.fleet = { - hosts: settings.fleet_server_hosts, - }; - } - } - return fullAgentPolicy; + return getFullAgentPolicy(soClient, id, options); } } diff --git a/x-pack/plugins/fleet/server/services/output.test.ts b/x-pack/plugins/fleet/server/services/output.test.ts index 26e3955607ada..8103794fb0805 100644 --- a/x-pack/plugins/fleet/server/services/output.test.ts +++ b/x-pack/plugins/fleet/server/services/output.test.ts @@ -5,8 +5,10 @@ * 2.0. */ -import { outputService } from './output'; +import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import type { OutputSOAttributes } from '../types'; +import { outputService, outputIdToUuid } from './output'; import { appContextService } from './app_context'; jest.mock('./app_context'); @@ -34,7 +36,97 @@ const CONFIG_WITHOUT_ES_HOSTS = { }, }; +function getMockedSoClient() { + const soClient = savedObjectsClientMock.create(); + soClient.get.mockImplementation(async (type: string, id: string) => { + switch (id) { + case outputIdToUuid('output-test'): { + return { + id: outputIdToUuid('output-test'), + type: 'ingest-outputs', + references: [], + attributes: { + output_id: 'output-test', + }, + }; + } + default: + throw new Error('not found'); + } + }); + + return soClient; +} + describe('Output Service', () => { + describe('create', () => { + it('work with a predefined id', async () => { + const soClient = getMockedSoClient(); + soClient.create.mockResolvedValue({ + id: outputIdToUuid('output-test'), + type: 'ingest-output', + attributes: {}, + references: [], + }); + await outputService.create( + soClient, + { + is_default: false, + name: 'Test', + type: 'elasticsearch', + }, + { id: 'output-test' } + ); + + expect(soClient.create).toBeCalled(); + + // ID should always be the same for a predefined id + expect(soClient.create.mock.calls[0][2]?.id).toEqual(outputIdToUuid('output-test')); + expect((soClient.create.mock.calls[0][1] as OutputSOAttributes).output_id).toEqual( + 'output-test' + ); + }); + }); + + describe('get', () => { + it('work with a predefined id', async () => { + const soClient = getMockedSoClient(); + const output = await outputService.get(soClient, 'output-test'); + + expect(soClient.get).toHaveBeenCalledWith('ingest-outputs', outputIdToUuid('output-test')); + + expect(output.id).toEqual('output-test'); + }); + }); + + describe('getDefaultOutputId', () => { + it('work with a predefined id', async () => { + const soClient = getMockedSoClient(); + soClient.find.mockResolvedValue({ + page: 1, + per_page: 100, + total: 1, + saved_objects: [ + { + id: outputIdToUuid('output-test'), + type: 'ingest-outputs', + references: [], + score: 0, + attributes: { + output_id: 'output-test', + is_default: true, + }, + }, + ], + }); + const defaultId = await outputService.getDefaultOutputId(soClient); + + expect(soClient.find).toHaveBeenCalled(); + + expect(defaultId).toEqual('output-test'); + }); + }); + describe('getDefaultESHosts', () => { afterEach(() => { mockedAppContextService.getConfig.mockReset(); diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index 8c6bc7eca0401..5a7ba1e2c1223 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { SavedObjectsClientContract } from 'src/core/server'; +import type { SavedObject, SavedObjectsClientContract } from 'src/core/server'; +import uuid from 'uuid/v5'; import type { NewOutput, Output, OutputSOAttributes } from '../types'; import { DEFAULT_OUTPUT, OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; @@ -17,8 +18,33 @@ const SAVED_OBJECT_TYPE = OUTPUT_SAVED_OBJECT_TYPE; const DEFAULT_ES_HOSTS = ['http://localhost:9200']; +// differentiate +function isUUID(val: string) { + return ( + typeof val === 'string' && + val.match(/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/) + ); +} + +export function outputIdToUuid(id: string) { + if (isUUID(id)) { + return id; + } + + // UUID v5 need a namespace (uuid.DNS), changing this params will result in loosing the ability to generate predicable uuid + return uuid(id, uuid.DNS); +} + +function outputSavedObjectToOutput(so: SavedObject<OutputSOAttributes>) { + const { output_id: outputId, ...atributes } = so.attributes; + return { + id: outputId ?? so.id, + ...atributes, + }; +} + class OutputService { - public async getDefaultOutput(soClient: SavedObjectsClientContract) { + private async _getDefaultOutputsSO(soClient: SavedObjectsClientContract) { return await soClient.find<OutputSOAttributes>({ type: OUTPUT_SAVED_OBJECT_TYPE, searchFields: ['is_default'], @@ -27,7 +53,7 @@ class OutputService { } public async ensureDefaultOutput(soClient: SavedObjectsClientContract) { - const outputs = await this.getDefaultOutput(soClient); + const outputs = await this._getDefaultOutputsSO(soClient); if (!outputs.saved_objects.length) { const newDefaultOutput = { @@ -39,10 +65,7 @@ class OutputService { return await this.create(soClient, newDefaultOutput); } - return { - id: outputs.saved_objects[0].id, - ...outputs.saved_objects[0].attributes, - }; + return outputSavedObjectToOutput(outputs.saved_objects[0]); } public getDefaultESHosts(): string[] { @@ -60,49 +83,84 @@ class OutputService { } public async getDefaultOutputId(soClient: SavedObjectsClientContract) { - const outputs = await this.getDefaultOutput(soClient); + const outputs = await this._getDefaultOutputsSO(soClient); if (!outputs.saved_objects.length) { return null; } - return outputs.saved_objects[0].id; + return outputSavedObjectToOutput(outputs.saved_objects[0]).id; } public async create( soClient: SavedObjectsClientContract, output: NewOutput, - options?: { id?: string } + options?: { id?: string; overwrite?: boolean } ): Promise<Output> { - const data = { ...output }; + const data: OutputSOAttributes = { ...output }; + + // ensure only default output exists + if (data.is_default) { + const defaultOuput = await this.getDefaultOutputId(soClient); + if (defaultOuput) { + throw new Error(`A default output already exists (${defaultOuput})`); + } + } if (data.hosts) { data.hosts = data.hosts.map(normalizeHostsForAgents); } - const newSo = await soClient.create<OutputSOAttributes>( - SAVED_OBJECT_TYPE, - data as Output, - options - ); + if (options?.id) { + data.output_id = options?.id; + } + + const newSo = await soClient.create<OutputSOAttributes>(SAVED_OBJECT_TYPE, data, { + ...options, + id: options?.id ? outputIdToUuid(options.id) : undefined, + }); return { - id: newSo.id, + id: options?.id ?? newSo.id, ...newSo.attributes, }; } + public async bulkGet( + soClient: SavedObjectsClientContract, + ids: string[], + { ignoreNotFound = false } = { ignoreNotFound: true } + ) { + const res = await soClient.bulkGet<OutputSOAttributes>( + ids.map((id) => ({ id: outputIdToUuid(id), type: SAVED_OBJECT_TYPE })) + ); + + return res.saved_objects + .map((so) => { + if (so.error) { + if (!ignoreNotFound || so.error.statusCode !== 404) { + throw so.error; + } + return undefined; + } + + return outputSavedObjectToOutput(so); + }) + .filter((output): output is Output => typeof output !== 'undefined'); + } + public async get(soClient: SavedObjectsClientContract, id: string): Promise<Output> { - const outputSO = await soClient.get<OutputSOAttributes>(SAVED_OBJECT_TYPE, id); + const outputSO = await soClient.get<OutputSOAttributes>(SAVED_OBJECT_TYPE, outputIdToUuid(id)); if (outputSO.error) { throw new Error(outputSO.error.message); } - return { - id: outputSO.id, - ...outputSO.attributes, - }; + return outputSavedObjectToOutput(outputSO); + } + + public async delete(soClient: SavedObjectsClientContract, id: string) { + return soClient.delete(SAVED_OBJECT_TYPE, outputIdToUuid(id)); } public async update(soClient: SavedObjectsClientContract, id: string, data: Partial<Output>) { @@ -111,8 +169,11 @@ class OutputService { if (updateData.hosts) { updateData.hosts = updateData.hosts.map(normalizeHostsForAgents); } - - const outputSO = await soClient.update<OutputSOAttributes>(SAVED_OBJECT_TYPE, id, updateData); + const outputSO = await soClient.update<OutputSOAttributes>( + SAVED_OBJECT_TYPE, + outputIdToUuid(id), + updateData + ); if (outputSO.error) { throw new Error(outputSO.error.message); @@ -127,12 +188,7 @@ class OutputService { }); return { - items: outputs.saved_objects.map<Output>((outputSO) => { - return { - id: outputSO.id, - ...outputSO.attributes, - }; - }), + items: outputs.saved_objects.map<Output>(outputSavedObjectToOutput), total: outputs.total, page: 1, perPage: 1000, diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index 86fdd2f0aa800..43887bc2787f4 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -9,7 +9,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/serve import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; -import type { PreconfiguredAgentPolicy } from '../../common/types'; +import type { PreconfiguredAgentPolicy, PreconfiguredOutput } from '../../common/types'; import type { AgentPolicy, NewPackagePolicy, Output } from '../types'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../constants'; @@ -19,9 +19,15 @@ import * as agentPolicy from './agent_policy'; import { ensurePreconfiguredPackagesAndPolicies, comparePreconfiguredPolicyToCurrent, + ensurePreconfiguredOutputs, + cleanPreconfiguredOutputs, } from './preconfiguration'; +import { outputService } from './output'; jest.mock('./agent_policy_update'); +jest.mock('./output'); + +const mockedOutputService = outputService as jest.Mocked<typeof outputService>; const mockInstalledPackages = new Map(); const mockConfiguredPolicies = new Map(); @@ -156,12 +162,17 @@ jest.mock('./app_context', () => ({ })); const spyAgentPolicyServiceUpdate = jest.spyOn(agentPolicy.agentPolicyService, 'update'); +const spyAgentPolicyServicBumpAllAgentPoliciesForOutput = jest.spyOn( + agentPolicy.agentPolicyService, + 'bumpAllAgentPoliciesForOutput' +); describe('policy preconfiguration', () => { beforeEach(() => { mockInstalledPackages.clear(); mockConfiguredPolicies.clear(); spyAgentPolicyServiceUpdate.mockClear(); + spyAgentPolicyServicBumpAllAgentPoliciesForOutput.mockClear(); }); it('should perform a no-op when passed no policies or packages', async () => { @@ -480,3 +491,168 @@ describe('comparePreconfiguredPolicyToCurrent', () => { expect(hasChanged).toBe(false); }); }); + +describe('output preconfiguration', () => { + beforeEach(() => { + mockedOutputService.create.mockReset(); + mockedOutputService.update.mockReset(); + mockedOutputService.getDefaultESHosts.mockReturnValue(['http://default-es:9200']); + mockedOutputService.bulkGet.mockImplementation(async (soClient, id): Promise<Output[]> => { + return [ + { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://es.co:80'], + is_preconfigured: true, + }, + ]; + }); + }); + + it('should create preconfigured output that does not exists', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await ensurePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: false, + hosts: ['http://test.fr'], + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).not.toBeCalled(); + }); + + it('should set default hosts if hosts is not set output that does not exists', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await ensurePreconfiguredOutputs(soClient, esClient, [ + { + id: 'non-existing-output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: false, + }, + ]); + + expect(mockedOutputService.create).toBeCalled(); + expect(mockedOutputService.create.mock.calls[0][1].hosts).toEqual(['http://default-es:9200']); + }); + + it('should update output if preconfigured output exists and changed', async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + soClient.find.mockResolvedValue({ saved_objects: [], page: 0, per_page: 0, total: 0 }); + await ensurePreconfiguredOutputs(soClient, esClient, [ + { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://newhostichanged.co:9201'], // field that changed + }, + ]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).toBeCalled(); + expect(spyAgentPolicyServicBumpAllAgentPoliciesForOutput).toBeCalled(); + }); + + const SCENARIOS: Array<{ name: string; data: PreconfiguredOutput }> = [ + { + name: 'no changes', + data: { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:80'], + }, + }, + { + name: 'hosts without port', + data: { + id: 'existing-output-1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co'], + }, + }, + ]; + SCENARIOS.forEach((scenario) => { + const { data, name } = scenario; + it(`should do nothing if preconfigured output exists and did not changed (${name})`, async () => { + const soClient = savedObjectsClientMock.create(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + await ensurePreconfiguredOutputs(soClient, esClient, [data]); + + expect(mockedOutputService.create).not.toBeCalled(); + expect(mockedOutputService.update).not.toBeCalled(); + }); + }); + + it('should not delete non deleted preconfigured output', async () => { + const soClient = savedObjectsClientMock.create(); + mockedOutputService.list.mockResolvedValue({ + items: [ + { id: 'output1', is_preconfigured: true } as Output, + { id: 'output2', is_preconfigured: true } as Output, + ], + page: 1, + perPage: 10000, + total: 1, + }); + await cleanPreconfiguredOutputs(soClient, [ + { + id: 'output1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:9201'], + }, + { + id: 'output2', + is_default: false, + name: 'Output 2', + type: 'elasticsearch', + hosts: ['http://es.co:9201'], + }, + ]); + + expect(mockedOutputService.delete).not.toBeCalled(); + }); + + it('should delete deleted preconfigured output', async () => { + const soClient = savedObjectsClientMock.create(); + mockedOutputService.list.mockResolvedValue({ + items: [ + { id: 'output1', is_preconfigured: true } as Output, + { id: 'output2', is_preconfigured: true } as Output, + ], + page: 1, + perPage: 10000, + total: 1, + }); + await cleanPreconfiguredOutputs(soClient, [ + { + id: 'output1', + is_default: false, + name: 'Output 1', + type: 'elasticsearch', + hosts: ['http://es.co:9201'], + }, + ]); + + expect(mockedOutputService.delete).toBeCalled(); + expect(mockedOutputService.delete).toBeCalledTimes(1); + expect(mockedOutputService.delete.mock.calls[0][1]).toEqual('output2'); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 37ed98a6f4aa0..30c5c27c68916 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -8,6 +8,7 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { i18n } from '@kbn/i18n'; import { groupBy, omit, pick, isEqual } from 'lodash'; +import { safeDump } from 'js-yaml'; import type { NewPackagePolicy, @@ -17,16 +18,15 @@ import type { PreconfiguredAgentPolicy, PreconfiguredPackage, PreconfigurationError, + PreconfiguredOutput, } from '../../common'; -import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../common'; - +import { AGENT_POLICY_SAVED_OBJECT_TYPE, normalizeHostsForAgents } from '../../common'; import { PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE, PRECONFIGURATION_LATEST_KEYWORD, } from '../constants'; import { escapeSearchQueryPhrase } from './saved_object'; - import { pkgToPkgKey } from './epm/registry'; import { getInstallation, getPackageInfo } from './epm/packages'; import { ensurePackagesCompletedInstall } from './epm/packages/install'; @@ -35,6 +35,7 @@ import { agentPolicyService, addPackageToAgentPolicy } from './agent_policy'; import type { InputsOverride } from './package_policy'; import { overridePackageInputs } from './package_policy'; import { appContextService } from './app_context'; +import { outputService } from './output'; interface PreconfigurationResult { policies: Array<{ id: string; updated_at: string }>; @@ -42,6 +43,89 @@ interface PreconfigurationResult { nonFatalErrors: PreconfigurationError[]; } +function isPreconfiguredOutputDifferentFromCurrent( + existingOutput: Output, + preconfiguredOutput: Partial<Output> +): boolean { + return ( + existingOutput.is_default !== preconfiguredOutput.is_default || + existingOutput.name !== preconfiguredOutput.name || + existingOutput.type !== preconfiguredOutput.type || + (preconfiguredOutput.hosts && + !isEqual( + existingOutput.hosts?.map(normalizeHostsForAgents), + preconfiguredOutput.hosts.map(normalizeHostsForAgents) + )) || + existingOutput.ca_sha256 !== preconfiguredOutput.ca_sha256 || + existingOutput.config_yaml !== preconfiguredOutput.config_yaml + ); +} + +export async function ensurePreconfiguredOutputs( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + outputs: PreconfiguredOutput[] +) { + if (outputs.length === 0) { + return; + } + + const existingOutputs = await outputService.bulkGet( + soClient, + outputs.map(({ id }) => id), + { ignoreNotFound: true } + ); + + await Promise.all( + outputs.map(async (output) => { + const existingOutput = existingOutputs.find((o) => o.id === output.id); + + const { id, config, ...outputData } = output; + + const configYaml = config ? safeDump(config) : undefined; + + const data = { + ...outputData, + config_yaml: configYaml, + is_preconfigured: true, + }; + + if (!data.hosts || data.hosts.length === 0) { + data.hosts = outputService.getDefaultESHosts(); + } + + if (!existingOutput) { + await outputService.create(soClient, data, { id, overwrite: true }); + } else if (isPreconfiguredOutputDifferentFromCurrent(existingOutput, data)) { + await outputService.update(soClient, id, data); + // Bump revision of all policies using that output + if (outputData.is_default) { + await agentPolicyService.bumpAllAgentPolicies(soClient, esClient); + } else { + await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, id); + } + } + }) + ); +} + +export async function cleanPreconfiguredOutputs( + soClient: SavedObjectsClientContract, + outputs: PreconfiguredOutput[] +) { + const existingPreconfiguredOutput = (await outputService.list(soClient)).items.filter( + (o) => o.is_preconfigured === true + ); + const logger = appContextService.getLogger(); + + for (const output of existingPreconfiguredOutput) { + if (!outputs.find(({ id }) => output.id === id)) { + logger.info(`Deleting preconfigured output ${output.id}`); + await outputService.delete(soClient, output.id); + } + } +} + export async function ensurePreconfiguredPackagesAndPolicies( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, @@ -224,7 +308,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( } // Add the is_managed flag after configuring package policies to avoid errors if (shouldAddIsManagedFlag) { - agentPolicyService.update(soClient, esClient, policy!.id, { is_managed: true }); + await agentPolicyService.update(soClient, esClient, policy!.id, { is_managed: true }); } } } diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 1f3c3c5082b34..8c49bffdbf25c 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -15,7 +15,11 @@ import { SO_SEARCH_LIMIT, DEFAULT_PACKAGES } from '../constants'; import { appContextService } from './app_context'; import { agentPolicyService } from './agent_policy'; -import { ensurePreconfiguredPackagesAndPolicies } from './preconfiguration'; +import { + cleanPreconfiguredOutputs, + ensurePreconfiguredOutputs, + ensurePreconfiguredPackagesAndPolicies, +} from './preconfiguration'; import { outputService } from './output'; import { generateEnrollmentAPIKey, hasEnrollementAPIKeysForPolicy } from './api_keys'; @@ -45,23 +49,27 @@ async function createSetupSideEffects( soClient: SavedObjectsClientContract, esClient: ElasticsearchClient ): Promise<SetupStatus> { - const [defaultOutput] = await Promise.all([ - outputService.ensureDefaultOutput(soClient), + const { + agentPolicies: policiesOrUndefined, + packages: packagesOrUndefined, + outputs: outputsOrUndefined, + } = appContextService.getConfig() ?? {}; + + const policies = policiesOrUndefined ?? []; + let packages = packagesOrUndefined ?? []; + + await Promise.all([ + ensurePreconfiguredOutputs(soClient, esClient, outputsOrUndefined ?? []), settingsService.settingsSetup(soClient), ]); + const defaultOutput = await outputService.ensureDefaultOutput(soClient); + await awaitIfFleetServerSetupPending(); if (appContextService.getConfig()?.agentIdVerificationEnabled) { await ensureFleetGlobalEsAssets(soClient, esClient); } - const { agentPolicies: policiesOrUndefined, packages: packagesOrUndefined } = - appContextService.getConfig() ?? {}; - - const policies = policiesOrUndefined ?? []; - - let packages = packagesOrUndefined ?? []; - // Ensure that required packages are always installed even if they're left out of the config const preconfiguredPackageNames = new Set(packages.map((pkg) => pkg.name)); @@ -90,6 +98,8 @@ async function createSetupSideEffects( defaultOutput ); + await cleanPreconfiguredOutputs(soClient, outputsOrUndefined ?? []); + await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient); await ensureAgentActionPolicyChangeExists(soClient, esClient); diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index f686b969fd038..63e6c277ed710 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -27,6 +27,7 @@ export { PackagePolicySOAttributes, FullAgentPolicyInput, FullAgentPolicy, + FullAgentPolicyOutput, AgentPolicy, AgentPolicySOAttributes, NewAgentPolicy, diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts new file mode 100644 index 0000000000000..eb349e0d0f823 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PreconfiguredOutputsSchema, PreconfiguredAgentPoliciesSchema } from './preconfiguration'; + +describe('Test preconfiguration schema', () => { + describe('PreconfiguredOutputsSchema', () => { + it('should not allow multiple default output', () => { + expect(() => { + PreconfiguredOutputsSchema.validate([ + { + id: 'output-1', + name: 'Output 1', + type: 'elasticsearch', + is_default: true, + }, + { + id: 'output-2', + name: 'Output 2', + type: 'elasticsearch', + is_default: true, + }, + ]); + }).toThrowError('preconfigured outputs need to have only one default output.'); + }); + it('should not allow multiple output with same ids', () => { + expect(() => { + PreconfiguredOutputsSchema.validate([ + { + id: 'nonuniqueid', + name: 'Output 1', + type: 'elasticsearch', + }, + { + id: 'nonuniqueid', + name: 'Output 2', + type: 'elasticsearch', + }, + ]); + }).toThrowError('preconfigured outputs need to have unique ids.'); + }); + it('should not allow multiple output with same names', () => { + expect(() => { + PreconfiguredOutputsSchema.validate([ + { + id: 'output-1', + name: 'nonuniquename', + type: 'elasticsearch', + }, + { + id: 'output-2', + name: 'nonuniquename', + type: 'elasticsearch', + }, + ]); + }).toThrowError('preconfigured outputs need to have unique names.'); + }); + }); + + describe('PreconfiguredAgentPoliciesSchema', () => { + it('should not allow multiple outputs in one policy', () => { + expect(() => { + PreconfiguredAgentPoliciesSchema.validate([ + { + id: 'policy-1', + name: 'Policy 1', + package_policies: [], + data_output_id: 'test1', + monitoring_output_id: 'test2', + }, + ]); + }).toThrowError( + '[0]: Currently Fleet only support one output per agent policy data_output_id should be the same as monitoring_output_id.' + ); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts index 4ea9f086bda68..b65fa122911dc 100644 --- a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts @@ -14,6 +14,8 @@ import { DEFAULT_FLEET_SERVER_AGENT_POLICY, DEFAULT_PACKAGES, } from '../../constants'; +import type { PreconfiguredOutput } from '../../../common'; +import { outputType } from '../../../common'; import { AgentPolicyBaseSchema } from './agent_policy'; import { NamespaceSchema } from './package_policy'; @@ -47,47 +49,94 @@ export const PreconfiguredPackagesSchema = schema.arrayOf( } ); -export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( +function validatePreconfiguredOutputs(outputs: PreconfiguredOutput[]) { + const acc = { names: new Set(), ids: new Set(), is_default: false }; + + for (const output of outputs) { + if (acc.names.has(output.name)) { + return 'preconfigured outputs need to have unique names.'; + } + if (acc.ids.has(output.id)) { + return 'preconfigured outputs need to have unique ids.'; + } + if (acc.is_default && output.is_default) { + return 'preconfigured outputs need to have only one default output.'; + } + + acc.ids.add(output.id); + acc.names.add(output.name); + acc.is_default = acc.is_default || output.is_default; + } +} + +export const PreconfiguredOutputsSchema = schema.arrayOf( schema.object({ - ...AgentPolicyBaseSchema, - namespace: schema.maybe(NamespaceSchema), - id: schema.maybe(schema.oneOf([schema.string(), schema.number()])), - is_default: schema.maybe(schema.boolean()), - is_default_fleet_server: schema.maybe(schema.boolean()), - package_policies: schema.arrayOf( - schema.object({ - name: schema.string(), - package: schema.object({ - name: schema.string(), - }), - description: schema.maybe(schema.string()), - namespace: schema.maybe(NamespaceSchema), - inputs: schema.maybe( - schema.arrayOf( - schema.object({ - type: schema.string(), - enabled: schema.maybe(schema.boolean()), - keep_enabled: schema.maybe(schema.boolean()), - vars: varsSchema, - streams: schema.maybe( - schema.arrayOf( - schema.object({ - data_stream: schema.object({ - type: schema.maybe(schema.string()), - dataset: schema.string(), - }), - enabled: schema.maybe(schema.boolean()), - keep_enabled: schema.maybe(schema.boolean()), - vars: varsSchema, - }) - ) - ), - }) - ) - ), - }) - ), + id: schema.string(), + is_default: schema.boolean({ defaultValue: false }), + name: schema.string(), + type: schema.oneOf([schema.literal(outputType.Elasticsearch)]), + hosts: schema.maybe(schema.arrayOf(schema.uri({ scheme: ['http', 'https'] }))), + ca_sha256: schema.maybe(schema.string()), + config: schema.maybe(schema.object({}, { unknowns: 'allow' })), }), + { + defaultValue: [], + validate: validatePreconfiguredOutputs, + } +); + +export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( + schema.object( + { + ...AgentPolicyBaseSchema, + namespace: schema.maybe(NamespaceSchema), + id: schema.maybe(schema.oneOf([schema.string(), schema.number()])), + is_default: schema.maybe(schema.boolean()), + is_default_fleet_server: schema.maybe(schema.boolean()), + data_output_id: schema.maybe(schema.string()), + monitoring_output_id: schema.maybe(schema.string()), + package_policies: schema.arrayOf( + schema.object({ + name: schema.string(), + package: schema.object({ + name: schema.string(), + }), + description: schema.maybe(schema.string()), + namespace: schema.maybe(NamespaceSchema), + inputs: schema.maybe( + schema.arrayOf( + schema.object({ + type: schema.string(), + enabled: schema.maybe(schema.boolean()), + keep_enabled: schema.maybe(schema.boolean()), + vars: varsSchema, + streams: schema.maybe( + schema.arrayOf( + schema.object({ + data_stream: schema.object({ + type: schema.maybe(schema.string()), + dataset: schema.string(), + }), + enabled: schema.maybe(schema.boolean()), + keep_enabled: schema.maybe(schema.boolean()), + vars: varsSchema, + }) + ) + ), + }) + ) + ), + }) + ), + }, + { + validate: (policy) => { + if (policy.data_output_id !== policy.monitoring_output_id) { + return 'Currently Fleet only support one output per agent policy data_output_id should be the same as monitoring_output_id.'; + } + }, + } + ), { defaultValue: [DEFAULT_AGENT_POLICY, DEFAULT_FLEET_SERVER_AGENT_POLICY], } From 65ec86da66919cc4d0c318d1eef3a2f2fb680f4f Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet <pierre.gayvallet@gmail.com> Date: Tue, 21 Sep 2021 20:19:34 +0200 Subject: [PATCH 38/69] handle source index without any mappings (#112664) --- .../migrations/core/migration_context.test.ts | 14 ++++++++++++++ .../migrations/core/migration_context.ts | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/core/server/saved_objects/migrations/core/migration_context.test.ts b/src/core/server/saved_objects/migrations/core/migration_context.test.ts index 240b41266abb6..27aae5968ba88 100644 --- a/src/core/server/saved_objects/migrations/core/migration_context.test.ts +++ b/src/core/server/saved_objects/migrations/core/migration_context.test.ts @@ -74,4 +74,18 @@ describe('disableUnknownTypeMappingFields', () => { }, }); }); + + it('does not fail if the source mapping does not have `properties` defined', () => { + const missingPropertiesMappings = { + ...sourceMappings, + properties: undefined, + }; + const result = disableUnknownTypeMappingFields( + activeMappings, + // @ts-expect-error `properties` should not be undefined + missingPropertiesMappings + ); + + expect(Object.keys(result.properties)).toEqual(['known_type']); + }); }); diff --git a/src/core/server/saved_objects/migrations/core/migration_context.ts b/src/core/server/saved_objects/migrations/core/migration_context.ts index d7f7aff45a470..96c47bcf38d0a 100644 --- a/src/core/server/saved_objects/migrations/core/migration_context.ts +++ b/src/core/server/saved_objects/migrations/core/migration_context.ts @@ -154,7 +154,7 @@ export function disableUnknownTypeMappingFields( ): IndexMapping { const targetTypes = Object.keys(activeMappings.properties); - const disabledTypesProperties = Object.keys(sourceMappings.properties) + const disabledTypesProperties = Object.keys(sourceMappings.properties ?? {}) .filter((sourceType) => { const isObjectType = 'properties' in sourceMappings.properties[sourceType]; // Only Object/Nested datatypes can be excluded from the field count by From ce6ed0875a0875cab0d814a828fd6ab4e742b2c4 Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tyler.smalley@elastic.co> Date: Tue, 21 Sep 2021 12:24:39 -0700 Subject: [PATCH 39/69] skip flaky suite (#111922) Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co> --- test/functional/apps/discover/_runtime_fields_editor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_runtime_fields_editor.ts b/test/functional/apps/discover/_runtime_fields_editor.ts index 642743d3a0377..4757807cb7ac1 100644 --- a/test/functional/apps/discover/_runtime_fields_editor.ts +++ b/test/functional/apps/discover/_runtime_fields_editor.ts @@ -31,7 +31,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.save(); }; - describe('discover integration with runtime fields editor', function describeIndexTests() { + // Failing: https://github.com/elastic/kibana/issues/111922 + describe.skip('discover integration with runtime fields editor', function describeIndexTests() { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); From 25bf7950999ae025a9275155c1641e3f007990d5 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens <jloleysens@gmail.com> Date: Tue, 21 Sep 2021 21:42:32 +0200 Subject: [PATCH 40/69] [Reporting/Discover/Search Sessions] Only use relative time filter when generating share data (#112588) * only use relative time filter when generating share data * Added comment on absolute time filter. Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com> * improve discover search session relative time range test * update discover tests and types for passing in data plugin to getSharingData function * updated reporting to pass in data plugin to getSharingData, also updates jest tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com> Co-authored-by: Anton Dosov <anton.dosov@elastic.co> --- .../components/top_nav/get_top_nav_links.ts | 2 +- .../apps/main/utils/get_sharing_data.test.ts | 37 ++++++++------ .../apps/main/utils/get_sharing_data.ts | 11 +++-- .../apps/main/utils/update_search_source.ts | 6 +-- .../get_csv_panel_action.test.ts | 49 +++++++++++-------- .../panel_actions/get_csv_panel_action.tsx | 17 ++++--- x-pack/plugins/reporting/public/plugin.ts | 7 ++- .../search_sessions_management_page.ts | 1 + .../tests/apps/discover/async_search.ts | 13 ++++- 9 files changed, 88 insertions(+), 55 deletions(-) diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts index 58a7242974ba2..ba4cd8c3cd524 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts @@ -112,7 +112,7 @@ export const getTopNavLinks = ({ const sharingData = await getSharingData( searchSource, state.appStateContainer.getState(), - services.uiSettings + services ); services.share.toggleShareContextMenu({ diff --git a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts index 25d0ca5d66eb4..e7205c3f9bc69 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.test.ts @@ -7,32 +7,37 @@ */ import { Capabilities, IUiSettingsClient } from 'kibana/public'; -import { IndexPattern } from 'src/plugins/data/public'; +import type { IndexPattern } from 'src/plugins/data/public'; +import type { DiscoverServices } from '../../../../build_services'; +import { dataPluginMock } from '../../../../../../data/public/mocks'; import { createSearchSourceMock } from '../../../../../../data/common/search/search_source/mocks'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import { indexPatternMock } from '../../../../__mocks__/index_pattern'; import { getSharingData, showPublicUrlSwitch } from './get_sharing_data'; describe('getSharingData', () => { - let mockConfig: IUiSettingsClient; + let services: DiscoverServices; beforeEach(() => { - mockConfig = { - get: (key: string) => { - if (key === SORT_DEFAULT_ORDER_SETTING) { - return 'desc'; - } - if (key === DOC_HIDE_TIME_COLUMN_SETTING) { + services = { + data: dataPluginMock.createStartContract(), + uiSettings: { + get: (key: string) => { + if (key === SORT_DEFAULT_ORDER_SETTING) { + return 'desc'; + } + if (key === DOC_HIDE_TIME_COLUMN_SETTING) { + return false; + } return false; - } - return false; + }, }, - } as unknown as IUiSettingsClient; + } as DiscoverServices; }); test('returns valid data for sharing', async () => { const searchSourceMock = createSearchSourceMock({ index: indexPatternMock }); - const result = await getSharingData(searchSourceMock, { columns: [] }, mockConfig); + const result = await getSharingData(searchSourceMock, { columns: [] }, services); expect(result).toMatchInlineSnapshot(` Object { "columns": Array [], @@ -53,7 +58,7 @@ describe('getSharingData', () => { const result = await getSharingData( searchSourceMock, { columns: ['column_a', 'column_b'] }, - mockConfig + services ); expect(result).toMatchInlineSnapshot(` Object { @@ -90,7 +95,7 @@ describe('getSharingData', () => { 'cool-field-6', ], }, - mockConfig + services ); expect(result).toMatchInlineSnapshot(` Object { @@ -116,7 +121,7 @@ describe('getSharingData', () => { }); test('fields conditionally do not have prepended timeField', async () => { - mockConfig = { + services.uiSettings = { get: (key: string) => { if (key === DOC_HIDE_TIME_COLUMN_SETTING) { return true; @@ -141,7 +146,7 @@ describe('getSharingData', () => { 'cool-field-6', ], }, - mockConfig + services ); expect(result).toMatchInlineSnapshot(` Object { diff --git a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts index 65001f49f4d68..420ff0fa11eeb 100644 --- a/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/application/apps/main/utils/get_sharing_data.ts @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import type { Capabilities, IUiSettingsClient } from 'kibana/public'; -import { ISearchSource } from '../../../../../../data/common'; +import type { Capabilities } from 'kibana/public'; +import type { IUiSettingsClient } from 'src/core/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { ISearchSource } from 'src/plugins/data/common'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import type { SavedSearch, SortOrder } from '../../../../saved_searches/types'; import { getSortForSearchSource } from '../components/doc_table'; @@ -19,8 +21,9 @@ import { AppState } from '../services/discover_state'; export async function getSharingData( currentSearchSource: ISearchSource, state: AppState | SavedSearch, - config: IUiSettingsClient + services: { uiSettings: IUiSettingsClient; data: DataPublicPluginStart } ) { + const { uiSettings: config, data } = services; const searchSource = currentSearchSource.createCopy(); const index = searchSource.getField('index')!; @@ -28,6 +31,8 @@ export async function getSharingData( 'sort', getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING)) ); + // When sharing externally we preserve relative time values + searchSource.setField('filter', data.query.timefilter.timefilter.createRelativeFilter(index)); searchSource.removeField('highlight'); searchSource.removeField('highlightAll'); searchSource.removeField('aggs'); diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts index 74e63c399743f..5a4a543a1d5c7 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts @@ -54,10 +54,8 @@ export function updateSearchSource( // this is not the default index pattern, it determines that it's not of type rollup if (indexPatternsUtils.isDefault(indexPattern)) { - searchSource.setField( - 'filter', - data.query.timefilter.timefilter.createRelativeFilter(indexPattern) - ); + // Set the date range filter fields from timeFilter using the absolute format. Search sessions requires that it be converted from a relative range + searchSource.setField('filter', data.query.timefilter.timefilter.createFilter(indexPattern)); } if (useNewFieldsApi) { diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts index ceb3ed180cd3b..bedf310725ae2 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.test.ts @@ -11,8 +11,10 @@ import { CoreStart } from 'src/core/public'; import type { SearchSource } from 'src/plugins/data/common'; import type { SavedSearch } from 'src/plugins/discover/public'; import { coreMock } from '../../../../../src/core/public/mocks'; +import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; import type { ILicense, LicensingPluginSetup } from '../../../licensing/public'; import { ReportingAPIClient } from '../lib/reporting_api_client'; +import type { ReportingPublicPluginStartDendencies } from '../plugin'; import type { ActionContext } from './get_csv_panel_action'; import { ReportingCsvPanelAction } from './get_csv_panel_action'; @@ -25,8 +27,8 @@ describe('GetCsvReportPanelAction', () => { let context: ActionContext; let mockLicense$: (state?: LicenseResults) => Rx.Observable<ILicense>; let mockSearchSource: SearchSource; - let mockStartServicesPayload: [CoreStart, object, unknown]; - let mockStartServices$: Rx.Subject<typeof mockStartServicesPayload>; + let mockStartServicesPayload: [CoreStart, ReportingPublicPluginStartDendencies, unknown]; + let mockStartServices$: Rx.Observable<typeof mockStartServicesPayload>; beforeAll(() => { if (typeof window.URL.revokeObjectURL === 'undefined') { @@ -48,14 +50,17 @@ describe('GetCsvReportPanelAction', () => { }) as unknown as LicensingPluginSetup['license$']; }; - mockStartServices$ = new Rx.Subject<[CoreStart, object, unknown]>(); mockStartServicesPayload = [ { + ...core, application: { capabilities: { dashboard: { downloadCsv: true } } }, } as unknown as CoreStart, - {}, + { + data: dataPluginMock.createStartContract(), + } as ReportingPublicPluginStartDendencies, null, ]; + mockStartServices$ = Rx.from(Promise.resolve(mockStartServicesPayload)); mockSearchSource = { createCopy: () => mockSearchSource, @@ -93,7 +98,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -130,7 +135,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -153,7 +158,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -169,7 +174,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -187,7 +192,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); await panel.execute(context); @@ -204,14 +209,13 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); - + await mockStartServices$.pipe(first()).toPromise(); await licenseMock$.pipe(first()).toPromise(); expect(await plugin.isCompatible(context)).toEqual(false); }); - it('sets a display and icon type', () => { + it('sets a display and icon type', async () => { const panel = new ReportingCsvPanelAction({ core, apiClient, @@ -220,7 +224,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); expect(panel.getIconType()).toMatchInlineSnapshot(`"document"`); expect(panel.getDisplayName()).toMatchInlineSnapshot(`"Download CSV"`); @@ -228,25 +232,28 @@ describe('GetCsvReportPanelAction', () => { describe('Application UI Capabilities', () => { it(`doesn't allow downloads when UI capability is not enabled`, async () => { + mockStartServicesPayload = [ + { application: { capabilities: {} } } as unknown as CoreStart, + { + data: dataPluginMock.createStartContract(), + } as ReportingPublicPluginStartDendencies, + null, + ]; + const startServices$ = Rx.from(Promise.resolve(mockStartServicesPayload)); const plugin = new ReportingCsvPanelAction({ core, apiClient, license$: mockLicense$(), - startServices$: mockStartServices$, + startServices$, usesUiCapabilities: true, }); - mockStartServices$.next([ - { application: { capabilities: {} } } as unknown as CoreStart, - {}, - null, - ]); + await startServices$.pipe(first()).toPromise(); expect(await plugin.isCompatible(context)).toEqual(false); }); it(`allows downloads when license is valid and UI capability is enabled`, async () => { - mockStartServices$ = new Rx.Subject(); const plugin = new ReportingCsvPanelAction({ core, apiClient, @@ -255,7 +262,7 @@ describe('GetCsvReportPanelAction', () => { usesUiCapabilities: true, }); - mockStartServices$.next(mockStartServicesPayload); + await mockStartServices$.pipe(first()).toPromise(); expect(await plugin.isCompatible(context)).toEqual(true); }); diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index eb14e32160869..ef32e64741765 100644 --- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -7,7 +7,8 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; -import type { CoreSetup, IUiSettingsClient, NotificationsSetup } from 'src/core/public'; +import { first } from 'rxjs/operators'; +import type { CoreSetup, NotificationsSetup } from 'src/core/public'; import { CoreStart } from 'src/core/public'; import type { ISearchEmbeddable, SavedSearch } from '../../../../../src/plugins/discover/public'; import { @@ -22,6 +23,7 @@ import type { LicensingPluginSetup } from '../../../licensing/public'; import { CSV_REPORTING_ACTION } from '../../common/constants'; import { checkLicense } from '../lib/license_check'; import { ReportingAPIClient } from '../lib/reporting_api_client'; +import type { ReportingPublicPluginStartDendencies } from '../plugin'; function isSavedSearchEmbeddable( embeddable: IEmbeddable | ISearchEmbeddable @@ -36,7 +38,7 @@ export interface ActionContext { interface Params { apiClient: ReportingAPIClient; core: CoreSetup; - startServices$: Rx.Observable<[CoreStart, object, unknown]>; + startServices$: Rx.Observable<[CoreStart, ReportingPublicPluginStartDendencies, unknown]>; license$: LicensingPluginSetup['license$']; usesUiCapabilities: boolean; } @@ -47,16 +49,16 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext> public readonly id = CSV_REPORTING_ACTION; private licenseHasDownloadCsv: boolean = false; private capabilityHasDownloadCsv: boolean = false; - private uiSettings: IUiSettingsClient; private notifications: NotificationsSetup; private apiClient: ReportingAPIClient; + private startServices$: Params['startServices$']; constructor({ core, startServices$, license$, usesUiCapabilities, apiClient }: Params) { this.isDownloading = false; - this.uiSettings = core.uiSettings; this.notifications = core.notifications; this.apiClient = apiClient; + this.startServices$ = startServices$; license$.subscribe((license) => { const results = license.check('reporting', 'basic'); @@ -65,7 +67,7 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext> }); if (usesUiCapabilities) { - startServices$.subscribe(([{ application }]) => { + this.startServices$.subscribe(([{ application }]) => { this.capabilityHasDownloadCsv = application.capabilities.dashboard?.downloadCsv === true; }); } else { @@ -84,11 +86,12 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext> } public async getSearchSource(savedSearch: SavedSearch, embeddable: ISearchEmbeddable) { + const [{ uiSettings }, { data }] = await this.startServices$.pipe(first()).toPromise(); const { getSharingData } = await loadSharingDataHelpers(); return await getSharingData( savedSearch.searchSource, - savedSearch, // TODO: get unsaved state (using embeddale.searchScope): https://github.com/elastic/kibana/issues/43977 - this.uiSettings + savedSearch, // TODO: get unsaved state (using embeddable.searchScope): https://github.com/elastic/kibana/issues/43977 + { uiSettings, data } ); } diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts index 28226751975a9..7fd6047470a0e 100644 --- a/x-pack/plugins/reporting/public/plugin.ts +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; import { catchError, filter, map, mergeMap, takeUntil } from 'rxjs/operators'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; import { CoreSetup, CoreStart, @@ -77,6 +78,7 @@ export interface ReportingPublicPluginSetupDendencies { export interface ReportingPublicPluginStartDendencies { home: HomePublicPluginStart; + data: DataPublicPluginStart; management: ManagementStart; licensing: LicensingPluginStart; uiActions: UiActionsStart; @@ -134,7 +136,10 @@ export class ReportingPublicPlugin return this.contract; } - public setup(core: CoreSetup, setupDeps: ReportingPublicPluginSetupDendencies) { + public setup( + core: CoreSetup<ReportingPublicPluginStartDendencies>, + setupDeps: ReportingPublicPluginSetupDendencies + ) { const { getStartServices, uiSettings } = core; const { home, diff --git a/x-pack/test/functional/page_objects/search_sessions_management_page.ts b/x-pack/test/functional/page_objects/search_sessions_management_page.ts index 86391b568fdf2..15c87ea450425 100644 --- a/x-pack/test/functional/page_objects/search_sessions_management_page.ts +++ b/x-pack/test/functional/page_objects/search_sessions_management_page.ts @@ -38,6 +38,7 @@ export function SearchSessionsPageProvider({ getService, getPageObjects }: FtrPr mainUrl: $.findTestSubject('sessionManagementNameCol').text(), created: $.findTestSubject('sessionManagementCreatedCol').text(), expires: $.findTestSubject('sessionManagementExpiresCol').text(), + searchesCount: Number($.findTestSubject('sessionManagementNumSearchesCol').text()), app: $.findTestSubject('sessionManagementAppIcon').attr('data-test-app-id'), view: async () => { log.debug('management ui: view the session'); diff --git a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts index 3f02e64056325..cd13f71cf1bb7 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts @@ -26,6 +26,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const searchSessions = getService('searchSessions'); const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); + const toasts = getService('toasts'); describe('discover async search', () => { before(async () => { @@ -112,12 +113,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { // load URL to restore a saved session await PageObjects.searchSessionsManagement.goTo(); - const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + const searchSessionListBeforeRestore = await PageObjects.searchSessionsManagement.getList(); + const searchesCountBeforeRestore = searchSessionListBeforeRestore[0].searchesCount; // navigate to Discover - await searchSessionList[0].view(); + await searchSessionListBeforeRestore[0].view(); await PageObjects.header.waitUntilLoadingHasFinished(); await searchSessions.expectState('restored'); expect(await PageObjects.discover.hasNoResults()).to.be(true); + expect(await toasts.getToastCount()).to.be(0); // no session restoration related warnings + + await PageObjects.searchSessionsManagement.goTo(); + const searchSessionListAfterRestore = await PageObjects.searchSessionsManagement.getList(); + const searchesCountAfterRestore = searchSessionListAfterRestore[0].searchesCount; + + expect(searchesCountBeforeRestore).to.be(searchesCountAfterRestore); // no new searches started during restore }); }); From 8cf0efe2f1bfed3fba23573a73428852f4601c48 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" <christiane.heiligers@elastic.co> Date: Tue, 21 Sep 2021 13:10:53 -0700 Subject: [PATCH 41/69] Changes edit view to json read-only view (#112034) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/doc_links/doc_links_service.ts | 1 + .../server/saved_objects/dashboard.ts | 3 - .../discover/server/saved_objects/search.ts | 3 - .../management_section/mount_section.tsx | 3 +- .../saved_object_view.test.tsx.snap | 64 ++ .../__snapshots__/header.test.tsx.snap | 70 +- .../__snapshots__/inspect.test.tsx.snap | 35 + .../__snapshots__/intro.test.tsx.snap | 80 -- .../not_found_errors.test.tsx.snap | 748 +++++++++++------- .../object_view/components/field.test.tsx | 84 -- .../object_view/components/field.tsx | 145 ---- .../object_view/components/form.tsx | 174 ---- .../object_view/components/header.test.tsx | 32 +- .../object_view/components/header.tsx | 38 +- .../object_view/components/index.ts | 3 +- .../object_view/components/inspect.test.tsx | 69 ++ .../object_view/components/inspect.tsx | 79 ++ .../object_view/components/intro.test.tsx | 23 - .../object_view/components/intro.tsx | 33 - .../components/not_found_errors.test.tsx | 23 +- .../components/not_found_errors.tsx | 25 +- .../object_view/saved_object_view.scss | 3 + .../saved_object_view.test.mocks.ts | 25 + .../object_view/saved_object_view.test.tsx | 329 ++++++++ .../object_view/saved_object_view.tsx | 117 ++- .../saved_objects_table.test.tsx.snap | 3 - .../__snapshots__/table.test.tsx.snap | 2 - .../objects_table/components/flyout.test.tsx | 2 - .../objects_table/components/flyout.tsx | 2 - .../components/import_summary.tsx | 2 +- .../components/relationships.tsx | 2 +- .../objects_table/components/table.tsx | 1 - .../saved_objects_table.test.tsx | 5 - .../objects_table/saved_objects_table.tsx | 3 - .../saved_objects_edition_page.scss | 3 + .../saved_objects_edition_page.tsx | 26 +- .../saved_objects_table_page.tsx | 12 +- .../server/saved_objects/visualization.ts | 3 - .../apis/saved_objects_management/find.ts | 8 - .../saved_objects_management/relationships.ts | 28 +- .../edit_saved_object.ts | 182 ----- .../apps/saved_objects_management/index.ts | 2 +- .../inspect_saved_objects.ts | 87 ++ .../translations/translations/ja-JP.json | 14 - .../translations/translations/zh-CN.json | 14 - .../saved_objects_management_security.ts | 41 +- .../spaces_integration.ts | 23 +- 47 files changed, 1352 insertions(+), 1322 deletions(-) create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/inspect.test.tsx.snap delete mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap delete mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/field.test.tsx delete mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx delete mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.tsx delete mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/intro.test.tsx delete mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.scss create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.mocks.ts create mode 100644 src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx create mode 100644 src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.scss delete mode 100644 test/functional/apps/saved_objects_management/edit_saved_object.ts create mode 100644 test/functional/apps/saved_objects_management/inspect_saved_objects.ts diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index c86a856577c37..a9b19a6e84050 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -241,6 +241,7 @@ export class DocLinksService { kibanaSearchSettings: `${KIBANA_DOCS}advanced-options.html#kibana-search-settings`, visualizationSettings: `${KIBANA_DOCS}advanced-options.html#kibana-visualization-settings`, timelionSettings: `${KIBANA_DOCS}advanced-options.html#kibana-timelion-settings`, + savedObjectsApiList: `${KIBANA_DOCS}saved-objects-api.html#saved-objects-api`, }, ml: { guide: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/index.html`, diff --git a/src/plugins/dashboard/server/saved_objects/dashboard.ts b/src/plugins/dashboard/server/saved_objects/dashboard.ts index dfda251e28779..068883c429e61 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard.ts @@ -27,9 +27,6 @@ export const createDashboardSavedObjectType = ({ getTitle(obj) { return obj.attributes.title; }, - getEditUrl(obj) { - return `/management/kibana/objects/savedDashboards/${encodeURIComponent(obj.id)}`; - }, getInAppUrl(obj) { return { path: `/app/dashboards#/view/${encodeURIComponent(obj.id)}`, diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index 070f0253f17e0..46284f3cf33b6 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -20,9 +20,6 @@ export const searchSavedObjectType: SavedObjectsType = { getTitle(obj) { return obj.attributes.title; }, - getEditUrl(obj) { - return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; - }, getInAppUrl(obj) { return { path: `/app/discover#/view/${encodeURIComponent(obj.id)}`, diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index 43cd5c93aa807..aa7797fbc95b5 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -62,12 +62,11 @@ export const mountManagementSection = async ({ <I18nProvider> <Router history={history}> <Switch> - <Route path={'/:service/:id'} exact={true}> + <Route path={'/:type/:id'} exact={true}> <RedirectToHomeIfUnauthorized> <Suspense fallback={<EuiLoadingSpinner />}> <SavedObjectsEditionPage coreStart={coreStart} - serviceRegistry={serviceRegistry} setBreadcrumbs={setBreadcrumbs} history={history} /> diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap new file mode 100644 index 0000000000000..8e3a954677b87 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SavedObjectEdition should render normally 1`] = ` +<Provider + services={ + Object { + "uiSettings": Object { + "get": [MockFunction], + "get$": [MockFunction], + "getAll": [MockFunction], + "getUpdate$": [MockFunction], + "getUpdateErrors$": [MockFunction], + "isCustom": [MockFunction], + "isDeclared": [MockFunction], + "isDefault": [MockFunction], + "isOverridden": [MockFunction], + "remove": [MockFunction], + "set": [MockFunction], + }, + } + } +> + <EuiFlexGroup + className="savedObjectsManagementObjectView" + data-test-subject="savedObjectsEdit" + direction="column" + > + <EuiFlexItem + grow={false} + > + <Header + canDelete={false} + canViewInApp={true} + onDeleteClick={[Function]} + title="MyDashboard*" + viewUrl="/app/dashboards#/view/1" + /> + </EuiFlexItem> + <EuiFlexItem + grow={true} + > + <Inspect + object={ + Object { + "attributes": Object { + "title": "MyDashboard*", + }, + "id": "1", + "meta": Object { + "icon": "dashboardApp", + "inAppUrl": Object { + "path": "/app/dashboards#/view/1", + "uiCapabilitiesPath": "management.kibana.dashboard", + }, + "title": "MyDashboard*", + }, + "type": "dashboard", + } + } + /> + </EuiFlexItem> + </EuiFlexGroup> +</Provider> +`; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap index c72e000e95e75..26ff23b50d87b 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap @@ -11,17 +11,7 @@ exports[`Intro component renders correctly 1`] = ` > <EuiPageHeader bottomBorder={true} - pageTitle={ - <FormattedMessage - defaultMessage="Edit {title}" - id="savedObjectsManagement.view.editItemTitle" - values={ - Object { - "title": "search", - } - } - /> - } + pageTitle="Inspect saved object" rightSideItems={ Array [ <EuiButton @@ -35,7 +25,7 @@ exports[`Intro component renders correctly 1`] = ` id="savedObjectsManagement.view.viewItemButtonLabel" values={ Object { - "title": "search", + "title": "saved object", } } /> @@ -48,13 +38,9 @@ exports[`Intro component renders correctly 1`] = ` size="s" > <FormattedMessage - defaultMessage="Delete {title}" + defaultMessage="Delete" id="savedObjectsManagement.view.deleteItemButtonLabel" - values={ - Object { - "title": "search", - } - } + values={Object {}} /> </EuiButton>, ] @@ -64,17 +50,7 @@ exports[`Intro component renders correctly 1`] = ` className="euiPageHeader euiPageHeader--bottomBorder euiPageHeader--responsive euiPageHeader--center" > <EuiPageHeaderContent - pageTitle={ - <FormattedMessage - defaultMessage="Edit {title}" - id="savedObjectsManagement.view.editItemTitle" - values={ - Object { - "title": "search", - } - } - /> - } + pageTitle="Inspect saved object" responsive={true} rightSideItems={ Array [ @@ -89,7 +65,7 @@ exports[`Intro component renders correctly 1`] = ` id="savedObjectsManagement.view.viewItemButtonLabel" values={ Object { - "title": "search", + "title": "saved object", } } /> @@ -102,13 +78,9 @@ exports[`Intro component renders correctly 1`] = ` size="s" > <FormattedMessage - defaultMessage="Delete {title}" + defaultMessage="Delete" id="savedObjectsManagement.view.deleteItemButtonLabel" - values={ - Object { - "title": "search", - } - } + values={Object {}} /> </EuiButton>, ] @@ -136,17 +108,7 @@ exports[`Intro component renders correctly 1`] = ` <h1 className="euiTitle euiTitle--large" > - <FormattedMessage - defaultMessage="Edit {title}" - id="savedObjectsManagement.view.editItemTitle" - values={ - Object { - "title": "search", - } - } - > - Edit search - </FormattedMessage> + Inspect saved object </h1> </EuiTitle> </div> @@ -234,11 +196,11 @@ exports[`Intro component renders correctly 1`] = ` id="savedObjectsManagement.view.viewItemButtonLabel" values={ Object { - "title": "search", + "title": "saved object", } } > - View search + View saved object </FormattedMessage> </span> </span> @@ -316,15 +278,11 @@ exports[`Intro component renders correctly 1`] = ` className="euiButton__text" > <FormattedMessage - defaultMessage="Delete {title}" + defaultMessage="Delete" id="savedObjectsManagement.view.deleteItemButtonLabel" - values={ - Object { - "title": "search", - } - } + values={Object {}} > - Delete search + Delete </FormattedMessage> </span> </span> diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/inspect.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/inspect.test.tsx.snap new file mode 100644 index 0000000000000..f35030ad736cc --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/inspect.test.tsx.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Inspect component renders correctly 1`] = ` +<CodeEditor + aria-label="inspect MyIndexPattern*" + languageId="xjson" + options={ + Object { + "automaticLayout": false, + "fontSize": 12, + "lineNumbers": "on", + "minimap": Object { + "enabled": false, + }, + "overviewRulerBorder": false, + "readOnly": true, + "renderIndentGuides": false, + "scrollBeyondLastLine": false, + "scrollbar": Object { + "alwaysConsumeMouseWheel": false, + }, + "wordWrap": "on", + "wrappingIndent": "indent", + } + } + value="{ + \\"id\\": \\"1\\", + \\"type\\": \\"index-pattern\\", + \\"attributes\\": { + \\"title\\": \\"MyIndexPattern*\\" + }, + \\"references\\": [] +}" +/> +`; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap deleted file mode 100644 index 9c9349b0524c0..0000000000000 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap +++ /dev/null @@ -1,80 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Intro component renders correctly 1`] = ` -<Intro> - <EuiCallOut - color="warning" - iconType="alert" - title={ - <FormattedMessage - defaultMessage="Proceed with caution!" - id="savedObjectsManagement.view.howToModifyObjectTitle" - values={Object {}} - /> - } - > - <div - className="euiCallOut euiCallOut--warning" - > - <div - className="euiCallOutHeader" - > - <EuiIcon - aria-hidden="true" - className="euiCallOutHeader__icon" - color="inherit" - size="m" - type="alert" - > - <span - aria-hidden="true" - className="euiCallOutHeader__icon" - color="inherit" - data-euiicon-type="alert" - size="m" - /> - </EuiIcon> - <span - className="euiCallOutHeader__title" - > - <FormattedMessage - defaultMessage="Proceed with caution!" - id="savedObjectsManagement.view.howToModifyObjectTitle" - values={Object {}} - > - Proceed with caution! - </FormattedMessage> - </span> - </div> - <EuiText - color="default" - size="s" - > - <div - className="euiText euiText--small" - > - <EuiTextColor - color="default" - component="div" - > - <div - className="euiTextColor euiTextColor--default" - style={Object {}} - > - <div> - <FormattedMessage - defaultMessage="Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn’t be." - id="savedObjectsManagement.view.howToModifyObjectDescription" - values={Object {}} - > - Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn’t be. - </FormattedMessage> - </div> - </div> - </EuiTextColor> - </div> - </EuiText> - </div> - </EuiCallOut> -</Intro> -`; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap index 4227351f8e94d..c55583679f264 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap @@ -1,353 +1,541 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`NotFoundErrors component renders correctly for index-pattern type 1`] = ` -<NotFoundErrors - type="index-pattern" +<EuiCallOut + color="danger" + iconType="alert" + title={ + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} + /> + } > - <EuiCallOut - color="danger" - iconType="alert" - title={ - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - /> - } + <div + className="euiCallOut euiCallOut--danger" > <div - className="euiCallOut euiCallOut--danger" + className="euiCallOutHeader" > - <div - className="euiCallOutHeader" + <EuiIcon + aria-hidden="true" + className="euiCallOutHeader__icon" + color="inherit" + size="m" + type="alert" > - <EuiIcon + <span aria-hidden="true" className="euiCallOutHeader__icon" color="inherit" + data-euiicon-type="alert" size="m" - type="alert" - > - <span - aria-hidden="true" - className="euiCallOutHeader__icon" - color="inherit" - data-euiicon-type="alert" - size="m" - /> - </EuiIcon> - <span - className="euiCallOutHeader__title" + /> + </EuiIcon> + <span + className="euiCallOutHeader__title" + > + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} > - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - > - There is a problem with this saved object - </FormattedMessage> - </span> - </div> - <EuiText - color="default" - size="s" + There is a problem with this saved object + </FormattedMessage> + </span> + </div> + <EuiText + color="default" + size="s" + > + <div + className="euiText euiText--small" > - <div - className="euiText euiText--small" + <EuiTextColor + color="default" + component="div" > - <EuiTextColor - color="default" - component="div" + <div + className="euiTextColor euiTextColor--default" + style={Object {}} > - <div - className="euiTextColor euiTextColor--default" - style={Object {}} - > - <div> - <FormattedMessage - defaultMessage="The index pattern associated with this object no longer exists." - id="savedObjectsManagement.view.indexPatternDoesNotExistErrorMessage" - values={Object {}} - > - The index pattern associated with this object no longer exists. - </FormattedMessage> - </div> - <div> - <FormattedMessage - defaultMessage="If you know what this error means, go ahead and fix it — otherwise click the delete button above." - id="savedObjectsManagement.view.howToFixErrorDescription" - values={Object {}} + <div> + <FormattedMessage + defaultMessage="The index pattern associated with this object no longer exists." + id="savedObjectsManagement.view.indexPatternDoesNotExistErrorMessage" + values={Object {}} + > + The index pattern associated with this object no longer exists. + </FormattedMessage> + </div> + <div> + <FormattedMessage + defaultMessage="If you know what this error means, you can use the {savedObjectsApis} to fix it — otherwise click the delete button above." + id="savedObjectsManagement.view.howToFixErrorDescription" + values={ + Object { + "savedObjectsApis": <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" + > + Saved objects APIs + </EuiLink>, + } + } + > + If you know what this error means, you can use the + <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" > - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - </FormattedMessage> - </div> + <a + aria-label="Saved objects APIs" + className="euiLink euiLink--primary" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + rel="noopener" + target="_blank" + > + Saved objects APIs + <EuiIcon + aria-label="External link" + className="euiLink__externalIcon" + size="s" + type="popout" + > + <span + aria-label="External link" + className="euiLink__externalIcon" + data-euiicon-type="popout" + size="s" + /> + </EuiIcon> + <EuiScreenReaderOnly> + <span + className="euiScreenReaderOnly" + > + <EuiI18n + default="(opens in a new tab or window)" + token="euiLink.newTarget.screenReaderOnlyText" + > + (opens in a new tab or window) + </EuiI18n> + </span> + </EuiScreenReaderOnly> + </a> + </EuiLink> + to fix it — otherwise click the delete button above. + </FormattedMessage> </div> - </EuiTextColor> - </div> - </EuiText> - </div> - </EuiCallOut> -</NotFoundErrors> + </div> + </EuiTextColor> + </div> + </EuiText> + </div> +</EuiCallOut> `; exports[`NotFoundErrors component renders correctly for index-pattern-field type 1`] = ` -<NotFoundErrors - type="index-pattern-field" +<EuiCallOut + color="danger" + iconType="alert" + title={ + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} + /> + } > - <EuiCallOut - color="danger" - iconType="alert" - title={ - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - /> - } + <div + className="euiCallOut euiCallOut--danger" > <div - className="euiCallOut euiCallOut--danger" + className="euiCallOutHeader" > - <div - className="euiCallOutHeader" + <EuiIcon + aria-hidden="true" + className="euiCallOutHeader__icon" + color="inherit" + size="m" + type="alert" > - <EuiIcon + <span aria-hidden="true" className="euiCallOutHeader__icon" color="inherit" + data-euiicon-type="alert" size="m" - type="alert" - > - <span - aria-hidden="true" - className="euiCallOutHeader__icon" - color="inherit" - data-euiicon-type="alert" - size="m" - /> - </EuiIcon> - <span - className="euiCallOutHeader__title" + /> + </EuiIcon> + <span + className="euiCallOutHeader__title" + > + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} > - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - > - There is a problem with this saved object - </FormattedMessage> - </span> - </div> - <EuiText - color="default" - size="s" + There is a problem with this saved object + </FormattedMessage> + </span> + </div> + <EuiText + color="default" + size="s" + > + <div + className="euiText euiText--small" > - <div - className="euiText euiText--small" + <EuiTextColor + color="default" + component="div" > - <EuiTextColor - color="default" - component="div" + <div + className="euiTextColor euiTextColor--default" + style={Object {}} > - <div - className="euiTextColor euiTextColor--default" - style={Object {}} - > - <div> - <FormattedMessage - defaultMessage="A field associated with this object no longer exists in the index pattern." - id="savedObjectsManagement.view.fieldDoesNotExistErrorMessage" - values={Object {}} - > - A field associated with this object no longer exists in the index pattern. - </FormattedMessage> - </div> - <div> - <FormattedMessage - defaultMessage="If you know what this error means, go ahead and fix it — otherwise click the delete button above." - id="savedObjectsManagement.view.howToFixErrorDescription" - values={Object {}} + <div> + <FormattedMessage + defaultMessage="A field associated with this object no longer exists in the index pattern." + id="savedObjectsManagement.view.fieldDoesNotExistErrorMessage" + values={Object {}} + > + A field associated with this object no longer exists in the index pattern. + </FormattedMessage> + </div> + <div> + <FormattedMessage + defaultMessage="If you know what this error means, you can use the {savedObjectsApis} to fix it — otherwise click the delete button above." + id="savedObjectsManagement.view.howToFixErrorDescription" + values={ + Object { + "savedObjectsApis": <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" + > + Saved objects APIs + </EuiLink>, + } + } + > + If you know what this error means, you can use the + <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" > - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - </FormattedMessage> - </div> + <a + aria-label="Saved objects APIs" + className="euiLink euiLink--primary" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + rel="noopener" + target="_blank" + > + Saved objects APIs + <EuiIcon + aria-label="External link" + className="euiLink__externalIcon" + size="s" + type="popout" + > + <span + aria-label="External link" + className="euiLink__externalIcon" + data-euiicon-type="popout" + size="s" + /> + </EuiIcon> + <EuiScreenReaderOnly> + <span + className="euiScreenReaderOnly" + > + <EuiI18n + default="(opens in a new tab or window)" + token="euiLink.newTarget.screenReaderOnlyText" + > + (opens in a new tab or window) + </EuiI18n> + </span> + </EuiScreenReaderOnly> + </a> + </EuiLink> + to fix it — otherwise click the delete button above. + </FormattedMessage> </div> - </EuiTextColor> - </div> - </EuiText> - </div> - </EuiCallOut> -</NotFoundErrors> + </div> + </EuiTextColor> + </div> + </EuiText> + </div> +</EuiCallOut> `; exports[`NotFoundErrors component renders correctly for search type 1`] = ` -<NotFoundErrors - type="search" +<EuiCallOut + color="danger" + iconType="alert" + title={ + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} + /> + } > - <EuiCallOut - color="danger" - iconType="alert" - title={ - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - /> - } + <div + className="euiCallOut euiCallOut--danger" > <div - className="euiCallOut euiCallOut--danger" + className="euiCallOutHeader" > - <div - className="euiCallOutHeader" + <EuiIcon + aria-hidden="true" + className="euiCallOutHeader__icon" + color="inherit" + size="m" + type="alert" > - <EuiIcon + <span aria-hidden="true" className="euiCallOutHeader__icon" color="inherit" + data-euiicon-type="alert" size="m" - type="alert" - > - <span - aria-hidden="true" - className="euiCallOutHeader__icon" - color="inherit" - data-euiicon-type="alert" - size="m" - /> - </EuiIcon> - <span - className="euiCallOutHeader__title" + /> + </EuiIcon> + <span + className="euiCallOutHeader__title" + > + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} > - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - > - There is a problem with this saved object - </FormattedMessage> - </span> - </div> - <EuiText - color="default" - size="s" + There is a problem with this saved object + </FormattedMessage> + </span> + </div> + <EuiText + color="default" + size="s" + > + <div + className="euiText euiText--small" > - <div - className="euiText euiText--small" + <EuiTextColor + color="default" + component="div" > - <EuiTextColor - color="default" - component="div" + <div + className="euiTextColor euiTextColor--default" + style={Object {}} > - <div - className="euiTextColor euiTextColor--default" - style={Object {}} - > - <div> - <FormattedMessage - defaultMessage="The saved search associated with this object no longer exists." - id="savedObjectsManagement.view.savedSearchDoesNotExistErrorMessage" - values={Object {}} - > - The saved search associated with this object no longer exists. - </FormattedMessage> - </div> - <div> - <FormattedMessage - defaultMessage="If you know what this error means, go ahead and fix it — otherwise click the delete button above." - id="savedObjectsManagement.view.howToFixErrorDescription" - values={Object {}} + <div> + <FormattedMessage + defaultMessage="The saved search associated with this object no longer exists." + id="savedObjectsManagement.view.savedSearchDoesNotExistErrorMessage" + values={Object {}} + > + The saved search associated with this object no longer exists. + </FormattedMessage> + </div> + <div> + <FormattedMessage + defaultMessage="If you know what this error means, you can use the {savedObjectsApis} to fix it — otherwise click the delete button above." + id="savedObjectsManagement.view.howToFixErrorDescription" + values={ + Object { + "savedObjectsApis": <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" + > + Saved objects APIs + </EuiLink>, + } + } + > + If you know what this error means, you can use the + <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" > - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - </FormattedMessage> - </div> + <a + aria-label="Saved objects APIs" + className="euiLink euiLink--primary" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + rel="noopener" + target="_blank" + > + Saved objects APIs + <EuiIcon + aria-label="External link" + className="euiLink__externalIcon" + size="s" + type="popout" + > + <span + aria-label="External link" + className="euiLink__externalIcon" + data-euiicon-type="popout" + size="s" + /> + </EuiIcon> + <EuiScreenReaderOnly> + <span + className="euiScreenReaderOnly" + > + <EuiI18n + default="(opens in a new tab or window)" + token="euiLink.newTarget.screenReaderOnlyText" + > + (opens in a new tab or window) + </EuiI18n> + </span> + </EuiScreenReaderOnly> + </a> + </EuiLink> + to fix it — otherwise click the delete button above. + </FormattedMessage> </div> - </EuiTextColor> - </div> - </EuiText> - </div> - </EuiCallOut> -</NotFoundErrors> + </div> + </EuiTextColor> + </div> + </EuiText> + </div> +</EuiCallOut> `; exports[`NotFoundErrors component renders correctly for unknown type 1`] = ` -<NotFoundErrors - type="unknown" +<EuiCallOut + color="danger" + iconType="alert" + title={ + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} + /> + } > - <EuiCallOut - color="danger" - iconType="alert" - title={ - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - /> - } + <div + className="euiCallOut euiCallOut--danger" > <div - className="euiCallOut euiCallOut--danger" + className="euiCallOutHeader" > - <div - className="euiCallOutHeader" + <EuiIcon + aria-hidden="true" + className="euiCallOutHeader__icon" + color="inherit" + size="m" + type="alert" > - <EuiIcon + <span aria-hidden="true" className="euiCallOutHeader__icon" color="inherit" + data-euiicon-type="alert" size="m" - type="alert" - > - <span - aria-hidden="true" - className="euiCallOutHeader__icon" - color="inherit" - data-euiicon-type="alert" - size="m" - /> - </EuiIcon> - <span - className="euiCallOutHeader__title" + /> + </EuiIcon> + <span + className="euiCallOutHeader__title" + > + <FormattedMessage + defaultMessage="There is a problem with this saved object" + id="savedObjectsManagement.view.savedObjectProblemErrorMessage" + values={Object {}} > - <FormattedMessage - defaultMessage="There is a problem with this saved object" - id="savedObjectsManagement.view.savedObjectProblemErrorMessage" - values={Object {}} - > - There is a problem with this saved object - </FormattedMessage> - </span> - </div> - <EuiText - color="default" - size="s" + There is a problem with this saved object + </FormattedMessage> + </span> + </div> + <EuiText + color="default" + size="s" + > + <div + className="euiText euiText--small" > - <div - className="euiText euiText--small" + <EuiTextColor + color="default" + component="div" > - <EuiTextColor - color="default" - component="div" + <div + className="euiTextColor euiTextColor--default" + style={Object {}} > - <div - className="euiTextColor euiTextColor--default" - style={Object {}} - > - <div /> - <div> - <FormattedMessage - defaultMessage="If you know what this error means, go ahead and fix it — otherwise click the delete button above." - id="savedObjectsManagement.view.howToFixErrorDescription" - values={Object {}} + <div /> + <div> + <FormattedMessage + defaultMessage="If you know what this error means, you can use the {savedObjectsApis} to fix it — otherwise click the delete button above." + id="savedObjectsManagement.view.howToFixErrorDescription" + values={ + Object { + "savedObjectsApis": <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" + > + Saved objects APIs + </EuiLink>, + } + } + > + If you know what this error means, you can use the + <EuiLink + aria-label="Saved objects APIs" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + target="_blank" > - If you know what this error means, go ahead and fix it — otherwise click the delete button above. - </FormattedMessage> - </div> + <a + aria-label="Saved objects APIs" + className="euiLink euiLink--primary" + href="https://www.elastic.co/guide/en/kibana/mocked-test-branch/saved-objects-api.html#saved-objects-api" + rel="noopener" + target="_blank" + > + Saved objects APIs + <EuiIcon + aria-label="External link" + className="euiLink__externalIcon" + size="s" + type="popout" + > + <span + aria-label="External link" + className="euiLink__externalIcon" + data-euiicon-type="popout" + size="s" + /> + </EuiIcon> + <EuiScreenReaderOnly> + <span + className="euiScreenReaderOnly" + > + <EuiI18n + default="(opens in a new tab or window)" + token="euiLink.newTarget.screenReaderOnlyText" + > + (opens in a new tab or window) + </EuiI18n> + </span> + </EuiScreenReaderOnly> + </a> + </EuiLink> + to fix it — otherwise click the delete button above. + </FormattedMessage> </div> - </EuiTextColor> - </div> - </EuiText> - </div> - </EuiCallOut> -</NotFoundErrors> + </div> + </EuiTextColor> + </div> + </EuiText> + </div> +</EuiCallOut> `; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.test.tsx deleted file mode 100644 index 1fd70c65fd9c4..0000000000000 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.test.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { I18nProvider } from '@kbn/i18n/react'; -import { Field } from './field'; -import { FieldState, FieldType } from '../../types'; - -describe('Field component', () => { - const mountField = (props: { - type: FieldType; - name: string; - value: any; - disabled: boolean; - state?: FieldState; - onChange: (name: string, state: FieldState) => void; - }) => - mount( - <I18nProvider> - <Field {...props} /> - </I18nProvider> - ).find('Field'); - - const defaultProps = { - type: 'text' as FieldType, - name: 'field', - value: '', - disabled: false, - state: undefined, - onChange: (name: string, state: FieldState) => undefined, - }; - - it('uses the field name as the label', () => { - let mounted = mountField({ ...defaultProps, name: 'some.name' }); - expect(mounted.find('EuiFormLabel').text()).toMatchInlineSnapshot(`"some.name"`); - - mounted = mountField({ ...defaultProps, name: 'someother.name' }); - expect(mounted.find('EuiFormLabel').text()).toMatchInlineSnapshot(`"someother.name"`); - }); - - it('renders a EuiCodeEditor for json type', () => { - const mounted = mountField({ ...defaultProps, type: 'json' }); - expect(mounted.exists('EuiCodeEditor')).toEqual(true); - }); - - it('renders a EuiCodeEditor for array type', () => { - const mounted = mountField({ ...defaultProps, type: 'array' }); - expect(mounted.exists('EuiCodeEditor')).toEqual(true); - }); - - it('renders a EuiSwitch for boolean type', () => { - const mounted = mountField({ ...defaultProps, type: 'boolean' }); - expect(mounted.exists('EuiSwitch')).toEqual(true); - }); - - it('display correct label for boolean type depending on value', () => { - let mounted = mountField({ ...defaultProps, type: 'boolean', value: true }); - expect(mounted.find('EuiSwitch').text()).toMatchInlineSnapshot(`"On"`); - - mounted = mountField({ ...defaultProps, type: 'boolean', value: false }); - expect(mounted.find('EuiSwitch').text()).toMatchInlineSnapshot(`"Off"`); - }); - - it('renders a EuiFieldNumber for number type', () => { - const mounted = mountField({ ...defaultProps, type: 'number' }); - expect(mounted.exists('EuiFieldNumber')).toEqual(true); - }); - - it('renders a EuiFieldText for text type', () => { - const mounted = mountField({ ...defaultProps, type: 'text' }); - expect(mounted.exists('EuiFieldText')).toEqual(true); - }); - - it('renders a EuiFieldText as fallback', () => { - const mounted = mountField({ ...defaultProps, type: 'unknown-type' as any }); - expect(mounted.exists('EuiFieldText')).toEqual(true); - }); -}); diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx deleted file mode 100644 index 2273527dd63f1..0000000000000 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/field.tsx +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { PureComponent } from 'react'; -import { EuiFieldNumber, EuiFieldText, EuiFormRow, EuiSwitch, EuiCodeEditor } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { FieldState, FieldType } from '../../types'; - -interface FieldProps { - type: FieldType; - name: string; - value: any; - disabled: boolean; - state?: FieldState; - onChange: (name: string, state: FieldState) => void; -} - -export class Field extends PureComponent<FieldProps> { - render() { - const { name } = this.props; - - return ( - <EuiFormRow fullWidth={true} label={name}> - {this.renderField()} - </EuiFormRow> - ); - } - - onCodeEditorChange(targetValue: any) { - const { name, onChange } = this.props; - let invalid = false; - try { - JSON.parse(targetValue); - } catch (e) { - invalid = true; - } - onChange(name, { - value: targetValue, - invalid, - }); - } - - onFieldChange(targetValue: any) { - const { name, type, onChange } = this.props; - - let newParsedValue = targetValue; - let invalid = false; - if (type === 'number') { - try { - newParsedValue = Number(newParsedValue); - } catch (e) { - invalid = true; - } - } - onChange(name, { - value: newParsedValue, - invalid, - }); - } - - renderField() { - const { type, name, state, disabled } = this.props; - const currentValue = state?.value ?? this.props.value; - - switch (type) { - case 'number': - return ( - <EuiFieldNumber - name={name} - id={this.fieldId} - value={currentValue} - onChange={(e) => this.onFieldChange(e.target.value)} - disabled={disabled} - data-test-subj={`savedObjects-editField-${name}`} - /> - ); - case 'boolean': - return ( - <EuiSwitch - name={name} - id={this.fieldId} - label={ - !!currentValue ? ( - <FormattedMessage id="savedObjectsManagement.field.onLabel" defaultMessage="On" /> - ) : ( - <FormattedMessage id="savedObjectsManagement.field.offLabel" defaultMessage="Off" /> - ) - } - checked={!!currentValue} - onChange={(e) => this.onFieldChange(e.target.checked)} - disabled={disabled} - data-test-subj={`savedObjects-editField-${name}`} - /> - ); - case 'json': - case 'array': - return ( - <div data-test-subj={`savedObjects-editField-${name}`}> - <EuiCodeEditor - name={`savedObjects-editField-${name}-aceEditor`} - mode="json" - theme="textmate" - value={currentValue} - onChange={(value: any) => this.onCodeEditorChange(value)} - width="100%" - height="auto" - minLines={6} - maxLines={30} - isReadOnly={disabled} - setOptions={{ - showLineNumbers: true, - tabSize: 2, - useSoftTabs: true, - }} - editorProps={{ - $blockScrolling: Infinity, - }} - showGutter={true} - /> - </div> - ); - default: - return ( - <EuiFieldText - id={this.fieldId} - name={name} - value={currentValue} - onChange={(e) => this.onFieldChange(e.target.value)} - disabled={disabled} - data-test-subj={`savedObjects-editField-${name}`} - /> - ); - } - } - - private get fieldId() { - const { name } = this.props; - return `savedObjects-editField-${name}`; - } -} diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx deleted file mode 100644 index 8e33e0fbdc5e2..0000000000000 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { Component } from 'react'; -import { - EuiForm, - EuiFlexGroup, - EuiFlexItem, - EuiButton, - EuiButtonEmpty, - EuiSpacer, -} from '@elastic/eui'; -import { set } from '@elastic/safer-lodash-set'; -import { cloneDeep } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { SavedObjectsClientContract } from '../../../../../../core/public'; -import { SavedObjectLoader } from '../../../../../saved_objects/public'; -import { Field } from './field'; -import { ObjectField, FieldState, SubmittedFormData } from '../../types'; -import { createFieldList } from '../../../lib'; -import { SavedObjectWithMetadata } from '../../../types'; - -interface FormProps { - object: SavedObjectWithMetadata; - service: SavedObjectLoader; - savedObjectsClient: SavedObjectsClientContract; - editionEnabled: boolean; - onSave: (form: SubmittedFormData) => Promise<void>; -} - -interface FormState { - fields: ObjectField[]; - fieldStates: Record<string, FieldState>; - submitting: boolean; -} - -export class Form extends Component<FormProps, FormState> { - constructor(props: FormProps) { - super(props); - this.state = { - fields: [], - fieldStates: {}, - submitting: false, - }; - } - - componentDidMount() { - const { object, service } = this.props; - - const fields = createFieldList(object, service); - - this.setState({ - fields, - }); - } - - render() { - const { editionEnabled, service } = this.props; - const { fields, fieldStates, submitting } = this.state; - const isValid = this.isFormValid(); - return ( - <EuiForm data-test-subj="savedObjectEditForm" role="form"> - {fields.map((field) => ( - <Field - key={`${field.type}-${field.name}`} - type={field.type} - name={field.name} - value={field.value} - state={fieldStates[field.name]} - disabled={!editionEnabled} - onChange={this.handleFieldChange} - /> - ))} - <EuiSpacer size={'l'} /> - <EuiFlexGroup responsive={false} gutterSize={'m'}> - {editionEnabled && ( - <EuiFlexItem grow={false}> - <EuiButton - fill={true} - aria-label={i18n.translate('savedObjectsManagement.view.saveButtonAriaLabel', { - defaultMessage: 'Save { title } object', - values: { - title: service.type, - }, - })} - onClick={this.onSubmit} - disabled={!isValid || submitting} - data-test-subj="savedObjectEditSave" - > - <FormattedMessage - id="savedObjectsManagement.view.saveButtonLabel" - defaultMessage="Save { title } object" - values={{ title: service.type }} - /> - </EuiButton> - </EuiFlexItem> - )} - - <EuiFlexItem grow={false}> - <EuiButtonEmpty - aria-label={i18n.translate('savedObjectsManagement.view.cancelButtonAriaLabel', { - defaultMessage: 'Cancel', - })} - onClick={this.onCancel} - data-test-subj="savedObjectEditCancel" - > - <FormattedMessage - id="savedObjectsManagement.view.cancelButtonLabel" - defaultMessage="Cancel" - /> - </EuiButtonEmpty> - </EuiFlexItem> - </EuiFlexGroup> - </EuiForm> - ); - } - - handleFieldChange = (name: string, newState: FieldState) => { - this.setState({ - fieldStates: { - ...this.state.fieldStates, - [name]: newState, - }, - }); - }; - - isFormValid() { - const { fieldStates } = this.state; - return !Object.values(fieldStates).some((state) => state.invalid === true); - } - - onCancel = () => { - window.history.back(); - }; - - onSubmit = async () => { - const { object, onSave } = this.props; - const { fields, fieldStates } = this.state; - - if (!this.isFormValid()) { - return; - } - - this.setState({ - submitting: true, - }); - - const source = cloneDeep(object.attributes as any); - fields.forEach((field) => { - let value = fieldStates[field.name]?.value ?? field.value; - - if (field.type === 'array' && typeof value === 'string') { - value = JSON.parse(value); - } - - set(source, field.name, value); - }); - - // we extract the `references` field that does not belong to attributes - const { references, ...attributes } = source; - - await onSave({ attributes, references }); - - this.setState({ - submitting: false, - }); - }; -} diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/header.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.test.tsx index dbbd2485096f9..796632f9747a9 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/header.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.test.tsx @@ -19,6 +19,7 @@ describe('Intro component', () => { type: string; viewUrl: string; onDeleteClick: () => void; + title?: string; }) => mount( <I18nProvider> @@ -42,32 +43,11 @@ describe('Intro component', () => { expect(mounted).toMatchSnapshot(); }); - it('displays correct title depending on canEdit', () => { - let mounted = mountHeader({ - ...defaultProps, - canEdit: true, - }); - expect(mounted.find('h1').text()).toMatchInlineSnapshot(`"Edit search"`); - - mounted = mountHeader({ - ...defaultProps, - canEdit: false, - }); - expect(mounted.find('h1').text()).toMatchInlineSnapshot(`"View search"`); - }); - - it('displays correct title depending on type', () => { - let mounted = mountHeader({ - ...defaultProps, - type: 'some-type', - }); - expect(mounted.find('h1').text()).toMatchInlineSnapshot(`"Edit some-type"`); - - mounted = mountHeader({ - ...defaultProps, - type: 'another-type', - }); - expect(mounted.find('h1').text()).toMatchInlineSnapshot(`"Edit another-type"`); + it('displays correct title if one is provided', () => { + let mounted = mountHeader({ ...defaultProps, title: 'my saved search' }); + expect(mounted.find('h1').text()).toMatchInlineSnapshot(`"Inspect my saved search"`); + mounted = mountHeader({ ...defaultProps, title: 'my other saved search' }); + expect(mounted.find('h1').text()).toMatchInlineSnapshot(`"Inspect my other saved search"`); }); it('only displays delete button if canDelete is true', () => { diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx index 9a13a1d232cb3..10374b839ca48 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/header.tsx @@ -9,43 +9,24 @@ import React from 'react'; import { EuiButton, EuiPageHeader } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; interface HeaderProps { - canEdit: boolean; canDelete: boolean; canViewInApp: boolean; - type: string; viewUrl: string; onDeleteClick: () => void; + title?: string; } -const renderConditionalTitle = (canEdit: boolean, type: string) => - canEdit ? ( - <FormattedMessage - id="savedObjectsManagement.view.editItemTitle" - defaultMessage="Edit {title}" - values={{ title: type }} - /> - ) : ( - <FormattedMessage - id="savedObjectsManagement.view.viewItemTitle" - defaultMessage="View {title}" - values={{ title: type }} - /> - ); - -export const Header = ({ - canEdit, - canDelete, - canViewInApp, - type, - viewUrl, - onDeleteClick, -}: HeaderProps) => { +export const Header = ({ canDelete, canViewInApp, viewUrl, onDeleteClick, title }: HeaderProps) => { return ( <EuiPageHeader bottomBorder - pageTitle={renderConditionalTitle(canEdit, type)} + pageTitle={i18n.translate('savedObjectsManagement.view.inspectItemTitle', { + defaultMessage: 'Inspect {title}', + values: { title: title || 'saved object' }, + })} rightSideItems={[ canViewInApp && ( <EuiButton @@ -57,7 +38,7 @@ export const Header = ({ <FormattedMessage id="savedObjectsManagement.view.viewItemButtonLabel" defaultMessage="View {title}" - values={{ title: type }} + values={{ title: title || 'saved object' }} /> </EuiButton> ), @@ -71,8 +52,7 @@ export const Header = ({ > <FormattedMessage id="savedObjectsManagement.view.deleteItemButtonLabel" - defaultMessage="Delete {title}" - values={{ title: type }} + defaultMessage="Delete" /> </EuiButton> ), diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/index.ts b/src/plugins/saved_objects_management/public/management_section/object_view/components/index.ts index ffffd589d5ef8..55322afb3fabb 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/index.ts +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/index.ts @@ -7,6 +7,5 @@ */ export { Header } from './header'; +export { Inspect } from './inspect'; export { NotFoundErrors } from './not_found_errors'; -export { Intro } from './intro'; -export { Form } from './form'; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx new file mode 100644 index 0000000000000..433728baf6fc1 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { ShallowWrapper } from 'enzyme'; +import { shallowWithI18nProvider } from '@kbn/test/jest'; +import { Inspect, InspectProps } from './inspect'; +import { SavedObjectWithMetadata } from '../../../../common'; + +describe('Inspect component', () => { + let defaultProps: { object: SavedObjectWithMetadata }; + const shallowRender = (overrides: Partial<SavedObjectWithMetadata> = {}) => { + return shallowWithI18nProvider( + <Inspect {...defaultProps} {...overrides} /> + ) as unknown as ShallowWrapper<InspectProps>; + }; + beforeEach(() => { + defaultProps = { + object: { + id: '1', + type: 'index-pattern', + attributes: { + title: `MyIndexPattern*`, + }, + meta: { + title: `MyIndexPattern*`, + icon: 'indexPatternApp', + editUrl: '#/management/kibana/indexPatterns/patterns/1', + inAppUrl: { + path: '/management/kibana/indexPatterns/patterns/1', + uiCapabilitiesPath: 'management.kibana.indexPatterns', + }, + }, + references: [], + }, + }; + }); + + it('renders correctly', async () => { + const component = shallowRender(); + await new Promise((resolve) => process.nextTick(resolve)); + component.update(); + const codeEditorComponent = component.find('CodeEditor'); + expect(codeEditorComponent).toMatchSnapshot(); + }); + + it("does not include `meta` in the value that's rendered", async () => { + const component = shallowRender(); + await new Promise((resolve) => process.nextTick(resolve)); + component.update(); + const codeEditorComponent = component.find('CodeEditor'); + // find could return nothing + const editorValue = codeEditorComponent + ? (codeEditorComponent.prop('value') as unknown as string) + : ''; + // we assert against the expected object props rather than asserting that 'meta' is removed + expect(Object.keys(JSON.parse(editorValue))).toEqual([ + 'id', + 'type', + 'attributes', + 'references', + ]); + }); +}); diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.tsx new file mode 100644 index 0000000000000..58d6da8ce935b --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { FC, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { XJsonLang } from '@kbn/monaco'; +import { omit } from 'lodash'; +import { EuiButtonEmpty, EuiCopy, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { CodeEditor } from '../../../../../kibana_react/public'; +import { SavedObjectWithMetadata } from '../../../../common'; + +export interface InspectProps { + object: SavedObjectWithMetadata<any>; +} +const codeEditorAriaLabel = (title: string) => + i18n.translate('savedObjectsManagement.view.inspectCodeEditorAriaLabel', { + defaultMessage: 'inspect { title }', + values: { + title, + }, + }); +const copyToClipboardLabel = i18n.translate('savedObjectsManagement.view.copyToClipboardLabel', { + defaultMessage: 'Copy to clipboard', +}); + +export const Inspect: FC<InspectProps> = ({ object }) => { + const title = object.meta.title || 'saved object'; + + const objectAsJsonString = useMemo(() => JSON.stringify(omit(object, 'meta'), null, 2), [object]); + + return ( + <EuiFlexGroup direction="column" gutterSize="s"> + <EuiFlexItem> + <div className="eui-textRight"> + <EuiCopy textToCopy={objectAsJsonString}> + {(copy) => ( + <EuiButtonEmpty + aria-label={copyToClipboardLabel} + size="s" + flush="right" + iconType="copyClipboard" + onClick={copy} + > + {copyToClipboardLabel} + </EuiButtonEmpty> + )} + </EuiCopy> + <EuiSpacer size="s" /> + </div> + <CodeEditor + languageId={XJsonLang.ID} + value={objectAsJsonString} + aria-label={codeEditorAriaLabel(title)} + options={{ + automaticLayout: false, + fontSize: 12, + lineNumbers: 'on', + minimap: { + enabled: false, + }, + overviewRulerBorder: false, + readOnly: true, + scrollbar: { + alwaysConsumeMouseWheel: false, + }, + scrollBeyondLastLine: false, + wordWrap: 'on', + wrappingIndent: 'indent', + renderIndentGuides: false, + }} + /> + </EuiFlexItem> + </EuiFlexGroup> + ); +}; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.test.tsx deleted file mode 100644 index 0b869743f03c7..0000000000000 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.test.tsx +++ /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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { I18nProvider } from '@kbn/i18n/react'; -import { Intro } from './intro'; - -describe('Intro component', () => { - it('renders correctly', () => { - const mounted = mount( - <I18nProvider> - <Intro /> - </I18nProvider> - ); - expect(mounted.find('Intro')).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx deleted file mode 100644 index 0431208d34ad5..0000000000000 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/intro.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { EuiCallOut } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -export const Intro = () => { - return ( - <EuiCallOut - title={ - <FormattedMessage - id="savedObjectsManagement.view.howToModifyObjectTitle" - defaultMessage="Proceed with caution!" - /> - } - iconType="alert" - color="warning" - > - <div> - <FormattedMessage - id="savedObjectsManagement.view.howToModifyObjectDescription" - defaultMessage="Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn’t be." - /> - </div> - </EuiCallOut> - ); -}; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx index 767cc1ac59f47..5eab44cb416e9 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx @@ -10,44 +10,49 @@ import React from 'react'; import { mount } from 'enzyme'; import { I18nProvider } from '@kbn/i18n/react'; import { NotFoundErrors } from './not_found_errors'; +import { docLinksServiceMock } from '../../../../../../core/public/mocks'; describe('NotFoundErrors component', () => { const mountError = (type: string) => mount( <I18nProvider> - <NotFoundErrors type={type} /> + <NotFoundErrors type={type} docLinks={docLinksServiceMock.createStartContract().links} /> </I18nProvider> ).find('NotFoundErrors'); it('renders correctly for search type', () => { const mounted = mountError('search'); - expect(mounted).toMatchSnapshot(); + const callOut = mounted.find('EuiCallOut'); + expect(callOut).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectThe saved search associated with this object no longer exists.If you know what this error means, go ahead and fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectThe saved search associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIs(opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); it('renders correctly for index-pattern type', () => { const mounted = mountError('index-pattern'); - expect(mounted).toMatchSnapshot(); + const callOut = mounted.find('EuiCallOut'); + expect(callOut).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectThe index pattern associated with this object no longer exists.If you know what this error means, go ahead and fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectThe index pattern associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIs(opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); it('renders correctly for index-pattern-field type', () => { const mounted = mountError('index-pattern-field'); - expect(mounted).toMatchSnapshot(); + const callOut = mounted.find('EuiCallOut'); + expect(callOut).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectA field associated with this object no longer exists in the index pattern.If you know what this error means, go ahead and fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectA field associated with this object no longer exists in the index pattern.If you know what this error means, you can use the Saved objects APIs(opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); it('renders correctly for unknown type', () => { const mounted = mountError('unknown'); - expect(mounted).toMatchSnapshot(); + const callOut = mounted.find('EuiCallOut'); + expect(callOut).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectIf you know what this error means, go ahead and fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectIf you know what this error means, you can use the Saved objects APIs(opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); }); diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx index 2bce7b387a7e4..e3a349b1f4aa5 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.tsx @@ -8,13 +8,23 @@ import React from 'react'; import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiLink } from '@elastic/eui'; +import { DocLinksStart } from '../../../../../../core/public'; interface NotFoundErrors { type: string; + docLinks: DocLinksStart['links']; } +const savedObjectsApisLinkText = i18n.translate( + 'savedObjectsManagement.view.howToFixErrorDescriptionLinkText', + { + defaultMessage: 'Saved objects APIs', + } +); -export const NotFoundErrors = ({ type }: NotFoundErrors) => { +export const NotFoundErrors = ({ type, docLinks }: NotFoundErrors) => { const getMessage = () => { switch (type) { case 'search': @@ -58,7 +68,18 @@ export const NotFoundErrors = ({ type }: NotFoundErrors) => { <div> <FormattedMessage id="savedObjectsManagement.view.howToFixErrorDescription" - defaultMessage="If you know what this error means, go ahead and fix it — otherwise click the delete button above." + defaultMessage="If you know what this error means, you can use the {savedObjectsApis} to fix it — otherwise click the delete button above." + values={{ + savedObjectsApis: ( + <EuiLink + aria-label={savedObjectsApisLinkText} + href={`${docLinks.management.savedObjectsApiList}`} + target="_blank" + > + {savedObjectsApisLinkText} + </EuiLink> + ), + }} /> </div> </EuiCallOut> diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.scss b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.scss new file mode 100644 index 0000000000000..656f93468db90 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.scss @@ -0,0 +1,3 @@ +.savedObjectsManagementObjectView { + height: 100%; +} diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.mocks.ts b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.mocks.ts new file mode 100644 index 0000000000000..7243955100690 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.mocks.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +jest.doMock('lodash', () => { + const original = jest.requireActual('lodash'); + return { + ...original, + get: (func: Function) => { + function get(this: any, args: any[]) { + return func.apply(this, args); + } + return get; + }, + }; +}); + +export const bulkGetObjectsMock = jest.fn(); +jest.doMock('../../lib/bulk_get_objects', () => ({ + bulkGetObjects: bulkGetObjectsMock, +})); diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx new file mode 100644 index 0000000000000..13a806361e543 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx @@ -0,0 +1,329 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { bulkGetObjectsMock } from './saved_object_view.test.mocks'; + +import React from 'react'; +import { ShallowWrapper } from 'enzyme'; +import { shallowWithI18nProvider } from '@kbn/test/jest'; + +import { + httpServiceMock, + overlayServiceMock, + notificationServiceMock, + savedObjectsServiceMock, + applicationServiceMock, + uiSettingsServiceMock, + scopedHistoryMock, + docLinksServiceMock, +} from '../../../../../core/public/mocks'; + +import { + SavedObjectEdition, + SavedObjectEditionProps, + SavedObjectEditionState, +} from './saved_object_view'; + +const resolvePromises = () => new Promise((resolve) => process.nextTick(resolve)); + +describe('SavedObjectEdition', () => { + let defaultProps: SavedObjectEditionProps; + let http: ReturnType<typeof httpServiceMock.createStartContract>; + let overlays: ReturnType<typeof overlayServiceMock.createStartContract>; + let notifications: ReturnType<typeof notificationServiceMock.createStartContract>; + let savedObjects: ReturnType<typeof savedObjectsServiceMock.createStartContract>; + let uiSettings: ReturnType<typeof uiSettingsServiceMock.createStartContract>; + let history: ReturnType<typeof scopedHistoryMock.create>; + let applications: ReturnType<typeof applicationServiceMock.createStartContract>; + let docLinks: ReturnType<typeof docLinksServiceMock.createStartContract>; + + const shallowRender = (overrides: Partial<SavedObjectEditionProps> = {}) => { + return shallowWithI18nProvider( + <SavedObjectEdition {...defaultProps} {...overrides} /> + ) as unknown as ShallowWrapper< + SavedObjectEditionProps, + SavedObjectEditionState, + SavedObjectEdition + >; + }; + + beforeEach(() => { + http = httpServiceMock.createStartContract(); + overlays = overlayServiceMock.createStartContract(); + notifications = notificationServiceMock.createStartContract(); + savedObjects = savedObjectsServiceMock.createStartContract(); + uiSettings = uiSettingsServiceMock.createStartContract(); + history = scopedHistoryMock.create(); + docLinks = docLinksServiceMock.createStartContract(); + applications = applicationServiceMock.createStartContract(); + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: false, + }, + }; + + http.post.mockResolvedValue([]); + + defaultProps = { + id: '1', + savedObjectType: 'dashboard', + http, + capabilities: applications.capabilities, + overlays, + notifications, + savedObjectsClient: savedObjects.client, + history, + uiSettings, + docLinks: docLinks.links, + }; + + bulkGetObjectsMock.mockImplementation(() => [{}]); + }); + + it('should render normally', async () => { + bulkGetObjectsMock.mockImplementation(() => + Promise.resolve([ + { + id: '1', + type: 'dashboard', + attributes: { + title: `MyDashboard*`, + }, + meta: { + title: `MyDashboard*`, + icon: 'dashboardApp', + inAppUrl: { + path: '/app/dashboards#/view/1', + uiCapabilitiesPath: 'management.kibana.dashboard', + }, + }, + }, + ]) + ); + const component = shallowRender(); + // Ensure all promises resolve + await resolvePromises(); + // Ensure the state changes are reflected + component.update(); + expect(component).toMatchSnapshot(); + }); + + it('should add danger toast when bulk get fails', async () => { + bulkGetObjectsMock.mockImplementation(() => + Promise.resolve([ + { + error: { + message: 'Not found', + }, + }, + ]) + ); + const component = shallowRender({ notFoundType: 'does_not_exist' }); + + await resolvePromises(); + + component.update(); + + expect(notifications.toasts.addDanger).toHaveBeenCalledTimes(1); + }); + + it('should add danger toast when bulk get throws', async () => { + bulkGetObjectsMock.mockImplementation(() => Promise.reject(new Error('fail'))); + const component = shallowRender({ notFoundType: 'does_not_exist' }); + + await resolvePromises(); + + component.update(); + + expect(notifications.toasts.addDanger).toHaveBeenCalledTimes(1); + }); + + it('should pass the correct props to the child components', async () => { + const savedObjectItem = { + id: '1', + type: 'index-pattern', + attributes: { + title: `MyIndexPattern*`, + }, + meta: { + title: `MyIndexPattern*`, + icon: 'indexPatternApp', + editUrl: '#/management/kibana/indexPatterns/patterns/1', + inAppUrl: { + path: '/management/kibana/indexPatterns/patterns/1', + uiCapabilitiesPath: 'management.kibana.indexPatterns', + }, + hiddenType: false, + }, + }; + bulkGetObjectsMock.mockImplementation(() => Promise.resolve([savedObjectItem])); + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: true, + }, + }; + const component = shallowRender({ + capabilities: applications.capabilities, + }); + + await resolvePromises(); + + component.update(); + const headerComponent = component.find('Header'); + expect(headerComponent.prop('canViewInApp')).toBe(true); + expect(headerComponent.prop('canDelete')).toBe(true); + expect(headerComponent.prop('viewUrl')).toEqual('/management/kibana/indexPatterns/patterns/1'); + const inspectComponent = component.find('Inspect'); + expect(inspectComponent.prop('object')).toEqual(savedObjectItem); + }); + + it("does not render Inspect if there isn't an object", async () => { + bulkGetObjectsMock.mockImplementation(() => Promise.resolve([])); + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: true, + }, + }; + const component = shallowRender({ + capabilities: applications.capabilities, + }); + + await resolvePromises(); + + component.update(); + const inspectComponent = component.find('Inspect'); + expect(inspectComponent).toEqual({}); + }); + + describe('delete', () => { + const savedObjectItem = { + id: '1', + type: 'index-pattern', + attributes: { + title: `MyIndexPattern*`, + }, + meta: { + title: `MyIndexPattern*`, + icon: 'indexPatternApp', + editUrl: '#/management/kibana/indexPatterns/patterns/1', + inAppUrl: { + path: '/management/kibana/indexPatterns/patterns/1', + uiCapabilitiesPath: 'management.kibana.indexPatterns', + }, + hiddenType: false, + }, + }; + + it('should display a confirmation message on deleting the saved object', async () => { + bulkGetObjectsMock.mockImplementation(() => Promise.resolve([savedObjectItem])); + const mockSavedObjectsClient = { + ...defaultProps.savedObjectsClient, + delete: jest.fn().mockImplementation(() => ({})), + }; + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: true, + }, + }; + overlays.openConfirm.mockResolvedValue(false); + const component = shallowRender({ + capabilities: applications.capabilities, + savedObjectsClient: mockSavedObjectsClient, + overlays, + }); + + await resolvePromises(); + + component.update(); + component.instance().delete(); + expect(overlays.openConfirm).toHaveBeenCalledWith( + 'This action permanently removes the object from Kibana.', + { + buttonColor: 'danger', + confirmButtonText: 'Delete', + title: `Delete '${savedObjectItem.meta.title}'?`, + } + ); + }); + + it('should route back if action is confirm and user accepted', async () => { + bulkGetObjectsMock.mockImplementation(() => Promise.resolve([savedObjectItem])); + const mockSavedObjectsClient = { + ...defaultProps.savedObjectsClient, + delete: jest.fn().mockImplementation(() => ({})), + }; + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: true, + }, + }; + overlays.openConfirm.mockResolvedValue(true); + const component = shallowRender({ + capabilities: applications.capabilities, + savedObjectsClient: mockSavedObjectsClient, + overlays, + }); + + await resolvePromises(); + + component.update(); + component.instance().delete(); + expect(overlays.openConfirm).toHaveBeenCalledTimes(1); + expect(history.location.pathname).toEqual('/'); + }); + + it('should not enable delete if the saved object is hidden', async () => { + bulkGetObjectsMock.mockImplementation(() => + Promise.resolve([{ ...savedObjectItem, meta: { hiddenType: true } }]) + ); + applications.capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + savedObjectsManagement: { + read: true, + edit: false, + delete: true, + }, + }; + const component = shallowRender({ + capabilities: applications.capabilities, + }); + + await resolvePromises(); + + component.update(); + expect(component.find('Header').prop('canDelete')).toBe(false); + }); + }); +}); diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index 079a1c07da197..64b6e27309dd2 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -8,7 +8,9 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import { get } from 'lodash'; +import { KibanaContextProvider } from '../../../../kibana_react/public'; import { Capabilities, SavedObjectsClientContract, @@ -16,27 +18,27 @@ import { NotificationsStart, ScopedHistory, HttpSetup, + IUiSettingsClient, + DocLinksStart, } from '../../../../../core/public'; -import { ISavedObjectsManagementServiceRegistry } from '../../services'; -import { Header, NotFoundErrors, Intro, Form } from './components'; -import { canViewInApp, bulkGetObjects } from '../../lib'; -import { SubmittedFormData } from '../types'; +import { Header, Inspect, NotFoundErrors } from './components'; +import { bulkGetObjects } from '../../lib/bulk_get_objects'; import { SavedObjectWithMetadata } from '../../types'; - -interface SavedObjectEditionProps { +import './saved_object_view.scss'; +export interface SavedObjectEditionProps { id: string; + savedObjectType: string; http: HttpSetup; - serviceName: string; - serviceRegistry: ISavedObjectsManagementServiceRegistry; capabilities: Capabilities; overlays: OverlayStart; notifications: NotificationsStart; notFoundType?: string; savedObjectsClient: SavedObjectsClientContract; history: ScopedHistory; + uiSettings: IUiSettingsClient; + docLinks: DocLinksStart['links']; } - -interface SavedObjectEditionState { +export interface SavedObjectEditionState { type: string; object?: SavedObjectWithMetadata<any>; } @@ -45,7 +47,6 @@ const unableFindSavedObjectNotificationMessage = i18n.translate( 'savedObjectsManagement.objectView.unableFindSavedObjectNotificationMessage', { defaultMessage: 'Unable to find saved object' } ); - export class SavedObjectEdition extends Component< SavedObjectEditionProps, SavedObjectEditionState @@ -53,8 +54,7 @@ export class SavedObjectEdition extends Component< constructor(props: SavedObjectEditionProps) { super(props); - const { serviceRegistry, serviceName } = props; - const type = serviceRegistry.get(serviceName)!.service.type; + const { savedObjectType: type } = props; this.state = { object: undefined, @@ -85,54 +85,46 @@ export class SavedObjectEdition extends Component< }); } + canViewInApp(capabilities: Capabilities, obj?: SavedObjectWithMetadata<any>) { + return obj && obj.meta.inAppUrl + ? get(capabilities, obj?.meta.inAppUrl?.uiCapabilitiesPath, false) && + Boolean(obj?.meta.inAppUrl?.path) + : false; + } + render() { - const { capabilities, notFoundType, serviceRegistry, http, serviceName, savedObjectsClient } = - this.props; - const { type } = this.state; + const { capabilities, notFoundType, http, uiSettings, docLinks } = this.props; const { object } = this.state; - const { edit: canEdit, delete: canDelete } = capabilities.savedObjectsManagement as Record< - string, - boolean - >; - const canView = canViewInApp(capabilities, type) && Boolean(object?.meta.inAppUrl?.path); - const service = serviceRegistry.get(serviceName)!.service; - + const { delete: canDelete } = capabilities.savedObjectsManagement as Record<string, boolean>; + const canView = this.canViewInApp(capabilities, object); return ( - <div data-test-subj="savedObjectsEdit"> - <Header - canEdit={canEdit} - canDelete={canDelete && !object?.meta.hiddenType} - canViewInApp={canView} - type={type} - onDeleteClick={() => this.delete()} - viewUrl={http.basePath.prepend(object?.meta.inAppUrl?.path || '')} - /> - <EuiSpacer size="l" /> - {notFoundType && ( - <> - <EuiSpacer size="s" /> - <NotFoundErrors type={notFoundType} /> - </> - )} - {canEdit && ( - <> - <EuiSpacer size="s" /> - <Intro /> - </> - )} - {object && ( - <> - <EuiSpacer size="m" /> - <Form - object={object} - savedObjectsClient={savedObjectsClient} - service={service} - editionEnabled={canEdit} - onSave={this.saveChanges} + <KibanaContextProvider services={{ uiSettings }}> + <EuiFlexGroup + direction="column" + data-test-subject="savedObjectsEdit" + className="savedObjectsManagementObjectView" + > + <EuiFlexItem grow={false}> + <Header + canDelete={canDelete && !object?.meta.hiddenType} + canViewInApp={canView} + onDeleteClick={() => this.delete()} + viewUrl={http.basePath.prepend(object?.meta.inAppUrl?.path || '')} + title={object?.meta.title} /> - </> - )} - </div> + </EuiFlexItem> + {notFoundType && ( + <EuiFlexItem grow={false}> + <NotFoundErrors type={notFoundType} docLinks={docLinks} /> + </EuiFlexItem> + )} + {object && ( + <EuiFlexItem grow={true}> + <Inspect object={object} /> + </EuiFlexItem> + )} + </EuiFlexGroup> + </KibanaContextProvider> ); } @@ -167,15 +159,6 @@ export class SavedObjectEdition extends Component< } } - saveChanges = async ({ attributes, references }: SubmittedFormData) => { - const { savedObjectsClient, notifications } = this.props; - const { object, type } = this.state; - - await savedObjectsClient.update(object!.type, object!.id, attributes, { references }); - notifications.toasts.addSuccess(`Updated '${attributes.title}' ${type} object`); - this.redirectToListing(); - }; - redirectToListing() { this.props.history.push('/'); } diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index 46ea319ebc168..327a9635462cc 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -192,7 +192,6 @@ exports[`SavedObjectsTable should render normally 1`] = ` Object { "id": "2", "meta": Object { - "editUrl": "/management/kibana/objects/savedSearches/2", "icon": "search", "inAppUrl": Object { "path": "/discover/2", @@ -205,7 +204,6 @@ exports[`SavedObjectsTable should render normally 1`] = ` Object { "id": "3", "meta": Object { - "editUrl": "/management/kibana/objects/savedDashboards/3", "icon": "dashboardApp", "inAppUrl": Object { "path": "/dashboard/3", @@ -218,7 +216,6 @@ exports[`SavedObjectsTable should render normally 1`] = ` Object { "id": "4", "meta": Object { - "editUrl": "/management/kibana/objects/savedVisualizations/4", "icon": "visualizeApp", "inAppUrl": Object { "path": "/edit/4", diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap index bb426c91e827c..8325e7dc886e8 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap @@ -146,7 +146,6 @@ exports[`Table prevents saved objects from being deleted 1`] = ` Object { "actions": Array [ Object { - "available": [Function], "data-test-subj": "savedObjectsTableAction-inspect", "description": "Inspect this saved object", "icon": "inspect", @@ -362,7 +361,6 @@ exports[`Table should render normally 1`] = ` Object { "actions": Array [ Object { - "available": [Function], "data-test-subj": "savedObjectsTableAction-inspect", "description": "Inspect this saved object", "icon": "inspect", diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index 30d172b89256e..acdab1db40370 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -11,7 +11,6 @@ import { importFileMock, resolveImportErrorsMock } from './flyout.test.mocks'; import React from 'react'; import { shallowWithI18nProvider } from '@kbn/test/jest'; import { coreMock, httpServiceMock } from '../../../../../../core/public/mocks'; -import { serviceRegistryMock } from '../../../services/service_registry.mock'; import { Flyout, FlyoutProps, FlyoutState } from './flyout'; import { ShallowWrapper } from 'enzyme'; import { dataPluginMock } from '../../../../../data/public/mocks'; @@ -49,7 +48,6 @@ describe('Flyout', () => { } as any, http, allowedTypes: ['search', 'index-pattern', 'visualization'], - serviceRegistry: serviceRegistryMock.create(), search, basePath, }; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index 26de8c5f8b25a..607b3aeeac275 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -43,7 +43,6 @@ import { processImportResponse, ProcessedImportResponse, } from '../../../lib'; -import { ISavedObjectsManagementServiceRegistry } from '../../../services'; import { FailedImportConflict, RetryDecision } from '../../../lib/resolve_import_errors'; import { OverwriteModal } from './overwrite_modal'; import { ImportModeControl, ImportMode } from './import_mode_control'; @@ -53,7 +52,6 @@ const CREATE_NEW_COPIES_DEFAULT = false; const OVERWRITE_ALL_DEFAULT = true; export interface FlyoutProps { - serviceRegistry: ISavedObjectsManagementServiceRegistry; allowedTypes: string[]; close: () => void; done: () => void; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx index 8b07351f6c2c2..c24faf4e12687 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import './import_summary.scss'; import _ from 'lodash'; import React, { Fragment, FC, useMemo } from 'react'; import { @@ -30,6 +29,7 @@ import type { IBasePath, } from 'kibana/public'; import { getDefaultTitle, getSavedObjectLabel, FailedImport } from '../../../lib'; +import './import_summary.scss'; const DEFAULT_ICON = 'apps'; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx index f9171c7928dbe..8eb48ac91da66 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.tsx @@ -298,7 +298,7 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt icon: 'inspect', 'data-test-subj': 'relationshipsTableAction-inspect', onClick: (object: SavedObjectWithMetadata) => goInspectObject(object), - available: (object: SavedObjectWithMetadata) => !!object.meta.editUrl, + available: (object: SavedObjectWithMetadata) => !!(object.type && object.id), }, ], }, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx index 4e4bd51c4bb84..0645c0955f7ac 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx @@ -243,7 +243,6 @@ export class Table extends PureComponent<TableProps, TableState> { type: 'icon', icon: 'inspect', onClick: (object) => goInspectObject(object), - available: (object) => !!object.meta.editUrl, 'data-test-subj': 'savedObjectsTableAction-inspect', }, { diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx index 21a629097cbb4..025a7a320327f 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx @@ -28,7 +28,6 @@ import { applicationServiceMock, } from '../../../../../core/public/mocks'; import { dataPluginMock } from '../../../../data/public/mocks'; -import { serviceRegistryMock } from '../../services/service_registry.mock'; import { actionServiceMock } from '../../services/action_service.mock'; import { columnServiceMock } from '../../services/column_service.mock'; import { @@ -122,7 +121,6 @@ describe('SavedObjectsTable', () => { defaultProps = { allowedTypes, - serviceRegistry: serviceRegistryMock.create(), actionRegistry: actionServiceMock.createStart(), columnRegistry: columnServiceMock.createStart(), savedObjectsClient: savedObjects.client, @@ -159,7 +157,6 @@ describe('SavedObjectsTable', () => { meta: { title: `MySearch`, icon: 'search', - editUrl: '/management/kibana/objects/savedSearches/2', inAppUrl: { path: '/discover/2', uiCapabilitiesPath: 'discover.show', @@ -172,7 +169,6 @@ describe('SavedObjectsTable', () => { meta: { title: `MyDashboard`, icon: 'dashboardApp', - editUrl: '/management/kibana/objects/savedDashboards/3', inAppUrl: { path: '/dashboard/3', uiCapabilitiesPath: 'dashboard.show', @@ -185,7 +181,6 @@ describe('SavedObjectsTable', () => { meta: { title: `MyViz`, icon: 'visualizeApp', - editUrl: '/management/kibana/objects/savedVisualizations/4', inAppUrl: { path: '/edit/4', uiCapabilitiesPath: 'visualize.show', diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index d4067cc21c2be..5001b52e819c2 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -37,7 +37,6 @@ import { } from '../../lib'; import { SavedObjectWithMetadata } from '../../types'; import { - ISavedObjectsManagementServiceRegistry, SavedObjectsManagementActionServiceStart, SavedObjectsManagementColumnServiceStart, } from '../../services'; @@ -58,7 +57,6 @@ interface ExportAllOption { export interface SavedObjectsTableProps { allowedTypes: string[]; - serviceRegistry: ISavedObjectsManagementServiceRegistry; actionRegistry: SavedObjectsManagementActionServiceStart; columnRegistry: SavedObjectsManagementColumnServiceStart; savedObjectsClient: SavedObjectsClientContract; @@ -540,7 +538,6 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb close={this.hideImportFlyout} done={this.finishImport} http={this.props.http} - serviceRegistry={this.props.serviceRegistry} indexPatterns={this.props.indexPatterns} newIndexPatternUrl={newIndexPatternUrl} allowedTypes={this.props.allowedTypes} diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.scss b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.scss new file mode 100644 index 0000000000000..8938fac3aca53 --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.scss @@ -0,0 +1,3 @@ +.savedObjectsManagementEditionPage { + height: 100% +} diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx index f52ac64377c36..74d27199770b1 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx @@ -12,26 +12,24 @@ import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { CoreStart, ChromeBreadcrumb, ScopedHistory } from 'src/core/public'; import { RedirectAppLinks } from '../../../kibana_react/public'; -import { ISavedObjectsManagementServiceRegistry } from '../services'; import { SavedObjectEdition } from './object_view'; +import './saved_objects_edition_page.scss'; const SavedObjectsEditionPage = ({ coreStart, - serviceRegistry, setBreadcrumbs, history, }: { coreStart: CoreStart; - serviceRegistry: ISavedObjectsManagementServiceRegistry; setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; history: ScopedHistory; }) => { - const { service: serviceName, id } = useParams<{ service: string; id: string }>(); + const { type, id } = useParams<{ type: string; id: string }>(); const capabilities = coreStart.application.capabilities; + const dockLinks = coreStart.docLinks.links; const { search } = useLocation(); const query = parse(search); - const service = serviceRegistry.get(serviceName); useEffect(() => { setBreadcrumbs([ @@ -42,27 +40,31 @@ const SavedObjectsEditionPage = ({ href: '/', }, { - text: i18n.translate('savedObjectsManagement.breadcrumb.edit', { - defaultMessage: 'Edit {savedObjectType}', - values: { savedObjectType: service?.service.type ?? 'object' }, + text: i18n.translate('savedObjectsManagement.breadcrumb.inspect', { + defaultMessage: 'Inspect {savedObjectType}', + values: { savedObjectType: type }, }), }, ]); - }, [setBreadcrumbs, service]); + }, [setBreadcrumbs, type]); return ( - <RedirectAppLinks application={coreStart.application}> + <RedirectAppLinks + application={coreStart.application} + className="savedObjectsManagementEditionPage" + > <SavedObjectEdition id={id} + savedObjectType={type} http={coreStart.http} - serviceName={serviceName} - serviceRegistry={serviceRegistry} savedObjectsClient={coreStart.savedObjects.client} overlays={coreStart.overlays} notifications={coreStart.notifications} capabilities={capabilities} notFoundType={query.notFound as string} + uiSettings={coreStart.uiSettings} history={history} + docLinks={dockLinks} /> </RedirectAppLinks> ); diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx index f22f0333ec229..dccf33efc5317 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx @@ -75,13 +75,11 @@ const SavedObjectsTablePage = ({ spacesApi ? spacesApi.ui.components.getSpacesContextProvider : getEmptyFunctionComponent, [spacesApi] ); - return ( <ContextWrapper> <SavedObjectsTable initialQuery={initialQuery} allowedTypes={allowedTypes} - serviceRegistry={serviceRegistry} actionRegistry={actionRegistry} columnRegistry={columnRegistry} taggingApi={taggingApi} @@ -94,12 +92,10 @@ const SavedObjectsTablePage = ({ applications={coreStart.application} perPageConfig={itemsPerPage} goInspectObject={(savedObject) => { - const { editUrl } = savedObject.meta; - if (editUrl) { - return coreStart.application.navigateToUrl( - coreStart.http.basePath.prepend(`/app${editUrl}`) - ); - } + const savedObjectEditUrl = savedObject.meta.editUrl + ? `/app${savedObject.meta.editUrl}` + : `/app/management/kibana/objects/${savedObject.type}/${savedObject.id}`; + coreStart.application.navigateToUrl(coreStart.http.basePath.prepend(savedObjectEditUrl)); }} canGoInApp={(savedObject) => { const { inAppUrl } = savedObject.meta; diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts index 880e277294fc3..53027d5d5046c 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -20,9 +20,6 @@ export const visualizationSavedObjectType: SavedObjectsType = { getTitle(obj) { return obj.attributes.title; }, - getEditUrl(obj) { - return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; - }, getInAppUrl(obj) { return { path: `/app/visualize#/edit/${encodeURIComponent(obj.id)}`, diff --git a/test/api_integration/apis/saved_objects_management/find.ts b/test/api_integration/apis/saved_objects_management/find.ts index f3f4b56cdccf5..9a5f94f9d8b9d 100644 --- a/test/api_integration/apis/saved_objects_management/find.ts +++ b/test/api_integration/apis/saved_objects_management/find.ts @@ -180,8 +180,6 @@ export default function ({ getService }: FtrProviderContext) { icon: 'discoverApp', title: 'OneRecord', hiddenType: false, - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'discover.show', @@ -200,8 +198,6 @@ export default function ({ getService }: FtrProviderContext) { icon: 'dashboardApp', title: 'Dashboard', hiddenType: false, - editUrl: - '/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/dashboards#/view/b70c7ae0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'dashboard.show', @@ -220,8 +216,6 @@ export default function ({ getService }: FtrProviderContext) { icon: 'visualizeApp', title: 'VisualizationFromSavedSearch', hiddenType: false, - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -232,8 +226,6 @@ export default function ({ getService }: FtrProviderContext) { icon: 'visualizeApp', title: 'Visualization', hiddenType: false, - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', diff --git a/test/api_integration/apis/saved_objects_management/relationships.ts b/test/api_integration/apis/saved_objects_management/relationships.ts index 5fbd5cad8ec84..8ee5005348bcd 100644 --- a/test/api_integration/apis/saved_objects_management/relationships.ts +++ b/test/api_integration/apis/saved_objects_management/relationships.ts @@ -21,7 +21,7 @@ export default function ({ getService }: FtrProviderContext) { meta: schema.object({ title: schema.string(), icon: schema.string(), - editUrl: schema.string(), + editUrl: schema.maybe(schema.string()), inAppUrl: schema.object({ path: schema.string(), uiCapabilitiesPath: schema.string(), @@ -103,8 +103,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { title: 'VisualizationFromSavedSearch', icon: 'visualizeApp', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -147,8 +145,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'visualizeApp', title: 'VisualizationFromSavedSearch', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -192,8 +188,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'visualizeApp', title: 'Visualization', - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -209,8 +203,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'visualizeApp', title: 'VisualizationFromSavedSearch', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -234,8 +226,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'visualizeApp', title: 'Visualization', - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -251,8 +241,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'visualizeApp', title: 'VisualizationFromSavedSearch', - editUrl: - '/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -296,8 +284,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'discoverApp', title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'discover.show', @@ -313,8 +299,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'dashboardApp', title: 'Dashboard', - editUrl: - '/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/dashboards#/view/b70c7ae0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'dashboard.show', @@ -340,8 +324,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'discoverApp', title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'discover.show', @@ -385,8 +367,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'discoverApp', title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'discover.show', @@ -402,8 +382,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'visualizeApp', title: 'Visualization', - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'visualize.show', @@ -429,8 +407,6 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'discoverApp', title: 'OneRecord', - editUrl: - '/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357', inAppUrl: { path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'discover.show', @@ -475,8 +451,6 @@ export default function ({ getService }: FtrProviderContext) { { id: 'add810b0-3224-11e8-a572-ffca06da1357', meta: { - editUrl: - '/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357', icon: 'visualizeApp', inAppUrl: { path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', diff --git a/test/functional/apps/saved_objects_management/edit_saved_object.ts b/test/functional/apps/saved_objects_management/edit_saved_object.ts deleted file mode 100644 index f4bf45c0b7f70..0000000000000 --- a/test/functional/apps/saved_objects_management/edit_saved_object.ts +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common', 'settings', 'savedObjects']); - const browser = getService('browser'); - const find = getService('find'); - - const setFieldValue = async (fieldName: string, value: string) => { - return testSubjects.setValue(`savedObjects-editField-${fieldName}`, value); - }; - - const getFieldValue = async (fieldName: string) => { - return testSubjects.getAttribute(`savedObjects-editField-${fieldName}`, 'value'); - }; - - const setAceEditorFieldValue = async (fieldName: string, fieldValue: string) => { - const editorId = `savedObjects-editField-${fieldName}-aceEditor`; - await find.clickByCssSelector(`#${editorId}`); - return browser.execute( - (editor: string, value: string) => { - return (window as any).ace.edit(editor).setValue(value); - }, - editorId, - fieldValue - ); - }; - - const getAceEditorFieldValue = async (fieldName: string) => { - const editorId = `savedObjects-editField-${fieldName}-aceEditor`; - await find.clickByCssSelector(`#${editorId}`); - return browser.execute((editor: string) => { - return (window as any).ace.edit(editor).getValue() as string; - }, editorId); - }; - - const focusAndClickButton = async (buttonSubject: string) => { - const button = await testSubjects.find(buttonSubject); - await button.scrollIntoViewIfNecessary(); - await delay(10); - await button.focus(); - await delay(10); - await button.click(); - // Allow some time for the transition/animations to occur before assuming the click is done - await delay(10); - }; - - describe('saved objects edition page', () => { - beforeEach(async () => { - await esArchiver.load( - 'test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object' - ); - }); - - afterEach(async () => { - await esArchiver.unload( - 'test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object' - ); - }); - - it('allows to update the saved object when submitting', async () => { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaSavedObjects(); - - let objects = await PageObjects.savedObjects.getRowTitles(); - expect(objects.includes('A Dashboard')).to.be(true); - - await PageObjects.common.navigateToUrl( - 'management', - 'kibana/objects/savedDashboards/i-exist', - { - shouldUseHashForSubUrl: false, - } - ); - - await testSubjects.existOrFail('savedObjectEditSave'); - - expect(await getFieldValue('title')).to.eql('A Dashboard'); - - await setFieldValue('title', 'Edited Dashboard'); - await setFieldValue('description', 'Some description'); - - await focusAndClickButton('savedObjectEditSave'); - - objects = await PageObjects.savedObjects.getRowTitles(); - expect(objects.includes('A Dashboard')).to.be(false); - expect(objects.includes('Edited Dashboard')).to.be(true); - - await PageObjects.common.navigateToUrl( - 'management', - 'kibana/objects/savedDashboards/i-exist', - { - shouldUseHashForSubUrl: false, - } - ); - - expect(await getFieldValue('title')).to.eql('Edited Dashboard'); - expect(await getFieldValue('description')).to.eql('Some description'); - }); - - it('allows to delete a saved object', async () => { - await PageObjects.common.navigateToUrl( - 'management', - 'kibana/objects/savedDashboards/i-exist', - { - shouldUseHashForSubUrl: false, - } - ); - - await focusAndClickButton('savedObjectEditDelete'); - await PageObjects.common.clickConfirmOnModal(); - - const objects = await PageObjects.savedObjects.getRowTitles(); - expect(objects.includes('A Dashboard')).to.be(false); - }); - - it('preserves the object references when saving', async () => { - const testVisualizationUrl = - 'kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed'; - const visualizationRefs = [ - { - name: 'kibanaSavedObjectMeta.searchSourceJSON.index', - type: 'index-pattern', - id: 'logstash-*', - }, - ]; - - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaSavedObjects(); - - const objects = await PageObjects.savedObjects.getRowTitles(); - expect(objects.includes('A Pie')).to.be(true); - - await PageObjects.common.navigateToUrl('management', testVisualizationUrl, { - shouldUseHashForSubUrl: false, - }); - - await testSubjects.existOrFail('savedObjectEditSave'); - - let displayedReferencesValue = await getAceEditorFieldValue('references'); - - expect(JSON.parse(displayedReferencesValue)).to.eql(visualizationRefs); - - await focusAndClickButton('savedObjectEditSave'); - - await PageObjects.savedObjects.getRowTitles(); - - await PageObjects.common.navigateToUrl('management', testVisualizationUrl, { - shouldUseHashForSubUrl: false, - }); - - // Parsing to avoid random keys ordering issues in raw string comparison - expect(JSON.parse(await getAceEditorFieldValue('references'))).to.eql(visualizationRefs); - - await setAceEditorFieldValue('references', JSON.stringify([], undefined, 2)); - - await focusAndClickButton('savedObjectEditSave'); - - await PageObjects.savedObjects.getRowTitles(); - - await PageObjects.common.navigateToUrl('management', testVisualizationUrl, { - shouldUseHashForSubUrl: false, - }); - - displayedReferencesValue = await getAceEditorFieldValue('references'); - - expect(JSON.parse(displayedReferencesValue)).to.eql([]); - }); - }); -} diff --git a/test/functional/apps/saved_objects_management/index.ts b/test/functional/apps/saved_objects_management/index.ts index 0b367b284e741..12e0cc8863f12 100644 --- a/test/functional/apps/saved_objects_management/index.ts +++ b/test/functional/apps/saved_objects_management/index.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function savedObjectsManagementApp({ loadTestFile }: FtrProviderContext) { describe('saved objects management', function savedObjectsManagementAppTestSuite() { this.tags('ciGroup7'); - loadTestFile(require.resolve('./edit_saved_object')); + loadTestFile(require.resolve('./inspect_saved_objects')); loadTestFile(require.resolve('./show_relationships')); }); } diff --git a/test/functional/apps/saved_objects_management/inspect_saved_objects.ts b/test/functional/apps/saved_objects_management/inspect_saved_objects.ts new file mode 100644 index 0000000000000..839c262acffa0 --- /dev/null +++ b/test/functional/apps/saved_objects_management/inspect_saved_objects.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'settings', 'savedObjects']); + const find = getService('find'); + + const focusAndClickButton = async (buttonSubject: string) => { + const button = await testSubjects.find(buttonSubject); + await button.scrollIntoViewIfNecessary(); + await delay(10); + await button.focus(); + await delay(10); + await button.click(); + // Allow some time for the transition/animations to occur before assuming the click is done + await delay(10); + }; + const textIncludesAll = (text: string, items: string[]) => { + const bools = items.map((item) => !!text.includes(item)); + return bools.every((currBool) => currBool === true); + }; + + describe('saved objects edition page', () => { + beforeEach(async () => { + await esArchiver.load( + 'test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object' + ); + }); + + afterEach(async () => { + await esArchiver.unload( + 'test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object' + ); + }); + + it('allows to view the saved object', async () => { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + const objects = await PageObjects.savedObjects.getRowTitles(); + expect(objects.includes('A Dashboard')).to.be(true); + await PageObjects.common.navigateToUrl('management', 'kibana/objects/dashboard/i-exist', { + shouldUseHashForSubUrl: false, + }); + const inspectContainer = await find.byClassName('kibanaCodeEditor'); + const visibleContainerText = await inspectContainer.getVisibleText(); + // ensure that something renders visibly + expect( + textIncludesAll(visibleContainerText, [ + 'A Dashboard', + 'title', + 'id', + 'type', + 'attributes', + 'references', + ]) + ).to.be(true); + }); + + it('allows to delete a saved object', async () => { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + let objects = await PageObjects.savedObjects.getRowTitles(); + expect(objects.includes('A Dashboard')).to.be(true); + await PageObjects.savedObjects.clickInspectByTitle('A Dashboard'); + await PageObjects.common.navigateToUrl('management', 'kibana/objects/dashboard/i-exist', { + shouldUseHashForSubUrl: false, + }); + await focusAndClickButton('savedObjectEditDelete'); + await PageObjects.common.clickConfirmOnModal(); + + objects = await PageObjects.savedObjects.getRowTitles(); + expect(objects.includes('A Dashboard')).to.be(false); + }); + }); +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9c69e4fa612f5..aac2d45ec2120 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4369,14 +4369,11 @@ "savedObjects.saveModalOrigin.originAfterSavingSwitchLabel": "保存後に{originVerb}から{origin}", "savedObjects.saveModalOrigin.returnToOriginLabel": "戻る", "savedObjects.saveModalOrigin.saveAndReturnLabel": "保存して戻る", - "savedObjectsManagement.breadcrumb.edit": "{savedObjectType}を編集", "savedObjectsManagement.breadcrumb.index": "保存されたオブジェクト", "savedObjectsManagement.deleteConfirm.modalDeleteButtonLabel": "削除", "savedObjectsManagement.deleteConfirm.modalDescription": "このアクションはオブジェクトをKibanaから永久に削除します。", "savedObjectsManagement.deleteConfirm.modalTitle": "「{title}」を削除しますか?", "savedObjectsManagement.deleteSavedObjectsConfirmModalDescription": "この操作は次の保存されたオブジェクトを削除します:", - "savedObjectsManagement.field.offLabel": "オフ", - "savedObjectsManagement.field.onLabel": "オン", "savedObjectsManagement.importSummary.createdCountHeader": "{createdCount}件の新規項目", "savedObjectsManagement.importSummary.createdOutcomeLabel": "作成済み", "savedObjectsManagement.importSummary.errorCountHeader": "{errorCount}件のエラー", @@ -4478,21 +4475,10 @@ "savedObjectsManagement.objectsTable.unableFindSavedObjectNotificationMessage": "保存されたオブジェクトが見つかりません", "savedObjectsManagement.objectsTable.unableFindSavedObjectsNotificationMessage": "保存されたオブジェクトが見つかりません", "savedObjectsManagement.objectView.unableFindSavedObjectNotificationMessage": "保存されたオブジェクトが見つかりません", - "savedObjectsManagement.view.cancelButtonAriaLabel": "キャンセル", - "savedObjectsManagement.view.cancelButtonLabel": "キャンセル", - "savedObjectsManagement.view.deleteItemButtonLabel": "{title}を削除", - "savedObjectsManagement.view.editItemTitle": "{title}の編集", "savedObjectsManagement.view.fieldDoesNotExistErrorMessage": "このオブジェクトに関連付けられたフィールドは、現在このインデックスパターンに存在しません。", - "savedObjectsManagement.view.howToFixErrorDescription": "このエラーの原因がわかる場合は修正してください。わからない場合は上の削除ボタンをクリックしてください。", - "savedObjectsManagement.view.howToModifyObjectDescription": "オブジェクトの編集は上級ユーザー向けです。オブジェクトのプロパティが検証されておらず、無効なオブジェクトはエラー、データ損失、またはそれ以上の問題の原因となります。コードを熟知した人に指示されていない限り、この設定は変更しない方が無難です。", - "savedObjectsManagement.view.howToModifyObjectTitle": "十分ご注意ください!", "savedObjectsManagement.view.indexPatternDoesNotExistErrorMessage": "このオブジェクトに関連付けられたインデックスパターンは現在存在しません。", - "savedObjectsManagement.view.saveButtonAriaLabel": "{ title }オブジェクトを保存", - "savedObjectsManagement.view.saveButtonLabel": "{ title }オブジェクトを保存", "savedObjectsManagement.view.savedObjectProblemErrorMessage": "この保存されたオブジェクトに問題があります", "savedObjectsManagement.view.savedSearchDoesNotExistErrorMessage": "このオブジェクトに関連付けられた保存された検索は現在存在しません。", - "savedObjectsManagement.view.viewItemButtonLabel": "{title}を表示", - "savedObjectsManagement.view.viewItemTitle": "{title}を表示", "security.checkup.dismissButtonText": "閉じる", "security.checkup.dontShowAgain": "今後表示しない", "security.checkup.insecureClusterMessage": "1 ビットを失わないでください。Elastic では無料でデータを保護できます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1c91700a74e81..73ad85a85f427 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4409,14 +4409,11 @@ "savedObjects.saveModalOrigin.originAfterSavingSwitchLabel": "保存后{originVerb}至{origin}", "savedObjects.saveModalOrigin.returnToOriginLabel": "返回", "savedObjects.saveModalOrigin.saveAndReturnLabel": "保存并返回", - "savedObjectsManagement.breadcrumb.edit": "编辑 {savedObjectType}", "savedObjectsManagement.breadcrumb.index": "已保存对象", "savedObjectsManagement.deleteConfirm.modalDeleteButtonLabel": "删除", "savedObjectsManagement.deleteConfirm.modalDescription": "此操作会将对象从 Kibana 永久移除。", "savedObjectsManagement.deleteConfirm.modalTitle": "删除“{title}”?", "savedObjectsManagement.deleteSavedObjectsConfirmModalDescription": "此操作将删除以下已保存对象:", - "savedObjectsManagement.field.offLabel": "关闭", - "savedObjectsManagement.field.onLabel": "开启", "savedObjectsManagement.importSummary.createdCountHeader": "{createdCount} 个新", "savedObjectsManagement.importSummary.createdOutcomeLabel": "已创建", "savedObjectsManagement.importSummary.errorCountHeader": "{errorCount} 个错误", @@ -4523,21 +4520,10 @@ "savedObjectsManagement.objectsTable.unableFindSavedObjectNotificationMessage": "找不到已保存对象", "savedObjectsManagement.objectsTable.unableFindSavedObjectsNotificationMessage": "找不到已保存对象", "savedObjectsManagement.objectView.unableFindSavedObjectNotificationMessage": "找不到已保存对象", - "savedObjectsManagement.view.cancelButtonAriaLabel": "取消", - "savedObjectsManagement.view.cancelButtonLabel": "取消", - "savedObjectsManagement.view.deleteItemButtonLabel": "删除“{title}”", - "savedObjectsManagement.view.editItemTitle": "编辑“{title}”", "savedObjectsManagement.view.fieldDoesNotExistErrorMessage": "与此对象关联的字段在该索引模式中已不存在。", - "savedObjectsManagement.view.howToFixErrorDescription": "如果您清楚此错误的含义,请修复该错误 — 否则单击上面的删除按钮。", - "savedObjectsManagement.view.howToModifyObjectDescription": "修改对象仅适用于高级用户。对象属性未得到验证,无效的对象可能会导致错误、数据丢失或更坏的情况发生。除非熟悉该代码的人让您来这里,否则您可能不应到访此处。", - "savedObjectsManagement.view.howToModifyObjectTitle": "谨慎操作!", "savedObjectsManagement.view.indexPatternDoesNotExistErrorMessage": "与此对象关联的索引模式已不存在。", - "savedObjectsManagement.view.saveButtonAriaLabel": "保存 { title } 对象", - "savedObjectsManagement.view.saveButtonLabel": "保存 { title } 对象", "savedObjectsManagement.view.savedObjectProblemErrorMessage": "此已保存对象有问题", "savedObjectsManagement.view.savedSearchDoesNotExistErrorMessage": "与此对象关联的已保存搜索已不存在。", - "savedObjectsManagement.view.viewItemButtonLabel": "查看“{title}”", - "savedObjectsManagement.view.viewItemTitle": "查看“{title}”", "security.checkup.dismissButtonText": "关闭", "security.checkup.dontShowAgain": "不再显示", "security.checkup.insecureClusterMessage": "不要丢失一位。使用 Elastic,免费保护您的数据。", diff --git a/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts b/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts index 3d4c30c1bfdd6..58f08a1dfb9f7 100644 --- a/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts +++ b/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts @@ -14,6 +14,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'settings', 'security', 'error', 'savedObjects']); const kibanaServer = getService('kibanaServer'); let version: string = ''; + const find = getService('find'); describe('feature controls saved objects management', () => { before(async () => { @@ -108,12 +109,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(actual).to.be(true); }); }); - - describe('edit visualization', () => { + // From https://github.com/elastic/kibana/issues/59588 edit view became read-only json view + // test description changed from "edit" to "inspect" + // Skipping the test to allow code owners to delete or modify the test. + describe('inspect visualization', () => { before(async () => { await PageObjects.common.navigateToUrl( 'management', - 'kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed', + 'kibana/objects/visualization/75c3e060-1e7c-11e9-8488-65449e65d0ed', { shouldLoginIfPrompted: false, shouldUseHashForSubUrl: false, @@ -125,11 +128,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('savedObjectEditDelete'); }); - it('shows save button', async () => { + // no longer a feature + it.skip('shows save button', async () => { await testSubjects.existOrFail('savedObjectEditSave'); }); - it('has inputs without readonly attributes', async () => { + // no longer a feature + it.skip('has inputs without readonly attributes', async () => { const form = await testSubjects.find('savedObjectEditForm'); const inputs = await form.findAllByCssSelector('input'); expect(inputs.length).to.be.greaterThan(0); @@ -223,17 +228,30 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('edit visualization', () => { + // From https://github.com/elastic/kibana/issues/59588 edit view became read-only json view + // test description changed from "edit" to "inspect" + // Skipping the test to allow code owners to delete or modify the test. + describe('inspect visualization', () => { before(async () => { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + const objects = await PageObjects.savedObjects.getRowTitles(); + expect(objects.includes('A Pie')).to.be(true); + await PageObjects.savedObjects.clickInspectByTitle('A Pie'); await PageObjects.common.navigateToUrl( 'management', - 'kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed', + 'kibana/objects/visualization/75c3e060-1e7c-11e9-8488-65449e65d0ed', { shouldLoginIfPrompted: false, shouldUseHashForSubUrl: false, } ); - await testSubjects.existOrFail('savedObjectsEdit'); + }); + + it('allows viewing the object', async () => { + const inspectContainer = await find.byClassName('kibanaCodeEditor'); + const visibleContainerText = await inspectContainer.getVisibleText(); + expect(visibleContainerText.includes('A Pie')); }); it('does not show delete button', async () => { @@ -244,7 +262,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.missingOrFail('savedObjectEditSave'); }); - it('has inputs with only readonly attributes', async () => { + // No longer a feature + it.skip('has inputs with only readonly attributes', async () => { const form = await testSubjects.find('savedObjectEditForm'); const inputs = await form.findAllByCssSelector('input'); expect(inputs.length).to.be.greaterThan(0); @@ -309,11 +328,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('edit visualization', () => { + describe('inspect visualization', () => { it('redirects to management home', async () => { await PageObjects.common.navigateToUrl( 'management', - 'kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed', + 'kibana/objects/visualization/75c3e060-1e7c-11e9-8488-65449e65d0ed', { shouldLoginIfPrompted: false, ensureCurrentUrl: false, diff --git a/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts b/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts index 28d04c1f9c54c..e4da0b341dce9 100644 --- a/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts +++ b/x-pack/test/functional/apps/saved_objects_management/spaces_integration.ts @@ -14,7 +14,6 @@ const getSpacePrefix = (spaceId: string) => { export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects([ 'common', 'security', @@ -22,9 +21,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'spaceSelector', 'settings', ]); + const find = getService('find'); const spaceId = 'space_1'; + const textIncludesAll = (text: string, items: string[]) => { + const bools = items.map((item) => !!text.includes(item)); + return bools.every((currBool) => currBool === true); + }; + describe('spaces integration', () => { before(async () => { await esArchiver.load( @@ -54,9 +59,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.common.waitUntilUrlIncludes(getSpacePrefix(spaceId)); - expect(await testSubjects.getAttribute(`savedObjects-editField-title`, 'value')).to.eql( - 'A Pie' - ); + const inspectContainer = await find.byClassName('kibanaCodeEditor'); + const visibleContainerText = await inspectContainer.getVisibleText(); + expect( + textIncludesAll(visibleContainerText, [ + 'A Pie', + 'title', + 'id', + 'type', + 'attributes', + 'references', + ]) + ).to.be(true); + expect(visibleContainerText.includes('A Pie')); }); }); } From feae8e7815899a551aa3d544495ffab4ebee0998 Mon Sep 17 00:00:00 2001 From: Pete Hampton <pjhampton@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:45:13 +0100 Subject: [PATCH 42/69] Add allowlist filter for Exception list data (#112668) * Add allowlist filter to exception list telemetry. * Refactor away union type. * Bump interval back to 24h. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/lib/telemetry/constants.ts | 2 + .../server/lib/telemetry/filters.test.ts | 127 ++++++++++++++++++ .../server/lib/telemetry/filters.ts | 47 +++++++ .../server/lib/telemetry/helpers.test.ts | 40 +++--- .../server/lib/telemetry/helpers.ts | 77 ++++++----- .../server/lib/telemetry/receiver.ts | 15 ++- .../server/lib/telemetry/sender.test.ts | 119 +--------------- .../server/lib/telemetry/sender.ts | 33 +---- .../lib/telemetry/tasks/security_lists.ts | 9 +- .../server/lib/telemetry/types.ts | 14 +- 10 files changed, 266 insertions(+), 217 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/telemetry/filters.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts index 1c03e52c67ae7..771e3e059c336 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts @@ -11,6 +11,8 @@ export const TELEMETRY_CHANNEL_LISTS = 'security-lists'; export const TELEMETRY_CHANNEL_ENDPOINT_META = 'endpoint-metadata'; +export const LIST_TRUSTED_APPLICATION = 'trusted_application'; + export const LIST_ENDPOINT_EXCEPTION = 'endpoint_exception'; export const LIST_ENDPOINT_EVENT_FILTER = 'endpoint_event_filter'; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filters.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filters.test.ts new file mode 100644 index 0000000000000..4844a10d99f90 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filters.test.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { copyAllowlistedFields } from './filters'; + +describe('Security Telemetry filters', () => { + describe('allowlistEventFields', () => { + const allowlist = { + a: true, + b: true, + c: { + d: true, + }, + }; + + it('filters top level', () => { + const event = { + a: 'a', + a1: 'a1', + b: 'b', + b1: 'b1', + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: 'a', + b: 'b', + }); + }); + + it('filters nested', () => { + const event = { + a: { + a1: 'a1', + }, + a1: 'a1', + b: { + b1: 'b1', + }, + b1: 'b1', + c: { + d: 'd', + e: 'e', + f: 'f', + }, + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: { + a1: 'a1', + }, + b: { + b1: 'b1', + }, + c: { + d: 'd', + }, + }); + }); + + it('filters arrays of objects', () => { + const event = { + a: [ + { + a1: 'a1', + }, + ], + b: { + b1: 'b1', + }, + c: [ + { + d: 'd1', + e: 'e1', + f: 'f1', + }, + { + d: 'd2', + e: 'e2', + f: 'f2', + }, + { + d: 'd3', + e: 'e3', + f: 'f3', + }, + ], + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: [ + { + a1: 'a1', + }, + ], + b: { + b1: 'b1', + }, + c: [ + { + d: 'd1', + }, + { + d: 'd2', + }, + { + d: 'd3', + }, + ], + }); + }); + + it("doesn't create empty objects", () => { + const event = { + a: 'a', + b: 'b', + c: { + e: 'e', + }, + }; + expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ + a: 'a', + b: 'b', + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts index 18c3baccf9aa0..61172fac511f7 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { TelemetryEvent } from './types'; + export interface AllowlistFields { [key: string]: boolean | AllowlistFields; } @@ -124,3 +126,48 @@ export const allowlistEventFields: AllowlistFields = { }, ...allowlistBaseEventFields, }; + +export const exceptionListEventFields: AllowlistFields = { + created_at: true, + description: true, + effectScope: true, + entries: true, + id: true, + name: true, + os: true, + os_types: true, +}; + +/** + * Filters out information not required for downstream analysis + * + * @param allowlist + * @param event + * @returns + */ +export function copyAllowlistedFields( + allowlist: AllowlistFields, + event: TelemetryEvent +): TelemetryEvent { + return Object.entries(allowlist).reduce<TelemetryEvent>((newEvent, [allowKey, allowValue]) => { + const eventValue = event[allowKey]; + if (eventValue !== null && eventValue !== undefined) { + if (allowValue === true) { + return { ...newEvent, [allowKey]: eventValue }; + } else if (typeof allowValue === 'object' && Array.isArray(eventValue)) { + const subValues = eventValue.filter((v) => typeof v === 'object'); + return { + ...newEvent, + [allowKey]: subValues.map((v) => copyAllowlistedFields(allowValue, v as TelemetryEvent)), + }; + } else if (typeof allowValue === 'object' && typeof eventValue === 'object') { + const values = copyAllowlistedFields(allowValue, eventValue as TelemetryEvent); + return { + ...newEvent, + ...(Object.keys(values).length > 0 ? { [allowKey]: values } : {}), + }; + } + } + return newEvent; + }, {}); +} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts index a4d11b71f2a8e..647219e8c5585 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts @@ -7,17 +7,19 @@ import moment from 'moment'; import { createMockPackagePolicy } from './mocks'; -import { TrustedApp } from '../../../common/endpoint/types'; -import { LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER } from './constants'; +import { + LIST_ENDPOINT_EXCEPTION, + LIST_ENDPOINT_EVENT_FILTER, + LIST_TRUSTED_APPLICATION, +} from './constants'; import { getPreviousDiagTaskTimestamp, getPreviousEpMetaTaskTimestamp, batchTelemetryRecords, isPackagePolicyList, - templateTrustedApps, - templateEndpointExceptions, + templateExceptionList, } from './helpers'; -import { EndpointExceptionListItem } from './types'; +import { ExceptionListItem } from './types'; describe('test diagnostic telemetry scheduled task timing helper', () => { test('test -5 mins is returned when there is no previous task run', async () => { @@ -133,8 +135,8 @@ describe('test package policy type guard', () => { describe('list telemetry schema', () => { test('trusted apps document is correctly formed', () => { - const data = [{ id: 'test_1' }] as TrustedApp[]; - const templatedItems = templateTrustedApps(data); + const data = [{ id: 'test_1' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_TRUSTED_APPLICATION); expect(templatedItems[0]?.trusted_application.length).toEqual(1); expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); @@ -142,8 +144,8 @@ describe('list telemetry schema', () => { }); test('trusted apps document is correctly formed with multiple entries', () => { - const data = [{ id: 'test_2' }, { id: 'test_2' }] as TrustedApp[]; - const templatedItems = templateTrustedApps(data); + const data = [{ id: 'test_2' }, { id: 'test_2' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_TRUSTED_APPLICATION); expect(templatedItems[0]?.trusted_application.length).toEqual(1); expect(templatedItems[1]?.trusted_application.length).toEqual(1); @@ -152,8 +154,8 @@ describe('list telemetry schema', () => { }); test('endpoint exception document is correctly formed', () => { - const data = [{ id: 'test_3' }] as EndpointExceptionListItem[]; - const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EXCEPTION); + const data = [{ id: 'test_3' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EXCEPTION); expect(templatedItems[0]?.trusted_application.length).toEqual(0); expect(templatedItems[0]?.endpoint_exception.length).toEqual(1); @@ -161,12 +163,8 @@ describe('list telemetry schema', () => { }); test('endpoint exception document is correctly formed with multiple entries', () => { - const data = [ - { id: 'test_4' }, - { id: 'test_4' }, - { id: 'test_4' }, - ] as EndpointExceptionListItem[]; - const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EXCEPTION); + const data = [{ id: 'test_4' }, { id: 'test_4' }, { id: 'test_4' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EXCEPTION); expect(templatedItems[0]?.trusted_application.length).toEqual(0); expect(templatedItems[0]?.endpoint_exception.length).toEqual(1); @@ -176,8 +174,8 @@ describe('list telemetry schema', () => { }); test('endpoint event filters document is correctly formed', () => { - const data = [{ id: 'test_5' }] as EndpointExceptionListItem[]; - const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EVENT_FILTER); + const data = [{ id: 'test_5' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EVENT_FILTER); expect(templatedItems[0]?.trusted_application.length).toEqual(0); expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); @@ -185,8 +183,8 @@ describe('list telemetry schema', () => { }); test('endpoint event filters document is correctly formed with multiple entries', () => { - const data = [{ id: 'test_6' }, { id: 'test_6' }] as EndpointExceptionListItem[]; - const templatedItems = templateEndpointExceptions(data, LIST_ENDPOINT_EVENT_FILTER); + const data = [{ id: 'test_6' }, { id: 'test_6' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EVENT_FILTER); expect(templatedItems[0]?.trusted_application.length).toEqual(0); expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts index bb2cc4f42ca90..a9eaef3ce6edc 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts @@ -7,10 +7,15 @@ import moment from 'moment'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { TrustedApp } from '../../../common/endpoint/types'; import { PackagePolicy } from '../../../../fleet/common/types/models/package_policy'; -import { EndpointExceptionListItem, ListTemplate } from './types'; -import { LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER } from './constants'; +import { copyAllowlistedFields, exceptionListEventFields } from './filters'; +import { ExceptionListItem, ListTemplate, TelemetryEvent } from './types'; +import { + LIST_ENDPOINT_EXCEPTION, + LIST_ENDPOINT_EVENT_FILTER, + LIST_TRUSTED_APPLICATION, +} from './constants'; +import { TrustedApp } from '../../../common/endpoint/types'; /** * Determines the when the last run was in order to execute to. @@ -89,43 +94,41 @@ export function isPackagePolicyList( } /** - * Maps Exception list item to parsable object + * Maps trusted application to shared telemetry object * * @param exceptionListItem * @returns collection of endpoint exceptions */ -export const exceptionListItemToEndpointEntry = (exceptionListItem: ExceptionListItemSchema) => { +export const trustedApplicationToTelemetryEntry = (trustedApplication: TrustedApp) => { + return { + id: trustedApplication.id, + version: trustedApplication.version || '', + name: trustedApplication.name, + description: trustedApplication.description, + created_at: trustedApplication.created_at, + updated_at: trustedApplication.updated_at, + entries: trustedApplication.entries, + os: trustedApplication.os, + } as ExceptionListItem; +}; + +/** + * Maps endpoint lists to shared telemetry object + * + * @param exceptionListItem + * @returns collection of endpoint exceptions + */ +export const exceptionListItemToTelemetryEntry = (exceptionListItem: ExceptionListItemSchema) => { return { id: exceptionListItem.id, version: exceptionListItem._version || '', name: exceptionListItem.name, description: exceptionListItem.description, created_at: exceptionListItem.created_at, - created_by: exceptionListItem.created_by, updated_at: exceptionListItem.updated_at, - updated_by: exceptionListItem.updated_by, entries: exceptionListItem.entries, os_types: exceptionListItem.os_types, - } as EndpointExceptionListItem; -}; - -/** - * Constructs the lists telemetry schema from a collection of Trusted Apps - * - * @param listData - * @returns lists telemetry schema - */ -export const templateTrustedApps = (listData: TrustedApp[]) => { - return listData.map((item) => { - const template: ListTemplate = { - trusted_application: [], - endpoint_exception: [], - endpoint_event_filter: [], - }; - - template.trusted_application.push(item); - return template; - }); + } as ExceptionListItem; }; /** @@ -135,10 +138,7 @@ export const templateTrustedApps = (listData: TrustedApp[]) => { * @param listType * @returns lists telemetry schema */ -export const templateEndpointExceptions = ( - listData: EndpointExceptionListItem[], - listType: string -) => { +export const templateExceptionList = (listData: ExceptionListItem[], listType: string) => { return listData.map((item) => { const template: ListTemplate = { trusted_application: [], @@ -146,13 +146,24 @@ export const templateEndpointExceptions = ( endpoint_event_filter: [], }; + // cast exception list type to a TelemetryEvent for allowlist filtering + const filteredListItem = copyAllowlistedFields( + exceptionListEventFields, + item as unknown as TelemetryEvent + ); + + if (listType === LIST_TRUSTED_APPLICATION) { + template.trusted_application.push(filteredListItem); + return template; + } + if (listType === LIST_ENDPOINT_EXCEPTION) { - template.endpoint_exception.push(item); + template.endpoint_exception.push(filteredListItem); return template; } if (listType === LIST_ENDPOINT_EVENT_FILTER) { - template.endpoint_event_filter.push(item); + template.endpoint_event_filter.push(filteredListItem); return template; } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index 06dffdddc49c8..8b715b8e8d585 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -17,7 +17,7 @@ import { AgentService, AgentPolicyServiceInterface } from '../../../../fleet/ser import { ExceptionListClient } from '../../../../lists/server'; import { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { TELEMETRY_MAX_BUFFER_SIZE } from './constants'; -import { exceptionListItemToEndpointEntry } from './helpers'; +import { exceptionListItemToTelemetryEntry, trustedApplicationToTelemetryEntry } from './helpers'; import { TelemetryEvent, ESLicense, ESClusterInfo, GetEndpointListResponse } from './types'; export class TelemetryReceiver { @@ -202,7 +202,16 @@ export class TelemetryReceiver { throw Error('exception list client is unavailable: cannot retrieve trusted applications'); } - return getTrustedAppsList(this.exceptionListClient, { page: 1, per_page: 10_000 }); + const results = await getTrustedAppsList(this.exceptionListClient, { + page: 1, + per_page: 10_000, + }); + return { + data: results?.data.map(trustedApplicationToTelemetryEntry), + total: results?.total ?? 0, + page: results?.page ?? 1, + per_page: results?.per_page ?? this.max_records, + }; } public async fetchEndpointList(listId: string): Promise<GetEndpointListResponse> { @@ -224,7 +233,7 @@ export class TelemetryReceiver { }); return { - data: results?.data.map(exceptionListItemToEndpointEntry) ?? [], + data: results?.data.map(exceptionListItemToTelemetryEntry) ?? [], total: results?.total ?? 0, page: results?.page ?? 1, per_page: results?.per_page ?? this.max_records, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts index d04d0ab49afe9..21e6b2cf6d9c4 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts @@ -6,7 +6,7 @@ */ /* eslint-disable dot-notation */ -import { TelemetryEventsSender, copyAllowlistedFields } from './sender'; +import { TelemetryEventsSender } from './sender'; import { loggingSystemMock } from 'src/core/server/mocks'; import { usageCountersServiceMock } from 'src/plugins/usage_collection/server/usage_counters/usage_counters_service.mock'; import { URL } from 'url'; @@ -220,123 +220,6 @@ describe('TelemetryEventsSender', () => { }); }); -describe('allowlistEventFields', () => { - const allowlist = { - a: true, - b: true, - c: { - d: true, - }, - }; - - it('filters top level', () => { - const event = { - a: 'a', - a1: 'a1', - b: 'b', - b1: 'b1', - }; - expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ - a: 'a', - b: 'b', - }); - }); - - it('filters nested', () => { - const event = { - a: { - a1: 'a1', - }, - a1: 'a1', - b: { - b1: 'b1', - }, - b1: 'b1', - c: { - d: 'd', - e: 'e', - f: 'f', - }, - }; - expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ - a: { - a1: 'a1', - }, - b: { - b1: 'b1', - }, - c: { - d: 'd', - }, - }); - }); - - it('filters arrays of objects', () => { - const event = { - a: [ - { - a1: 'a1', - }, - ], - b: { - b1: 'b1', - }, - c: [ - { - d: 'd1', - e: 'e1', - f: 'f1', - }, - { - d: 'd2', - e: 'e2', - f: 'f2', - }, - { - d: 'd3', - e: 'e3', - f: 'f3', - }, - ], - }; - expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ - a: [ - { - a1: 'a1', - }, - ], - b: { - b1: 'b1', - }, - c: [ - { - d: 'd1', - }, - { - d: 'd2', - }, - { - d: 'd3', - }, - ], - }); - }); - - it("doesn't create empty objects", () => { - const event = { - a: 'a', - b: 'b', - c: { - e: 'e', - }, - }; - expect(copyAllowlistedFields(allowlist, event)).toStrictEqual({ - a: 'a', - b: 'b', - }); - }); -}); - describe('getV3UrlFromV2', () => { let logger: ReturnType<typeof loggingSystemMock.createLogger>; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index 2e615a2681174..0037aaa28fee3 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -17,7 +17,7 @@ import { TaskManagerStartContract, } from '../../../../task_manager/server'; import { TelemetryReceiver } from './receiver'; -import { AllowlistFields, allowlistEventFields } from './filters'; +import { allowlistEventFields, copyAllowlistedFields } from './filters'; import { DiagnosticTask, EndpointTask, ExceptionListsTask } from './tasks'; import { createUsageCounterLabel } from './helpers'; import { TelemetryEvent } from './types'; @@ -194,8 +194,8 @@ export class TelemetryEventsSender { /** * This function sends events to the elastic telemetry channel. Caution is required - * because it does no allowlist filtering. The caller is responsible for making sure - * that there is no sensitive material or PII in the records that are sent upstream. + * because it does no allowlist filtering at send time. The function call site is + * responsible for ensuring sure no sensitive material is in telemetry events. * * @param channel the elastic telemetry channel * @param toSend telemetry events @@ -294,30 +294,3 @@ export class TelemetryEventsSender { } } } - -export function copyAllowlistedFields( - allowlist: AllowlistFields, - event: TelemetryEvent -): TelemetryEvent { - return Object.entries(allowlist).reduce<TelemetryEvent>((newEvent, [allowKey, allowValue]) => { - const eventValue = event[allowKey]; - if (eventValue !== null && eventValue !== undefined) { - if (allowValue === true) { - return { ...newEvent, [allowKey]: eventValue }; - } else if (typeof allowValue === 'object' && Array.isArray(eventValue)) { - const subValues = eventValue.filter((v) => typeof v === 'object'); - return { - ...newEvent, - [allowKey]: subValues.map((v) => copyAllowlistedFields(allowValue, v as TelemetryEvent)), - }; - } else if (typeof allowValue === 'object' && typeof eventValue === 'object') { - const values = copyAllowlistedFields(allowValue, eventValue as TelemetryEvent); - return { - ...newEvent, - ...(Object.keys(values).length > 0 ? { [allowKey]: values } : {}), - }; - } - } - return newEvent; - }, {}); -} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts index b54858e1f5f42..fe2039419b02d 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/security_lists.ts @@ -19,9 +19,10 @@ import { import { LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER, + LIST_TRUSTED_APPLICATION, TELEMETRY_CHANNEL_LISTS, } from '../constants'; -import { batchTelemetryRecords, templateEndpointExceptions, templateTrustedApps } from '../helpers'; +import { batchTelemetryRecords, templateExceptionList } from '../helpers'; import { TelemetryEventsSender } from '../sender'; import { TelemetryReceiver } from '../receiver'; @@ -110,7 +111,7 @@ export class TelemetryExceptionListsTask { // Lists Telemetry: Trusted Applications const trustedApps = await this.receiver.fetchTrustedApplications(); - const trustedAppsJson = templateTrustedApps(trustedApps.data); + const trustedAppsJson = templateExceptionList(trustedApps.data, LIST_TRUSTED_APPLICATION); this.logger.debug(`Trusted Apps: ${trustedAppsJson}`); batchTelemetryRecords(trustedAppsJson, MAX_TELEMETRY_BATCH).forEach((batch) => @@ -120,7 +121,7 @@ export class TelemetryExceptionListsTask { // Lists Telemetry: Endpoint Exceptions const epExceptions = await this.receiver.fetchEndpointList(ENDPOINT_LIST_ID); - const epExceptionsJson = templateEndpointExceptions(epExceptions.data, LIST_ENDPOINT_EXCEPTION); + const epExceptionsJson = templateExceptionList(epExceptions.data, LIST_ENDPOINT_EXCEPTION); this.logger.debug(`EP Exceptions: ${epExceptionsJson}`); batchTelemetryRecords(epExceptionsJson, MAX_TELEMETRY_BATCH).forEach((batch) => @@ -130,7 +131,7 @@ export class TelemetryExceptionListsTask { // Lists Telemetry: Endpoint Event Filters const epFilters = await this.receiver.fetchEndpointList(ENDPOINT_EVENT_FILTERS_LIST_ID); - const epFiltersJson = templateEndpointExceptions(epFilters.data, LIST_ENDPOINT_EVENT_FILTER); + const epFiltersJson = templateExceptionList(epFilters.data, LIST_ENDPOINT_EVENT_FILTER); this.logger.debug(`EP Event Filters: ${epFiltersJson}`); batchTelemetryRecords(epFiltersJson, MAX_TELEMETRY_BATCH).forEach((batch) => diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts index b78017314a982..abcad26ed000c 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts @@ -6,7 +6,6 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { TrustedApp } from '../../../common/endpoint/types'; type BaseSearchTypes = string | number | boolean | object; export type SearchTypes = BaseSearchTypes | BaseSearchTypes[] | undefined; @@ -211,26 +210,25 @@ export interface GetEndpointListResponse { per_page: number; page: number; total: number; - data: EndpointExceptionListItem[]; + data: ExceptionListItem[]; } // Telemetry List types -export interface EndpointExceptionListItem { +export interface ExceptionListItem { id: string; version: string; name: string; description: string; created_at: string; - created_by: string; updated_at: string; - updated_by: string; entries: object; + os: string; os_types: object; } export interface ListTemplate { - trusted_application: TrustedApp[]; - endpoint_exception: EndpointExceptionListItem[]; - endpoint_event_filter: EndpointExceptionListItem[]; + trusted_application: TelemetryEvent[]; + endpoint_exception: TelemetryEvent[]; + endpoint_event_filter: TelemetryEvent[]; } From 7d8d89f50d6f3f7165a4bf23467b94076eb2892d Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tylersmalley@gmail.com> Date: Tue, 21 Sep 2021 14:21:08 -0700 Subject: [PATCH 43/69] skip flaky suite (#112732) --- .../tests/apps/dashboard/async_search/sessions_in_space.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/sessions_in_space.ts b/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/sessions_in_space.ts index d05ac46d5b88f..98eca99ff436c 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/sessions_in_space.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/dashboard/async_search/sessions_in_space.ts @@ -23,7 +23,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const searchSessions = getService('searchSessions'); - describe('dashboard in space', () => { + // Failing: See https://github.com/elastic/kibana/issues/112732 + describe.skip('dashboard in space', () => { describe('Storing search sessions in space', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/dashboard/session_in_space'); From e67a25c8b8511536b1925db77634ac1afee209ec Mon Sep 17 00:00:00 2001 From: Brian Seeders <brian.seeders@elastic.co> Date: Tue, 21 Sep 2021 17:26:36 -0400 Subject: [PATCH 44/69] [CI] [Buildkite] Only report test failures for hourly/daily pipelines (#112729) --- .buildkite/pipelines/hourly.yml | 2 ++ .../run_failed_tests_reporter_cli.ts | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index e8480b2d3b75d..89541023be8e2 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -1,3 +1,5 @@ +env: + REPORT_FAILED_TESTS_TO_GITHUB: 'true' steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build diff --git a/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts b/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts index d4bf625ee2d3b..0129614fe658d 100644 --- a/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts +++ b/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts @@ -37,17 +37,24 @@ export function runFailedTestsReporterCli() { } if (updateGithub) { - let branch: string | undefined = ''; + let branch: string = ''; let isPr = false; if (process.env.BUILDKITE === 'true') { - branch = process.env.BUILDKITE_BRANCH; + branch = process.env.BUILDKITE_BRANCH || ''; isPr = process.env.BUILDKITE_PULL_REQUEST === 'true'; + updateGithub = process.env.REPORT_FAILED_TESTS_TO_GITHUB === 'true'; } else { // JOB_NAME is formatted as `elastic+kibana+7.x` in some places and `elastic+kibana+7.x/JOB=kibana-intake,node=immutable` in others const jobNameSplit = (process.env.JOB_NAME || '').split(/\+|\//); - branch = jobNameSplit.length >= 3 ? jobNameSplit[2] : process.env.GIT_BRANCH; + branch = jobNameSplit.length >= 3 ? jobNameSplit[2] : process.env.GIT_BRANCH || ''; isPr = !!process.env.ghprbPullId; + + const isMasterOrVersion = branch === 'master' || branch.match(/^\d+\.(x|\d+)$/); + if (!isMasterOrVersion || isPr) { + log.info('Failure issues only created on master/version branch jobs'); + updateGithub = false; + } } if (!branch) { @@ -55,12 +62,6 @@ export function runFailedTestsReporterCli() { 'Unable to determine originating branch from job name or other environment variables' ); } - - const isMasterOrVersion = branch === 'master' || branch.match(/^\d+\.(x|\d+)$/); - if (!isMasterOrVersion || isPr) { - log.info('Failure issues only created on master/version branch jobs'); - updateGithub = false; - } } const githubApi = new GithubApi({ From e268e21f7b62160c800caac558cfbef06c78c0f7 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck <thomas@elastic.co> Date: Tue, 21 Sep 2021 18:18:11 -0400 Subject: [PATCH 45/69] [Maps] Remove regionmap test references (#112734) --- .../test/functional/apps/maps/layer_errors.js | 18 ------------------ .../functional/fixtures/kbn_archiver/maps.json | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/x-pack/test/functional/apps/maps/layer_errors.js b/x-pack/test/functional/apps/maps/layer_errors.js index 64973461c107b..23cbc8c51835c 100644 --- a/x-pack/test/functional/apps/maps/layer_errors.js +++ b/x-pack/test/functional/apps/maps/layer_errors.js @@ -104,24 +104,6 @@ export default function ({ getPageObjects }) { }); }); - describe('KibanaRegionmapSource with missing region map configuration', () => { - const MISSING_REGION_NAME = 'nameThatDoesNotExitForKibanaRegionmapSource'; - const LAYER_NAME = 'Custom_vector_shapes'; - - it('should diplay error message in layer panel', async () => { - const errorMsg = await PageObjects.maps.getLayerErrorText(LAYER_NAME); - expect(errorMsg).to.equal( - `Unable to find map.regionmap configuration for ${MISSING_REGION_NAME}` - ); - }); - - it('should allow deletion of layer', async () => { - await PageObjects.maps.removeLayer(LAYER_NAME); - const exists = await PageObjects.maps.doesLayerExist(LAYER_NAME); - expect(exists).to.be(false); - }); - }); - describe('KibanaTilemapSource with missing map.tilemap.url configuration', () => { const LAYER_NAME = 'Custom_TMS'; diff --git a/x-pack/test/functional/fixtures/kbn_archiver/maps.json b/x-pack/test/functional/fixtures/kbn_archiver/maps.json index b17b4a64f3485..78e49997d5c9e 100644 --- a/x-pack/test/functional/fixtures/kbn_archiver/maps.json +++ b/x-pack/test/functional/fixtures/kbn_archiver/maps.json @@ -684,7 +684,7 @@ { "attributes": { "description": "", - "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"KIBANA_TILEMAP\"},\"id\":\"ap0ys\",\"label\":\"Custom_TMS\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{},\"previousStyle\":null},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"REGIONMAP_FILE\",\"name\":\"nameThatDoesNotExitForKibanaRegionmapSource\"},\"temporary\":false,\"id\":\"0sabv\",\"label\":\"Custom_vector_shapes\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#3cb44b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"idThatDoesNotExitForEMSTile\"},\"temporary\":false,\"id\":\"plw9l\",\"label\":\"EMS_tiles\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{},\"previousStyle\":null},\"type\":\"VECTOR_TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"idThatDoesNotExitForEMSFileSource\"},\"temporary\":false,\"id\":\"2gro0\",\"label\":\"EMS_vector_shapes\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"id\":\"f67fe707-95dd-46d6-89b8-82617b251b61\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"resolution\":\"COARSE\",\"indexPatternRefName\":\"layer_4_source_index_pattern\",\"applyGlobalQuery\":true},\"temporary\":false,\"id\":\"pl5qd\",\"label\":\"\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32,\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"id\":\"a07072bb-3a92-4320-bd37-250ef6d04db7\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[],\"indexPatternRefName\":\"layer_5_source_index_pattern\",\"applyGlobalQuery\":true,\"scalingType\":\"LIMIT\"},\"temporary\":false,\"id\":\"9bw8h\",\"label\":\"\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[],\"indexPatternRefName\":\"layer_6_source_index_pattern\",\"applyGlobalQuery\":true,\"scalingType\":\"LIMIT\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1__855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"indexPatternRefName\":\"layer_6_join_0_index_pattern\",\"applyGlobalQuery\":true,\"type\":\"ES_TERM_SOURCE\"}}]}]", + "layerListJSON": "[{\"sourceDescriptor\":{\"type\":\"KIBANA_TILEMAP\"},\"id\":\"ap0ys\",\"label\":\"Custom_TMS\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{},\"previousStyle\":null},\"type\":\"TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"idThatDoesNotExitForEMSTile\"},\"temporary\":false,\"id\":\"plw9l\",\"label\":\"EMS_tiles\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{\"type\":\"TILE\",\"properties\":{},\"previousStyle\":null},\"type\":\"VECTOR_TILE\"},{\"sourceDescriptor\":{\"type\":\"EMS_FILE\",\"id\":\"idThatDoesNotExitForEMSFileSource\"},\"temporary\":false,\"id\":\"2gro0\",\"label\":\"EMS_vector_shapes\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"type\":\"ES_GEO_GRID\",\"id\":\"f67fe707-95dd-46d6-89b8-82617b251b61\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"resolution\":\"COARSE\",\"indexPatternRefName\":\"layer_4_source_index_pattern\",\"applyGlobalQuery\":true},\"temporary\":false,\"id\":\"pl5qd\",\"label\":\"\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32,\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"sourceDescriptor\":{\"id\":\"a07072bb-3a92-4320-bd37-250ef6d04db7\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"tooltipProperties\":[],\"indexPatternRefName\":\"layer_5_source_index_pattern\",\"applyGlobalQuery\":true,\"scalingType\":\"LIMIT\"},\"temporary\":false,\"id\":\"9bw8h\",\"label\":\"\",\"minZoom\":0,\"maxZoom\":24,\"alpha\":0.75,\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[],\"indexPatternRefName\":\"layer_6_source_index_pattern\",\"applyGlobalQuery\":true,\"scalingType\":\"LIMIT\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1__855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"origin\":\"join\"},\"color\":\"Blues\",\"fieldMetaOptions\":{\"isEnabled\":false,\"sigma\":3}}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"marker\"}}},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}],\"indexPatternRefName\":\"layer_6_join_0_index_pattern\",\"applyGlobalQuery\":true,\"type\":\"ES_TERM_SOURCE\"}}]}]", "mapStateJSON": "{\"zoom\":0.71,\"center\":{\"lon\":0.10268,\"lat\":0},\"timeFilters\":{\"from\":\"now-7d\",\"to\":\"now\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"settings\":{\"autoFitToDataBounds\":false}}", "title": "layer with errors", "uiStateJSON": "{}" From 8531e216559b491d2025becf7ae96a6c37aa31d9 Mon Sep 17 00:00:00 2001 From: Byron Hulcher <byronhulcher@gmail.com> Date: Tue, 21 Sep 2021 18:26:09 -0400 Subject: [PATCH 46/69] Prevent ResultSettingsLogic.actions.saveResultSettings from throwing a new exception after catching API error (#112742) --- .../result_settings/result_settings_logic.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts index 49a09b8a7a3f5..5ff153c3beb64 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts @@ -321,26 +321,25 @@ export const ResultSettingsLogic = kea<MakeLogicType<ResultSettingsValues, Resul actions.saving(); - let response; try { - response = await http.put(url, { + const response = await http.put(url, { body: JSON.stringify({ result_fields: values.reducedServerResultFields, }), }); + + actions.initializeResultFields(response.result_fields, values.schema); + flashSuccessToast( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.resultSettings.saveSuccessMessage', + { + defaultMessage: 'Result settings were saved', + } + ) + ); } catch (e) { flashAPIErrors(e); } - - actions.initializeResultFields(response.result_fields, values.schema); - flashSuccessToast( - i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.resultSettings.saveSuccessMessage', - { - defaultMessage: 'Result settings were saved', - } - ) - ); } }, }), From e9e08d09280e2e83a3aac1a26016b580a3f57d4d Mon Sep 17 00:00:00 2001 From: Brian Seeders <brian.seeders@elastic.co> Date: Wed, 22 Sep 2021 00:36:25 -0400 Subject: [PATCH 47/69] [CI] Balance ci groups (#110711) --- test/functional/apps/visualize/index.ts | 7 ++++++- .../security_and_spaces/tests/basic/index.ts | 2 +- .../security_and_spaces/tests/trial/index.ts | 2 +- x-pack/test/functional/apps/apm/index.ts | 2 +- x-pack/test/functional/apps/lens/index.ts | 6 +++++- x-pack/test/functional/apps/uptime/index.ts | 2 +- .../test/functional/apps/uptime/synthetics_integration.ts | 2 +- x-pack/test/functional/config.js | 2 +- 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index 878c7b88341af..3bc4da0163909 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -85,11 +85,16 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_add_to_dashboard.ts')); }); + describe('visualize ciGroup8', function () { + this.tags('ciGroup8'); + + loadTestFile(require.resolve('./_tsvb_chart')); + }); + describe('visualize ciGroup11', function () { this.tags('ciGroup11'); loadTestFile(require.resolve('./_tag_cloud')); - loadTestFile(require.resolve('./_tsvb_chart')); loadTestFile(require.resolve('./_tsvb_time_series')); loadTestFile(require.resolve('./_tsvb_markdown')); loadTestFile(require.resolve('./_tsvb_table')); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/index.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/index.ts index 90fbb10637434..3fb7d7a29af39 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/basic/index.ts @@ -12,7 +12,7 @@ import { createSpacesAndUsers, deleteSpacesAndUsers } from '../../../common/lib/ export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('cases security and spaces enabled: basic', function () { // Fastest ciGroup for the moment. - this.tags('ciGroup5'); + this.tags('ciGroup13'); before(async () => { await createSpacesAndUsers(getService); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/index.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/index.ts index 26bc6a072450d..cd4b062c065a0 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/index.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/index.ts @@ -12,7 +12,7 @@ import { createSpacesAndUsers, deleteSpacesAndUsers } from '../../../common/lib/ export default ({ loadTestFile, getService }: FtrProviderContext): void => { describe('cases security and spaces enabled: trial', function () { // Fastest ciGroup for the moment. - this.tags('ciGroup5'); + this.tags('ciGroup13'); before(async () => { await createSpacesAndUsers(getService); diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts index e4db5a66aa55f..20c2bc264a74f 100644 --- a/x-pack/test/functional/apps/apm/index.ts +++ b/x-pack/test/functional/apps/apm/index.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('APM specs', function () { - this.tags('ciGroup6'); + this.tags('ciGroup10'); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./correlations')); }); diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts index 09bbda595d55c..a851bdccaf72d 100644 --- a/x-pack/test/functional/apps/lens/index.ts +++ b/x-pack/test/functional/apps/lens/index.ts @@ -25,10 +25,14 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/lens/basic'); }); + describe('', function () { + this.tags(['ciGroup3', 'skipFirefox']); + loadTestFile(require.resolve('./smokescreen')); + }); + describe('', function () { this.tags(['ciGroup4', 'skipFirefox']); - loadTestFile(require.resolve('./smokescreen')); loadTestFile(require.resolve('./add_to_dashboard')); loadTestFile(require.resolve('./table')); loadTestFile(require.resolve('./runtime_fields')); diff --git a/x-pack/test/functional/apps/uptime/index.ts b/x-pack/test/functional/apps/uptime/index.ts index 501fec5002666..2db29bfca4054 100644 --- a/x-pack/test/functional/apps/uptime/index.ts +++ b/x-pack/test/functional/apps/uptime/index.ts @@ -42,7 +42,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { const uptime = getService('uptime'); describe('Uptime app', function () { - this.tags('ciGroup6'); + this.tags('ciGroup10'); beforeEach('delete settings', async () => { await deleteUptimeSettingsObject(server); diff --git a/x-pack/test/functional/apps/uptime/synthetics_integration.ts b/x-pack/test/functional/apps/uptime/synthetics_integration.ts index 1fe13227d2546..e403c4d25097c 100644 --- a/x-pack/test/functional/apps/uptime/synthetics_integration.ts +++ b/x-pack/test/functional/apps/uptime/synthetics_integration.ts @@ -130,7 +130,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('When on the Synthetics Integration Policy Create Page', function () { - this.tags(['ciGroup6']); + this.tags(['ciGroup10']); const basicConfig = { name: monitorName, apmServiceName: 'Sample APM Service', diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 95a962388cdd6..daa3ef3872cfb 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -32,7 +32,6 @@ export default async function ({ readConfigFile }) { resolve(__dirname, './apps/discover'), resolve(__dirname, './apps/security'), resolve(__dirname, './apps/spaces'), - resolve(__dirname, './apps/lens'), resolve(__dirname, './apps/logstash'), resolve(__dirname, './apps/grok_debugger'), resolve(__dirname, './apps/infra'), @@ -58,6 +57,7 @@ export default async function ({ readConfigFile }) { resolve(__dirname, './apps/reporting_management'), resolve(__dirname, './apps/management'), resolve(__dirname, './apps/reporting'), + resolve(__dirname, './apps/lens'), // smokescreen tests cause flakiness in other tests // This license_management file must be last because it is destructive. resolve(__dirname, './apps/license_management'), From da1ae4923ab8ecd1fe310ca9512e9a63168f409d Mon Sep 17 00:00:00 2001 From: Shahzad <shahzad.muhammad@elastic.co> Date: Wed, 22 Sep 2021 09:28:10 +0200 Subject: [PATCH 48/69] [User Experience App] Improve empty state loading (#112531) * improve empty state loading * only show loading in content panel * eslint fix --- .../components/app/RumDashboard/RumHome.tsx | 60 +++++++++++-------- .../app/RumDashboard/empty_state_loading.tsx | 35 +++++++++++ 2 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/RumDashboard/empty_state_loading.tsx diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx index 505fd5c1020b3..bbc77bcabca52 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiTitle, EuiFlexItem } from '@elastic/eui'; import { RumOverview } from '../RumDashboard'; @@ -18,6 +18,7 @@ import { UserPercentile } from './UserPercentile'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; import { KibanaPageTemplateProps } from '../../../../../../../src/plugins/kibana_react/public'; import { useHasRumData } from './hooks/useHasRumData'; +import { EmptyStateLoading } from './empty_state_loading'; export const UX_LABEL = i18n.translate('xpack.apm.ux.title', { defaultMessage: 'Dashboard', @@ -29,7 +30,7 @@ export function RumHome() { const { isSmall, isXXL } = useBreakpoints(); - const { data: rumHasData } = useHasRumData(); + const { data: rumHasData, status } = useHasRumData(); const envStyle = isSmall ? {} : { maxWidth: 500 }; @@ -58,31 +59,38 @@ export function RumHome() { } : undefined; + const isLoading = status === 'loading'; + return ( - <CsmSharedContextProvider> - <PageTemplateComponent - noDataConfig={noDataConfig} - pageHeader={ - isXXL - ? { - pageTitle: i18n.translate('xpack.apm.ux.overview', { - defaultMessage: 'Dashboard', - }), - rightSideItems: [ - <DatePicker />, - <div style={envStyle}> - <UxEnvironmentFilter /> - </div>, - <UserPercentile />, - <WebApplicationSelect />, - ], - } - : { children: <PageHeader /> } - } - > - <RumOverview /> - </PageTemplateComponent> - </CsmSharedContextProvider> + <Fragment> + <CsmSharedContextProvider> + <PageTemplateComponent + noDataConfig={isLoading ? undefined : noDataConfig} + pageHeader={ + isXXL + ? { + pageTitle: i18n.translate('xpack.apm.ux.overview', { + defaultMessage: 'Dashboard', + }), + rightSideItems: [ + <DatePicker />, + <div style={envStyle}> + <UxEnvironmentFilter /> + </div>, + <UserPercentile />, + <WebApplicationSelect />, + ], + } + : { children: <PageHeader /> } + } + > + {isLoading && <EmptyStateLoading />} + <div style={{ visibility: isLoading ? 'hidden' : 'initial' }}> + <RumOverview /> + </div> + </PageTemplateComponent> + </CsmSharedContextProvider> + </Fragment> ); } diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/empty_state_loading.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/empty_state_loading.tsx new file mode 100644 index 0000000000000..b02672721ce8e --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/empty_state_loading.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiEmptyPrompt, + EuiLoadingSpinner, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { Fragment } from 'react'; + +export function EmptyStateLoading() { + return ( + <EuiEmptyPrompt + body={ + <Fragment> + <EuiLoadingSpinner size="xl" /> + <EuiSpacer /> + <EuiTitle size="l"> + <h2> + {i18n.translate('xpack.apm.emptyState.loadingMessage', { + defaultMessage: 'Loading…', + })} + </h2> + </EuiTitle> + </Fragment> + } + /> + ); +} From abdb7a4c49aab2385f752069ac13e0baf9524d03 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Wed, 22 Sep 2021 11:13:53 +0300 Subject: [PATCH 49/69] [TSVB] Disables the input string mode (#110571) * [TSVB] Remove the input string mode * Fix some tests * Add some functional tests and fix failing CI * Update telemetry mappings * Rename useStringIndices to allowStringIndices, move it from TSVB to Data constants and refactor test * Apply text suggestions from code review Co-authored-by: Kaarina Tungseth <kaarina.tungseth@elastic.co> * Apply formatting and remove unused translations * Fix labels * Remove unused import * Move popover toggling to checkIndexPatternSelectionModeSwitchIsEnabled function to prevent flakiness * Update some visual_builder_page functions * Remove accidentally added newlines * Move TSVB ui settings to constants, remove tooltip and update popover text * Handle the case of editing advanced settings is restricted * Add requiresPageReload to UI setting and condition for the case the setting is already enabled Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kaarina Tungseth <kaarina.tungseth@elastic.co> --- .../server/collectors/management/schema.ts | 4 ++ .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 ++ .../vis_types/timeseries/common/constants.ts | 5 +- .../switch_mode_popover.tsx | 57 +++++++++++++++++-- .../use_index_patter_mode_callout.tsx | 2 +- .../timeseries/server/lib/get_vis_data.ts | 4 +- .../strategies/default_search_strategy.ts | 4 +- .../strategies/rollup_search_strategy.ts | 4 +- .../timeseries/server/ui_settings.ts | 17 +++++- test/functional/apps/visualize/_tsvb_chart.ts | 3 + .../apps/visualize/_tsvb_time_series.ts | 43 ++++++++++++++ .../page_objects/visual_builder_page.ts | 22 ++++++- .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - .../test/functional/apps/rollup_job/tsvb.js | 2 + 16 files changed, 158 insertions(+), 22 deletions(-) diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index bc38b63730b20..53eb49e1013b0 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -276,6 +276,10 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = { type: 'long', _meta: { description: 'Non-default value of setting.' }, }, + 'metrics:allowStringIndices': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'query:allowLeadingWildcards': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 24a20b458c789..b76ef14e62b8c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -91,6 +91,7 @@ export interface UsageStats { 'savedObjects:listingLimit': number; 'query:queryString:options': string; 'metrics:max_buckets': number; + 'metrics:allowStringIndices': boolean; 'query:allowLeadingWildcards': boolean; metaFields: string[]; 'indexPattern:placeholder': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 0a81da0cdceba..2363c0ca103a2 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7428,6 +7428,12 @@ "description": "Non-default value of setting." } }, + "metrics:allowStringIndices": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "query:allowLeadingWildcards": { "type": "boolean", "_meta": { diff --git a/src/plugins/vis_types/timeseries/common/constants.ts b/src/plugins/vis_types/timeseries/common/constants.ts index bddbf095e895e..4f15cea7faad3 100644 --- a/src/plugins/vis_types/timeseries/common/constants.ts +++ b/src/plugins/vis_types/timeseries/common/constants.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -export const MAX_BUCKETS_SETTING = 'metrics:max_buckets'; +export const UI_SETTINGS = { + MAX_BUCKETS_SETTING: 'metrics:max_buckets', + ALLOW_STRING_INDICES: 'metrics:allowStringIndices', +}; export const INDEXES_SEPARATOR = ','; export const AUTO_INTERVAL = 'auto'; export const ROUTES = { diff --git a/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx index 5f5506ce4a332..43ef091da251d 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx @@ -17,9 +17,17 @@ import { EuiSpacer, EuiSwitch, EuiText, + EuiLink, } from '@elastic/eui'; import type { PopoverProps } from './types'; +import { getCoreStart, getUISettings } from '../../../../services'; +import { UI_SETTINGS } from '../../../../../common/constants'; + +const allowStringIndicesMessage = i18n.translate( + 'visTypeTimeseries.indexPatternSelect.switchModePopover.allowStringIndices', + { defaultMessage: 'Allow string indices in TSVB' } +); export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverProps) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -30,6 +38,39 @@ export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverPro onModeChange(!useKibanaIndices); }, [onModeChange, useKibanaIndices]); + const { application } = getCoreStart(); + const canEditAdvancedSettings = application.capabilities.advancedSettings.save; + + const handleAllowStringIndicesLinkClick = useCallback( + () => + application.navigateToApp('management', { + path: `/kibana/settings?query=${UI_SETTINGS.ALLOW_STRING_INDICES}`, + }), + [application] + ); + + const stringIndicesAllowed = getUISettings().get(UI_SETTINGS.ALLOW_STRING_INDICES); + const isSwitchDisabled = useKibanaIndices && !stringIndicesAllowed; + + let allowStringIndicesLabel; + if (!stringIndicesAllowed) { + allowStringIndicesLabel = ( + <FormattedMessage + id="visTypeTimeseries.indexPatternSelect.switchModePopover.enableAllowStringIndices" + defaultMessage="To search by Elasticsearch indices enable {allowStringIndices} setting." + values={{ + allowStringIndices: canEditAdvancedSettings ? ( + <EuiLink color="accent" onClick={handleAllowStringIndicesLinkClick}> + {allowStringIndicesMessage} + </EuiLink> + ) : ( + <strong>{allowStringIndicesMessage}</strong> + ), + }} + /> + ); + } + return ( <EuiPopover button={ @@ -42,14 +83,18 @@ export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverPro } )} onClick={onButtonClick} - data-test-subj="switchIndexPatternSelectionModePopover" + data-test-subj="switchIndexPatternSelectionModePopoverButton" /> } isOpen={isPopoverOpen} closePopover={closePopover} style={{ height: 'auto' }} + initialFocus={false} > - <div style={{ width: '360px' }}> + <div + style={{ width: '360px' }} + data-test-subj="switchIndexPatternSelectionModePopoverContent" + > <EuiPopoverTitle> {i18n.translate('visTypeTimeseries.indexPatternSelect.switchModePopover.title', { defaultMessage: 'Index pattern selection mode', @@ -59,7 +104,10 @@ export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverPro <FormattedMessage id="visTypeTimeseries.indexPatternSelect.switchModePopover.text" defaultMessage="An index pattern identifies one or more Elasticsearch indices that you want to explore. - You can use Elasticsearch indices or Kibana index patterns (recommended)." + Kibana index patterns are used by default. {allowStringIndicesLabel}" + values={{ + allowStringIndicesLabel, + }} /> </EuiText> <EuiSpacer /> @@ -68,10 +116,11 @@ export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverPro label={i18n.translate( 'visTypeTimeseries.indexPatternSelect.switchModePopover.useKibanaIndices', { - defaultMessage: 'Use only Kibana index patterns', + defaultMessage: 'Use only index patterns', } )} onChange={switchMode} + disabled={isSwitchDisabled} data-test-subj="switchIndexPatternSelectionMode" /> </div> diff --git a/src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx b/src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx index 6191df2ecce5b..9684b7b7ff356 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/use_index_patter_mode_callout.tsx @@ -43,7 +43,7 @@ export const UseIndexPatternModeCallout = () => { <p> <FormattedMessage id="visTypeTimeseries.visEditorVisualization.indexPatternMode.notificationMessage" - defaultMessage="Great news! You can now visualize the data from Elasticsearch indices or Kibana index patterns. {indexPatternModeLink}." + defaultMessage="Great news! You can now visualize the data from Kibana index patterns (recommended) or Elasticsearch indices. {indexPatternModeLink}." values={{ indexPatternModeLink: ( <EuiLink href={indexPatternModeLink} target="_blank" external> diff --git a/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts index bc4fbf9159a00..a76132e0fbd21 100644 --- a/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts @@ -20,8 +20,8 @@ import { getSeriesData } from './vis_data/get_series_data'; import { getTableData } from './vis_data/get_table_data'; import { getEsQueryConfig } from './vis_data/helpers/get_es_query_uisettings'; import { getCachedIndexPatternFetcher } from './search_strategies/lib/cached_index_pattern_fetcher'; -import { MAX_BUCKETS_SETTING } from '../../common/constants'; import { getIntervalAndTimefield } from './vis_data/get_interval_and_timefield'; +import { UI_SETTINGS } from '../../common/constants'; export async function getVisData( requestContext: VisTypeTimeseriesRequestHandlerContext, @@ -57,7 +57,7 @@ export async function getVisData( index = await cachedIndexPatternFetcher(index.indexPatternString, true); } - const maxBuckets = await uiSettings.get<number>(MAX_BUCKETS_SETTING); + const maxBuckets = await uiSettings.get<number>(UI_SETTINGS.MAX_BUCKETS_SETTING); const { min, max } = request.body.timerange; return getIntervalAndTimefield( diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts index 0fa92b5f061fa..ff1c3c0ac71ee 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts @@ -15,7 +15,7 @@ import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesRequest, } from '../../../types'; -import { MAX_BUCKETS_SETTING } from '../../../../common/constants'; +import { UI_SETTINGS } from '../../../../common/constants'; export class DefaultSearchStrategy extends AbstractSearchStrategy { async checkForViability( @@ -29,7 +29,7 @@ export class DefaultSearchStrategy extends AbstractSearchStrategy { capabilities: new DefaultSearchCapabilities({ panel: req.body.panels ? req.body.panels[0] : null, timezone: req.body.timerange?.timezone, - maxBucketsLimit: await uiSettings.get(MAX_BUCKETS_SETTING), + maxBucketsLimit: await uiSettings.get(UI_SETTINGS.MAX_BUCKETS_SETTING), }), }; } diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts index 903e7f239f824..e3ede57774224 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts @@ -20,7 +20,7 @@ import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesVisDataRequest, } from '../../../types'; -import { MAX_BUCKETS_SETTING } from '../../../../common/constants'; +import { UI_SETTINGS } from '../../../../common/constants'; const getRollupIndices = (rollupData: { [key: string]: any }) => Object.keys(rollupData); const isIndexPatternContainsWildcard = (indexPattern: string) => indexPattern.includes('*'); @@ -75,7 +75,7 @@ export class RollupSearchStrategy extends AbstractSearchStrategy { capabilities = new RollupSearchCapabilities( { - maxBucketsLimit: await uiSettings.get(MAX_BUCKETS_SETTING), + maxBucketsLimit: await uiSettings.get(UI_SETTINGS.MAX_BUCKETS_SETTING), panel: req.body.panels ? req.body.panels[0] : null, }, fieldsCapabilities, diff --git a/src/plugins/vis_types/timeseries/server/ui_settings.ts b/src/plugins/vis_types/timeseries/server/ui_settings.ts index e61635058cee0..2adbc31482f04 100644 --- a/src/plugins/vis_types/timeseries/server/ui_settings.ts +++ b/src/plugins/vis_types/timeseries/server/ui_settings.ts @@ -10,11 +10,10 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { UiSettingsParams } from 'kibana/server'; - -import { MAX_BUCKETS_SETTING } from '../common/constants'; +import { UI_SETTINGS } from '../common/constants'; export const getUiSettings: () => Record<string, UiSettingsParams> = () => ({ - [MAX_BUCKETS_SETTING]: { + [UI_SETTINGS.MAX_BUCKETS_SETTING]: { name: i18n.translate('visTypeTimeseries.advancedSettings.maxBucketsTitle', { defaultMessage: 'TSVB buckets limit', }), @@ -25,4 +24,16 @@ export const getUiSettings: () => Record<string, UiSettingsParams> = () => ({ }), schema: schema.number(), }, + [UI_SETTINGS.ALLOW_STRING_INDICES]: { + name: i18n.translate('visTypeTimeseries.advancedSettings.allowStringIndicesTitle', { + defaultMessage: 'Allow string indices in TSVB', + }), + value: false, + requiresPageReload: true, + description: i18n.translate('visTypeTimeseries.advancedSettings.allowStringIndicesText', { + defaultMessage: + 'Enables you to use index patterns and Elasticsearch indices in <strong>TSVB</strong> visualizations.', + }), + schema: schema.boolean(), + }, }); diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index 6a5c062268c25..c530b00364fd1 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -16,6 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const inspector = getService('inspector'); const retry = getService('retry'); const security = getService('security'); + const kibanaServer = getService('kibanaServer'); const { timePicker, visChart, visualBuilder, visualize, settings } = getPageObjects([ 'timePicker', @@ -95,6 +96,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await visualBuilder.setFieldForAggregation('machine.ram'); const kibanaIndexPatternModeValue = await visualBuilder.getMetricValue(); + await kibanaServer.uiSettings.update({ 'metrics:allowStringIndices': true }); + await browser.refresh(); await visualBuilder.clickPanelOptions('metric'); await visualBuilder.switchIndexPatternSelectionMode(false); const stringIndexPatternModeValue = await visualBuilder.getMetricValue(); diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index 21bee2d16442f..09dc61e9f5f62 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -433,6 +433,49 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { after(async () => await visualBuilder.toggleNewChartsLibraryWithDebug(false)); }); + + describe('index pattern selection mode', () => { + it('should disable switch for Kibana index patterns mode by default', async () => { + await visualBuilder.clickPanelOptions('timeSeries'); + const isEnabled = await visualBuilder.checkIndexPatternSelectionModeSwitchIsEnabled(); + expect(isEnabled).to.be(false); + }); + + describe('metrics:allowStringIndices = true', () => { + before(async () => { + await kibanaServer.uiSettings.update({ 'metrics:allowStringIndices': true }); + await browser.refresh(); + }); + + beforeEach(async () => await visualBuilder.clickPanelOptions('timeSeries')); + + it('should not disable switch for Kibana index patterns mode', async () => { + await visualBuilder.switchIndexPatternSelectionMode(true); + + const isEnabled = await visualBuilder.checkIndexPatternSelectionModeSwitchIsEnabled(); + expect(isEnabled).to.be(true); + }); + + it('should disable switch after selecting Kibana index patterns mode and metrics:allowStringIndices = false', async () => { + await visualBuilder.switchIndexPatternSelectionMode(false); + await kibanaServer.uiSettings.update({ 'metrics:allowStringIndices': false }); + await browser.refresh(); + await visualBuilder.clickPanelOptions('timeSeries'); + + let isEnabled = await visualBuilder.checkIndexPatternSelectionModeSwitchIsEnabled(); + expect(isEnabled).to.be(true); + + await visualBuilder.switchIndexPatternSelectionMode(true); + isEnabled = await visualBuilder.checkIndexPatternSelectionModeSwitchIsEnabled(); + expect(isEnabled).to.be(false); + }); + + after( + async () => + await kibanaServer.uiSettings.update({ 'metrics:allowStringIndices': false }) + ); + }); + }); }); }); } diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index c324de1231b7d..a9116591f8f73 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -502,12 +502,32 @@ export class VisualBuilderPageObject extends FtrService { return await annotationTooltipDetails.getVisibleText(); } + public async toggleIndexPatternSelectionModePopover(shouldOpen: boolean) { + const isPopoverOpened = await this.testSubjects.exists( + 'switchIndexPatternSelectionModePopoverContent' + ); + if ((shouldOpen && !isPopoverOpened) || (!shouldOpen && isPopoverOpened)) { + await this.testSubjects.click('switchIndexPatternSelectionModePopoverButton'); + } + } + public async switchIndexPatternSelectionMode(useKibanaIndices: boolean) { - await this.testSubjects.click('switchIndexPatternSelectionModePopover'); + await this.toggleIndexPatternSelectionModePopover(true); await this.testSubjects.setEuiSwitch( 'switchIndexPatternSelectionMode', useKibanaIndices ? 'check' : 'uncheck' ); + await this.toggleIndexPatternSelectionModePopover(false); + } + + public async checkIndexPatternSelectionModeSwitchIsEnabled() { + await this.toggleIndexPatternSelectionModePopover(true); + let isEnabled; + await this.testSubjects.retry.tryForTime(2000, async () => { + isEnabled = await this.testSubjects.isEnabled('switchIndexPatternSelectionMode'); + }); + await this.toggleIndexPatternSelectionModePopover(false); + return isEnabled; } public async setIndexPatternValue(value: string, useKibanaIndices?: boolean) { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index aac2d45ec2120..38c957292ec7d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5227,9 +5227,7 @@ "visTypeTimeseries.indexPatternSelect.label": "インデックスパターン", "visTypeTimeseries.indexPatternSelect.queryAllIndexesText": "すべてのインデックスにクエリを実行するには * を使用します", "visTypeTimeseries.indexPatternSelect.switchModePopover.areaLabel": "インデックスパターン選択モードを構成", - "visTypeTimeseries.indexPatternSelect.switchModePopover.text": "インデックスパターンは、データ探索で対象とする1つ以上のElasticsearchインデックスを定義します。ElasticsearchインデックスまたはKibanaインデックスパターン(推奨)を使用できます。", "visTypeTimeseries.indexPatternSelect.switchModePopover.title": "インデックスパターン選択モード", - "visTypeTimeseries.indexPatternSelect.switchModePopover.useKibanaIndices": "Kibanaインデックスパターンのみを使用", "visTypeTimeseries.kbnVisTypes.metricsDescription": "時系列データの高度な分析を実行します。", "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", "visTypeTimeseries.lastValueModeIndicator.lastBucketDate": "バケット:{lastBucketDate}", @@ -5556,7 +5554,6 @@ "visTypeTimeseries.visEditorVisualization.changesWillBeAutomaticallyAppliedMessage": "変更が自動的に適用されます。", "visTypeTimeseries.visEditorVisualization.indexPatternMode.dismissNoticeButtonText": "閉じる", "visTypeTimeseries.visEditorVisualization.indexPatternMode.link": "確認してください。", - "visTypeTimeseries.visEditorVisualization.indexPatternMode.notificationMessage": "お知らせElasticsearchインデックスまたはKibanaインデックスパターンからデータを可視化できるようになりました。{indexPatternModeLink}。", "visTypeTimeseries.visEditorVisualization.indexPatternMode.notificationTitle": "TSVBはインデックスパターンをサポートします", "visTypeTimeseries.visPicker.gaugeLabel": "ゲージ", "visTypeTimeseries.visPicker.metricLabel": "メトリック", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 73ad85a85f427..9f046ffc53cc0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5272,9 +5272,7 @@ "visTypeTimeseries.indexPatternSelect.label": "索引模式", "visTypeTimeseries.indexPatternSelect.queryAllIndexesText": "要查询所有索引,请使用 *", "visTypeTimeseries.indexPatternSelect.switchModePopover.areaLabel": "配置索引模式选择模式", - "visTypeTimeseries.indexPatternSelect.switchModePopover.text": "索引模式可识别一个或多个您希望浏览的 Elasticsearch 索引。可用使用 Elasticsearch 索引或 Kibana 索引模式(推荐)。", "visTypeTimeseries.indexPatternSelect.switchModePopover.title": "索引模式选择模式", - "visTypeTimeseries.indexPatternSelect.switchModePopover.useKibanaIndices": "仅使用 Kibana 索引模式", "visTypeTimeseries.kbnVisTypes.metricsDescription": "对时间序列数据执行高级分析。", "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", "visTypeTimeseries.lastValueModeIndicator.lastBucketDate": "存储桶:{lastBucketDate}", @@ -5602,7 +5600,6 @@ "visTypeTimeseries.visEditorVisualization.changesWillBeAutomaticallyAppliedMessage": "将自动应用更改。", "visTypeTimeseries.visEditorVisualization.indexPatternMode.dismissNoticeButtonText": "关闭", "visTypeTimeseries.visEditorVisualization.indexPatternMode.link": "请查看。", - "visTypeTimeseries.visEditorVisualization.indexPatternMode.notificationMessage": "好消息!现在可以可视化 Elasticsearch 索引或 Kibana 索引模式的数据。{indexPatternModeLink}。", "visTypeTimeseries.visEditorVisualization.indexPatternMode.notificationTitle": "TSVB 现在支持索引模式", "visTypeTimeseries.visPicker.gaugeLabel": "仪表盘", "visTypeTimeseries.visPicker.metricLabel": "指标", diff --git a/x-pack/test/functional/apps/rollup_job/tsvb.js b/x-pack/test/functional/apps/rollup_job/tsvb.js index 3b216bfb34b9b..4ef918aab09e4 100644 --- a/x-pack/test/functional/apps/rollup_job/tsvb.js +++ b/x-pack/test/functional/apps/rollup_job/tsvb.js @@ -43,6 +43,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.replace({ defaultIndex: 'rollup', }); + await kibanaServer.uiSettings.update({ 'metrics:allowStringIndices': true }); }); it('create rollup tsvb', async () => { @@ -110,6 +111,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.importExport.unload( 'x-pack/test/functional/fixtures/kbn_archiver/rollup/rollup.json' ); + await kibanaServer.uiSettings.update({ 'metrics:allowStringIndices': false }); await security.testUser.restoreDefaults(); }); }); From 914f2b7429bb2826a677dd068d20e8e92078d9d2 Mon Sep 17 00:00:00 2001 From: Marco Liberati <dej611@users.noreply.github.com> Date: Wed, 22 Sep 2021 10:14:43 +0200 Subject: [PATCH 50/69] :wrench: Make Timelion server send error details to client (#112663) --- .../helpers/timelion_request_handler.ts | 2 +- .../timelion/server/handlers/chain_runner.js | 10 ++++++++- .../vis_types/timelion/server/routes/run.ts | 21 +++++++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts index e44d74cfd72ab..e9a076b4dc832 100644 --- a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts +++ b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts @@ -120,7 +120,7 @@ export function getTimelionRequestHandler({ const err = new Error( `${i18n.translate('timelion.requestHandlerErrorTitle', { defaultMessage: 'Timelion request error', - })}: ${e.body.title} ${e.body.message}` + })}:${e.body.title ? ' ' + e.body.title : ''} ${e.body.message}` ); err.stack = e.stack; throw err; diff --git a/src/plugins/vis_types/timelion/server/handlers/chain_runner.js b/src/plugins/vis_types/timelion/server/handlers/chain_runner.js index b7bdbcdcb57a6..3710d015f3f69 100644 --- a/src/plugins/vis_types/timelion/server/handlers/chain_runner.js +++ b/src/plugins/vis_types/timelion/server/handlers/chain_runner.js @@ -25,7 +25,15 @@ export default function chainRunner(tlConfig) { let sheet; function throwWithCell(cell, exception) { - throw new Error(' in cell #' + (cell + 1) + ': ' + exception.message); + throw new Error( + i18n.translate('timelion.serverSideErrors.errorInCell', { + defaultMessage: ' in cell #{number}: {message}', + values: { + number: cell + 1, + message: exception.message, + }, + }) + ); } // Invokes a modifier function, resolving arguments into series as needed diff --git a/src/plugins/vis_types/timelion/server/routes/run.ts b/src/plugins/vis_types/timelion/server/routes/run.ts index b3ab3c61c15d8..b8c0ce4ea6599 100644 --- a/src/plugins/vis_types/timelion/server/routes/run.ts +++ b/src/plugins/vis_types/timelion/server/routes/run.ts @@ -94,15 +94,18 @@ export function runRoute( allowedGraphiteUrls: configManager.getGraphiteUrls(), esShardTimeout: configManager.getEsShardTimeout(), }); - const chainRunner = chainRunnerFn(tlConfig); - const sheet = await Bluebird.all(chainRunner.processRequest(request.body)); - - return response.ok({ - body: { - sheet, - stats: chainRunner.getStats(), - }, - }); + try { + const chainRunner = chainRunnerFn(tlConfig); + const sheet = await Bluebird.all(chainRunner.processRequest(request.body)); + return response.ok({ + body: { + sheet, + stats: chainRunner.getStats(), + }, + }); + } catch (e) { + return response.badRequest({ body: { message: e.message } }); + } }) ); } From ac010acea890b6937eb4a06fc25058db30c687cb Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Wed, 22 Sep 2021 04:20:25 -0400 Subject: [PATCH 51/69] Policy Details Store refactor into multiple modules (#112697) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../store/policy_details/action/index.ts | 11 ++ .../policy_settings_action.ts} | 12 +- .../action/policy_trusted_apps_action.ts | 13 ++ .../policy/store/policy_details/index.ts | 4 +- .../policy/store/policy_details/middleware.ts | 163 ------------------ .../store/policy_details/middleware/index.ts | 22 +++ .../middleware/policy_settings_middleware.ts | 144 ++++++++++++++++ .../policy_trusted_apps_middleware.ts | 16 ++ .../store/policy_details/reducer/index.ts | 25 +++ .../reducer/initial_policy_details_state.ts | 39 +++++ .../policy_settings_reducer.ts} | 47 +---- .../reducer/trusted_apps_reducer.ts | 19 ++ .../store/policy_details/selectors/index.ts | 9 + .../policy_settings_selectors.ts} | 16 +- .../selectors/trusted_apps_selectors.ts | 10 ++ .../public/management/pages/policy/types.ts | 13 ++ .../antivirus_registration_form/index.tsx | 2 +- .../public/management/store/reducer.ts | 2 +- 18 files changed, 348 insertions(+), 219 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/index.ts rename x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/{action.ts => action/policy_settings_action.ts} (83%) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_trusted_apps_action.ts delete mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/index.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_settings_middleware.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/index.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/initial_policy_details_state.ts rename x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/{reducer.ts => reducer/policy_settings_reducer.ts} (78%) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/trusted_apps_reducer.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/index.ts rename x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/{selectors.ts => selectors/policy_settings_selectors.ts} (95%) create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/trusted_apps_selectors.ts diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/index.ts new file mode 100644 index 0000000000000..ab84bb4f253ea --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PolicySettingsAction } from './policy_settings_action'; +import { PolicyTrustedAppsAction } from './policy_trusted_apps_action'; + +export type PolicyDetailsAction = PolicySettingsAction | PolicyTrustedAppsAction; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_settings_action.ts similarity index 83% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_settings_action.ts index 6bd39e9c24f96..eec0ab1c6445c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_settings_action.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { ILicense } from '../../../../../../../licensing/common/types'; -import { GetAgentStatusResponse } from '../../../../../../../fleet/common/types/rest_spec'; -import { PolicyData, UIPolicyConfig } from '../../../../../../common/endpoint/types'; -import { ServerApiError } from '../../../../../common/types'; -import { PolicyDetailsState } from '../../types'; +import { ILicense } from '../../../../../../../../licensing/common/types'; +import { GetAgentStatusResponse } from '../../../../../../../../fleet/common/types/rest_spec'; +import { PolicyData, UIPolicyConfig } from '../../../../../../../common/endpoint/types'; +import { ServerApiError } from '../../../../../../common/types'; +import { PolicyDetailsState } from '../../../types'; export interface ServerReturnedPolicyDetailsData { type: 'serverReturnedPolicyDetailsData'; @@ -69,7 +69,7 @@ export interface LicenseChanged { payload: ILicense; } -export type PolicyDetailsAction = +export type PolicySettingsAction = | ServerReturnedPolicyDetailsData | UserClickedPolicyDetailsSaveButton | ServerReturnedPolicyDetailsAgentSummaryData diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_trusted_apps_action.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_trusted_apps_action.ts new file mode 100644 index 0000000000000..46e0f8293cc33 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action/policy_trusted_apps_action.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// TODO: defined trusted apps actions (code below only here to silence TS) +export type PolicyTrustedAppsAction = + | { + type: 'a'; + } + | { type: 'b' }; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.ts index bc83c7438eba8..bc9e42ddf7f52 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.ts @@ -11,8 +11,7 @@ import { AppAction } from '../../../../../common/store/actions'; import { Immutable } from '../../../../../../common/endpoint/types'; export { policyDetailsMiddlewareFactory } from './middleware'; -export { PolicyDetailsAction } from './action'; -export { policyDetailsReducer } from './reducer'; +export { policyDetailsReducer, initialPolicyDetailsState } from './reducer'; export interface EndpointPolicyDetailsStatePluginState { policyDetails: Immutable<PolicyDetailsState>; @@ -21,3 +20,4 @@ export interface EndpointPolicyDetailsStatePluginState { export interface EndpointPolicyDetailsStatePluginReducer { policyDetails: ImmutableReducer<PolicyDetailsState, AppAction>; } +export { PolicyDetailsAction } from './action'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts deleted file mode 100644 index 3d90b8d640ac8..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IHttpFetchError } from 'kibana/public'; -import { - DefaultPolicyNotificationMessage, - DefaultPolicyRuleNotificationMessage, -} from '../../../../../../common/endpoint/models/policy_config'; -import { PolicyDetailsState, UpdatePolicyResponse } from '../../types'; -import { - policyIdFromParams, - isOnPolicyDetailsPage, - policyDetails, - policyDetailsForUpdate, - needsToRefresh, -} from './selectors'; -import { - sendGetPackagePolicy, - sendGetFleetAgentStatusForPolicy, - sendPutPackagePolicy, -} from '../services/ingest'; -import { NewPolicyData, PolicyData } from '../../../../../../common/endpoint/types'; -import { ImmutableMiddlewareFactory } from '../../../../../common/store'; -import { getPolicyDataForUpdate } from '../../../../../../common/endpoint/service/policy/get_policy_data_for_update'; - -export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory<PolicyDetailsState> = ( - coreStart -) => { - const http = coreStart.http; - return ({ getState, dispatch }) => - (next) => - async (action) => { - next(action); - const state = getState(); - - if ( - action.type === 'userChangedUrl' && - needsToRefresh(state) && - isOnPolicyDetailsPage(state) - ) { - const id = policyIdFromParams(state); - let policyItem: PolicyData; - - try { - policyItem = (await sendGetPackagePolicy(http, id)).item; - // sets default user notification message if policy config message is empty - if (policyItem.inputs[0].config.policy.value.windows.popup.malware.message === '') { - policyItem.inputs[0].config.policy.value.windows.popup.malware.message = - DefaultPolicyNotificationMessage; - policyItem.inputs[0].config.policy.value.mac.popup.malware.message = - DefaultPolicyNotificationMessage; - policyItem.inputs[0].config.policy.value.linux.popup.malware.message = - DefaultPolicyNotificationMessage; - } - if (policyItem.inputs[0].config.policy.value.windows.popup.ransomware.message === '') { - policyItem.inputs[0].config.policy.value.windows.popup.ransomware.message = - DefaultPolicyNotificationMessage; - } - if ( - policyItem.inputs[0].config.policy.value.windows.popup.memory_protection.message === '' - ) { - policyItem.inputs[0].config.policy.value.windows.popup.memory_protection.message = - DefaultPolicyRuleNotificationMessage; - } - if ( - policyItem.inputs[0].config.policy.value.windows.popup.behavior_protection.message === - '' - ) { - policyItem.inputs[0].config.policy.value.windows.popup.behavior_protection.message = - DefaultPolicyRuleNotificationMessage; - } - if ( - policyItem.inputs[0].config.policy.value.mac.popup.behavior_protection.message === '' - ) { - policyItem.inputs[0].config.policy.value.mac.popup.behavior_protection.message = - DefaultPolicyRuleNotificationMessage; - } - if ( - policyItem.inputs[0].config.policy.value.linux.popup.behavior_protection.message === '' - ) { - policyItem.inputs[0].config.policy.value.linux.popup.behavior_protection.message = - DefaultPolicyRuleNotificationMessage; - } - } catch (error) { - dispatch({ - type: 'serverFailedToReturnPolicyDetailsData', - payload: error.body || error, - }); - return; - } - - dispatch({ - type: 'serverReturnedPolicyDetailsData', - payload: { - policyItem, - }, - }); - - // Agent summary is secondary data, so its ok for it to come after the details - // page is populated with the main content - if (policyItem.policy_id) { - const { results } = await sendGetFleetAgentStatusForPolicy(http, policyItem.policy_id); - dispatch({ - type: 'serverReturnedPolicyDetailsAgentSummaryData', - payload: { - agentStatusSummary: results, - }, - }); - } - } else if (action.type === 'userClickedPolicyDetailsSaveButton') { - const { id } = policyDetails(state) as PolicyData; - const updatedPolicyItem = policyDetailsForUpdate(state) as NewPolicyData; - - let apiResponse: UpdatePolicyResponse; - try { - apiResponse = await sendPutPackagePolicy(http, id, updatedPolicyItem).catch( - (error: IHttpFetchError) => { - if (!error.response || error.response.status !== 409) { - return Promise.reject(error); - } - // Handle 409 error (version conflict) here, by using the latest document - // for the package policy and adding the updated policy to it, ensuring that - // any recent updates to `manifest_artifacts` are retained. - return sendGetPackagePolicy(http, id).then((packagePolicy) => { - const latestUpdatedPolicyItem = packagePolicy.item; - latestUpdatedPolicyItem.inputs[0].config.policy = - updatedPolicyItem.inputs[0].config.policy; - - return sendPutPackagePolicy( - http, - id, - getPolicyDataForUpdate(latestUpdatedPolicyItem) - ); - }); - } - ); - } catch (error) { - dispatch({ - type: 'serverReturnedPolicyDetailsUpdateFailure', - payload: { - success: false, - error: error.body || error, - }, - }); - return; - } - - dispatch({ - type: 'serverReturnedUpdatedPolicyDetailsData', - payload: { - policyItem: apiResponse.item, - updateStatus: { - success: true, - }, - }, - }); - } - }; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/index.ts new file mode 100644 index 0000000000000..6b7e4e7d541c8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ImmutableMiddlewareFactory } from '../../../../../../common/store'; +import { PolicyDetailsState } from '../../../types'; +import { policyTrustedAppsMiddlewareRunner } from './policy_trusted_apps_middleware'; +import { policySettingsMiddlewareRunner } from './policy_settings_middleware'; + +export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory<PolicyDetailsState> = ( + coreStart +) => { + return (store) => (next) => async (action) => { + next(action); + + policySettingsMiddlewareRunner(coreStart, store, action); + policyTrustedAppsMiddlewareRunner(coreStart, store, action); + }; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_settings_middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_settings_middleware.ts new file mode 100644 index 0000000000000..73b244944e502 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_settings_middleware.ts @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IHttpFetchError } from 'kibana/public'; +import { + DefaultPolicyNotificationMessage, + DefaultPolicyRuleNotificationMessage, +} from '../../../../../../../common/endpoint/models/policy_config'; +import { MiddlewareRunner, UpdatePolicyResponse } from '../../../types'; +import { + policyIdFromParams, + isOnPolicyDetailsPage, + policyDetails, + policyDetailsForUpdate, + needsToRefresh, +} from '../selectors/policy_settings_selectors'; +import { + sendGetPackagePolicy, + sendGetFleetAgentStatusForPolicy, + sendPutPackagePolicy, +} from '../../services/ingest'; +import { NewPolicyData, PolicyData } from '../../../../../../../common/endpoint/types'; +import { getPolicyDataForUpdate } from '../../../../../../../common/endpoint/service/policy'; + +export const policySettingsMiddlewareRunner: MiddlewareRunner = async ( + coreStart, + { dispatch, getState }, + action +) => { + const http = coreStart.http; + const state = getState(); + + if (action.type === 'userChangedUrl' && needsToRefresh(state) && isOnPolicyDetailsPage(state)) { + const id = policyIdFromParams(state); + let policyItem: PolicyData; + + try { + policyItem = (await sendGetPackagePolicy(http, id)).item; + // sets default user notification message if policy config message is empty + if (policyItem.inputs[0].config.policy.value.windows.popup.malware.message === '') { + policyItem.inputs[0].config.policy.value.windows.popup.malware.message = + DefaultPolicyNotificationMessage; + policyItem.inputs[0].config.policy.value.mac.popup.malware.message = + DefaultPolicyNotificationMessage; + policyItem.inputs[0].config.policy.value.linux.popup.malware.message = + DefaultPolicyNotificationMessage; + } + if (policyItem.inputs[0].config.policy.value.windows.popup.ransomware.message === '') { + policyItem.inputs[0].config.policy.value.windows.popup.ransomware.message = + DefaultPolicyNotificationMessage; + } + if (policyItem.inputs[0].config.policy.value.windows.popup.memory_protection.message === '') { + policyItem.inputs[0].config.policy.value.windows.popup.memory_protection.message = + DefaultPolicyRuleNotificationMessage; + } + if ( + policyItem.inputs[0].config.policy.value.windows.popup.behavior_protection.message === '' + ) { + policyItem.inputs[0].config.policy.value.windows.popup.behavior_protection.message = + DefaultPolicyRuleNotificationMessage; + } + if (policyItem.inputs[0].config.policy.value.mac.popup.behavior_protection.message === '') { + policyItem.inputs[0].config.policy.value.mac.popup.behavior_protection.message = + DefaultPolicyRuleNotificationMessage; + } + if (policyItem.inputs[0].config.policy.value.linux.popup.behavior_protection.message === '') { + policyItem.inputs[0].config.policy.value.linux.popup.behavior_protection.message = + DefaultPolicyRuleNotificationMessage; + } + } catch (error) { + dispatch({ + type: 'serverFailedToReturnPolicyDetailsData', + payload: error.body || error, + }); + return; + } + + dispatch({ + type: 'serverReturnedPolicyDetailsData', + payload: { + policyItem, + }, + }); + + // Agent summary is secondary data, so its ok for it to come after the details + // page is populated with the main content + if (policyItem.policy_id) { + const { results } = await sendGetFleetAgentStatusForPolicy(http, policyItem.policy_id); + dispatch({ + type: 'serverReturnedPolicyDetailsAgentSummaryData', + payload: { + agentStatusSummary: results, + }, + }); + } + } else if (action.type === 'userClickedPolicyDetailsSaveButton') { + const { id } = policyDetails(state) as PolicyData; + const updatedPolicyItem = policyDetailsForUpdate(state) as NewPolicyData; + + let apiResponse: UpdatePolicyResponse; + try { + apiResponse = await sendPutPackagePolicy(http, id, updatedPolicyItem).catch( + (error: IHttpFetchError) => { + if (!error.response || error.response.status !== 409) { + return Promise.reject(error); + } + // Handle 409 error (version conflict) here, by using the latest document + // for the package policy and adding the updated policy to it, ensuring that + // any recent updates to `manifest_artifacts` are retained. + return sendGetPackagePolicy(http, id).then((packagePolicy) => { + const latestUpdatedPolicyItem = packagePolicy.item; + latestUpdatedPolicyItem.inputs[0].config.policy = + updatedPolicyItem.inputs[0].config.policy; + + return sendPutPackagePolicy(http, id, getPolicyDataForUpdate(latestUpdatedPolicyItem)); + }); + } + ); + } catch (error) { + dispatch({ + type: 'serverReturnedPolicyDetailsUpdateFailure', + payload: { + success: false, + error: error.body || error, + }, + }); + return; + } + + dispatch({ + type: 'serverReturnedUpdatedPolicyDetailsData', + payload: { + policyItem: apiResponse.item, + updateStatus: { + success: true, + }, + }, + }); + } +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.ts new file mode 100644 index 0000000000000..171bbd881302e --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware/policy_trusted_apps_middleware.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MiddlewareRunner } from '../../../types'; + +export const policyTrustedAppsMiddlewareRunner: MiddlewareRunner = async ( + coreStart, + store, + action +) => { + // FIXME: implement middlware for trusted apps +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/index.ts new file mode 100644 index 0000000000000..a577c1ca85ef0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/index.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ImmutableReducer } from '../../../../../../common/store'; +import { PolicyDetailsState } from '../../../types'; +import { AppAction } from '../../../../../../common/store/actions'; +import { policySettingsReducer } from './policy_settings_reducer'; +import { initialPolicyDetailsState } from './initial_policy_details_state'; +import { policyTrustedAppsReducer } from './trusted_apps_reducer'; + +export * from './initial_policy_details_state'; + +export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = ( + state = initialPolicyDetailsState(), + action +) => { + return [policySettingsReducer, policyTrustedAppsReducer].reduce( + (updatedState, runReducer) => runReducer(updatedState, action), + state + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/initial_policy_details_state.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/initial_policy_details_state.ts new file mode 100644 index 0000000000000..723f8fe31bd2a --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/initial_policy_details_state.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Immutable } from '../../../../../../../common/endpoint/types'; +import { PolicyDetailsState } from '../../../types'; +import { + MANAGEMENT_DEFAULT_PAGE, + MANAGEMENT_DEFAULT_PAGE_SIZE, +} from '../../../../../common/constants'; +import { createUninitialisedResourceState } from '../../../../../state'; + +/** + * Return a fresh copy of initial state, since we mutate state in the reducer. + */ +export const initialPolicyDetailsState: () => Immutable<PolicyDetailsState> = () => ({ + policyItem: undefined, + isLoading: false, + agentStatusSummary: { + error: 0, + events: 0, + offline: 0, + online: 0, + total: 0, + other: 0, + }, + artifacts: { + location: { + page_index: MANAGEMENT_DEFAULT_PAGE, + page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: undefined, + filter: '', + }, + availableList: createUninitialisedResourceState(), + }, +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/policy_settings_reducer.ts similarity index 78% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/policy_settings_reducer.ts index cfd808fb9b621..9997e547e8148 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/policy_settings_reducer.ts @@ -7,22 +7,18 @@ // eslint-disable-next-line import/no-nodejs-modules import { parse } from 'querystring'; -import { fullPolicy, isOnPolicyDetailsPage, license } from './selectors'; +import { fullPolicy, isOnPolicyDetailsPage, license } from '../selectors/policy_settings_selectors'; import { Immutable, PolicyConfig, - UIPolicyConfig, PolicyData, -} from '../../../../../../common/endpoint/types'; -import { ImmutableReducer } from '../../../../../common/store'; -import { AppAction } from '../../../../../common/store/actions'; -import { PolicyDetailsState } from '../../types'; -import { - MANAGEMENT_DEFAULT_PAGE, - MANAGEMENT_DEFAULT_PAGE_SIZE, -} from '../../../../common/constants'; -import { extractPolicyDetailsArtifactsListPageLocation } from '../../../../common/routing'; -import { createUninitialisedResourceState } from '../../../../state'; + UIPolicyConfig, +} from '../../../../../../../common/endpoint/types'; +import { ImmutableReducer } from '../../../../../../common/store'; +import { AppAction } from '../../../../../../common/store/actions'; +import { PolicyDetailsState } from '../../../types'; +import { extractPolicyDetailsArtifactsListPageLocation } from '../../../../../common/routing'; +import { initialPolicyDetailsState } from './initial_policy_details_state'; const updatePolicyConfigInPolicyData = ( policyData: Immutable<PolicyData>, @@ -41,32 +37,7 @@ const updatePolicyConfigInPolicyData = ( })), }); -/** - * Return a fresh copy of initial state, since we mutate state in the reducer. - */ -export const initialPolicyDetailsState: () => Immutable<PolicyDetailsState> = () => ({ - policyItem: undefined, - isLoading: false, - agentStatusSummary: { - error: 0, - events: 0, - offline: 0, - online: 0, - total: 0, - other: 0, - }, - artifacts: { - location: { - page_index: MANAGEMENT_DEFAULT_PAGE, - page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, - show: undefined, - filter: '', - }, - availableList: createUninitialisedResourceState(), - }, -}); - -export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = ( +export const policySettingsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = ( state = initialPolicyDetailsState(), action ) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/trusted_apps_reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/trusted_apps_reducer.ts new file mode 100644 index 0000000000000..7f2f9e437ca06 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer/trusted_apps_reducer.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ImmutableReducer } from '../../../../../../common/store'; +import { PolicyDetailsState } from '../../../types'; +import { AppAction } from '../../../../../../common/store/actions'; +import { initialPolicyDetailsState } from './initial_policy_details_state'; + +export const policyTrustedAppsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = ( + state = initialPolicyDetailsState(), + action +) => { + // FIXME: implement trusted apps reducer + return state; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/index.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/index.ts new file mode 100644 index 0000000000000..af5be5c39480e --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './policy_settings_selectors'; +export * from './trusted_apps_selectors'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/policy_settings_selectors.ts similarity index 95% rename from x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts rename to x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/policy_settings_selectors.ts index 1aae538cbf47e..23ab0fd73c9e1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/policy_settings_selectors.ts @@ -7,23 +7,23 @@ import { createSelector } from 'reselect'; import { matchPath } from 'react-router-dom'; -import { ILicense } from '../../../../../../../licensing/common/types'; -import { unsetPolicyFeaturesAccordingToLicenseLevel } from '../../../../../../common/license/policy_config'; -import { PolicyDetailsArtifactsPageLocation, PolicyDetailsState } from '../../types'; +import { ILicense } from '../../../../../../../../licensing/common/types'; +import { unsetPolicyFeaturesAccordingToLicenseLevel } from '../../../../../../../common/license/policy_config'; +import { PolicyDetailsArtifactsPageLocation, PolicyDetailsState } from '../../../types'; import { Immutable, NewPolicyData, PolicyConfig, PolicyData, UIPolicyConfig, -} from '../../../../../../common/endpoint/types'; -import { policyFactory as policyConfigFactory } from '../../../../../../common/endpoint/models/policy_config'; +} from '../../../../../../../common/endpoint/types'; +import { policyFactory as policyConfigFactory } from '../../../../../../../common/endpoint/models/policy_config'; import { MANAGEMENT_ROUTING_POLICY_DETAILS_FORM_PATH, MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH, -} from '../../../../common/constants'; -import { ManagementRoutePolicyDetailsParams } from '../../../../types'; -import { getPolicyDataForUpdate } from '../../../../../../common/endpoint/service/policy/get_policy_data_for_update'; +} from '../../../../../common/constants'; +import { ManagementRoutePolicyDetailsParams } from '../../../../../types'; +import { getPolicyDataForUpdate } from '../../../../../../../common/endpoint/service/policy/get_policy_data_for_update'; /** Returns the policy details */ export const policyDetails = (state: Immutable<PolicyDetailsState>) => state.policyItem; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/trusted_apps_selectors.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/trusted_apps_selectors.ts new file mode 100644 index 0000000000000..f7a568b5ade0e --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/selectors/trusted_apps_selectors.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const isOnTrustedAppsView = () => { + return true; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts index 14d740510d251..9000fb469afd3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { CoreStart } from 'kibana/public'; import { ILicense } from '../../../../../licensing/common/types'; import { AppLocation, @@ -12,6 +13,7 @@ import { ProtectionFields, PolicyData, UIPolicyConfig, + MaybeImmutable, } from '../../../../common/endpoint/types'; import { ServerApiError } from '../../../common/types'; import { @@ -23,6 +25,17 @@ import { } from '../../../../../fleet/common'; import { AsyncResourceState } from '../../state'; import { TrustedAppsListData } from '../trusted_apps/state'; +import { ImmutableMiddlewareAPI } from '../../../common/store'; +import { AppAction } from '../../../common/store/actions'; + +/** + * Function that runs Policy Details middleware + */ +export type MiddlewareRunner = ( + coreStart: CoreStart, + store: ImmutableMiddlewareAPI<PolicyDetailsState, AppAction>, + action: MaybeImmutable<AppAction> +) => Promise<void>; /** * Policy list store state diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx index 51edf1a200c53..45aad6c3d1432 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/antivirus_registration_form/index.tsx @@ -13,7 +13,7 @@ import { EuiSpacer, EuiSwitch, EuiText } from '@elastic/eui'; import { OperatingSystem } from '../../../../../../../common/endpoint/types'; import { isAntivirusRegistrationEnabled } from '../../../store/policy_details/selectors'; import { usePolicyDetailsSelector } from '../../policy_hooks'; -import { ConfigForm } from '../../components/config_form'; +import { ConfigForm } from '../config_form'; const TRANSLATIONS: Readonly<{ [K in 'title' | 'description' | 'label']: string }> = { title: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/management/store/reducer.ts b/x-pack/plugins/security_solution/public/management/store/reducer.ts index 25c7c87c6f5c9..662d2b4322bcb 100644 --- a/x-pack/plugins/security_solution/public/management/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/store/reducer.ts @@ -9,7 +9,7 @@ import { combineReducers } from 'redux'; import { policyDetailsReducer, initialPolicyDetailsState, -} from '../pages/policy/store/policy_details/reducer'; +} from '../pages/policy/store/policy_details'; import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, From 9b410ce5447447ad7b2dda26eba354447d3c0d45 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli <efstratia.kalafateli@elastic.co> Date: Wed, 22 Sep 2021 11:21:53 +0300 Subject: [PATCH 52/69] Fixes bug with nested terms, one with other bucket and the other disabled (#112636) --- .../_terms_other_bucket_helper.test.ts | 80 +++++++++++++++++++ .../buckets/_terms_other_bucket_helper.ts | 4 +- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts index 215d3ce13f55e..7ec176d7ab11a 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts @@ -377,6 +377,86 @@ describe('Terms Agg Other bucket helper', () => { } }); + test('correctly builds query for nested terms agg with one disabled', () => { + const oneDisabledNestedTerms = { + aggs: [ + { + id: '2', + type: BUCKET_TYPES.TERMS, + enabled: false, + params: { + field: { + name: 'machine.os.raw', + indexPattern, + filterable: true, + }, + size: 2, + otherBucket: false, + missingBucket: true, + }, + }, + { + id: '1', + type: BUCKET_TYPES.TERMS, + params: { + field: { + name: 'geo.src', + indexPattern, + filterable: true, + }, + size: 2, + otherBucket: true, + missingBucket: false, + }, + }, + ], + }; + const aggConfigs = getAggConfigs(oneDisabledNestedTerms.aggs); + const agg = buildOtherBucketAgg( + aggConfigs, + aggConfigs.aggs[1] as IBucketAggConfig, + singleTermResponse + ); + const expectedResponse = { + 'other-filter': { + aggs: undefined, + filters: { + filters: { + '': { + bool: { + filter: [ + { + exists: { + field: 'geo.src', + }, + }, + ], + must: [], + must_not: [ + { + match_phrase: { + 'geo.src': 'ios', + }, + }, + { + match_phrase: { + 'geo.src': 'win xp', + }, + }, + ], + should: [], + }, + }, + }, + }, + }, + }; + expect(agg).toBeDefined(); + if (agg) { + expect(agg()).toEqual(expectedResponse); + } + }); + test('does not build query if sum_other_doc_count is 0 (exhaustive terms)', () => { const aggConfigs = getAggConfigs(nestedTerm.aggs); expect( diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts index 39fba23a42210..436cc5614ac80 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts @@ -129,7 +129,9 @@ export const buildOtherBucketAgg = ( aggWithOtherBucket: IBucketAggConfig, response: any ) => { - const bucketAggs = aggConfigs.aggs.filter((agg) => agg.type.type === AggGroupNames.Buckets); + const bucketAggs = aggConfigs.aggs.filter( + (agg) => agg.type.type === AggGroupNames.Buckets && agg.enabled + ); const index = bucketAggs.findIndex((agg) => agg.id === aggWithOtherBucket.id); const aggs = aggConfigs.toDsl(); const indexPattern = aggWithOtherBucket.aggConfigs.indexPattern; From 0cbdf3f259de2e7846e13681a051a763f3934f46 Mon Sep 17 00:00:00 2001 From: Marta Bondyra <marta.bondyra@gmail.com> Date: Wed, 22 Sep 2021 11:14:40 +0200 Subject: [PATCH 53/69] [Lens] Thresholds added (#108342) Co-authored-by: Marta Bondyra <marta.bondyra@gmail.com> Co-authored-by: dej611 <dej611@gmail.com> Co-authored-by: Marco Liberati <dej611@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../expressions/xy_chart/axis_config.ts | 26 +- .../public/assets/chart_bar_threshold.tsx | 40 ++ .../editor_frame/config_panel/add_layer.tsx | 15 +- .../config_panel/config_panel.test.tsx | 168 +++++++- .../config_panel/config_panel.tsx | 109 ++++- .../config_panel/layer_panel.test.tsx | 30 ++ .../editor_frame/config_panel/layer_panel.tsx | 39 +- .../config_panel/layer_settings.test.tsx | 71 +++ .../config_panel/layer_settings.tsx | 27 +- .../editor_frame/suggestion_helpers.test.ts | 277 ++++++------ .../editor_frame/suggestion_helpers.ts | 48 +-- .../editor_frame/suggestion_panel.tsx | 4 +- .../workspace_panel/chart_switch.tsx | 5 +- .../dimension_panel/dimension_editor.tsx | 300 +++++++------ .../dimension_panel/dimension_panel.test.tsx | 139 +++++- .../dimension_panel/dimension_panel.tsx | 29 +- .../dimensions_editor_helpers.tsx | 223 ++++++++++ .../droppable/droppable.test.ts | 4 +- .../droppable/on_drop_handler.ts | 8 +- .../indexpattern.test.ts | 83 ++++ .../indexpattern_datasource/indexpattern.tsx | 32 +- .../indexpattern_suggestions.test.tsx | 85 ++++ .../indexpattern_suggestions.ts | 8 +- .../definitions/formula/formula.test.tsx | 27 ++ .../definitions/formula/generate.ts | 5 + .../operations/definitions/helpers.tsx | 9 +- .../operations/definitions/index.ts | 7 +- .../definitions/static_value.test.tsx | 404 ++++++++++++++++++ .../operations/definitions/static_value.tsx | 222 ++++++++++ .../operations/layer_helpers.ts | 31 +- .../operations/operations.test.ts | 4 + x-pack/plugins/lens/public/mocks.tsx | 10 +- .../shared_components/debounced_value.ts | 4 + .../lens/public/shared_components/index.ts | 1 + .../shared_components/static_header.tsx | 31 ++ .../init_middleware/load_initial.ts | 2 +- x-pack/plugins/lens/public/types.ts | 19 +- .../xy_visualization/axes_configuration.ts | 25 +- .../public/xy_visualization/expression.tsx | 16 + .../expression_thresholds.tsx | 193 +++++++++ .../public/xy_visualization/state_helpers.ts | 8 + .../xy_visualization/threshold_helpers.tsx | 165 +++++++ .../public/xy_visualization/to_expression.ts | 4 + .../xy_visualization/visualization.test.ts | 335 ++++++++++++++- .../public/xy_visualization/visualization.tsx | 281 ++++++++---- .../visualization_helpers.tsx | 116 +++++ .../axis_settings_popover.test.tsx | 4 +- .../axis_settings_popover.tsx | 18 +- .../xy_config_panel/color_picker.tsx | 175 ++++++++ .../index.tsx} | 270 ++---------- .../xy_config_panel/layer_header.tsx | 115 +++++ .../xy_config_panel/threshold_panel.tsx | 326 ++++++++++++++ .../fill_opacity_option.test.tsx | 0 .../fill_opacity_option.tsx | 2 +- .../visual_options_popover/index.tsx} | 10 +- .../line_curve_option.test.tsx | 0 .../line_curve_option.tsx | 2 +- .../missing_value_option.test.tsx | 0 .../missing_values_option.tsx | 4 +- .../visual_options_popover.test.tsx | 12 +- .../xy_config_panel.scss | 0 .../xy_config_panel.test.tsx | 10 +- x-pack/test/functional/apps/lens/formula.ts | 32 ++ x-pack/test/functional/apps/lens/index.ts | 1 + .../test/functional/apps/lens/smokescreen.ts | 11 + .../test/functional/apps/lens/thresholds.ts | 68 +++ .../test/functional/page_objects/lens_page.ts | 19 +- 67 files changed, 4007 insertions(+), 761 deletions(-) create mode 100644 x-pack/plugins/lens/public/assets/chart_bar_threshold.tsx create mode 100644 x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.test.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx create mode 100644 x-pack/plugins/lens/public/shared_components/static_header.tsx create mode 100644 x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx create mode 100644 x-pack/plugins/lens/public/xy_visualization/threshold_helpers.tsx create mode 100644 x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/axis_settings_popover.test.tsx (98%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/axis_settings_popover.tsx (96%) create mode 100644 x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx rename x-pack/plugins/lens/public/xy_visualization/{xy_config_panel.tsx => xy_config_panel/index.tsx} (72%) create mode 100644 x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx create mode 100644 x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/fill_opacity_option.test.tsx (100%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/fill_opacity_option.tsx (95%) rename x-pack/plugins/lens/public/xy_visualization/{visual_options_popover/visual_options_popover.tsx => xy_config_panel/visual_options_popover/index.tsx} (93%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/line_curve_option.test.tsx (100%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/line_curve_option.tsx (95%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/missing_value_option.test.tsx (100%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/missing_values_option.tsx (97%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/visual_options_popover/visual_options_popover.test.tsx (96%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/xy_config_panel.scss (100%) rename x-pack/plugins/lens/public/xy_visualization/{ => xy_config_panel}/xy_config_panel.test.tsx (98%) create mode 100644 x-pack/test/functional/apps/lens/thresholds.ts diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts index 9a9273e43f6f1..29b0fb1352e5b 100644 --- a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts +++ b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts @@ -27,12 +27,18 @@ interface AxisConfig { hide?: boolean; } -export type YAxisMode = 'auto' | 'left' | 'right'; +export type YAxisMode = 'auto' | 'left' | 'right' | 'bottom'; +export type LineStyle = 'solid' | 'dashed' | 'dotted'; +export type FillStyle = 'none' | 'above' | 'below'; export interface YConfig { forAccessor: string; axisMode?: YAxisMode; color?: string; + icon?: string; + lineWidth?: number; + lineStyle?: LineStyle; + fill?: FillStyle; } export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { @@ -161,6 +167,24 @@ export const yAxisConfig: ExpressionFunctionDefinition< types: ['string'], help: 'The color of the series', }, + lineStyle: { + types: ['string'], + options: ['solid', 'dotted', 'dashed'], + help: 'The style of the threshold line', + }, + lineWidth: { + types: ['number'], + help: 'The width of the threshold line', + }, + icon: { + types: ['string'], + help: 'An optional icon used for threshold lines', + }, + fill: { + types: ['string'], + options: ['none', 'above', 'below'], + help: '', + }, }, fn: function fn(input: unknown, args: YConfig) { return { diff --git a/x-pack/plugins/lens/public/assets/chart_bar_threshold.tsx b/x-pack/plugins/lens/public/assets/chart_bar_threshold.tsx new file mode 100644 index 0000000000000..88e0a46b5538c --- /dev/null +++ b/x-pack/plugins/lens/public/assets/chart_bar_threshold.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const LensIconChartBarThreshold = ({ + title, + titleId, + ...props +}: Omit<EuiIconProps, 'type'>) => ( + <svg + viewBox="0 0 16 12" + width={30} + height={22} + fill="none" + xmlns="http://www.w3.org/2000/svg" + aria-labelledby={titleId} + {...props} + > + {title ? <title id={titleId}>{title} : null} + + + + + +); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx index 0259acc4dcca1..69e4aa629cec6 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; import { EuiToolTip, EuiButton, @@ -38,12 +38,17 @@ export function AddLayerButton({ }: AddLayerButtonProps) { const [showLayersChoice, toggleLayersChoice] = useState(false); - const hasMultipleLayers = Boolean(visualization.appendLayer && visualizationState); - if (!hasMultipleLayers) { + const supportedLayers = useMemo(() => { + if (!visualization.appendLayer || !visualizationState) { + return null; + } + return visualization.getSupportedLayers?.(visualizationState, layersMeta); + }, [visualization, visualizationState, layersMeta]); + + if (supportedLayers == null) { return null; } - const supportedLayers = visualization.getSupportedLayers?.(visualizationState, layersMeta); - if (supportedLayers?.length === 1) { + if (supportedLayers.length === 1) { return ( new Promise((r) => setTimeout(r, time)); + let container: HTMLDivElement | undefined; beforeEach(() => { @@ -137,7 +141,7 @@ describe('ConfigPanel', () => { const updater = () => 'updated'; updateDatasource('mockindexpattern', updater); - await new Promise((r) => setTimeout(r, 0)); + await waitMs(0); expect(lensStore.dispatch).toHaveBeenCalledTimes(1); expect( (lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater( @@ -147,7 +151,7 @@ describe('ConfigPanel', () => { updateAll('mockindexpattern', updater, props.visualizationState); // wait for one tick so async updater has a chance to trigger - await new Promise((r) => setTimeout(r, 0)); + await waitMs(0); expect(lensStore.dispatch).toHaveBeenCalledTimes(2); expect( (lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater( @@ -293,4 +297,164 @@ describe('ConfigPanel', () => { expect(focusedEl?.children[0].getAttribute('data-test-subj')).toEqual('lns-layerPanel-1'); }); }); + + describe('initial default value', () => { + function prepareAndMountComponent(props: ReturnType) { + (generateId as jest.Mock).mockReturnValue(`newId`); + return mountWithProvider( + , + + { + preloadedState: { + datasourceStates: { + mockindexpattern: { + isLoading: false, + state: 'state', + }, + }, + activeDatasourceId: 'mockindexpattern', + }, + }, + { + attachTo: container, + } + ); + } + function clickToAddLayer(instance: ReactWrapper) { + act(() => { + instance.find('[data-test-subj="lnsLayerAddButton"]').first().simulate('click'); + }); + instance.update(); + act(() => { + instance + .find(`[data-test-subj="lnsLayerAddButton-${layerTypes.THRESHOLD}"]`) + .first() + .simulate('click'); + }); + instance.update(); + + return waitMs(0); + } + + function clickToAddDimension(instance: ReactWrapper) { + act(() => { + instance.find('[data-test-subj="lns-empty-dimension"]').last().simulate('click'); + }); + return waitMs(0); + } + + it('should not add an initial dimension when not specified', async () => { + const props = getDefaultProps(); + props.activeVisualization.getSupportedLayers = jest.fn(() => [ + { type: layerTypes.DATA, label: 'Data Layer' }, + { + type: layerTypes.THRESHOLD, + label: 'Threshold layer', + }, + ]); + mockDatasource.initializeDimension = jest.fn(); + + const { instance, lensStore } = await prepareAndMountComponent(props); + await clickToAddLayer(instance); + + expect(lensStore.dispatch).toHaveBeenCalledTimes(1); + expect(mockDatasource.initializeDimension).not.toHaveBeenCalled(); + }); + + it('should not add an initial dimension when initialDimensions are not available for the given layer type', async () => { + const props = getDefaultProps(); + props.activeVisualization.getSupportedLayers = jest.fn(() => [ + { + type: layerTypes.DATA, + label: 'Data Layer', + initialDimensions: [ + { + groupId: 'testGroup', + columnId: 'myColumn', + dataType: 'number', + label: 'Initial value', + staticValue: 100, + }, + ], + }, + { + type: layerTypes.THRESHOLD, + label: 'Threshold layer', + }, + ]); + mockDatasource.initializeDimension = jest.fn(); + + const { instance, lensStore } = await prepareAndMountComponent(props); + await clickToAddLayer(instance); + + expect(lensStore.dispatch).toHaveBeenCalledTimes(1); + expect(mockDatasource.initializeDimension).not.toHaveBeenCalled(); + }); + + it('should use group initial dimension value when adding a new layer if available', async () => { + const props = getDefaultProps(); + props.activeVisualization.getSupportedLayers = jest.fn(() => [ + { type: layerTypes.DATA, label: 'Data Layer' }, + { + type: layerTypes.THRESHOLD, + label: 'Threshold layer', + initialDimensions: [ + { + groupId: 'testGroup', + columnId: 'myColumn', + dataType: 'number', + label: 'Initial value', + staticValue: 100, + }, + ], + }, + ]); + mockDatasource.initializeDimension = jest.fn(); + + const { instance, lensStore } = await prepareAndMountComponent(props); + await clickToAddLayer(instance); + + expect(lensStore.dispatch).toHaveBeenCalledTimes(1); + expect(mockDatasource.initializeDimension).toHaveBeenCalledWith(undefined, 'newId', { + columnId: 'myColumn', + dataType: 'number', + groupId: 'testGroup', + label: 'Initial value', + staticValue: 100, + }); + }); + + it('should add an initial dimension value when clicking on the empty dimension button', async () => { + const props = getDefaultProps(); + props.activeVisualization.getSupportedLayers = jest.fn(() => [ + { + type: layerTypes.DATA, + label: 'Data Layer', + initialDimensions: [ + { + groupId: 'a', + columnId: 'newId', + dataType: 'number', + label: 'Initial value', + staticValue: 100, + }, + ], + }, + ]); + mockDatasource.initializeDimension = jest.fn(); + + const { instance, lensStore } = await prepareAndMountComponent(props); + + await clickToAddDimension(instance); + expect(lensStore.dispatch).toHaveBeenCalledTimes(1); + + expect(mockDatasource.initializeDimension).toHaveBeenCalledWith('state', 'first', { + groupId: 'a', + columnId: 'newId', + dataType: 'number', + label: 'Initial value', + staticValue: 100, + }); + }); + }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index f7fe2beefa963..57e4cf5b8dffd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -26,8 +26,9 @@ import { useLensSelector, selectVisualization, VisualizationState, + LensAppState, } from '../../../state_management'; -import { AddLayerButton } from './add_layer'; +import { AddLayerButton, getLayerType } from './add_layer'; export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: ConfigPanelWrapperProps) { const visualization = useLensSelector(selectVisualization); @@ -177,6 +178,33 @@ export function LayerPanels( layerIds.length ) === 'clear' } + onEmptyDimensionAdd={(columnId, { groupId }) => { + // avoid state update if the datasource does not support initializeDimension + if ( + activeDatasourceId != null && + datasourceMap[activeDatasourceId]?.initializeDimension + ) { + dispatchLens( + updateState({ + subType: 'LAYER_DEFAULT_DIMENSION', + updater: (state) => + addInitialValueIfAvailable({ + ...props, + state, + activeDatasourceId, + layerId, + layerType: getLayerType( + activeVisualization, + state.visualization.state, + layerId + ), + columnId, + groupId, + }), + }) + ); + } + }} onRemoveLayer={() => { dispatchLens( updateState({ @@ -232,21 +260,92 @@ export function LayerPanels( dispatchLens( updateState({ subType: 'ADD_LAYER', - updater: (state) => - appendLayer({ + updater: (state) => { + const newState = appendLayer({ activeVisualization, generateId: () => id, trackUiEvent, activeDatasource: datasourceMap[activeDatasourceId!], state, layerType, - }), + }); + return addInitialValueIfAvailable({ + ...props, + activeDatasourceId: activeDatasourceId!, + state: newState, + layerId: id, + layerType, + }); + }, }) ); - setNextFocusedLayerId(id); }} /> ); } + +function addInitialValueIfAvailable({ + state, + activeVisualization, + framePublicAPI, + layerType, + activeDatasourceId, + datasourceMap, + layerId, + columnId, + groupId, +}: ConfigPanelWrapperProps & { + state: LensAppState; + activeDatasourceId: string; + activeVisualization: Visualization; + layerId: string; + layerType: string; + columnId?: string; + groupId?: string; +}) { + const layerInfo = activeVisualization + .getSupportedLayers(state.visualization.state, framePublicAPI) + .find(({ type }) => type === layerType); + + const activeDatasource = datasourceMap[activeDatasourceId]; + + if (layerInfo?.initialDimensions && activeDatasource?.initializeDimension) { + const info = groupId + ? layerInfo.initialDimensions.find(({ groupId: id }) => id === groupId) + : // pick the first available one if not passed + layerInfo.initialDimensions[0]; + + if (info) { + return { + ...state, + datasourceStates: { + ...state.datasourceStates, + [activeDatasourceId]: { + ...state.datasourceStates[activeDatasourceId], + state: activeDatasource.initializeDimension( + state.datasourceStates[activeDatasourceId].state, + layerId, + { + ...info, + columnId: columnId || info.columnId, + } + ), + }, + }, + visualization: { + ...state.visualization, + state: activeVisualization.setDimension({ + groupId: info.groupId, + layerId, + columnId: columnId || info.columnId, + prevState: state.visualization.state, + frame: framePublicAPI, + }), + }, + }; + } + } + return state; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index 13b7b8cfecf56..f777fd0976dfd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -83,6 +83,7 @@ describe('LayerPanel', () => { registerNewLayerRef: jest.fn(), isFullscreen: false, toggleFullscreen: jest.fn(), + onEmptyDimensionAdd: jest.fn(), }; } @@ -920,4 +921,33 @@ describe('LayerPanel', () => { expect(updateVisualization).toHaveBeenCalledTimes(1); }); }); + + describe('add a new dimension', () => { + it('should call onEmptyDimensionAdd callback on new dimension creation', async () => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [], + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + }, + ], + }); + const props = getDefaultProps(); + const { instance } = await mountWithProvider(); + + act(() => { + instance.find('[data-test-subj="lns-empty-dimension"]').first().simulate('click'); + }); + instance.update(); + + expect(props.onEmptyDimensionAdd).toHaveBeenCalledWith( + 'newid', + expect.objectContaining({ groupId: 'a' }) + ); + }); + }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 520c2bc837c60..8c947d3502f93 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -57,6 +57,7 @@ export function LayerPanel( onRemoveLayer: () => void; registerNewLayerRef: (layerId: string, instance: HTMLDivElement | null) => void; toggleFullscreen: () => void; + onEmptyDimensionAdd: (columnId: string, group: { groupId: string }) => void; } ) { const [activeDimension, setActiveDimension] = useState( @@ -124,7 +125,11 @@ export function LayerPanel( dateRange, }; - const { groups, supportStaticValue } = useMemo( + const { + groups, + supportStaticValue, + supportFieldFormat = true, + } = useMemo( () => activeVisualization.getConfiguration(layerVisualizationConfigProps), // eslint-disable-next-line react-hooks/exhaustive-deps [ @@ -227,13 +232,25 @@ export function LayerPanel( const isDimensionPanelOpen = Boolean(activeId); const updateDataLayerState = useCallback( - (newState: unknown, { isDimensionComplete = true }: { isDimensionComplete?: boolean } = {}) => { + ( + newState: unknown, + { + isDimensionComplete = true, + // this flag is a hack to force a sync render where it was planned an async/setTimeout state update + // TODO: revisit this once we get rid of updateDatasourceAsync upstream + forceRender = false, + }: { isDimensionComplete?: boolean; forceRender?: boolean } = {} + ) => { if (!activeGroup || !activeId) { return; } if (allAccessors.includes(activeId)) { if (isDimensionComplete) { - updateDatasourceAsync(datasourceId, newState); + if (forceRender) { + updateDatasource(datasourceId, newState); + } else { + updateDatasourceAsync(datasourceId, newState); + } } else { // The datasource can indicate that the previously-valid column is no longer // complete, which clears the visualization. This keeps the flyout open and reuses @@ -263,7 +280,11 @@ export function LayerPanel( ); setActiveDimension({ ...activeDimension, isNew: false }); } else { - updateDatasourceAsync(datasourceId, newState); + if (forceRender) { + updateDatasource(datasourceId, newState); + } else { + updateDatasourceAsync(datasourceId, newState); + } } }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -295,11 +316,10 @@ export function LayerPanel( hasBorder hasShadow > -
+
)} -
+ {groups.map((group, groupIndex) => { const isMissing = !isEmptyLayer && group.required && group.accessors.length === 0; @@ -460,6 +480,8 @@ export function LayerPanel( columnId: accessorConfig.columnId, groupId: group.groupId, filterOperations: group.filterOperations, + invalid: group.invalid, + invalidMessage: group.invalidMessage, }} /> @@ -478,6 +500,7 @@ export function LayerPanel( layerDatasource={layerDatasource} layerDatasourceDropProps={layerDatasourceDropProps} onClick={(id) => { + props.onEmptyDimensionAdd(id, group); setActiveDimension({ activeGroup: group, activeId: id, @@ -538,6 +561,8 @@ export function LayerPanel( toggleFullscreen, isFullscreen, setState: updateDataLayerState, + supportStaticValue: Boolean(supportStaticValue), + supportFieldFormat: Boolean(supportFieldFormat), layerType: activeVisualization.getLayerType(layerId, visualizationState), }} /> diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.test.tsx new file mode 100644 index 0000000000000..04c430143a3c8 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.test.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { + createMockFramePublicAPI, + createMockVisualization, + mountWithProvider, +} from '../../../mocks'; +import { Visualization } from '../../../types'; +import { LayerSettings } from './layer_settings'; + +describe('LayerSettings', () => { + let mockVisualization: jest.Mocked; + const frame = createMockFramePublicAPI(); + + function getDefaultProps() { + return { + activeVisualization: mockVisualization, + layerConfigProps: { + layerId: 'myLayer', + state: {}, + frame, + dateRange: { fromDate: 'now-7d', toDate: 'now' }, + activeData: frame.activeData, + setState: jest.fn(), + }, + }; + } + + beforeEach(() => { + mockVisualization = { + ...createMockVisualization(), + id: 'testVis', + visualizationTypes: [ + { + icon: 'empty', + id: 'testVis', + label: 'TEST1', + groupLabel: 'testVisGroup', + }, + ], + }; + }); + + it('should render nothing with no custom renderer nor description', async () => { + // @ts-expect-error + mockVisualization.getDescription.mockReturnValue(undefined); + const { instance } = await mountWithProvider(); + expect(instance.html()).toBe(null); + }); + + it('should render a static header if visualization has only a description value', async () => { + mockVisualization.getDescription.mockReturnValue({ + icon: 'myIcon', + label: 'myVisualizationType', + }); + const { instance } = await mountWithProvider(); + expect(instance.find('StaticHeader').first().prop('label')).toBe('myVisualizationType'); + }); + + it('should call the custom renderer if available', async () => { + mockVisualization.renderLayerHeader = jest.fn(); + await mountWithProvider(); + expect(mockVisualization.renderLayerHeader).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx index 467b1ecfe1b5b..fc88ff2af8bbe 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx @@ -6,44 +6,23 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle } from '@elastic/eui'; import { NativeRenderer } from '../../../native_renderer'; import { Visualization, VisualizationLayerWidgetProps } from '../../../types'; +import { StaticHeader } from '../../../shared_components'; export function LayerSettings({ - layerId, activeVisualization, layerConfigProps, }: { - layerId: string; activeVisualization: Visualization; layerConfigProps: VisualizationLayerWidgetProps; }) { - const description = activeVisualization.getDescription(layerConfigProps.state); - if (!activeVisualization.renderLayerHeader) { + const description = activeVisualization.getDescription(layerConfigProps.state); if (!description) { return null; } - return ( - - {description.icon && ( - - {' '} - - )} - - -
{description.label}
-
-
-
- ); + return ; } return ( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts index 632989057b488..90fa2ab080dd2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts @@ -45,21 +45,22 @@ describe('suggestion helpers', () => { generateSuggestion(), ]); const suggestedState = {}; - const suggestions = getSuggestions({ - visualizationMap: { - vis1: { - ...mockVisualization, - getSuggestions: () => [ - { - score: 0.5, - title: 'Test', - state: suggestedState, - previewIcon: 'empty', - }, - ], - }, + const visualizationMap = { + vis1: { + ...mockVisualization, + getSuggestions: () => [ + { + score: 0.5, + title: 'Test', + state: suggestedState, + previewIcon: 'empty', + }, + ], }, - activeVisualizationId: 'vis1', + }; + const suggestions = getSuggestions({ + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -74,38 +75,39 @@ describe('suggestion helpers', () => { datasourceMap.mock.getDatasourceSuggestionsFromCurrentState.mockReturnValue([ generateSuggestion(), ]); - const suggestions = getSuggestions({ - visualizationMap: { - vis1: { - ...mockVisualization1, - getSuggestions: () => [ - { - score: 0.5, - title: 'Test', - state: {}, - previewIcon: 'empty', - }, - { - score: 0.5, - title: 'Test2', - state: {}, - previewIcon: 'empty', - }, - ], - }, - vis2: { - ...mockVisualization2, - getSuggestions: () => [ - { - score: 0.5, - title: 'Test3', - state: {}, - previewIcon: 'empty', - }, - ], - }, + const visualizationMap = { + vis1: { + ...mockVisualization1, + getSuggestions: () => [ + { + score: 0.5, + title: 'Test', + state: {}, + previewIcon: 'empty', + }, + { + score: 0.5, + title: 'Test2', + state: {}, + previewIcon: 'empty', + }, + ], + }, + vis2: { + ...mockVisualization2, + getSuggestions: () => [ + { + score: 0.5, + title: 'Test3', + state: {}, + previewIcon: 'empty', + }, + ], }, - activeVisualizationId: 'vis1', + }; + const suggestions = getSuggestions({ + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -116,11 +118,12 @@ describe('suggestion helpers', () => { it('should call getDatasourceSuggestionsForField when a field is passed', () => { datasourceMap.mock.getDatasourceSuggestionsForField.mockReturnValue([generateSuggestion()]); const droppedField = {}; + const visualizationMap = { + vis1: createMockVisualization(), + }; getSuggestions({ - visualizationMap: { - vis1: createMockVisualization(), - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -128,7 +131,8 @@ describe('suggestion helpers', () => { }); expect(datasourceMap.mock.getDatasourceSuggestionsForField).toHaveBeenCalledWith( datasourceStates.mock.state, - droppedField + droppedField, + expect.any(Function) ); }); @@ -148,12 +152,13 @@ describe('suggestion helpers', () => { mock2: createMockDatasource('a'), mock3: createMockDatasource('a'), }; + const visualizationMap = { + vis1: createMockVisualization(), + }; const droppedField = {}; getSuggestions({ - visualizationMap: { - vis1: createMockVisualization(), - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap: multiDatasourceMap, datasourceStates: multiDatasourceStates, @@ -161,11 +166,13 @@ describe('suggestion helpers', () => { }); expect(multiDatasourceMap.mock.getDatasourceSuggestionsForField).toHaveBeenCalledWith( multiDatasourceStates.mock.state, - droppedField + droppedField, + expect.any(Function) ); expect(multiDatasourceMap.mock2.getDatasourceSuggestionsForField).toHaveBeenCalledWith( multiDatasourceStates.mock2.state, - droppedField + droppedField, + expect.any(Function) ); expect(multiDatasourceMap.mock3.getDatasourceSuggestionsForField).not.toHaveBeenCalled(); }); @@ -174,11 +181,14 @@ describe('suggestion helpers', () => { datasourceMap.mock.getDatasourceSuggestionsForVisualizeField.mockReturnValue([ generateSuggestion(), ]); + + const visualizationMap = { + vis1: createMockVisualization(), + }; + getSuggestions({ - visualizationMap: { - vis1: createMockVisualization(), - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -214,11 +224,13 @@ describe('suggestion helpers', () => { indexPatternId: '1', fieldName: 'test', }; + + const visualizationMap = { + vis1: createMockVisualization(), + }; getSuggestions({ - visualizationMap: { - vis1: createMockVisualization(), - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap: multiDatasourceMap, datasourceStates: multiDatasourceStates, @@ -245,38 +257,39 @@ describe('suggestion helpers', () => { datasourceMap.mock.getDatasourceSuggestionsFromCurrentState.mockReturnValue([ generateSuggestion(), ]); - const suggestions = getSuggestions({ - visualizationMap: { - vis1: { - ...mockVisualization1, - getSuggestions: () => [ - { - score: 0.2, - title: 'Test', - state: {}, - previewIcon: 'empty', - }, - { - score: 0.8, - title: 'Test2', - state: {}, - previewIcon: 'empty', - }, - ], - }, - vis2: { - ...mockVisualization2, - getSuggestions: () => [ - { - score: 0.6, - title: 'Test3', - state: {}, - previewIcon: 'empty', - }, - ], - }, + const visualizationMap = { + vis1: { + ...mockVisualization1, + getSuggestions: () => [ + { + score: 0.2, + title: 'Test', + state: {}, + previewIcon: 'empty', + }, + { + score: 0.8, + title: 'Test2', + state: {}, + previewIcon: 'empty', + }, + ], }, - activeVisualizationId: 'vis1', + vis2: { + ...mockVisualization2, + getSuggestions: () => [ + { + score: 0.6, + title: 'Test3', + state: {}, + previewIcon: 'empty', + }, + ], + }, + }; + const suggestions = getSuggestions({ + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -305,12 +318,13 @@ describe('suggestion helpers', () => { { state: {}, table: table1, keptLayerIds: ['first'] }, { state: {}, table: table2, keptLayerIds: ['first'] }, ]); + const visualizationMap = { + vis1: mockVisualization1, + vis2: mockVisualization2, + }; getSuggestions({ - visualizationMap: { - vis1: mockVisualization1, - vis2: mockVisualization2, - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -357,18 +371,20 @@ describe('suggestion helpers', () => { previewIcon: 'empty', }, ]); - const suggestions = getSuggestions({ - visualizationMap: { - vis1: { - ...mockVisualization1, - getSuggestions: vis1Suggestions, - }, - vis2: { - ...mockVisualization2, - getSuggestions: vis2Suggestions, - }, + const visualizationMap = { + vis1: { + ...mockVisualization1, + getSuggestions: vis1Suggestions, }, - activeVisualizationId: 'vis1', + vis2: { + ...mockVisualization2, + getSuggestions: vis2Suggestions, + }, + }; + + const suggestions = getSuggestions({ + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -389,12 +405,15 @@ describe('suggestion helpers', () => { generateSuggestion(0), generateSuggestion(1), ]); + + const visualizationMap = { + vis1: mockVisualization1, + vis2: mockVisualization2, + }; + getSuggestions({ - visualizationMap: { - vis1: mockVisualization1, - vis2: mockVisualization2, - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -419,12 +438,13 @@ describe('suggestion helpers', () => { generateSuggestion(0), generateSuggestion(1), ]); + const visualizationMap = { + vis1: mockVisualization1, + vis2: mockVisualization2, + }; getSuggestions({ - visualizationMap: { - vis1: mockVisualization1, - vis2: mockVisualization2, - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -451,12 +471,14 @@ describe('suggestion helpers', () => { generateSuggestion(0), generateSuggestion(1), ]); + const visualizationMap = { + vis1: mockVisualization1, + vis2: mockVisualization2, + }; + getSuggestions({ - visualizationMap: { - vis1: mockVisualization1, - vis2: mockVisualization2, - }, - activeVisualizationId: 'vis1', + visualizationMap, + activeVisualization: visualizationMap.vis1, visualizationState: {}, datasourceMap, datasourceStates, @@ -538,7 +560,8 @@ describe('suggestion helpers', () => { humanData: { label: 'myfieldLabel', }, - } + }, + expect.any(Function) ); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index 2f3fe3795a881..a5c7871f33dfc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -58,7 +58,7 @@ export function getSuggestions({ datasourceMap, datasourceStates, visualizationMap, - activeVisualizationId, + activeVisualization, subVisualizationId, visualizationState, field, @@ -69,7 +69,7 @@ export function getSuggestions({ datasourceMap: DatasourceMap; datasourceStates: DatasourceStates; visualizationMap: VisualizationMap; - activeVisualizationId: string | null; + activeVisualization?: Visualization; subVisualizationId?: string; visualizationState: unknown; field?: unknown; @@ -83,16 +83,12 @@ export function getSuggestions({ const layerTypesMap = datasources.reduce((memo, [datasourceId, datasource]) => { const datasourceState = datasourceStates[datasourceId].state; - if (!activeVisualizationId || !datasourceState || !visualizationMap[activeVisualizationId]) { + if (!activeVisualization || !datasourceState) { return memo; } const layers = datasource.getLayers(datasourceState); for (const layerId of layers) { - const type = getLayerType( - visualizationMap[activeVisualizationId], - visualizationState, - layerId - ); + const type = getLayerType(activeVisualization, visualizationState, layerId); memo[layerId] = type; } return memo; @@ -112,7 +108,11 @@ export function getSuggestions({ visualizeTriggerFieldContext.fieldName ); } else if (field) { - dataSourceSuggestions = datasource.getDatasourceSuggestionsForField(datasourceState, field); + dataSourceSuggestions = datasource.getDatasourceSuggestionsForField( + datasourceState, + field, + (layerId) => isLayerSupportedByVisualization(layerId, [layerTypes.DATA]) // a field dragged to workspace should added to data layer + ); } else { dataSourceSuggestions = datasource.getDatasourceSuggestionsFromCurrentState( datasourceState, @@ -121,7 +121,6 @@ export function getSuggestions({ } return dataSourceSuggestions.map((suggestion) => ({ ...suggestion, datasourceId })); }); - // Pass all table suggestions to all visualization extensions to get visualization suggestions // and rank them by score return Object.entries(visualizationMap) @@ -139,12 +138,8 @@ export function getSuggestions({ .flatMap((datasourceSuggestion) => { const table = datasourceSuggestion.table; const currentVisualizationState = - visualizationId === activeVisualizationId ? visualizationState : undefined; - const palette = - mainPalette || - (activeVisualizationId && visualizationMap[activeVisualizationId]?.getMainPalette - ? visualizationMap[activeVisualizationId].getMainPalette?.(visualizationState) - : undefined); + visualizationId === activeVisualization?.id ? visualizationState : undefined; + const palette = mainPalette || activeVisualization?.getMainPalette?.(visualizationState); return getVisualizationSuggestions( visualization, @@ -169,14 +164,14 @@ export function getVisualizeFieldSuggestions({ datasourceMap, datasourceStates, visualizationMap, - activeVisualizationId, + activeVisualization, visualizationState, visualizeTriggerFieldContext, }: { datasourceMap: DatasourceMap; datasourceStates: DatasourceStates; visualizationMap: VisualizationMap; - activeVisualizationId: string | null; + activeVisualization: Visualization; subVisualizationId?: string; visualizationState: unknown; visualizeTriggerFieldContext?: VisualizeFieldContext; @@ -185,12 +180,12 @@ export function getVisualizeFieldSuggestions({ datasourceMap, datasourceStates, visualizationMap, - activeVisualizationId, + activeVisualization, visualizationState, visualizeTriggerFieldContext, }); if (suggestions.length) { - return suggestions.find((s) => s.visualizationId === activeVisualizationId) || suggestions[0]; + return suggestions.find((s) => s.visualizationId === activeVisualization?.id) || suggestions[0]; } } @@ -263,18 +258,19 @@ export function getTopSuggestionForField( (datasourceLayer) => datasourceLayer.getTableSpec().length > 0 ); - const mainPalette = - visualization.activeId && visualizationMap[visualization.activeId]?.getMainPalette - ? visualizationMap[visualization.activeId].getMainPalette?.(visualization.state) - : undefined; + const activeVisualization = visualization.activeId + ? visualizationMap[visualization.activeId] + : undefined; + + const mainPalette = activeVisualization?.getMainPalette?.(visualization.state); const suggestions = getSuggestions({ datasourceMap: { [datasource.id]: datasource }, datasourceStates, visualizationMap: hasData && visualization.activeId - ? { [visualization.activeId]: visualizationMap[visualization.activeId] } + ? { [visualization.activeId]: activeVisualization! } : visualizationMap, - activeVisualizationId: visualization.activeId, + activeVisualization, visualizationState: visualization.state, field, mainPalette, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index 858fcedf215ef..5e5e19ea29e84 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -201,7 +201,9 @@ export function SuggestionPanel({ datasourceMap, datasourceStates: currentDatasourceStates, visualizationMap, - activeVisualizationId: currentVisualization.activeId, + activeVisualization: currentVisualization.activeId + ? visualizationMap[currentVisualization.activeId] + : undefined, visualizationState: currentVisualization.state, activeData, }) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx index 28c0567d784ea..51d4f2955a52b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx @@ -515,11 +515,14 @@ function getTopSuggestion( props.visualizationMap[visualization.activeId].getMainPalette ? props.visualizationMap[visualization.activeId].getMainPalette!(visualization.state) : undefined; + const unfilteredSuggestions = getSuggestions({ datasourceMap: props.datasourceMap, datasourceStates, visualizationMap: { [visualizationId]: newVisualization }, - activeVisualizationId: visualization.activeId, + activeVisualization: visualization.activeId + ? props.visualizationMap[visualization.activeId] + : undefined, visualizationState: visualization.state, subVisualizationId, activeData: props.framePublicAPI.activeData, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index e386bac026fdc..d25e6754fe03f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -11,15 +11,11 @@ import { i18n } from '@kbn/i18n'; import { EuiListGroup, EuiFormRow, - EuiFieldText, EuiSpacer, EuiListGroupItemProps, EuiFormLabel, EuiToolTip, EuiText, - EuiTabs, - EuiTab, - EuiCallOut, } from '@elastic/eui'; import { IndexPatternDimensionEditorProps } from './dimension_panel'; import { OperationSupportMatrix } from './operation_support'; @@ -47,41 +43,29 @@ import { setTimeScaling, TimeScaling } from './time_scaling'; import { defaultFilter, Filtering, setFilter } from './filtering'; import { AdvancedOptions } from './advanced_options'; import { setTimeShift, TimeShift } from './time_shift'; -import { useDebouncedValue } from '../../shared_components'; +import { LayerType } from '../../../common'; +import { + quickFunctionsName, + staticValueOperationName, + isQuickFunction, + getParamEditor, + formulaOperationName, + DimensionEditorTabs, + CalloutWarning, + LabelInput, + getErrorMessage, +} from './dimensions_editor_helpers'; +import type { TemporaryState } from './dimensions_editor_helpers'; const operationPanels = getOperationDisplay(); export interface DimensionEditorProps extends IndexPatternDimensionEditorProps { selectedColumn?: IndexPatternColumn; + layerType: LayerType; operationSupportMatrix: OperationSupportMatrix; currentIndexPattern: IndexPattern; } -const LabelInput = ({ value, onChange }: { value: string; onChange: (value: string) => void }) => { - const { inputValue, handleInputChange, initialValue } = useDebouncedValue({ onChange, value }); - - return ( - - { - handleInputChange(e.target.value); - }} - placeholder={initialValue} - /> - - ); -}; - export function DimensionEditor(props: DimensionEditorProps) { const { selectedColumn, @@ -96,6 +80,8 @@ export function DimensionEditor(props: DimensionEditorProps) { dimensionGroups, toggleFullscreen, isFullscreen, + supportStaticValue, + supportFieldFormat = true, layerType, } = props; const services = { @@ -110,6 +96,11 @@ export function DimensionEditor(props: DimensionEditorProps) { const selectedOperationDefinition = selectedColumn && operationDefinitionMap[selectedColumn.operationType]; + const [temporaryState, setTemporaryState] = useState('none'); + + const temporaryQuickFunction = Boolean(temporaryState === quickFunctionsName); + const temporaryStaticValue = Boolean(temporaryState === staticValueOperationName); + const updateLayer = useCallback( (newLayer) => setState((prevState) => mergeLayer({ state: prevState, layerId, newLayer })), [layerId, setState] @@ -141,9 +132,64 @@ export function DimensionEditor(props: DimensionEditorProps) { ...incompleteParams } = incompleteInfo || {}; - const ParamEditor = selectedOperationDefinition?.paramEditor; + const isQuickFunctionSelected = Boolean( + supportStaticValue + ? selectedOperationDefinition && isQuickFunction(selectedOperationDefinition.type) + : !selectedOperationDefinition || isQuickFunction(selectedOperationDefinition.type) + ); + const showQuickFunctions = temporaryQuickFunction || isQuickFunctionSelected; + + const showStaticValueFunction = + temporaryStaticValue || + (temporaryState === 'none' && + supportStaticValue && + (!selectedColumn || selectedColumn?.operationType === staticValueOperationName)); + + const addStaticValueColumn = (prevLayer = props.state.layers[props.layerId]) => { + if (selectedColumn?.operationType !== staticValueOperationName) { + trackUiEvent(`indexpattern_dimension_operation_static_value`); + return insertOrReplaceColumn({ + layer: prevLayer, + indexPattern: currentIndexPattern, + columnId, + op: staticValueOperationName, + visualizationGroups: dimensionGroups, + }); + } + return prevLayer; + }; + + // this function intercepts the state update for static value function + // and. if in temporary state, it merges the "add new static value column" state with the incoming + // changes from the static value operation (which has to be a function) + // Note: it forced a rerender at this point to avoid UI glitches in async updates (another hack upstream) + // TODO: revisit this once we get rid of updateDatasourceAsync upstream + const moveDefinetelyToStaticValueAndUpdate = ( + setter: IndexPatternLayer | ((prevLayer: IndexPatternLayer) => IndexPatternLayer) + ) => { + if (temporaryStaticValue) { + setTemporaryState('none'); + if (typeof setter === 'function') { + return setState( + (prevState) => { + const layer = setter(addStaticValueColumn(prevState.layers[layerId])); + return mergeLayer({ state: prevState, layerId, newLayer: layer }); + }, + { + isDimensionComplete: true, + forceRender: true, + } + ); + } + } + return setStateWrapper(setter); + }; - const [temporaryQuickFunction, setQuickFunction] = useState(false); + const ParamEditor = getParamEditor( + temporaryStaticValue, + selectedOperationDefinition, + supportStaticValue && !showQuickFunctions + ); const possibleOperations = useMemo(() => { return Object.values(operationDefinitionMap) @@ -245,9 +291,9 @@ export function DimensionEditor(props: DimensionEditorProps) { [`aria-pressed`]: isActive, onClick() { if ( - operationDefinitionMap[operationType].input === 'none' || - operationDefinitionMap[operationType].input === 'managedReference' || - operationDefinitionMap[operationType].input === 'fullReference' + ['none', 'fullReference', 'managedReference'].includes( + operationDefinitionMap[operationType].input + ) ) { // Clear invalid state because we are reseting to a valid column if (selectedColumn?.operationType === operationType) { @@ -264,9 +310,12 @@ export function DimensionEditor(props: DimensionEditorProps) { visualizationGroups: dimensionGroups, targetGroup: props.groupId, }); - if (temporaryQuickFunction && newLayer.columns[columnId].operationType !== 'formula') { + if ( + temporaryQuickFunction && + isQuickFunction(newLayer.columns[columnId].operationType) + ) { // Only switch the tab once the formula is fully removed - setQuickFunction(false); + setTemporaryState('none'); } setStateWrapper(newLayer); trackUiEvent(`indexpattern_dimension_operation_${operationType}`); @@ -297,9 +346,12 @@ export function DimensionEditor(props: DimensionEditorProps) { }); // ); } - if (temporaryQuickFunction && newLayer.columns[columnId].operationType !== 'formula') { + if ( + temporaryQuickFunction && + isQuickFunction(newLayer.columns[columnId].operationType) + ) { // Only switch the tab once the formula is fully removed - setQuickFunction(false); + setTemporaryState('none'); } setStateWrapper(newLayer); trackUiEvent(`indexpattern_dimension_operation_${operationType}`); @@ -314,7 +366,7 @@ export function DimensionEditor(props: DimensionEditorProps) { } if (temporaryQuickFunction) { - setQuickFunction(false); + setTemporaryState('none'); } const newLayer = replaceColumn({ layer: props.state.layers[props.layerId], @@ -348,29 +400,10 @@ export function DimensionEditor(props: DimensionEditorProps) { !currentFieldIsInvalid && !incompleteInfo && selectedColumn && - selectedColumn.operationType !== 'formula'; + isQuickFunction(selectedColumn.operationType); const quickFunctions = ( <> - {temporaryQuickFunction && selectedColumn?.operationType === 'formula' && ( - <> - -

- {i18n.translate('xpack.lens.indexPattern.formulaWarningText', { - defaultMessage: 'To overwrite your formula, select a quick function', - })} -

-
- - )}
{i18n.translate('xpack.lens.indexPattern.functionsLabel', { @@ -608,24 +641,28 @@ export function DimensionEditor(props: DimensionEditorProps) { ); - const formulaTab = ParamEditor ? ( - + const customParamEditor = ParamEditor ? ( + <> + + ) : null; + const TabContent = showQuickFunctions ? quickFunctions : customParamEditor; + const onFormatChange = useCallback( (newFormat) => { updateLayer( @@ -640,58 +677,69 @@ export function DimensionEditor(props: DimensionEditorProps) { [columnId, layerId, state.layers, updateLayer] ); + const hasFormula = + !isFullscreen && operationSupportMatrix.operationWithoutField.has(formulaOperationName); + + const hasTabs = hasFormula || supportStaticValue; + return (
- {!isFullscreen && operationSupportMatrix.operationWithoutField.has('formula') ? ( - - { - if (selectedColumn?.operationType === 'formula') { - setQuickFunction(true); + {hasTabs ? ( + { + if (tabClicked === 'quickFunctions') { + if (selectedColumn && !isQuickFunction(selectedColumn.operationType)) { + setTemporaryState(quickFunctionsName); + return; } - }} - > - {i18n.translate('xpack.lens.indexPattern.quickFunctionsLabel', { - defaultMessage: 'Quick functions', - })} - - { - if (selectedColumn?.operationType !== 'formula') { - setQuickFunction(false); + } + + if (tabClicked === 'static_value') { + // when coming from a formula, set a temporary state + if (selectedColumn?.operationType === formulaOperationName) { + return setTemporaryState(staticValueOperationName); + } + setTemporaryState('none'); + setStateWrapper(addStaticValueColumn()); + return; + } + + if (tabClicked === 'formula') { + setTemporaryState('none'); + if (selectedColumn?.operationType !== formulaOperationName) { const newLayer = insertOrReplaceColumn({ layer: props.state.layers[props.layerId], indexPattern: currentIndexPattern, columnId, - op: 'formula', + op: formulaOperationName, visualizationGroups: dimensionGroups, }); setStateWrapper(newLayer); trackUiEvent(`indexpattern_dimension_operation_formula`); - return; - } else { - setQuickFunction(false); } - }} - > - {i18n.translate('xpack.lens.indexPattern.formulaLabel', { - defaultMessage: 'Formula', - })} - - + } + }} + /> ) : null} - {isFullscreen - ? formulaTab - : selectedOperationDefinition?.type === 'formula' && !temporaryQuickFunction - ? formulaTab - : quickFunctions} + + {TabContent} - {!isFullscreen && !currentFieldIsInvalid && !temporaryQuickFunction && ( + {!isFullscreen && !currentFieldIsInvalid && temporaryState === 'none' && (
{!incompleteInfo && selectedColumn && ( )} - {!isFullscreen && + {supportFieldFormat && + !isFullscreen && selectedColumn && (selectedColumn.dataType === 'number' || selectedColumn.operationType === 'range') ? ( @@ -735,26 +784,3 @@ export function DimensionEditor(props: DimensionEditorProps) {
); } - -function getErrorMessage( - selectedColumn: IndexPatternColumn | undefined, - incompleteOperation: boolean, - input: 'none' | 'field' | 'fullReference' | 'managedReference' | undefined, - fieldInvalid: boolean -) { - if (selectedColumn && incompleteOperation) { - if (input === 'field') { - return i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { - defaultMessage: 'This field does not work with the selected function.', - }); - } - return i18n.translate('xpack.lens.indexPattern.chooseFieldLabel', { - defaultMessage: 'To use this function, select a field.', - }); - } - if (fieldInvalid) { - return i18n.translate('xpack.lens.indexPattern.invalidFieldLabel', { - defaultMessage: 'Invalid field. Check your index pattern or pick another field.', - }); - } -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 5d56661f15915..d823def1da114 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -52,6 +52,13 @@ jest.mock('lodash', () => { }; }); jest.mock('../../id_generator'); +// Mock the Monaco Editor component +jest.mock('../operations/definitions/formula/editor/formula_editor', () => { + return { + WrappedFormulaEditor: () =>
, + FormulaEditor: () =>
, + }; +}); const fields = [ { @@ -211,6 +218,7 @@ describe('IndexPatternDimensionEditorPanel', () => { dimensionGroups: [], groupId: 'a', isFullscreen: false, + supportStaticValue: false, toggleFullscreen: jest.fn(), }; @@ -402,8 +410,9 @@ describe('IndexPatternDimensionEditorPanel', () => { const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; - expect(items.find(({ id }) => id === 'math')).toBeUndefined(); - expect(items.find(({ id }) => id === 'formula')).toBeUndefined(); + ['math', 'formula', 'static_value'].forEach((hiddenOp) => { + expect(items.some(({ id }) => id === hiddenOp)).toBe(false); + }); }); it('should indicate that reference-based operations are not compatible when they are incomplete', () => { @@ -2217,4 +2226,130 @@ describe('IndexPatternDimensionEditorPanel', () => { 0 ); }); + + it('should not show tabs when formula and static_value operations are not available', () => { + const stateWithInvalidCol: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Average of memory', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'average', + sourceField: 'memory', + params: { + format: { id: 'bytes', params: { decimals: 2 } }, + }, + }, + }); + + const props = { + ...defaultProps, + filterOperations: jest.fn((op) => { + // the formula operation will fall into this metadata category + return !(op.dataType === 'number' && op.scale === 'ratio'); + }), + }; + + wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="lens-dimensionTabs"]').exists()).toBeFalsy(); + }); + + it('should show the formula tab when supported', () => { + const stateWithFormulaColumn: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Formula', + dataType: 'number', + isBucketed: false, + operationType: 'formula', + references: ['ref1'], + params: {}, + }, + }); + + wrapper = mount( + + ); + + expect( + wrapper.find('[data-test-subj="lens-dimensionTabs-formula"]').first().prop('isSelected') + ).toBeTruthy(); + }); + + it('should now show the static_value tab when not supported', () => { + const stateWithFormulaColumn: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Formula', + dataType: 'number', + isBucketed: false, + operationType: 'formula', + references: ['ref1'], + params: {}, + }, + }); + + wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="lens-dimensionTabs-static_value"]').exists()).toBeFalsy(); + }); + + it('should show the static value tab when supported', () => { + const staticWithFormulaColumn: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Formula', + dataType: 'number', + isBucketed: false, + operationType: 'formula', + references: ['ref1'], + params: {}, + }, + }); + + wrapper = mount( + + ); + + expect( + wrapper.find('[data-test-subj="lens-dimensionTabs-static_value"]').exists() + ).toBeTruthy(); + }); + + it('should select the quick function tab by default', () => { + const stateWithNoColumn: IndexPatternPrivateState = getStateWithColumns({}); + + wrapper = mount( + + ); + + expect( + wrapper + .find('[data-test-subj="lens-dimensionTabs-quickFunctions"]') + .first() + .prop('isSelected') + ).toBeTruthy(); + }); + + it('should select the static value tab when supported by default', () => { + const stateWithNoColumn: IndexPatternPrivateState = getStateWithColumns({}); + + wrapper = mount( + + ); + + expect( + wrapper.find('[data-test-subj="lens-dimensionTabs-static_value"]').first().prop('isSelected') + ).toBeTruthy(); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index f3e51516d161c..ac8296cca968e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -16,7 +16,7 @@ import { IndexPatternColumn } from '../indexpattern'; import { isColumnInvalid } from '../utils'; import { IndexPatternPrivateState } from '../types'; import { DimensionEditor } from './dimension_editor'; -import type { DateRange } from '../../../common'; +import { DateRange, layerTypes } from '../../../common'; import { getOperationSupportMatrix } from './operation_support'; export type IndexPatternDimensionTriggerProps = @@ -49,11 +49,11 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens const layerId = props.layerId; const layer = props.state.layers[layerId]; const currentIndexPattern = props.state.indexPatterns[layer.indexPatternId]; - const { columnId, uniqueLabel } = props; + const { columnId, uniqueLabel, invalid, invalidMessage } = props; const currentColumnHasErrors = useMemo( - () => isColumnInvalid(layer, columnId, currentIndexPattern), - [layer, columnId, currentIndexPattern] + () => invalid || isColumnInvalid(layer, columnId, currentIndexPattern), + [layer, columnId, currentIndexPattern, invalid] ); const selectedColumn: IndexPatternColumn | null = layer.columns[props.columnId] ?? null; @@ -67,15 +67,17 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens return ( - {i18n.translate('xpack.lens.configure.invalidConfigTooltip', { - defaultMessage: 'Invalid configuration.', - })} -
- {i18n.translate('xpack.lens.configure.invalidConfigTooltipClick', { - defaultMessage: 'Click for more details.', - })} -

+ invalidMessage ?? ( +

+ {i18n.translate('xpack.lens.configure.invalidConfigTooltip', { + defaultMessage: 'Invalid configuration.', + })} +
+ {i18n.translate('xpack.lens.configure.invalidConfigTooltipClick', { + defaultMessage: 'Click for more details.', + })} +

+ ) } anchorClassName="eui-displayBlock" > @@ -127,6 +129,7 @@ export const IndexPatternDimensionEditorComponent = function IndexPatternDimensi return ( void; +}) => { + const { inputValue, handleInputChange, initialValue } = useDebouncedValue({ onChange, value }); + + return ( + + { + handleInputChange(e.target.value); + }} + placeholder={initialValue} + /> + + ); +}; + +export function getParamEditor( + temporaryStaticValue: boolean, + selectedOperationDefinition: typeof operationDefinitionMap[string] | undefined, + showDefaultStaticValue: boolean +) { + if (temporaryStaticValue) { + return operationDefinitionMap[staticValueOperationName].paramEditor; + } + if (selectedOperationDefinition?.paramEditor) { + return selectedOperationDefinition.paramEditor; + } + if (showDefaultStaticValue) { + return operationDefinitionMap[staticValueOperationName].paramEditor; + } + return null; +} + +export const CalloutWarning = ({ + currentOperationType, + temporaryStateType, +}: { + currentOperationType: keyof typeof operationDefinitionMap | undefined; + temporaryStateType: TemporaryState; +}) => { + if ( + temporaryStateType === 'none' || + (currentOperationType != null && isQuickFunction(currentOperationType)) + ) { + return null; + } + if ( + currentOperationType === staticValueOperationName && + temporaryStateType === 'quickFunctions' + ) { + return ( + <> + +

+ {i18n.translate('xpack.lens.indexPattern.staticValueWarningText', { + defaultMessage: 'To overwrite your static value, select a quick function', + })} +

+
+ + ); + } + return ( + <> + + {temporaryStateType !== 'quickFunctions' ? ( +

+ {i18n.translate('xpack.lens.indexPattern.formulaWarningStaticValueText', { + defaultMessage: 'To overwrite your formula, change the value in the input field', + })} +

+ ) : ( +

+ {i18n.translate('xpack.lens.indexPattern.formulaWarningText', { + defaultMessage: 'To overwrite your formula, select a quick function', + })} +

+ )} +
+ + ); +}; + +type DimensionEditorTabsType = + | typeof quickFunctionsName + | typeof staticValueOperationName + | typeof formulaOperationName; + +export const DimensionEditorTabs = ({ + tabsEnabled, + tabsState, + onClick, +}: { + tabsEnabled: Record; + tabsState: Record; + onClick: (tabClicked: DimensionEditorTabsType) => void; +}) => { + return ( + + {tabsEnabled.static_value ? ( + onClick(staticValueOperationName)} + > + {i18n.translate('xpack.lens.indexPattern.staticValueLabel', { + defaultMessage: 'Static value', + })} + + ) : null} + onClick(quickFunctionsName)} + > + {i18n.translate('xpack.lens.indexPattern.quickFunctionsLabel', { + defaultMessage: 'Quick functions', + })} + + {tabsEnabled.formula ? ( + onClick(formulaOperationName)} + > + {i18n.translate('xpack.lens.indexPattern.formulaLabel', { + defaultMessage: 'Formula', + })} + + ) : null} + + ); +}; + +export function getErrorMessage( + selectedColumn: IndexPatternColumn | undefined, + incompleteOperation: boolean, + input: 'none' | 'field' | 'fullReference' | 'managedReference' | undefined, + fieldInvalid: boolean +) { + if (selectedColumn && incompleteOperation) { + if (input === 'field') { + return i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { + defaultMessage: 'This field does not work with the selected function.', + }); + } + return i18n.translate('xpack.lens.indexPattern.chooseFieldLabel', { + defaultMessage: 'To use this function, select a field.', + }); + } + if (fieldInvalid) { + return i18n.translate('xpack.lens.indexPattern.invalidFieldLabel', { + defaultMessage: 'Invalid field. Check your index pattern or pick another field.', + }); + } +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts index 26aac5dab31e3..85807721f80f6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts @@ -17,6 +17,7 @@ import { OperationMetadata, DropType } from '../../../types'; import { IndexPatternColumn, MedianIndexPatternColumn } from '../../operations'; import { getFieldByNameFactory } from '../../pure_helpers'; import { generateId } from '../../../id_generator'; +import { layerTypes } from '../../../../common'; jest.mock('../../../id_generator'); @@ -263,7 +264,6 @@ describe('IndexPatternDimensionEditorPanel', () => { dateRange: { fromDate: 'now-1d', toDate: 'now' }, columnId: 'col1', layerId: 'first', - layerType: 'data', uniqueLabel: 'stuff', groupId: 'group1', filterOperations: () => true, @@ -287,6 +287,8 @@ describe('IndexPatternDimensionEditorPanel', () => { dimensionGroups: [], isFullscreen: false, toggleFullscreen: () => {}, + supportStaticValue: false, + layerType: layerTypes.DATA, }; jest.clearAllMocks(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts index e09c3e904f535..b518f667a0bfb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/on_drop_handler.ts @@ -121,8 +121,12 @@ function onMoveCompatible( indexPattern, }); - let updatedColumnOrder = getColumnOrder(modifiedLayer); - updatedColumnOrder = reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId); + const updatedColumnOrder = reorderByGroups( + dimensionGroups, + groupId, + getColumnOrder(modifiedLayer), + columnId + ); // Time to replace setState( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 06c8a50cd2dfa..1dfc7d40f6f3e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -1623,4 +1623,87 @@ describe('IndexPattern Data Source', () => { expect(indexPatternDatasource.isTimeBased(state)).toEqual(false); }); }); + + describe('#initializeDimension', () => { + it('should return the same state if no static value is passed', () => { + const state = enrichBaseState({ + currentIndexPatternId: '1', + layers: { + first: { + indexPatternId: '1', + columnOrder: ['metric'], + columns: { + metric: { + label: 'Count of records', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + }, + }, + }); + expect( + indexPatternDatasource.initializeDimension!(state, 'first', { + columnId: 'newStatic', + label: 'MyNewColumn', + groupId: 'a', + dataType: 'number', + }) + ).toBe(state); + }); + + it('should add a new static value column if a static value is passed', () => { + const state = enrichBaseState({ + currentIndexPatternId: '1', + layers: { + first: { + indexPatternId: '1', + columnOrder: ['metric'], + columns: { + metric: { + label: 'Count of records', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + }, + }, + }); + expect( + indexPatternDatasource.initializeDimension!(state, 'first', { + columnId: 'newStatic', + label: 'MyNewColumn', + groupId: 'a', + dataType: 'number', + staticValue: 0, // use a falsy value to check also this corner case + }) + ).toEqual({ + ...state, + layers: { + ...state.layers, + first: { + ...state.layers.first, + incompleteColumns: {}, + columnOrder: ['metric', 'newStatic'], + columns: { + ...state.layers.first.columns, + newStatic: { + dataType: 'number', + isBucketed: false, + label: 'Static value: 0', + operationType: 'static_value', + params: { value: 0 }, + references: [], + scale: 'ratio', + }, + }, + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 6a45e3c987f3d..2138b06a4c344 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -44,7 +44,7 @@ import { import { isDraggedField, normalizeOperationDataType } from './utils'; import { LayerPanel } from './layerpanel'; -import { IndexPatternColumn, getErrorMessages } from './operations'; +import { IndexPatternColumn, getErrorMessages, insertNewColumn } from './operations'; import { IndexPatternField, IndexPatternPrivateState, IndexPatternPersistedState } from './types'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; @@ -192,6 +192,27 @@ export function getIndexPatternDatasource({ }); }, + initializeDimension(state, layerId, { columnId, groupId, label, dataType, staticValue }) { + const indexPattern = state.indexPatterns[state.layers[layerId]?.indexPatternId]; + if (staticValue == null) { + return state; + } + return mergeLayer({ + state, + layerId, + newLayer: insertNewColumn({ + layer: state.layers[layerId], + op: 'static_value', + columnId, + field: undefined, + indexPattern, + visualizationGroups: [], + initialParams: { params: { value: staticValue } }, + targetGroup: groupId, + }), + }); + }, + toExpression: (state, layerId) => toExpression(state, layerId, uiSettings), renderDataPanel( @@ -404,9 +425,14 @@ export function getIndexPatternDatasource({ }, }; }, - getDatasourceSuggestionsForField(state, draggedField) { + getDatasourceSuggestionsForField(state, draggedField, filterLayers) { return isDraggedField(draggedField) - ? getDatasourceSuggestionsForField(state, draggedField.indexPatternId, draggedField.field) + ? getDatasourceSuggestionsForField( + state, + draggedField.indexPatternId, + draggedField.field, + filterLayers + ) : []; }, getDatasourceSuggestionsFromCurrentState, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 4b8bbc09c6799..a5d6db4be3319 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1198,6 +1198,91 @@ describe('IndexPattern Data Source suggestions', () => { }) ); }); + + it('should apply layers filter if passed and model the suggestion based on that', () => { + (generateId as jest.Mock).mockReturnValue('newid'); + const initialState = stateWithNonEmptyTables(); + + const modifiedState: IndexPatternPrivateState = { + ...initialState, + layers: { + thresholdLayer: { + indexPatternId: '1', + columnOrder: ['threshold'], + columns: { + threshold: { + dataType: 'number', + isBucketed: false, + label: 'Static Value: 0', + operationType: 'static_value', + params: { value: '0' }, + references: [], + scale: 'ratio', + }, + }, + }, + currentLayer: { + indexPatternId: '1', + columnOrder: ['metric', 'ref'], + columns: { + metric: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'average', + sourceField: 'bytes', + }, + ref: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + + const suggestions = getSuggestionSubset( + getDatasourceSuggestionsForField( + modifiedState, + '1', + documentField, + (layerId) => layerId !== 'thresholdLayer' + ) + ); + // should ignore the threshold layer + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'extended', + columns: [ + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + { + columnId: 'newid', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Count of records', + scale: 'ratio', + }, + }, + ], + }), + }) + ); + }); }); describe('finding the layer that is using the current index pattern', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index b0793bf912bb2..0fe0ef617dc27 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -95,10 +95,14 @@ function buildSuggestion({ export function getDatasourceSuggestionsForField( state: IndexPatternPrivateState, indexPatternId: string, - field: IndexPatternField + field: IndexPatternField, + filterLayers?: (layerId: string) => boolean ): IndexPatternSuggestion[] { const layers = Object.keys(state.layers); - const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); + let layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); + if (filterLayers) { + layerIds = layerIds.filter(filterLayers); + } if (layerIds.length === 0) { // The field we're suggesting on does not match any existing layer. diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx index c2ba893a9b90e..499170349c3d5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx @@ -355,6 +355,33 @@ describe('formula', () => { references: [], }); }); + + it('should move into Formula previous static_value operation', () => { + expect( + formulaOperation.buildColumn({ + previousColumn: { + label: 'Static value: 0', + dataType: 'number', + isBucketed: false, + operationType: 'static_value', + references: [], + params: { + value: '0', + }, + }, + layer, + indexPattern, + }) + ).toEqual({ + label: '0', + dataType: 'number', + operationType: 'formula', + isBucketed: false, + scale: 'ratio', + params: { isFormulaBroken: false, formula: '0' }, + references: [], + }); + }); }); describe('regenerateLayerFromAst()', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts index 589f547434b91..3db9ebc6f969d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/generate.ts @@ -38,6 +38,11 @@ export function generateFormula( previousFormula: string, operationDefinitionMap: Record | undefined ) { + if (previousColumn.operationType === 'static_value') { + if (previousColumn.params && 'value' in previousColumn.params) { + return String(previousColumn.params.value); // make sure it's a string + } + } if ('references' in previousColumn) { const metric = layer.columns[previousColumn.references[0]]; if (metric && 'sourceField' in metric && metric.dataType === 'number') { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx index 45abbcd3d9cf9..a399183694863 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { IndexPatternColumn, operationDefinitionMap } from '.'; -import { FieldBasedIndexPatternColumn } from './column_types'; +import { FieldBasedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from './column_types'; import { IndexPattern } from '../../types'; export function getInvalidFieldMessage( @@ -81,8 +81,7 @@ export function isValidNumber( const inputValueAsNumber = Number(inputValue); return ( inputValue !== '' && - inputValue !== null && - inputValue !== undefined && + inputValue != null && !Number.isNaN(inputValueAsNumber) && Number.isFinite(inputValueAsNumber) && (!integer || Number.isInteger(inputValueAsNumber)) && @@ -91,7 +90,9 @@ export function isValidNumber( ); } -export function getFormatFromPreviousColumn(previousColumn: IndexPatternColumn | undefined) { +export function getFormatFromPreviousColumn( + previousColumn: IndexPatternColumn | ReferenceBasedIndexPatternColumn | undefined +) { return previousColumn?.dataType === 'number' && previousColumn.params && 'format' in previousColumn.params && diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 326b71f72c060..0212c73f46879 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -49,6 +49,7 @@ import { formulaOperation, FormulaIndexPatternColumn, } from './formula'; +import { staticValueOperation, StaticValueIndexPatternColumn } from './static_value'; import { lastValueOperation, LastValueIndexPatternColumn } from './last_value'; import { FrameDatasourceAPI, OperationMetadata } from '../../../types'; import type { BaseIndexPatternColumn, ReferenceBasedIndexPatternColumn } from './column_types'; @@ -87,7 +88,8 @@ export type IndexPatternColumn = | DerivativeIndexPatternColumn | MovingAverageIndexPatternColumn | MathIndexPatternColumn - | FormulaIndexPatternColumn; + | FormulaIndexPatternColumn + | StaticValueIndexPatternColumn; export type FieldBasedIndexPatternColumn = Extract; @@ -119,6 +121,7 @@ export { CountIndexPatternColumn } from './count'; export { LastValueIndexPatternColumn } from './last_value'; export { RangeIndexPatternColumn } from './ranges'; export { FormulaIndexPatternColumn, MathIndexPatternColumn } from './formula'; +export { StaticValueIndexPatternColumn } from './static_value'; // List of all operation definitions registered to this data source. // If you want to implement a new operation, add the definition to this array and @@ -147,6 +150,7 @@ const internalOperationDefinitions = [ overallMinOperation, overallMaxOperation, overallAverageOperation, + staticValueOperation, ]; export { termsOperation } from './terms'; @@ -168,6 +172,7 @@ export { overallMinOperation, } from './calculations'; export { formulaOperation } from './formula/formula'; +export { staticValueOperation } from './static_value'; /** * Properties passed to the operation-specific part of the popover editor diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx new file mode 100644 index 0000000000000..0a6620eecf308 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx @@ -0,0 +1,404 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { shallow, mount } from 'enzyme'; +import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; +import { createMockedIndexPattern } from '../../mocks'; +import { staticValueOperation } from './index'; +import { IndexPattern, IndexPatternLayer } from '../../types'; +import { StaticValueIndexPatternColumn } from './static_value'; +import { EuiFieldNumber } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; + +jest.mock('lodash', () => { + const original = jest.requireActual('lodash'); + + return { + ...original, + debounce: (fn: unknown) => fn, + }; +}); + +const uiSettingsMock = {} as IUiSettingsClient; + +const defaultProps = { + storage: {} as IStorageWrapper, + uiSettings: uiSettingsMock, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + data: dataPluginMock.createStartContract(), + http: {} as HttpSetup, + indexPattern: { + ...createMockedIndexPattern(), + hasRestrictions: false, + } as IndexPattern, + operationDefinitionMap: {}, + isFullscreen: false, + toggleFullscreen: jest.fn(), + setIsCloseable: jest.fn(), + layerId: '1', +}; + +describe('static_value', () => { + let layer: IndexPatternLayer; + + beforeEach(() => { + layer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col2: { + label: 'Static value: 23', + dataType: 'number', + isBucketed: false, + operationType: 'static_value', + references: [], + params: { + value: '23', + }, + }, + }, + }; + }); + + function getLayerWithStaticValue(newValue: string): IndexPatternLayer { + return { + ...layer, + columns: { + ...layer.columns, + col2: { + ...layer.columns.col2, + label: `Static value: ${newValue}`, + params: { + value: newValue, + }, + } as StaticValueIndexPatternColumn, + }, + }; + } + + describe('getDefaultLabel', () => { + it('should return the label for the given value', () => { + expect( + staticValueOperation.getDefaultLabel( + { + label: 'Static value: 23', + dataType: 'number', + isBucketed: false, + operationType: 'static_value', + references: [], + params: { + value: '23', + }, + }, + createMockedIndexPattern(), + layer.columns + ) + ).toBe('Static value: 23'); + }); + + it('should return the default label for non valid value', () => { + expect( + staticValueOperation.getDefaultLabel( + { + label: 'Static value', + dataType: 'number', + isBucketed: false, + operationType: 'static_value', + references: [], + params: { + value: '', + }, + }, + createMockedIndexPattern(), + layer.columns + ) + ).toBe('Static value'); + }); + }); + + describe('getErrorMessage', () => { + it('should return no error for valid values', () => { + expect( + staticValueOperation.getErrorMessage!( + getLayerWithStaticValue('23'), + 'col2', + createMockedIndexPattern() + ) + ).toBeUndefined(); + // test for potential falsy value + expect( + staticValueOperation.getErrorMessage!( + getLayerWithStaticValue('0'), + 'col2', + createMockedIndexPattern() + ) + ).toBeUndefined(); + }); + + it('should return error for invalid values', () => { + for (const value of ['NaN', 'Infinity', 'string']) { + expect( + staticValueOperation.getErrorMessage!( + getLayerWithStaticValue(value), + 'col2', + createMockedIndexPattern() + ) + ).toEqual(expect.arrayContaining([expect.stringMatching('is not a valid number')])); + } + }); + }); + + describe('toExpression', () => { + it('should return a mathColumn operation with valid value', () => { + for (const value of ['23', '0', '-1']) { + expect( + staticValueOperation.toExpression( + getLayerWithStaticValue(value), + 'col2', + createMockedIndexPattern() + ) + ).toEqual([ + { + type: 'function', + function: 'mathColumn', + arguments: { + id: ['col2'], + name: [`Static value: ${value}`], + expression: [value], + }, + }, + ]); + } + }); + + it('should fallback to mapColumn for invalid value', () => { + for (const value of ['NaN', '', 'Infinity']) { + expect( + staticValueOperation.toExpression( + getLayerWithStaticValue(value), + 'col2', + createMockedIndexPattern() + ) + ).toEqual([ + { + type: 'function', + function: 'mapColumn', + arguments: { + id: ['col2'], + name: [`Static value`], + expression: ['100'], + }, + }, + ]); + } + }); + }); + + describe('buildColumn', () => { + it('should set default static value', () => { + expect( + staticValueOperation.buildColumn({ + indexPattern: createMockedIndexPattern(), + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + }) + ).toEqual({ + label: 'Static value', + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { value: '100' }, + references: [], + }); + }); + + it('should merge a previousColumn', () => { + expect( + staticValueOperation.buildColumn({ + indexPattern: createMockedIndexPattern(), + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + previousColumn: { + label: 'Static value', + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { value: '23' }, + references: [], + }, + }) + ).toEqual({ + label: 'Static value: 23', + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { value: '23' }, + references: [], + }); + }); + + it('should create a static_value from passed arguments', () => { + expect( + staticValueOperation.buildColumn( + { + indexPattern: createMockedIndexPattern(), + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + }, + { value: '23' } + ) + ).toEqual({ + label: 'Static value: 23', + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { value: '23' }, + references: [], + }); + }); + + it('should prioritize passed arguments over previousColumn', () => { + expect( + staticValueOperation.buildColumn( + { + indexPattern: createMockedIndexPattern(), + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + previousColumn: { + label: 'Static value', + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { value: '23' }, + references: [], + }, + }, + { value: '53' } + ) + ).toEqual({ + label: 'Static value: 53', + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { value: '53' }, + references: [], + }); + }); + }); + + describe('paramEditor', () => { + const ParamEditor = staticValueOperation.paramEditor!; + it('should render current static_value', () => { + const updateLayerSpy = jest.fn(); + const instance = shallow( + + ); + + const input = instance.find('[data-test-subj="lns-indexPattern-static_value-input"]'); + + expect(input.prop('value')).toEqual('23'); + }); + + it('should update state on change', async () => { + const updateLayerSpy = jest.fn(); + const instance = mount( + + ); + + const input = instance + .find('[data-test-subj="lns-indexPattern-static_value-input"]') + .find(EuiFieldNumber); + + await act(async () => { + input.prop('onChange')!({ + currentTarget: { value: '27' }, + } as React.ChangeEvent); + }); + + instance.update(); + + expect(updateLayerSpy.mock.calls[0]).toEqual([expect.any(Function)]); + // check that the result of the setter call is correct + expect(updateLayerSpy.mock.calls[0][0](layer)).toEqual({ + ...layer, + columns: { + ...layer.columns, + col2: { + ...layer.columns.col2, + params: { + value: '27', + }, + label: 'Static value: 27', + }, + }, + }); + }); + + it('should not update on invalid input, but show invalid value locally', async () => { + const updateLayerSpy = jest.fn(); + const instance = mount( + + ); + + const input = instance + .find('[data-test-subj="lns-indexPattern-static_value-input"]') + .find(EuiFieldNumber); + + await act(async () => { + input.prop('onChange')!({ + currentTarget: { value: '' }, + } as React.ChangeEvent); + }); + + instance.update(); + + expect(updateLayerSpy).not.toHaveBeenCalled(); + expect( + instance + .find('[data-test-subj="lns-indexPattern-static_value-input"]') + .find(EuiFieldNumber) + .prop('value') + ).toEqual(''); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx new file mode 100644 index 0000000000000..a76c5f64d1750 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.tsx @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFieldNumber, EuiFormLabel, EuiSpacer } from '@elastic/eui'; +import { OperationDefinition } from './index'; +import { ReferenceBasedIndexPatternColumn } from './column_types'; +import type { IndexPattern } from '../../types'; +import { useDebouncedValue } from '../../../shared_components'; +import { getFormatFromPreviousColumn, isValidNumber } from './helpers'; + +const defaultLabel = i18n.translate('xpack.lens.indexPattern.staticValueLabelDefault', { + defaultMessage: 'Static value', +}); + +const defaultValue = 100; + +function isEmptyValue(value: number | string | undefined) { + return value == null || value === ''; +} + +function ofName(value: number | string | undefined) { + if (isEmptyValue(value)) { + return defaultLabel; + } + return i18n.translate('xpack.lens.indexPattern.staticValueLabelWithValue', { + defaultMessage: 'Static value: {value}', + values: { value }, + }); +} + +export interface StaticValueIndexPatternColumn extends ReferenceBasedIndexPatternColumn { + operationType: 'static_value'; + params: { + value?: string; + format?: { + id: string; + params?: { + decimals: number; + }; + }; + }; +} + +export const staticValueOperation: OperationDefinition< + StaticValueIndexPatternColumn, + 'managedReference' +> = { + type: 'static_value', + displayName: defaultLabel, + getDefaultLabel: (column) => ofName(column.params.value), + input: 'managedReference', + hidden: true, + getDisabledStatus(indexPattern: IndexPattern) { + return undefined; + }, + getErrorMessage(layer, columnId) { + const column = layer.columns[columnId] as StaticValueIndexPatternColumn; + + return !isValidNumber(column.params.value) + ? [ + i18n.translate('xpack.lens.indexPattern.staticValueError', { + defaultMessage: 'The static value of {value} is not a valid number', + values: { value: column.params.value }, + }), + ] + : undefined; + }, + getPossibleOperation() { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + }, + toExpression: (layer, columnId) => { + const currentColumn = layer.columns[columnId] as StaticValueIndexPatternColumn; + const params = currentColumn.params; + // TODO: improve this logic + const useDisplayLabel = currentColumn.label !== defaultLabel; + const label = isValidNumber(params.value) + ? useDisplayLabel + ? currentColumn.label + : params?.value ?? defaultLabel + : defaultLabel; + + return [ + { + type: 'function', + function: isValidNumber(params.value) ? 'mathColumn' : 'mapColumn', + arguments: { + id: [columnId], + name: [label || defaultLabel], + expression: [isValidNumber(params.value) ? params.value! : String(defaultValue)], + }, + }, + ]; + }, + buildColumn({ previousColumn, layer, indexPattern }, columnParams, operationDefinitionMap) { + const existingStaticValue = + previousColumn?.params && + 'value' in previousColumn.params && + isValidNumber(previousColumn.params.value) + ? previousColumn.params.value + : undefined; + const previousParams: StaticValueIndexPatternColumn['params'] = { + ...{ value: existingStaticValue }, + ...getFormatFromPreviousColumn(previousColumn), + ...columnParams, + }; + return { + label: ofName(previousParams.value), + dataType: 'number', + operationType: 'static_value', + isBucketed: false, + scale: 'ratio', + params: { ...previousParams, value: previousParams.value ?? String(defaultValue) }, + references: [], + }; + }, + isTransferable: (column) => { + return true; + }, + createCopy(layer, sourceId, targetId, indexPattern, operationDefinitionMap) { + const currentColumn = layer.columns[sourceId] as StaticValueIndexPatternColumn; + return { + ...layer, + columns: { + ...layer.columns, + [targetId]: { ...currentColumn }, + }, + }; + }, + + paramEditor: function StaticValueEditor({ + layer, + updateLayer, + currentColumn, + columnId, + activeData, + layerId, + indexPattern, + }) { + const onChange = useCallback( + (newValue) => { + // even if debounced it's triggering for empty string with the previous valid value + if (currentColumn.params.value === newValue) { + return; + } + // Because of upstream specific UX flows, we need fresh layer state here + // so need to use the updater pattern + updateLayer((newLayer) => { + const newColumn = newLayer.columns[columnId] as StaticValueIndexPatternColumn; + return { + ...newLayer, + columns: { + ...newLayer.columns, + [columnId]: { + ...newColumn, + label: newColumn?.customLabel ? newColumn.label : ofName(newValue), + params: { + ...newColumn.params, + value: newValue, + }, + }, + }, + }; + }); + }, + [columnId, updateLayer, currentColumn?.params?.value] + ); + + // Pick the data from the current activeData (to be used when the current operation is not static_value) + const activeDataValue = + activeData && + activeData[layerId] && + activeData[layerId]?.rows?.length === 1 && + activeData[layerId].rows[0][columnId]; + + const fallbackValue = + currentColumn?.operationType !== 'static_value' && activeDataValue != null + ? activeDataValue + : String(defaultValue); + + const { inputValue, handleInputChange } = useDebouncedValue( + { + value: currentColumn?.params?.value || fallbackValue, + onChange, + }, + { allowFalsyValue: true } + ); + + const onChangeHandler = useCallback( + (e: React.ChangeEvent) => { + const value = e.currentTarget.value; + handleInputChange(isValidNumber(value) ? value : undefined); + }, + [handleInputChange] + ); + + return ( +
+ + {i18n.translate('xpack.lens.indexPattern.staticValue.label', { + defaultMessage: 'Threshold value', + })} + + + +
+ ); + }, +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 11c8206fee021..baacc7bb64d16 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -184,6 +184,7 @@ export function insertNewColumn({ targetGroup, shouldResetLabel, incompleteParams, + initialParams, }: ColumnChange): IndexPatternLayer { const operationDefinition = operationDefinitionMap[op]; @@ -197,7 +198,7 @@ export function insertNewColumn({ const baseOptions = { indexPattern, - previousColumn: { ...incompleteParams, ...layer.columns[columnId] }, + previousColumn: { ...incompleteParams, ...initialParams, ...layer.columns[columnId] }, }; if (operationDefinition.input === 'none' || operationDefinition.input === 'managedReference') { @@ -396,9 +397,17 @@ export function replaceColumn({ tempLayer = resetIncomplete(tempLayer, columnId); - if (previousDefinition.input === 'managedReference') { + if ( + previousDefinition.input === 'managedReference' && + operationDefinition.input !== previousDefinition.input + ) { // If the transition is incomplete, leave the managed state until it's finished. - tempLayer = deleteColumn({ layer: tempLayer, columnId, indexPattern }); + tempLayer = removeOrphanedColumns( + previousDefinition, + previousColumn, + tempLayer, + indexPattern + ); const hypotheticalLayer = insertNewColumn({ layer: tempLayer, @@ -641,21 +650,31 @@ function removeOrphanedColumns( previousDefinition: | OperationDefinition | OperationDefinition - | OperationDefinition, + | OperationDefinition + | OperationDefinition, previousColumn: IndexPatternColumn, tempLayer: IndexPatternLayer, indexPattern: IndexPattern ) { + let newLayer: IndexPatternLayer = tempLayer; + if (previousDefinition.input === 'managedReference') { + const [columnId] = + Object.entries(tempLayer.columns).find(([_, currColumn]) => currColumn === previousColumn) || + []; + if (columnId != null) { + newLayer = deleteColumn({ layer: tempLayer, columnId, indexPattern }); + } + } if (previousDefinition.input === 'fullReference') { (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { - tempLayer = deleteColumn({ + newLayer = deleteColumn({ layer: tempLayer, columnId: id, indexPattern, }); }); } - return tempLayer; + return newLayer; } export function canTransition({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts index 2ed6e2b3a7bcb..08136ed501cfc 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts @@ -378,6 +378,10 @@ describe('getOperationTypesForField', () => { "operationType": "formula", "type": "managedReference", }, + Object { + "operationType": "static_value", + "type": "managedReference", + }, ], }, Object { diff --git a/x-pack/plugins/lens/public/mocks.tsx b/x-pack/plugins/lens/public/mocks.tsx index 47a2e00055ce3..402440f3302f6 100644 --- a/x-pack/plugins/lens/public/mocks.tsx +++ b/x-pack/plugins/lens/public/mocks.tsx @@ -59,9 +59,9 @@ export function mockDatasourceStates() { }; } -export function createMockVisualization(): jest.Mocked { +export function createMockVisualization(id = 'vis1'): jest.Mocked { return { - id: 'TEST_VIS', + id, clearLayer: jest.fn((state, _layerId) => state), removeLayer: jest.fn(), getLayerIds: jest.fn((_state) => ['layer1']), @@ -70,9 +70,9 @@ export function createMockVisualization(): jest.Mocked { visualizationTypes: [ { icon: 'empty', - id: 'TEST_VIS', + id, label: 'TEST', - groupLabel: 'TEST_VISGroup', + groupLabel: `${id}Group`, }, ], getVisualizationTypeId: jest.fn((_state) => 'empty'), @@ -122,7 +122,7 @@ export function createMockDatasource(id: string): DatasourceMock { return { id: 'mockindexpattern', clearLayer: jest.fn((state, _layerId) => state), - getDatasourceSuggestionsForField: jest.fn((_state, _item) => []), + getDatasourceSuggestionsForField: jest.fn((_state, _item, filterFn) => []), getDatasourceSuggestionsForVisualizeField: jest.fn((_state, _indexpatternId, _fieldName) => []), getDatasourceSuggestionsFromCurrentState: jest.fn((_state) => []), getPersistableState: jest.fn((x) => ({ diff --git a/x-pack/plugins/lens/public/shared_components/debounced_value.ts b/x-pack/plugins/lens/public/shared_components/debounced_value.ts index fa8fc22dedd57..412199a371f1f 100644 --- a/x-pack/plugins/lens/public/shared_components/debounced_value.ts +++ b/x-pack/plugins/lens/public/shared_components/debounced_value.ts @@ -11,6 +11,10 @@ import { debounce } from 'lodash'; /** * Debounces value changes and updates inputValue on root state changes if no debounced changes * are in flight because the user is currently modifying the value. + * + * * allowFalsyValue: update upstream with all falsy values but null or undefined + * + * When testing this function mock the "debounce" function in lodash (see this module test for an example) */ export const useDebouncedValue = ( diff --git a/x-pack/plugins/lens/public/shared_components/index.ts b/x-pack/plugins/lens/public/shared_components/index.ts index c200a18a25caf..f947ce699dce4 100644 --- a/x-pack/plugins/lens/public/shared_components/index.ts +++ b/x-pack/plugins/lens/public/shared_components/index.ts @@ -14,3 +14,4 @@ export * from './coloring'; export { useDebouncedValue } from './debounced_value'; export * from './helpers'; export { LegendActionPopover } from './legend_action_popover'; +export * from './static_header'; diff --git a/x-pack/plugins/lens/public/shared_components/static_header.tsx b/x-pack/plugins/lens/public/shared_components/static_header.tsx new file mode 100644 index 0000000000000..2250358234a7f --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/static_header.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiTitle, IconType } from '@elastic/eui'; + +export const StaticHeader = ({ label, icon }: { label: string; icon?: IconType }) => { + return ( + + {icon && ( + + {' '} + + )} + + +
{label}
+
+
+
+ ); +}; diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index 0f5f1c15d4fa7..7db03a17a3a8f 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -149,7 +149,7 @@ export function loadInitial( datasourceMap, datasourceStates, visualizationMap, - activeVisualizationId: Object.keys(visualizationMap)[0] || null, + activeVisualization: visualizationMap?.[Object.keys(visualizationMap)[0]] || null, visualizationState: null, visualizeTriggerFieldContext: initialContext, }); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index a4a483fa95d37..cf6634c200d55 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -234,7 +234,11 @@ export interface Datasource { toExpression: (state: T, layerId: string) => ExpressionAstExpression | string | null; - getDatasourceSuggestionsForField: (state: T, field: unknown) => Array>; + getDatasourceSuggestionsForField: ( + state: T, + field: unknown, + filterFn: (layerId: string) => boolean + ) => Array>; getDatasourceSuggestionsForVisualizeField: ( state: T, indexPatternId: string, @@ -326,6 +330,8 @@ export type DatasourceDimensionProps = SharedDimensionProps & { onRemove?: (accessor: string) => void; state: T; activeData?: Record; + invalid?: boolean; + invalidMessage?: string; }; // The only way a visualization has to restrict the query building @@ -335,6 +341,7 @@ export type DatasourceDimensionEditorProps = DatasourceDimensionPro newState: Parameters>[0], publishToVisualization?: { isDimensionComplete?: boolean; + forceRender?: boolean; } ) => void; core: Pick; @@ -343,6 +350,8 @@ export type DatasourceDimensionEditorProps = DatasourceDimensionPro toggleFullscreen: () => void; isFullscreen: boolean; layerType: LayerType | undefined; + supportStaticValue: boolean; + supportFieldFormat?: boolean; }; export type DatasourceDimensionTriggerProps = DatasourceDimensionProps; @@ -434,7 +443,7 @@ export interface VisualizationToolbarProps { export type VisualizationDimensionEditorProps = VisualizationConfigProps & { groupId: string; accessor: string; - setState: (newState: T) => void; + setState(newState: T | ((currState: T) => T)): void; panelRef: MutableRefObject; }; @@ -466,13 +475,16 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { // this dimension group in the hierarchy. If not specified, the position of the dimension in the array is used. specified nesting // orders are always higher in the hierarchy than non-specified ones. nestingOrder?: number; + // some type of layers can produce groups even if invalid. Keep this information to visually show the user that. + invalid?: boolean; + invalidMessage?: string; }; interface VisualizationDimensionChangeProps { layerId: string; columnId: string; prevState: T; - frame: Pick; + frame: Pick; } /** @@ -655,6 +667,7 @@ export interface Visualization { getConfiguration: (props: VisualizationConfigProps) => { groups: VisualizationDimensionGroupConfig[]; supportStaticValue?: boolean; + supportFieldFormat?: boolean; }; /** diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 95c9140624e63..9c83e2c58146e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -30,16 +30,17 @@ export function isFormatterCompatible( return formatter1.id === formatter2.id; } -export function getAxesConfiguration( - layers: XYLayerConfig[], - shouldRotate: boolean, - tables?: Record, - formatFactory?: FormatFactory -): GroupsConfiguration { - const series: { auto: FormattedMetric[]; left: FormattedMetric[]; right: FormattedMetric[] } = { +export function groupAxesByType(layers: XYLayerConfig[], tables?: Record) { + const series: { + auto: FormattedMetric[]; + left: FormattedMetric[]; + right: FormattedMetric[]; + bottom: FormattedMetric[]; + } = { auto: [], left: [], right: [], + bottom: [], }; layers?.forEach((layer) => { @@ -89,6 +90,16 @@ export function getAxesConfiguration( series.right.push(currentSeries); } }); + return series; +} + +export function getAxesConfiguration( + layers: XYLayerConfig[], + shouldRotate: boolean, + tables?: Record, + formatFactory?: FormatFactory +): GroupsConfiguration { + const series = groupAxesByType(layers, tables); const axisGroups: GroupsConfiguration = []; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index 026d9da71beea..863289c31bba4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -59,6 +59,7 @@ import { getAxesConfiguration, GroupsConfiguration, validateExtent } from './axe import { getColorAssignments } from './color_assignment'; import { getXDomain, XyEndzones } from './x_domain'; import { getLegendAction } from './get_legend_action'; +import { ThresholdAnnotations } from './expression_thresholds'; declare global { interface Window { @@ -251,6 +252,7 @@ export function XYChart({ const icon: IconType = layers.length > 0 ? getIconForSeriesType(layers[0].seriesType) : 'bar'; return ; } + const thresholdLayers = layers.filter((layer) => layer.layerType === layerTypes.THRESHOLD); // use formatting hint of first x axis column to format ticks const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( @@ -832,6 +834,20 @@ export function XYChart({ } }) )} + {thresholdLayers.length ? ( + groupId === 'left')?.formatter, + right: yAxesConfiguration.find(({ groupId }) => groupId === 'right')?.formatter, + bottom: xAxisFormatter, + }} + /> + ) : null} ); } diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx new file mode 100644 index 0000000000000..171e2f1cfba9e --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { groupBy } from 'lodash'; +import { EuiIcon } from '@elastic/eui'; +import { RectAnnotation, AnnotationDomainType, LineAnnotation } from '@elastic/charts'; +import type { PaletteRegistry, SeriesLayer } from 'src/plugins/charts/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; +import type { LayerArgs } from '../../common/expressions'; +import type { LensMultiTable } from '../../common/types'; +import type { ColorAssignments } from './color_assignment'; + +export const ThresholdAnnotations = ({ + thresholdLayers, + data, + colorAssignments, + formatters, + paletteService, + syncColors, +}: { + thresholdLayers: LayerArgs[]; + data: LensMultiTable; + colorAssignments: ColorAssignments; + formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>; + paletteService: PaletteRegistry; + syncColors: boolean; +}) => { + return ( + <> + {thresholdLayers.flatMap((thresholdLayer) => { + if (!thresholdLayer.yConfig) { + return []; + } + const { columnToLabel, palette, yConfig: yConfigs, layerId } = thresholdLayer; + const columnToLabelMap: Record = columnToLabel + ? JSON.parse(columnToLabel) + : {}; + const table = data.tables[layerId]; + const colorAssignment = colorAssignments[palette.name]; + + const row = table.rows[0]; + + const yConfigByValue = yConfigs.sort( + ({ forAccessor: idA }, { forAccessor: idB }) => row[idA] - row[idB] + ); + + const groupedByDirection = groupBy(yConfigByValue, 'fill'); + + return yConfigByValue.flatMap((yConfig, i) => { + // Find the formatter for the given axis + const groupId = + yConfig.axisMode === 'bottom' + ? undefined + : yConfig.axisMode === 'right' + ? 'right' + : 'left'; + + const formatter = formatters[groupId || 'bottom']; + + const seriesLayers: SeriesLayer[] = [ + { + name: columnToLabelMap[yConfig.forAccessor], + totalSeriesAtDepth: colorAssignment.totalSeriesCount, + rankAtDepth: colorAssignment.getRank( + thresholdLayer, + String(yConfig.forAccessor), + String(yConfig.forAccessor) + ), + }, + ]; + const defaultColor = paletteService.get(palette.name).getCategoricalColor( + seriesLayers, + { + maxDepth: 1, + behindText: false, + totalSeries: colorAssignment.totalSeriesCount, + syncColors, + }, + palette.params + ); + + const props = { + groupId, + marker: yConfig.icon ? : undefined, + }; + const annotations = []; + + const dashStyle = + yConfig.lineStyle === 'dashed' + ? [(yConfig.lineWidth || 1) * 3, yConfig.lineWidth || 1] + : yConfig.lineStyle === 'dotted' + ? [yConfig.lineWidth || 1, yConfig.lineWidth || 1] + : undefined; + + const sharedStyle = { + strokeWidth: yConfig.lineWidth || 1, + stroke: (yConfig.color || defaultColor) ?? '#f00', + dash: dashStyle, + }; + + annotations.push( + ({ + dataValue: row[yConfig.forAccessor], + header: columnToLabelMap[yConfig.forAccessor], + details: formatter?.convert(row[yConfig.forAccessor]) || row[yConfig.forAccessor], + }))} + domainType={ + yConfig.axisMode === 'bottom' + ? AnnotationDomainType.XDomain + : AnnotationDomainType.YDomain + } + style={{ + line: { + ...sharedStyle, + opacity: 1, + }, + }} + /> + ); + + if (yConfig.fill && yConfig.fill !== 'none') { + const isFillAbove = yConfig.fill === 'above'; + const indexFromSameType = groupedByDirection[yConfig.fill].findIndex( + ({ forAccessor }) => forAccessor === yConfig.forAccessor + ); + const shouldCheckNextThreshold = + indexFromSameType < groupedByDirection[yConfig.fill].length - 1; + annotations.push( + { + if (yConfig.axisMode === 'bottom') { + return { + coordinates: { + x0: isFillAbove ? row[yConfig.forAccessor] : undefined, + y0: undefined, + x1: isFillAbove + ? shouldCheckNextThreshold + ? row[ + groupedByDirection[yConfig.fill!][indexFromSameType + 1].forAccessor + ] + : undefined + : row[yConfig.forAccessor], + y1: undefined, + }, + header: columnToLabelMap[yConfig.forAccessor], + details: + formatter?.convert(row[yConfig.forAccessor]) || row[yConfig.forAccessor], + }; + } + return { + coordinates: { + x0: undefined, + y0: isFillAbove ? row[yConfig.forAccessor] : undefined, + x1: undefined, + y1: isFillAbove + ? shouldCheckNextThreshold + ? row[ + groupedByDirection[yConfig.fill!][indexFromSameType + 1].forAccessor + ] + : undefined + : row[yConfig.forAccessor], + }, + header: columnToLabelMap[yConfig.forAccessor], + details: + formatter?.convert(row[yConfig.forAccessor]) || row[yConfig.forAccessor], + }; + })} + style={{ + ...sharedStyle, + fill: (yConfig.color || defaultColor) ?? '#f00', + opacity: 0.1, + }} + /> + ); + } + return annotations; + }); + })} + + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts index e3b16f5981f88..4edf7fdf5e512 100644 --- a/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts +++ b/x-pack/plugins/lens/public/xy_visualization/state_helpers.ts @@ -18,6 +18,14 @@ export function isHorizontalSeries(seriesType: SeriesType) { ); } +export function isPercentageSeries(seriesType: SeriesType) { + return ( + seriesType === 'bar_percentage_stacked' || + seriesType === 'bar_horizontal_percentage_stacked' || + seriesType === 'area_percentage_stacked' + ); +} + export function isHorizontalChart(layers: Array<{ seriesType: SeriesType }>) { return layers.every((l) => isHorizontalSeries(l.seriesType)); } diff --git a/x-pack/plugins/lens/public/xy_visualization/threshold_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/threshold_helpers.tsx new file mode 100644 index 0000000000000..ec47350709473 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/threshold_helpers.tsx @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { layerTypes } from '../../common'; +import type { XYLayerConfig, YConfig } from '../../common/expressions'; +import { Datatable } from '../../../../../src/plugins/expressions/public'; +import type { DatasourcePublicAPI, FramePublicAPI } from '../types'; +import { groupAxesByType } from './axes_configuration'; +import { isPercentageSeries } from './state_helpers'; +import type { XYState } from './types'; +import { checkScaleOperation } from './visualization_helpers'; + +export interface ThresholdBase { + label: 'x' | 'yRight' | 'yLeft'; +} + +/** + * Return the threshold layers groups to show based on multiple criteria: + * * what groups are current defined in data layers + * * what existing threshold are currently defined in data thresholds + */ +export function getGroupsToShow( + thresholdLayers: T[], + state: XYState | undefined, + datasourceLayers: Record, + tables: Record | undefined +): Array { + if (!state) { + return []; + } + const dataLayers = state.layers.filter( + ({ layerType = layerTypes.DATA }) => layerType === layerTypes.DATA + ); + const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); + return thresholdLayers + .filter(({ label, config }: T) => groupsAvailable[label] || config?.length) + .map((layer) => ({ ...layer, valid: groupsAvailable[layer.label] })); +} + +/** + * Returns the threshold layers groups to show based on what groups are current defined in data layers. + */ +export function getGroupsRelatedToData( + thresholdLayers: T[], + state: XYState | undefined, + datasourceLayers: Record, + tables: Record | undefined +): T[] { + if (!state) { + return []; + } + const dataLayers = state.layers.filter( + ({ layerType = layerTypes.DATA }) => layerType === layerTypes.DATA + ); + const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables); + return thresholdLayers.filter(({ label }: T) => groupsAvailable[label]); +} +/** + * Returns a dictionary with the groups filled in all the data layers + */ +export function getGroupsAvailableInData( + dataLayers: XYState['layers'], + datasourceLayers: Record, + tables: Record | undefined +) { + const hasNumberHistogram = dataLayers.some( + checkScaleOperation('interval', 'number', datasourceLayers) + ); + const { right, left } = groupAxesByType(dataLayers, tables); + return { + x: dataLayers.some(({ xAccessor }) => xAccessor != null) && hasNumberHistogram, + yLeft: left.length > 0, + yRight: right.length > 0, + }; +} + +export function getStaticValue( + dataLayers: XYState['layers'], + groupId: 'x' | 'yLeft' | 'yRight', + { activeData }: Pick, + layerHasNumberHistogram: (layer: XYLayerConfig) => boolean +) { + const fallbackValue = 100; + if (!activeData) { + return fallbackValue; + } + + // filter and organize data dimensions into threshold groups + // now pick the columnId in the active data + const { dataLayer, accessor } = getAccessorCriteriaForGroup(groupId, dataLayers, activeData); + if (groupId === 'x' && dataLayer && !layerHasNumberHistogram(dataLayer)) { + return fallbackValue; + } + return ( + computeStaticValueForGroup( + dataLayer, + accessor, + activeData, + groupId !== 'x' // histogram axis should compute the min based on the current data + ) || fallbackValue + ); +} + +function getAccessorCriteriaForGroup( + groupId: 'x' | 'yLeft' | 'yRight', + dataLayers: XYState['layers'], + activeData: FramePublicAPI['activeData'] +) { + switch (groupId) { + case 'x': + const dataLayer = dataLayers.find(({ xAccessor }) => xAccessor); + return { + dataLayer, + accessor: dataLayer?.xAccessor, + }; + case 'yLeft': + const { left } = groupAxesByType(dataLayers, activeData); + return { + dataLayer: dataLayers.find(({ layerId }) => layerId === left[0]?.layer), + accessor: left[0]?.accessor, + }; + case 'yRight': + const { right } = groupAxesByType(dataLayers, activeData); + return { + dataLayer: dataLayers.find(({ layerId }) => layerId === right[0]?.layer), + accessor: right[0]?.accessor, + }; + } +} + +function computeStaticValueForGroup( + dataLayer: XYLayerConfig | undefined, + accessorId: string | undefined, + activeData: NonNullable, + minZeroBased: boolean +) { + const defaultThresholdFactor = 3 / 4; + + if (dataLayer && accessorId) { + if (isPercentageSeries(dataLayer?.seriesType)) { + return defaultThresholdFactor; + } + const tableId = Object.keys(activeData).find((key) => + activeData[key].columns.some(({ id }) => id === accessorId) + ); + if (tableId) { + const columnMax = activeData[tableId].rows.reduce( + (max, row) => Math.max(row[accessorId], max), + -Infinity + ); + const columnMin = activeData[tableId].rows.reduce( + (max, row) => Math.min(row[accessorId], max), + Infinity + ); + // Custom axis bounds can go below 0, so consider also lower values than 0 + const finalMinValue = minZeroBased ? Math.min(0, columnMin) : columnMin; + const interval = columnMax - finalMinValue; + return Number((finalMinValue + interval * defaultThresholdFactor).toFixed(2)); + } + } +} diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 5290e0298ae5e..aa99aa9b7b316 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -322,6 +322,10 @@ export const buildExpression = ( forAccessor: [yConfig.forAccessor], axisMode: yConfig.axisMode ? [yConfig.axisMode] : [], color: yConfig.color ? [yConfig.color] : [], + lineStyle: yConfig.lineStyle ? [yConfig.lineStyle] : [], + lineWidth: yConfig.lineWidth ? [yConfig.lineWidth] : [], + fill: [yConfig.fill || 'none'], + icon: yConfig.icon ? [yConfig.icon] : [], }, }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts index 7aef40b1481dc..8907db4954f99 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -15,6 +15,7 @@ import { createMockDatasource, createMockFramePublicAPI } from '../mocks'; import { LensIconChartBar } from '../assets/chart_bar'; import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks'; +import { Datatable } from 'src/plugins/expressions'; function exampleState(): State { return { @@ -216,8 +217,8 @@ describe('xy_visualization', () => { }); describe('#getSupportedLayers', () => { - it('should return a single layer type', () => { - expect(xyVisualization.getSupportedLayers()).toHaveLength(1); + it('should return a double layer types', () => { + expect(xyVisualization.getSupportedLayers()).toHaveLength(2); }); it('should return the icon for the visualization type', () => { @@ -317,6 +318,42 @@ describe('xy_visualization', () => { accessors: [], }); }); + + it('should add a dimension to a threshold layer', () => { + expect( + xyVisualization.setDimension({ + frame, + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'threshold', + layerType: layerTypes.THRESHOLD, + seriesType: 'line', + accessors: [], + }, + ], + }, + layerId: 'threshold', + groupId: 'xThreshold', + columnId: 'newCol', + }).layers[0] + ).toEqual({ + layerId: 'threshold', + layerType: layerTypes.THRESHOLD, + seriesType: 'line', + accessors: ['newCol'], + yConfig: [ + { + axisMode: 'bottom', + forAccessor: 'newCol', + icon: undefined, + lineStyle: 'solid', + lineWidth: 1, + }, + ], + }); + }); }); describe('#removeDimension', () => { @@ -504,6 +541,300 @@ describe('xy_visualization', () => { expect(ops.filter(filterOperations).map((x) => x.dataType)).toEqual(['number']); }); + describe('thresholds', () => { + beforeEach(() => { + frame.datasourceLayers = { + first: mockDatasource.publicAPIMock, + threshold: mockDatasource.publicAPIMock, + }; + }); + + function getStateWithBaseThreshold(): State { + return { + ...exampleState(), + layers: [ + { + layerId: 'first', + layerType: layerTypes.DATA, + seriesType: 'area', + splitAccessor: undefined, + xAccessor: undefined, + accessors: ['a'], + }, + { + layerId: 'threshold', + layerType: layerTypes.THRESHOLD, + seriesType: 'line', + accessors: [], + yConfig: [{ axisMode: 'left', forAccessor: 'a' }], + }, + ], + }; + } + + it('should support static value', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].accessors = []; + state.layers[1].yConfig = undefined; + + expect( + xyVisualization.getConfiguration({ + state: getStateWithBaseThreshold(), + frame, + layerId: 'threshold', + }).supportStaticValue + ).toBeTruthy(); + }); + + it('should return no threshold groups for a empty data layer', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].accessors = []; + state.layers[1].yConfig = undefined; + + const options = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(options).toHaveLength(0); + }); + + it('should return a group for the vertical left axis', () => { + const options = xyVisualization.getConfiguration({ + state: getStateWithBaseThreshold(), + frame, + layerId: 'threshold', + }).groups; + + expect(options).toHaveLength(1); + expect(options[0].groupId).toBe('yThresholdLeft'); + }); + + it('should return a group for the vertical right axis', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].yConfig = [{ axisMode: 'right', forAccessor: 'a' }]; + state.layers[1].yConfig![0].axisMode = 'right'; + + const options = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(options).toHaveLength(1); + expect(options[0].groupId).toBe('yThresholdRight'); + }); + + it('should compute no groups for thresholds when the only data accessor available is a date histogram', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].xAccessor = 'b'; + state.layers[0].accessors = []; + state.layers[1].yConfig = []; // empty the configuration + // set the xAccessor as date_histogram + frame.datasourceLayers.threshold.getOperationForColumnId = jest.fn((accessor) => { + if (accessor === 'b') { + return { + dataType: 'date', + isBucketed: true, + scale: 'interval', + label: 'date_histogram', + }; + } + return null; + }); + + const options = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(options).toHaveLength(0); + }); + + it('should mark horizontal group is invalid when xAccessor is changed to a date histogram', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].xAccessor = 'b'; + state.layers[0].accessors = []; + state.layers[1].yConfig![0].axisMode = 'bottom'; + // set the xAccessor as date_histogram + frame.datasourceLayers.threshold.getOperationForColumnId = jest.fn((accessor) => { + if (accessor === 'b') { + return { + dataType: 'date', + isBucketed: true, + scale: 'interval', + label: 'date_histogram', + }; + } + return null; + }); + + const options = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(options[0]).toEqual( + expect.objectContaining({ + invalid: true, + groupId: 'xThreshold', + }) + ); + }); + + it('should return groups in a specific order (left, right, bottom)', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].xAccessor = 'c'; + state.layers[0].accessors = ['a', 'b']; + // invert them on purpose + state.layers[0].yConfig = [ + { axisMode: 'right', forAccessor: 'b' }, + { axisMode: 'left', forAccessor: 'a' }, + ]; + state.layers[1].yConfig = [ + { forAccessor: 'c', axisMode: 'bottom' }, + { forAccessor: 'b', axisMode: 'right' }, + { forAccessor: 'a', axisMode: 'left' }, + ]; + // set the xAccessor as number histogram + frame.datasourceLayers.threshold.getOperationForColumnId = jest.fn((accessor) => { + if (accessor === 'c') { + return { + dataType: 'number', + isBucketed: true, + scale: 'interval', + label: 'histogram', + }; + } + return null; + }); + + const [left, right, bottom] = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(left.groupId).toBe('yThresholdLeft'); + expect(right.groupId).toBe('yThresholdRight'); + expect(bottom.groupId).toBe('xThreshold'); + }); + + it('should ignore terms operation for xAccessor', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].xAccessor = 'b'; + state.layers[0].accessors = []; + state.layers[1].yConfig = []; // empty the configuration + // set the xAccessor as top values + frame.datasourceLayers.threshold.getOperationForColumnId = jest.fn((accessor) => { + if (accessor === 'b') { + return { + dataType: 'string', + isBucketed: true, + scale: 'ordinal', + label: 'top values', + }; + } + return null; + }); + + const options = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(options).toHaveLength(0); + }); + + it('should mark horizontal group is invalid when accessor is changed to a terms operation', () => { + const state = getStateWithBaseThreshold(); + state.layers[0].xAccessor = 'b'; + state.layers[0].accessors = []; + state.layers[1].yConfig![0].axisMode = 'bottom'; + // set the xAccessor as date_histogram + frame.datasourceLayers.threshold.getOperationForColumnId = jest.fn((accessor) => { + if (accessor === 'b') { + return { + dataType: 'string', + isBucketed: true, + scale: 'ordinal', + label: 'top values', + }; + } + return null; + }); + + const options = xyVisualization.getConfiguration({ + state, + frame, + layerId: 'threshold', + }).groups; + + expect(options[0]).toEqual( + expect.objectContaining({ + invalid: true, + groupId: 'xThreshold', + }) + ); + }); + + it('differ vertical axis if the formatters are not compatibles between each other', () => { + const tables: Record = { + first: { + type: 'datatable', + rows: [], + columns: [ + { + id: 'xAccessorId', + name: 'horizontal axis', + meta: { + type: 'date', + params: { params: { id: 'date', params: { pattern: 'HH:mm' } } }, + }, + }, + { + id: 'yAccessorId', + name: 'left axis', + meta: { + type: 'number', + params: { id: 'number' }, + }, + }, + { + id: 'yAccessorId2', + name: 'right axis', + meta: { + type: 'number', + params: { id: 'bytes' }, + }, + }, + ], + }, + }; + + const state = getStateWithBaseThreshold(); + state.layers[0].accessors = ['yAccessorId', 'yAccessorId2']; + state.layers[1].yConfig = []; // empty the configuration + + const options = xyVisualization.getConfiguration({ + state, + frame: { ...frame, activeData: tables }, + layerId: 'threshold', + }).groups; + + expect(options).toEqual( + expect.arrayContaining([ + expect.objectContaining({ groupId: 'yThresholdLeft' }), + expect.objectContaining({ groupId: 'yThresholdRight' }), + ]) + ); + }); + }); + describe('color assignment', () => { function callConfig(layerConfigOverride: Partial) { const baseState = exampleState(); diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx index 026c2827cedbd..ed1cc015806c5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { uniq } from 'lodash'; +import { groupBy, uniq } from 'lodash'; import { render } from 'react-dom'; import { Position } from '@elastic/charts'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; @@ -14,15 +14,10 @@ import { i18n } from '@kbn/i18n'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { FieldFormatsStart } from 'src/plugins/field_formats/public'; import { getSuggestions } from './xy_suggestions'; -import { XyToolbar, DimensionEditor, LayerHeader } from './xy_config_panel'; -import type { - Visualization, - OperationMetadata, - VisualizationType, - AccessorConfig, - DatasourcePublicAPI, -} from '../types'; -import { State, visualizationTypes, XYState } from './types'; +import { XyToolbar, DimensionEditor } from './xy_config_panel'; +import { LayerHeader } from './xy_config_panel/layer_header'; +import type { Visualization, OperationMetadata, VisualizationType, AccessorConfig } from '../types'; +import { State, visualizationTypes } from './types'; import { SeriesType, XYLayerConfig } from '../../common/expressions'; import { LayerType, layerTypes } from '../../common'; import { isHorizontalChart } from './state_helpers'; @@ -32,6 +27,19 @@ import { LensIconChartMixedXy } from '../assets/chart_mixed_xy'; import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal'; import { getAccessorColorConfig, getColorAssignments } from './color_assignment'; import { getColumnToLabelMap } from './state_helpers'; +import { LensIconChartBarThreshold } from '../assets/chart_bar_threshold'; +import { generateId } from '../id_generator'; +import { + getGroupsAvailableInData, + getGroupsRelatedToData, + getGroupsToShow, + getStaticValue, +} from './threshold_helpers'; +import { + checkScaleOperation, + checkXAccessorCompatibility, + getAxisName, +} from './visualization_helpers'; const defaultIcon = LensIconChartBarStacked; const defaultSeriesType = 'bar_stacked'; @@ -186,6 +194,39 @@ export const getXyVisualization = ({ }, getSupportedLayers(state, frame) { + const thresholdGroupIds = [ + { + id: 'yThresholdLeft', + label: 'yLeft' as const, + }, + { + id: 'yThresholdRight', + label: 'yRight' as const, + }, + { + id: 'xThreshold', + label: 'x' as const, + }, + ]; + + const dataLayers = + state?.layers.filter(({ layerType = layerTypes.DATA }) => layerType === layerTypes.DATA) || + []; + const filledDataLayers = dataLayers.filter( + ({ accessors, xAccessor }) => accessors.length || xAccessor + ); + const layerHasNumberHistogram = checkScaleOperation( + 'interval', + 'number', + frame?.datasourceLayers || {} + ); + const thresholdGroups = getGroupsRelatedToData( + thresholdGroupIds, + state, + frame?.datasourceLayers || {}, + frame?.activeData + ); + const layers = [ { type: layerTypes.DATA, @@ -194,6 +235,36 @@ export const getXyVisualization = ({ }), icon: LensIconChartMixedXy, }, + { + type: layerTypes.THRESHOLD, + label: i18n.translate('xpack.lens.xyChart.addThresholdLayerLabel', { + defaultMessage: 'Add threshold layer', + }), + icon: LensIconChartBarThreshold, + disabled: + !filledDataLayers.length || + (!dataLayers.some(layerHasNumberHistogram) && + dataLayers.every(({ accessors }) => !accessors.length)), + tooltipContent: filledDataLayers.length + ? undefined + : i18n.translate('xpack.lens.xyChart.addThresholdLayerLabelDisabledHelp', { + defaultMessage: 'Add some data to enable threshold layer', + }), + initialDimensions: state + ? thresholdGroups.map(({ id, label }) => ({ + groupId: id, + columnId: generateId(), + dataType: 'number', + label: getAxisName(label, { isHorizontal: isHorizontalChart(state?.layers || []) }), + staticValue: getStaticValue( + dataLayers, + label, + { activeData: frame?.activeData }, + layerHasNumberHistogram + ), + })) + : undefined, + }, ]; return layers; @@ -233,8 +304,70 @@ export const getXyVisualization = ({ const isDataLayer = !layer.layerType || layer.layerType === layerTypes.DATA; if (!isDataLayer) { + const idToIndex = sortedAccessors.reduce>((memo, id, index) => { + memo[id] = index; + return memo; + }, {}); + const { bottom, left, right } = groupBy( + [...(layer.yConfig || [])].sort( + ({ forAccessor: forA }, { forAccessor: forB }) => idToIndex[forA] - idToIndex[forB] + ), + ({ axisMode }) => { + return axisMode; + } + ); + const groupsToShow = getGroupsToShow( + [ + // When a threshold layer panel is added, a static threshold should automatically be included by default + // in the first available axis, in the following order: vertical left, vertical right, horizontal. + { + config: left, + id: 'yThresholdLeft', + label: 'yLeft', + dataTestSubj: 'lnsXY_yThresholdLeftPanel', + }, + { + config: right, + id: 'yThresholdRight', + label: 'yRight', + dataTestSubj: 'lnsXY_yThresholdRightPanel', + }, + { + config: bottom, + id: 'xThreshold', + label: 'x', + dataTestSubj: 'lnsXY_xThresholdPanel', + }, + ], + state, + frame.datasourceLayers, + frame?.activeData + ); return { - groups: [], + supportFieldFormat: false, + supportStaticValue: true, + // Each thresholds layer panel will have sections for each available axis + // (horizontal axis, vertical axis left, vertical axis right). + // Only axes that support numeric thresholds should be shown + groups: groupsToShow.map(({ config = [], id, label, dataTestSubj, valid }) => ({ + groupId: id, + groupLabel: getAxisName(label, { isHorizontal }), + accessors: config.map(({ forAccessor, color }) => ({ + columnId: forAccessor, + color: color || mappedAccessors.find(({ columnId }) => columnId === forAccessor)?.color, + triggerIcon: 'color', + })), + filterOperations: isNumericMetric, + supportsMoreColumns: true, + required: false, + enableDimensionEditor: true, + dataTestSubj, + invalid: !valid, + invalidMessage: i18n.translate('xpack.lens.configure.invalidThresholdDimension', { + defaultMessage: + 'This threshold is assigned to an axis that no longer exists. You may move this threshold to another available axis or remove it.', + }), + })), }; } @@ -305,6 +438,30 @@ export const getXyVisualization = ({ newLayer.splitAccessor = columnId; } + if (newLayer.layerType === layerTypes.THRESHOLD) { + newLayer.accessors = [...newLayer.accessors.filter((a) => a !== columnId), columnId]; + const hasYConfig = newLayer.yConfig?.some(({ forAccessor }) => forAccessor === columnId); + if (!hasYConfig) { + newLayer.yConfig = [ + ...(newLayer.yConfig || []), + // TODO: move this + // add a default config if none is available + { + forAccessor: columnId, + axisMode: + groupId === 'xThreshold' + ? 'bottom' + : groupId === 'yThresholdRight' + ? 'right' + : 'left', + icon: undefined, + lineStyle: 'solid', + lineWidth: 1, + }, + ]; + } + } + return { ...prevState, layers: prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)), @@ -331,7 +488,24 @@ export const getXyVisualization = ({ newLayer.yConfig = newLayer.yConfig.filter(({ forAccessor }) => forAccessor !== columnId); } - const newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)); + let newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)); + // // check if there's any threshold layer and pull it off if all data layers have no dimensions set + const layersByType = groupBy(newLayers, ({ layerType }) => layerType); + // // check for data layers if they all still have xAccessors + const groupsAvailable = getGroupsAvailableInData( + layersByType[layerTypes.DATA], + frame.datasourceLayers, + frame?.activeData + ); + if ( + (Object.keys(groupsAvailable) as Array<'x' | 'yLeft' | 'yRight'>).every( + (id) => !groupsAvailable[id] + ) + ) { + newLayers = newLayers.filter( + ({ layerType, accessors }) => layerType === layerTypes.DATA || accessors.length + ); + } return { ...prevState, @@ -510,19 +684,6 @@ function validateLayersForDimension( }; } -function getAxisName(axis: 'x' | 'y', { isHorizontal }: { isHorizontal: boolean }) { - const vertical = i18n.translate('xpack.lens.xyChart.verticalAxisLabel', { - defaultMessage: 'Vertical axis', - }); - const horizontal = i18n.translate('xpack.lens.xyChart.horizontalAxisLabel', { - defaultMessage: 'Horizontal axis', - }); - if (axis === 'x') { - return isHorizontal ? vertical : horizontal; - } - return isHorizontal ? horizontal : vertical; -} - // i18n ids cannot be dynamically generated, hence the function below function getMessageIdsForDimension(dimension: string, layers: number[], isHorizontal: boolean) { const layersList = layers.map((i: number) => i + 1).join(', '); @@ -566,76 +727,6 @@ function newLayerState( }; } -// min requirement for the bug: -// * 2 or more layers -// * at least one with date histogram -// * at least one with interval function -function checkXAccessorCompatibility( - state: XYState, - datasourceLayers: Record -) { - const errors = []; - const hasDateHistogramSet = state.layers.some( - checkScaleOperation('interval', 'date', datasourceLayers) - ); - const hasNumberHistogram = state.layers.some( - checkScaleOperation('interval', 'number', datasourceLayers) - ); - const hasOrdinalAxis = state.layers.some( - checkScaleOperation('ordinal', undefined, datasourceLayers) - ); - if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) { - errors.push({ - shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { - defaultMessage: `Wrong data type for {axis}.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXLong', { - defaultMessage: `Data type mismatch for the {axis}. Cannot mix date and number interval types.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - }); - } - if (state.layers.length > 1 && (hasDateHistogramSet || hasNumberHistogram) && hasOrdinalAxis) { - errors.push({ - shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { - defaultMessage: `Wrong data type for {axis}.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXOrdinalLong', { - defaultMessage: `Data type mismatch for the {axis}, use a different function.`, - values: { - axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), - }, - }), - }); - } - return errors; -} - -function checkScaleOperation( - scaleType: 'ordinal' | 'interval' | 'ratio', - dataType: 'date' | 'number' | 'string' | undefined, - datasourceLayers: Record -) { - return (layer: XYLayerConfig) => { - const datasourceAPI = datasourceLayers[layer.layerId]; - if (!layer.xAccessor) { - return false; - } - const operation = datasourceAPI?.getOperationForColumnId(layer.xAccessor); - return Boolean( - operation && (!dataType || operation.dataType === dataType) && operation.scale === scaleType - ); - }; -} - function getLayersByType(state: State, byType?: string) { return state.layers.filter(({ layerType = layerTypes.DATA }) => byType ? layerType === byType : true diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx new file mode 100644 index 0000000000000..22c3c7e895323 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/visualization_helpers.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { DatasourcePublicAPI } from '../types'; +import { XYState } from './types'; +import { isHorizontalChart } from './state_helpers'; +import { XYLayerConfig } from '../../common/expressions'; + +export function getAxisName( + axis: 'x' | 'y' | 'yLeft' | 'yRight', + { isHorizontal }: { isHorizontal: boolean } +) { + const vertical = i18n.translate('xpack.lens.xyChart.verticalAxisLabel', { + defaultMessage: 'Vertical axis', + }); + const horizontal = i18n.translate('xpack.lens.xyChart.horizontalAxisLabel', { + defaultMessage: 'Horizontal axis', + }); + if (axis === 'x') { + return isHorizontal ? vertical : horizontal; + } + if (axis === 'y') { + return isHorizontal ? horizontal : vertical; + } + const verticalLeft = i18n.translate('xpack.lens.xyChart.verticalLeftAxisLabel', { + defaultMessage: 'Vertical left axis', + }); + const verticalRight = i18n.translate('xpack.lens.xyChart.verticalRightAxisLabel', { + defaultMessage: 'Vertical right axis', + }); + const horizontalTop = i18n.translate('xpack.lens.xyChart.horizontalLeftAxisLabel', { + defaultMessage: 'Horizontal top axis', + }); + const horizontalBottom = i18n.translate('xpack.lens.xyChart.horizontalRightAxisLabel', { + defaultMessage: 'Horizontal bottom axis', + }); + if (axis === 'yLeft') { + return isHorizontal ? horizontalTop : verticalLeft; + } + return isHorizontal ? horizontalBottom : verticalRight; +} + +// min requirement for the bug: +// * 2 or more layers +// * at least one with date histogram +// * at least one with interval function +export function checkXAccessorCompatibility( + state: XYState, + datasourceLayers: Record +) { + const errors = []; + const hasDateHistogramSet = state.layers.some( + checkScaleOperation('interval', 'date', datasourceLayers) + ); + const hasNumberHistogram = state.layers.some( + checkScaleOperation('interval', 'number', datasourceLayers) + ); + const hasOrdinalAxis = state.layers.some( + checkScaleOperation('ordinal', undefined, datasourceLayers) + ); + if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) { + errors.push({ + shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { + defaultMessage: `Wrong data type for {axis}.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXLong', { + defaultMessage: `Data type mismatch for the {axis}. Cannot mix date and number interval types.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + }); + } + if (state.layers.length > 1 && (hasDateHistogramSet || hasNumberHistogram) && hasOrdinalAxis) { + errors.push({ + shortMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXShort', { + defaultMessage: `Wrong data type for {axis}.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + longMessage: i18n.translate('xpack.lens.xyVisualization.dataTypeFailureXOrdinalLong', { + defaultMessage: `Data type mismatch for the {axis}, use a different function.`, + values: { + axis: getAxisName('x', { isHorizontal: isHorizontalChart(state.layers) }), + }, + }), + }); + } + return errors; +} + +export function checkScaleOperation( + scaleType: 'ordinal' | 'interval' | 'ratio', + dataType: 'date' | 'number' | 'string' | undefined, + datasourceLayers: Record +) { + return (layer: XYLayerConfig) => { + const datasourceAPI = datasourceLayers[layer.layerId]; + if (!layer.xAccessor) { + return false; + } + const operation = datasourceAPI?.getOperationForColumnId(layer.xAccessor); + return Boolean( + operation && (!dataType || operation.dataType === dataType) && operation.scale === scaleType + ); + }; +} diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.test.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx index aa287795c8181..ebe0e536a4d77 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { shallowWithIntl as shallow } from '@kbn/test/jest'; import { AxisSettingsPopover, AxisSettingsPopoverProps } from './axis_settings_popover'; -import { ToolbarPopover } from '../shared_components'; -import { layerTypes } from '../../common'; +import { ToolbarPopover } from '../../shared_components'; +import { layerTypes } from '../../../common'; describe('Axes Settings', () => { let props: AxisSettingsPopoverProps; diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx similarity index 96% rename from x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx index 2285cd1a7a43a..e0a30bdb2c511 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/axis_settings_popover.tsx @@ -20,15 +20,15 @@ import { EuiFieldNumber, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from '../../common/expressions'; -import { ToolbarPopover, useDebouncedValue } from '../shared_components'; -import { isHorizontalChart } from './state_helpers'; -import { EuiIconAxisBottom } from '../assets/axis_bottom'; -import { EuiIconAxisLeft } from '../assets/axis_left'; -import { EuiIconAxisRight } from '../assets/axis_right'; -import { EuiIconAxisTop } from '../assets/axis_top'; -import { ToolbarButtonProps } from '../../../../../src/plugins/kibana_react/public'; -import { validateExtent } from './axes_configuration'; +import { XYLayerConfig, AxesSettingsConfig, AxisExtentConfig } from '../../../common/expressions'; +import { ToolbarPopover, useDebouncedValue } from '../../shared_components'; +import { isHorizontalChart } from '../state_helpers'; +import { EuiIconAxisBottom } from '../../assets/axis_bottom'; +import { EuiIconAxisLeft } from '../../assets/axis_left'; +import { EuiIconAxisRight } from '../../assets/axis_right'; +import { EuiIconAxisTop } from '../../assets/axis_top'; +import { ToolbarButtonProps } from '../../../../../../src/plugins/kibana_react/public'; +import { validateExtent } from '../axes_configuration'; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.tsx new file mode 100644 index 0000000000000..5a6458a4654d0 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/color_picker.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import './xy_config_panel.scss'; +import React, { useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { debounce } from 'lodash'; +import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon } from '@elastic/eui'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { VisualizationDimensionEditorProps } from '../../types'; +import { State } from '../types'; +import { FormatFactory } from '../../../common'; +import { getSeriesColor } from '../state_helpers'; +import { getAccessorColorConfig, getColorAssignments } from '../color_assignment'; +import { getSortedAccessors } from '../to_expression'; +import { updateLayer } from '.'; +import { TooltipWrapper } from '../../shared_components'; + +const tooltipContent = { + auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { + defaultMessage: 'Lens automatically picks colors for you unless you specify a custom color.', + }), + custom: i18n.translate('xpack.lens.configPanel.color.tooltip.custom', { + defaultMessage: 'Clear the custom color to return to “Auto” mode.', + }), + disabled: i18n.translate('xpack.lens.configPanel.color.tooltip.disabled', { + defaultMessage: + 'Individual series cannot be custom colored when the layer includes a “Break down by.“', + }), +}; + +export const ColorPicker = ({ + state, + setState, + layerId, + accessor, + frame, + formatFactory, + paletteService, + label, + disableHelpTooltip, +}: VisualizationDimensionEditorProps & { + formatFactory: FormatFactory; + paletteService: PaletteRegistry; + label?: string; + disableHelpTooltip?: boolean; +}) => { + const index = state.layers.findIndex((l) => l.layerId === layerId); + const layer = state.layers[index]; + const disabled = Boolean(layer.splitAccessor); + + const overwriteColor = getSeriesColor(layer, accessor); + const currentColor = useMemo(() => { + if (overwriteColor || !frame.activeData) return overwriteColor; + + const datasource = frame.datasourceLayers[layer.layerId]; + const sortedAccessors: string[] = getSortedAccessors(datasource, layer); + + const colorAssignments = getColorAssignments( + state.layers, + { tables: frame.activeData }, + formatFactory + ); + const mappedAccessors = getAccessorColorConfig( + colorAssignments, + frame, + { + ...layer, + accessors: sortedAccessors.filter((sorted) => layer.accessors.includes(sorted)), + }, + paletteService + ); + + return mappedAccessors.find((a) => a.columnId === accessor)?.color || null; + }, [overwriteColor, frame, paletteService, state.layers, accessor, formatFactory, layer]); + + const [color, setColor] = useState(currentColor); + + const handleColor: EuiColorPickerProps['onChange'] = (text, output) => { + setColor(text); + if (output.isValid || text === '') { + updateColorInState(text, output); + } + }; + + const updateColorInState: EuiColorPickerProps['onChange'] = useMemo( + () => + debounce((text, output) => { + const newYConfigs = [...(layer.yConfig || [])]; + const existingIndex = newYConfigs.findIndex((yConfig) => yConfig.forAccessor === accessor); + if (existingIndex !== -1) { + if (text === '') { + newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], color: undefined }; + } else { + newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], color: output.hex }; + } + } else { + newYConfigs.push({ + forAccessor: accessor, + color: output.hex, + }); + } + setState(updateLayer(state, { ...layer, yConfig: newYConfigs }, index)); + }, 256), + [state, setState, layer, accessor, index] + ); + + const inputLabel = + label ?? + i18n.translate('xpack.lens.xyChart.seriesColor.label', { + defaultMessage: 'Series color', + }); + + const colorPicker = ( + + ); + + return ( + + + {inputLabel} + {!disableHelpTooltip && ( + <> + {''} + + + )} + + + } + > + {disabled ? ( + + {colorPicker} + + ) : ( + colorPicker + )} + + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx similarity index 72% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index cd90fa52cd402..1427a3d28ea39 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -6,24 +6,15 @@ */ import './xy_config_panel.scss'; -import React, { useMemo, useState, memo, useCallback } from 'react'; +import React, { memo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { Position, ScaleType, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; -import { debounce } from 'lodash'; import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiFormRow, htmlIdGenerator, - EuiColorPicker, - EuiColorPickerProps, - EuiToolTip, - EuiIcon, - EuiPopover, - EuiSelectable, - EuiText, - EuiPopoverTitle, } from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { @@ -31,30 +22,34 @@ import type { VisualizationToolbarProps, VisualizationDimensionEditorProps, FramePublicAPI, -} from '../types'; -import { State, visualizationTypes, XYState } from './types'; -import type { FormatFactory } from '../../common'; +} from '../../types'; +import { State, visualizationTypes, XYState } from '../types'; +import type { FormatFactory } from '../../../common'; import { SeriesType, YAxisMode, AxesSettingsConfig, AxisExtentConfig, -} from '../../common/expressions'; -import { isHorizontalChart, isHorizontalSeries, getSeriesColor } from './state_helpers'; -import { trackUiEvent } from '../lens_ui_telemetry'; -import { LegendSettingsPopover } from '../shared_components'; +} from '../../../common/expressions'; +import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; +import { trackUiEvent } from '../../lens_ui_telemetry'; +import { LegendSettingsPopover } from '../../shared_components'; import { AxisSettingsPopover } from './axis_settings_popover'; -import { getAxesConfiguration, GroupsConfiguration } from './axes_configuration'; -import { PalettePicker, TooltipWrapper } from '../shared_components'; -import { getAccessorColorConfig, getColorAssignments } from './color_assignment'; -import { getScaleType, getSortedAccessors } from './to_expression'; -import { VisualOptionsPopover } from './visual_options_popover/visual_options_popover'; -import { ToolbarButton } from '../../../../../src/plugins/kibana_react/public'; +import { getAxesConfiguration, GroupsConfiguration } from '../axes_configuration'; +import { VisualOptionsPopover } from './visual_options_popover'; +import { getScaleType } from '../to_expression'; +import { ColorPicker } from './color_picker'; +import { ThresholdPanel } from './threshold_panel'; +import { PalettePicker, TooltipWrapper } from '../../shared_components'; type UnwrapArray = T extends Array ? P : T; type AxesSettingsConfigKeys = keyof AxesSettingsConfig; -function updateLayer(state: State, layer: UnwrapArray, index: number): State { +export function updateLayer( + state: State, + layer: UnwrapArray, + index: number +): State { const newLayers = [...state.layers]; newLayers[index] = layer; @@ -92,90 +87,6 @@ const legendOptions: Array<{ }, ]; -export function LayerHeader(props: VisualizationLayerWidgetProps) { - const [isPopoverOpen, setPopoverIsOpen] = useState(false); - const { state, layerId } = props; - const horizontalOnly = isHorizontalChart(state.layers); - const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index]; - if (!layer) { - return null; - } - - const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; - - const createTrigger = function () { - return ( - setPopoverIsOpen(!isPopoverOpen)} - fullWidth - size="s" - > - <> - - - {currentVisType.fullLabel || currentVisType.label} - - - - ); - }; - - return ( - <> - setPopoverIsOpen(false)} - display="block" - panelPaddingSize="s" - ownFocus - > - - {i18n.translate('xpack.lens.layerPanel.layerVisualizationType', { - defaultMessage: 'Layer visualization type', - })} - -
- - singleSelection="always" - options={visualizationTypes - .filter((t) => isHorizontalSeries(t.id as SeriesType) === horizontalOnly) - .map((t) => ({ - value: t.id, - key: t.id, - checked: t.id === currentVisType.id ? 'on' : undefined, - prepend: , - label: t.fullLabel || t.label, - 'data-test-subj': `lnsXY_seriesType-${t.id}`, - }))} - onChange={(newOptions) => { - const chosenType = newOptions.find(({ checked }) => checked === 'on'); - if (!chosenType) { - return; - } - const id = chosenType.value!; - trackUiEvent('xy_change_layer_display'); - props.setState(updateLayer(state, { ...layer, seriesType: id as SeriesType }, index)); - setPopoverIsOpen(false); - }} - > - {(list) => <>{list}} - -
-
- - ); -} - export function LayerContextMenu(props: VisualizationLayerWidgetProps) { const { state, layerId } = props; const horizontalOnly = isHorizontalChart(state.layers); @@ -622,7 +533,7 @@ export const XyToolbar = memo(function XyToolbar(props: VisualizationToolbarProp ); }); -const idPrefix = htmlIdGenerator()(); +export const idPrefix = htmlIdGenerator()(); export function DimensionEditor( props: VisualizationDimensionEditorProps & { @@ -653,6 +564,10 @@ export function DimensionEditor( ); } + if (layer.layerType === 'threshold') { + return ; + } + return ( <> @@ -728,140 +643,3 @@ export function DimensionEditor( ); } - -const tooltipContent = { - auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', { - defaultMessage: 'Lens automatically picks colors for you unless you specify a custom color.', - }), - custom: i18n.translate('xpack.lens.configPanel.color.tooltip.custom', { - defaultMessage: 'Clear the custom color to return to “Auto” mode.', - }), - disabled: i18n.translate('xpack.lens.configPanel.color.tooltip.disabled', { - defaultMessage: - 'Individual series cannot be custom colored when the layer includes a “Break down by.“', - }), -}; - -const ColorPicker = ({ - state, - setState, - layerId, - accessor, - frame, - formatFactory, - paletteService, -}: VisualizationDimensionEditorProps & { - formatFactory: FormatFactory; - paletteService: PaletteRegistry; -}) => { - const index = state.layers.findIndex((l) => l.layerId === layerId); - const layer = state.layers[index]; - const disabled = !!layer.splitAccessor; - - const overwriteColor = getSeriesColor(layer, accessor); - const currentColor = useMemo(() => { - if (overwriteColor || !frame.activeData) return overwriteColor; - - const datasource = frame.datasourceLayers[layer.layerId]; - const sortedAccessors: string[] = getSortedAccessors(datasource, layer); - - const colorAssignments = getColorAssignments( - state.layers, - { tables: frame.activeData }, - formatFactory - ); - const mappedAccessors = getAccessorColorConfig( - colorAssignments, - frame, - { - ...layer, - accessors: sortedAccessors.filter((sorted) => layer.accessors.includes(sorted)), - }, - paletteService - ); - - return mappedAccessors.find((a) => a.columnId === accessor)?.color || null; - }, [overwriteColor, frame, paletteService, state.layers, accessor, formatFactory, layer]); - - const [color, setColor] = useState(currentColor); - - const handleColor: EuiColorPickerProps['onChange'] = (text, output) => { - setColor(text); - if (output.isValid || text === '') { - updateColorInState(text, output); - } - }; - - const updateColorInState: EuiColorPickerProps['onChange'] = useMemo( - () => - debounce((text, output) => { - const newYConfigs = [...(layer.yConfig || [])]; - const existingIndex = newYConfigs.findIndex((yConfig) => yConfig.forAccessor === accessor); - if (existingIndex !== -1) { - if (text === '') { - newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], color: undefined }; - } else { - newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], color: output.hex }; - } - } else { - newYConfigs.push({ - forAccessor: accessor, - color: output.hex, - }); - } - setState(updateLayer(state, { ...layer, yConfig: newYConfigs }, index)); - }, 256), - [state, setState, layer, accessor, index] - ); - - const colorPicker = ( - - ); - - return ( - - - {i18n.translate('xpack.lens.xyChart.seriesColor.label', { - defaultMessage: 'Series color', - })}{' '} - - -
- } - > - {disabled ? ( - - {colorPicker} - - ) : ( - colorPicker - )} - - ); -}; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx new file mode 100644 index 0000000000000..dde4de0dd4bc3 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/layer_header.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import './xy_config_panel.scss'; +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui'; +import type { VisualizationLayerWidgetProps } from '../../types'; +import { State, visualizationTypes } from '../types'; +import { layerTypes } from '../../../common'; +import { SeriesType } from '../../../common/expressions'; +import { isHorizontalChart, isHorizontalSeries } from '../state_helpers'; +import { trackUiEvent } from '../../lens_ui_telemetry'; +import { StaticHeader } from '../../shared_components'; +import { ToolbarButton } from '../../../../../../src/plugins/kibana_react/public'; +import { LensIconChartBarThreshold } from '../../assets/chart_bar_threshold'; +import { updateLayer } from '.'; + +export function LayerHeader(props: VisualizationLayerWidgetProps) { + const [isPopoverOpen, setPopoverIsOpen] = useState(false); + const { state, layerId } = props; + const horizontalOnly = isHorizontalChart(state.layers); + const index = state.layers.findIndex((l) => l.layerId === layerId); + const layer = state.layers[index]; + if (!layer) { + return null; + } + // if it's a threshold just draw a static text + if (layer.layerType === layerTypes.THRESHOLD) { + return ( + + ); + } + const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!; + + const createTrigger = function () { + return ( + setPopoverIsOpen(!isPopoverOpen)} + fullWidth + size="s" + > + <> + + + {currentVisType.fullLabel || currentVisType.label} + + + + ); + }; + + return ( + <> + setPopoverIsOpen(false)} + display="block" + panelPaddingSize="s" + ownFocus + > + + {i18n.translate('xpack.lens.layerPanel.layerVisualizationType', { + defaultMessage: 'Layer visualization type', + })} + +
+ + singleSelection="always" + options={visualizationTypes + .filter((t) => isHorizontalSeries(t.id as SeriesType) === horizontalOnly) + .map((t) => ({ + value: t.id, + key: t.id, + checked: t.id === currentVisType.id ? 'on' : undefined, + prepend: , + label: t.fullLabel || t.label, + 'data-test-subj': `lnsXY_seriesType-${t.id}`, + }))} + onChange={(newOptions) => { + const chosenType = newOptions.find(({ checked }) => checked === 'on'); + if (!chosenType) { + return; + } + const id = chosenType.value!; + trackUiEvent('xy_change_layer_display'); + props.setState(updateLayer(state, { ...layer, seriesType: id as SeriesType }, index)); + setPopoverIsOpen(false); + }} + > + {(list) => <>{list}} + +
+
+ + ); +} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx new file mode 100644 index 0000000000000..1e5b90e41b623 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx @@ -0,0 +1,326 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import './xy_config_panel.scss'; +import React, { useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonGroup, EuiComboBox, EuiFormRow, EuiIcon, EuiRange } from '@elastic/eui'; +import type { PaletteRegistry } from 'src/plugins/charts/public'; +import type { VisualizationDimensionEditorProps } from '../../types'; +import { State } from '../types'; +import { FormatFactory } from '../../../common'; +import { YConfig } from '../../../common/expressions'; +import { LineStyle, FillStyle } from '../../../common/expressions/xy_chart'; + +import { ColorPicker } from './color_picker'; +import { updateLayer, idPrefix } from '.'; +import { useDebouncedValue } from '../../shared_components'; + +const icons = [ + { + value: 'none', + label: i18n.translate('xpack.lens.xyChart.thresholds.noIconLabel', { defaultMessage: 'None' }), + }, + { + value: 'asterisk', + label: i18n.translate('xpack.lens.xyChart.thresholds.asteriskIconLabel', { + defaultMessage: 'Asterisk', + }), + }, + { + value: 'bell', + label: i18n.translate('xpack.lens.xyChart.thresholds.bellIconLabel', { + defaultMessage: 'Bell', + }), + }, + { + value: 'bolt', + label: i18n.translate('xpack.lens.xyChart.thresholds.boltIconLabel', { + defaultMessage: 'Bolt', + }), + }, + { + value: 'bug', + label: i18n.translate('xpack.lens.xyChart.thresholds.bugIconLabel', { + defaultMessage: 'Bug', + }), + }, + { + value: 'editorComment', + label: i18n.translate('xpack.lens.xyChart.thresholds.commentIconLabel', { + defaultMessage: 'Comment', + }), + }, + { + value: 'alert', + label: i18n.translate('xpack.lens.xyChart.thresholds.alertIconLabel', { + defaultMessage: 'Alert', + }), + }, + { + value: 'flag', + label: i18n.translate('xpack.lens.xyChart.thresholds.flagIconLabel', { + defaultMessage: 'Flag', + }), + }, + { + value: 'tag', + label: i18n.translate('xpack.lens.xyChart.thresholds.tagIconLabel', { + defaultMessage: 'Tag', + }), + }, +]; + +const IconView = (props: { value?: string; label: string }) => { + if (!props.value) return null; + return ( + + + {` ${props.label}`} + + ); +}; + +const IconSelect = ({ + value, + onChange, +}: { + value?: string; + onChange: (newIcon: string) => void; +}) => { + const selectedIcon = icons.find((option) => value === option.value) || icons[0]; + + return ( + { + onChange(selection[0].value!); + }} + singleSelection={{ asPlainText: true }} + renderOption={IconView} + compressed + /> + ); +}; + +export const ThresholdPanel = ( + props: VisualizationDimensionEditorProps & { + formatFactory: FormatFactory; + paletteService: PaletteRegistry; + } +) => { + const { state, setState, layerId, accessor } = props; + const index = state.layers.findIndex((l) => l.layerId === layerId); + const layer = state.layers[index]; + + const setYConfig = useCallback( + (yConfig: Partial | undefined) => { + if (yConfig == null) { + return; + } + setState((currState) => { + const currLayer = currState.layers[index]; + const newYConfigs = [...(currLayer.yConfig || [])]; + const existingIndex = newYConfigs.findIndex( + (yAxisConfig) => yAxisConfig.forAccessor === accessor + ); + if (existingIndex !== -1) { + newYConfigs[existingIndex] = { ...newYConfigs[existingIndex], ...yConfig }; + } else { + newYConfigs.push({ forAccessor: accessor, ...yConfig }); + } + return updateLayer(currState, { ...currLayer, yConfig: newYConfigs }, index); + }); + }, + [accessor, index, setState] + ); + + const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor); + + return ( + <> + + + { + const newMode = id.replace(idPrefix, '') as LineStyle; + setYConfig({ forAccessor: accessor, lineStyle: newMode }); + }} + /> + + + { + setYConfig({ forAccessor: accessor, lineWidth: value }); + }} + /> + + + { + const newMode = id.replace(idPrefix, '') as FillStyle; + setYConfig({ forAccessor: accessor, fill: newMode }); + }} + /> + + + { + setYConfig({ forAccessor: accessor, icon: newIcon }); + }} + /> + + + ); +}; + +const minRange = 1; +const maxRange = 10; + +function getSafeValue(value: number | '', prevValue: number, min: number, max: number) { + if (value === '') { + return prevValue; + } + return Math.max(minRange, Math.min(value, maxRange)); +} + +const LineThicknessSlider = ({ + value, + onChange, +}: { + value: number; + onChange: (value: number) => void; +}) => { + const onChangeWrapped = useCallback( + (newValue) => { + if (Number.isInteger(newValue)) { + onChange(getSafeValue(newValue, newValue, minRange, maxRange)); + } + }, + [onChange] + ); + const { inputValue, handleInputChange } = useDebouncedValue( + { value, onChange: onChangeWrapped }, + { allowFalsyValue: true } + ); + + return ( + { + const newValue = e.currentTarget.value; + handleInputChange(newValue === '' ? '' : Number(newValue)); + }} + onBlur={() => { + handleInputChange(getSafeValue(inputValue, value, minRange, maxRange)); + }} + /> + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/fill_opacity_option.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/fill_opacity_option.test.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/fill_opacity_option.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.tsx similarity index 95% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/fill_opacity_option.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.tsx index eb8d35c54a99b..09b381dd03f7a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/fill_opacity_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/fill_opacity_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiRange } from '@elastic/eui'; -import { useDebouncedValue } from '../../shared_components'; +import { useDebouncedValue } from '../../../shared_components'; export interface FillOpacityOptionProps { /** diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx similarity index 93% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx index 6d0e5c2d55b70..2a19897445e63 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/index.tsx @@ -7,14 +7,14 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ToolbarPopover, TooltipWrapper } from '../../shared_components'; +import { ToolbarPopover, TooltipWrapper } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { LineCurveOption } from './line_curve_option'; import { FillOpacityOption } from './fill_opacity_option'; -import { XYState } from '../types'; -import { hasHistogramSeries } from '../state_helpers'; -import { ValidLayer } from '../../../common/expressions'; -import type { FramePublicAPI } from '../../types'; +import { XYState } from '../../types'; +import { hasHistogramSeries } from '../../state_helpers'; +import { ValidLayer } from '../../../../common/expressions'; +import type { FramePublicAPI } from '../../../types'; function getValueLabelDisableReason({ isAreaPercentage, diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.test.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx similarity index 95% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx index 6080a8c68e57d..96926412afb8a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/line_curve_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/line_curve_option.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; -import type { XYCurveType } from '../../../common/expressions'; +import type { XYCurveType } from '../../../../common/expressions'; export interface LineCurveOptionProps { /** diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_value_option.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_value_option.test.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_value_option.test.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_value_option.test.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx similarity index 97% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx index 3dba8757903e9..b12e2d2f57112 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/missing_values_option.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/missing_values_option.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFormRow, EuiIconTip, EuiSuperSelect, EuiText } from '@elastic/eui'; -import { fittingFunctionDefinitions } from '../../../common/expressions'; -import type { FittingFunction, ValueLabelConfig } from '../../../common/expressions'; +import { fittingFunctionDefinitions } from '../../../../common/expressions'; +import type { FittingFunction, ValueLabelConfig } from '../../../../common/expressions'; export interface MissingValuesOptionProps { valueLabels?: ValueLabelConfig; diff --git a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx similarity index 96% rename from x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx index cd6a20c37dd38..0136612c46705 100644 --- a/x-pack/plugins/lens/public/xy_visualization/visual_options_popover/visual_options_popover.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/visual_options_popover/visual_options_popover.test.tsx @@ -8,14 +8,14 @@ import React from 'react'; import { shallowWithIntl as shallow } from '@kbn/test/jest'; import { Position } from '@elastic/charts'; -import type { FramePublicAPI } from '../../types'; -import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; -import { State } from '../types'; -import { VisualOptionsPopover } from './visual_options_popover'; -import { ToolbarPopover } from '../../shared_components'; +import type { FramePublicAPI } from '../../../types'; +import { createMockDatasource, createMockFramePublicAPI } from '../../../mocks'; +import { State } from '../../types'; +import { VisualOptionsPopover } from '.'; +import { ToolbarPopover } from '../../../shared_components'; import { MissingValuesOptions } from './missing_values_option'; import { FillOpacityOption } from './fill_opacity_option'; -import { layerTypes } from '../../../common'; +import { layerTypes } from '../../../../common'; describe('Visual options popover', () => { let frame: FramePublicAPI; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.scss similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.scss diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx similarity index 98% rename from x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx rename to x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx index 9ca9021382fda..e5b1870c73404 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/xy_config_panel.test.tsx @@ -8,15 +8,15 @@ import React from 'react'; import { mountWithIntl as mount, shallowWithIntl as shallow } from '@kbn/test/jest'; import { EuiButtonGroupProps, EuiButtonGroup } from '@elastic/eui'; -import { LayerContextMenu, XyToolbar, DimensionEditor } from './xy_config_panel'; +import { LayerContextMenu, XyToolbar, DimensionEditor } from '.'; import { AxisSettingsPopover } from './axis_settings_popover'; -import { FramePublicAPI } from '../types'; -import { State } from './types'; +import { FramePublicAPI } from '../../types'; +import { State } from '../types'; import { Position } from '@elastic/charts'; -import { createMockFramePublicAPI, createMockDatasource } from '../mocks'; +import { createMockFramePublicAPI, createMockDatasource } from '../../mocks'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; import { EuiColorPicker } from '@elastic/eui'; -import { layerTypes } from '../../common'; +import { layerTypes } from '../../../common'; describe('XY Config panels', () => { let frame: FramePublicAPI; diff --git a/x-pack/test/functional/apps/lens/formula.ts b/x-pack/test/functional/apps/lens/formula.ts index f645b2c64629c..a8d074ad0631b 100644 --- a/x-pack/test/functional/apps/lens/formula.ts +++ b/x-pack/test/functional/apps/lens/formula.ts @@ -236,6 +236,38 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + it('should keep the formula if the user does not fully transition to a static value', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'average', + field: 'bytes', + }); + + await PageObjects.lens.createLayer('threshold'); + + await PageObjects.lens.configureDimension( + { + dimension: 'lnsXY_yThresholdLeftPanel > lns-dimensionTrigger', + operation: 'formula', + formula: `count()`, + keepOpen: true, + }, + 1 + ); + + await PageObjects.lens.switchToStaticValue(); + await PageObjects.lens.closeDimensionEditor(); + await PageObjects.common.sleep(1000); + + expect(await PageObjects.lens.getDimensionTriggerText('lnsXY_yThresholdLeftPanel', 0)).to.eql( + 'count()' + ); + }); + it('should allow numeric only formulas', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts index a851bdccaf72d..50dbe05df166c 100644 --- a/x-pack/test/functional/apps/lens/index.ts +++ b/x-pack/test/functional/apps/lens/index.ts @@ -47,6 +47,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./lens_tagging')); loadTestFile(require.resolve('./formula')); loadTestFile(require.resolve('./heatmap')); + loadTestFile(require.resolve('./thresholds')); loadTestFile(require.resolve('./inspector')); // has to be last one in the suite because it overrides saved objects diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 6d9360ac32b4b..ff5bae8aa7e61 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -180,6 +180,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + it('should not show static value tab for data layers', async () => { + await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel > lns-dimensionTrigger'); + // Quick functions and Formula tabs should be visible + expect(await testSubjects.exists('lens-dimensionTabs-quickFunctions')).to.eql(true); + expect(await testSubjects.exists('lens-dimensionTabs-formula')).to.eql(true); + // Static value tab should not be visible + expect(await testSubjects.exists('lens-dimensionTabs-static_value')).to.eql(false); + + await PageObjects.lens.closeDimensionEditor(); + }); + it('should be able to add very long labels and still be able to remove a dimension', async () => { await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel > lns-dimensionTrigger'); const longLabel = diff --git a/x-pack/test/functional/apps/lens/thresholds.ts b/x-pack/test/functional/apps/lens/thresholds.ts new file mode 100644 index 0000000000000..bf6535acc7c8e --- /dev/null +++ b/x-pack/test/functional/apps/lens/thresholds.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']); + const find = getService('find'); + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + + describe('lens thresholds tests', () => { + it('should show a disabled threshold layer button if no data dimension is defined', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + + await testSubjects.click('lnsLayerAddButton'); + await retry.waitFor('wait for layer popup to appear', async () => + testSubjects.exists(`lnsLayerAddButton-threshold`) + ); + expect( + await (await testSubjects.find(`lnsLayerAddButton-threshold`)).getAttribute('disabled') + ).to.be('true'); + }); + + it('should add a threshold layer with a static value in it', async () => { + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'average', + field: 'bytes', + }); + + await PageObjects.lens.createLayer('threshold'); + + expect((await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`)).length).to.eql(2); + expect( + await ( + await testSubjects.find('lnsXY_yThresholdLeftPanel > lns-dimensionTrigger') + ).getVisibleText() + ).to.eql('Static value: 4992.44'); + }); + + it('should create a dynamic threshold when dragging a field to a threshold dimension group', async () => { + await PageObjects.lens.dragFieldToDimensionTrigger( + 'bytes', + 'lnsXY_yThresholdLeftPanel > lns-empty-dimension' + ); + + expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_yThresholdLeftPanel')).to.eql([ + 'Static value: 4992.44', + 'Median of bytes', + ]); + }); + }); +} diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 2e1151602f311..e26ea8f598c46 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -645,8 +645,21 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont /** * Adds a new layer to the chart, fails if the chart does not support new layers */ - async createLayer() { + async createLayer(layerType: string = 'data') { await testSubjects.click('lnsLayerAddButton'); + const layerCount = (await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`)) + .length; + + await retry.waitFor('check for layer type support', async () => { + const fasterChecks = await Promise.all([ + (await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`)).length > layerCount, + testSubjects.exists(`lnsLayerAddButton-${layerType}`), + ]); + return fasterChecks.filter(Boolean).length > 0; + }); + if (await testSubjects.exists(`lnsLayerAddButton-${layerType}`)) { + await testSubjects.click(`lnsLayerAddButton-${layerType}`); + } }, /** @@ -1075,6 +1088,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await testSubjects.click('lens-dimensionTabs-formula'); }, + async switchToStaticValue() { + await testSubjects.click('lens-dimensionTabs-static_value'); + }, + async toggleFullscreen() { await testSubjects.click('lnsFormula-fullscreen'); }, From 210fb50d0b3c2682cf5b86eb0312dd67b377854e Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Wed, 22 Sep 2021 10:53:37 +0100 Subject: [PATCH 54/69] [SecuritySolution] add global filter to topN (#112401) * add global filter to topN * sort lines * add unit test * fix duplicate queries Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/components/events_viewer/index.tsx | 18 +++---- .../hover_actions/actions/show_top_n.tsx | 8 ++- .../common/components/top_n/index.test.tsx | 54 +++++++++++++++++++ .../public/common/components/top_n/index.tsx | 7 +-- .../lib/cell_actions/default_cell_actions.tsx | 4 ++ .../detection_engine/rules/details/index.tsx | 3 +- .../common/types/timeline/columns/index.tsx | 8 +-- .../public/components/t_grid/body/index.tsx | 18 ++++--- .../components/t_grid/integrated/index.tsx | 17 +++--- 9 files changed, 103 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index c62337b2426d3..9e1fd3a769eee 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -177,8 +177,7 @@ const StatefulEventsViewerComponent: React.FC = ({ {tGridEnabled ? ( timelinesUi.getTGrid<'embedded'>({ - id, - type: 'embedded', + additionalFilters, browserFields, bulkActions, columns, @@ -189,9 +188,12 @@ const StatefulEventsViewerComponent: React.FC = ({ end, entityType, filters: globalFilters, + filterStatus: currentFilter, globalFullScreen, + graphEventId, graphOverlay, hasAlertsCrud, + id, indexNames: selectedPatterns, indexPattern, isLive, @@ -199,19 +201,17 @@ const StatefulEventsViewerComponent: React.FC = ({ itemsPerPage, itemsPerPageOptions: itemsPerPageOptions!, kqlMode, - query, + leadingControlColumns, onRuleChange, + query, renderCellValue, rowRenderers, setQuery, - start, sort, - additionalFilters, - graphEventId, - filterStatus: currentFilter, - leadingControlColumns, - trailingControlColumns, + start, tGridEventRenderedViewEnabled, + trailingControlColumns, + type: 'embedded', unit, }) ) : ( diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx index 0d6e59483fbc4..e1546c5220e22 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx @@ -21,6 +21,7 @@ import { useSourcererScope } from '../../../containers/sourcerer'; import { TooltipWithKeyboardShortcut } from '../../accessibility'; import { getAdditionalScreenReaderOnlyContext } from '../utils'; import { SHOW_TOP_N_KEYBOARD_SHORTCUT } from '../keyboard_shortcut_constants'; +import { Filter } from '../../../../../../../../src/plugins/data/public'; const SHOW_TOP = (fieldName: string) => i18n.translate('xpack.securitySolution.hoverActions.showTopTooltip', { @@ -35,11 +36,12 @@ interface Props { Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon | typeof EuiContextMenuItem; enablePopOver?: boolean; field: string; + globalFilters?: Filter[]; onClick: () => void; onFilterAdded?: () => void; ownFocus: boolean; - showTopN: boolean; showTooltip?: boolean; + showTopN: boolean; timelineId?: string | null; value?: string[] | string | null; } @@ -56,6 +58,7 @@ export const ShowTopNButton: React.FC = React.memo( showTopN, timelineId, value, + globalFilters, }) => { const activeScope: SourcererScopeName = timelineId === TimelineId.active @@ -128,9 +131,10 @@ export const ShowTopNButton: React.FC = React.memo( timelineId={timelineId ?? undefined} toggleTopN={onClick} value={value} + globalFilters={globalFilters} /> ), - [browserFields, field, indexPattern, onClick, onFilterAdded, timelineId, value] + [browserFields, field, indexPattern, onClick, onFilterAdded, timelineId, value, globalFilters] ); return showTopN ? ( diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index 787b7e8f88703..6962ed03e81d4 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -160,6 +160,60 @@ let testProps = { }; describe('StatefulTopN', () => { + describe('rendering globalFilter', () => { + let wrapper: ReactWrapper; + const globalFilters = [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'signal.rule.id', + params: { + query: 'd62249f0-1632-11ec-b035-19607969bc20', + }, + }, + query: { + match_phrase: { + 'signal.rule.id': 'd62249f0-1632-11ec-b035-19607969bc20', + }, + }, + }, + ]; + beforeEach(() => { + wrapper = mount( + + + + ); + }); + + test(`provides filters from non Redux state when rendering in alerts table`, () => { + const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; + + expect(props.filters).toEqual([ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'signal.rule.id', + params: { + query: 'd62249f0-1632-11ec-b035-19607969bc20', + }, + }, + query: { + match_phrase: { + 'signal.rule.id': 'd62249f0-1632-11ec-b035-19607969bc20', + }, + }, + }, + ]); + }); + }); + describe('rendering in a global NON-timeline context', () => { let wrapper: ReactWrapper; diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx index 2286a53030784..1556f2d0f3d13 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx @@ -41,11 +41,11 @@ const makeMapStateToProps = () => { // The mapped Redux state provided to this component includes the global // filters that appear at the top of most views in the app, and all the // filters in the active timeline: - const mapStateToProps = (state: State) => { + const mapStateToProps = (state: State, ownProps: { globalFilters?: Filter[] }) => { const activeTimeline: TimelineModel = getTimeline(state, TimelineId.active) ?? timelineDefaults; const activeTimelineFilters = activeTimeline.filters ?? EMPTY_FILTERS; const activeTimelineInput: inputsModel.InputsRange = getInputsTimeline(state); - + const { globalFilters } = ownProps; return { activeTimelineEventType: activeTimeline.eventType, activeTimelineFilters: @@ -59,7 +59,7 @@ const makeMapStateToProps = () => { dataProviders: activeTimeline.activeTab === TimelineTabs.query ? activeTimeline.dataProviders : [], globalQuery: getGlobalQuerySelector(state), - globalFilters: getGlobalFiltersQuerySelector(state), + globalFilters: globalFilters ?? getGlobalFiltersQuerySelector(state), kqlMode: activeTimeline.kqlMode, }; }; @@ -82,6 +82,7 @@ export interface OwnProps { toggleTopN: () => void; onFilterAdded?: () => void; value?: string[] | string | null; + globalFilters?: Filter[]; } type PropsFromRedux = ConnectedProps; type Props = OwnProps & PropsFromRedux; diff --git a/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx b/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx index 8279613e67db7..149a0c62b8b6a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/cell_actions/default_cell_actions.tsx @@ -6,6 +6,7 @@ */ import React, { useCallback, useState, useMemo } from 'react'; +import { Filter } from '../../../../../../../src/plugins/data/public'; import type { BrowserFields, @@ -167,11 +168,13 @@ export const defaultCellActions: TGridCellAction[] = [ ({ browserFields, data, + globalFilters, timelineId, pageSize, }: { browserFields: BrowserFields; data: TimelineNonEcsData[][]; + globalFilters?: Filter[]; timelineId: string; pageSize: number; }) => @@ -205,6 +208,7 @@ export const defaultCellActions: TGridCellAction[] = [ enablePopOver data-test-subj="hover-actions-show-top-n" field={columnId} + globalFilters={globalFilters} onClick={onClick} onFilterAdded={onFilterAdded} ownFocus={false} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 8de3f0065f5f9..70d7faa47b9ee 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -391,7 +391,6 @@ const RuleDetailsPageComponent: React.FC = ({ const alertsTableDefaultFilters = useMemo( () => [ ...buildAlertsRuleIdFilter(ruleId), - ...filters, ...(ruleRegistryEnabled ? [ // TODO: Once we are past experimental phase this code should be removed @@ -400,7 +399,7 @@ const RuleDetailsPageComponent: React.FC = ({ : [...buildShowBuildingBlockFilter(showBuildingBlockAlerts)]), ...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts), ], - [ruleId, filters, ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts] + [ruleId, ruleRegistryEnabled, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts] ); const alertMergedFilters = useMemo( diff --git a/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx b/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx index cc20c856f0e13..644b38d551337 100644 --- a/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx +++ b/x-pack/plugins/timelines/common/types/timeline/columns/index.tsx @@ -8,7 +8,7 @@ import { ReactNode } from 'react'; import { EuiDataGridColumn, EuiDataGridColumnCellActionProps } from '@elastic/eui'; -import { IFieldSubType } from '../../../../../../../src/plugins/data/common'; +import { Filter, IFieldSubType } from '../../../../../../../src/plugins/data/common'; import { BrowserFields } from '../../../search_strategy/index_fields'; import { TimelineNonEcsData } from '../../../search_strategy/timeline'; @@ -45,14 +45,16 @@ export type ColumnId = string; export type TGridCellAction = ({ browserFields, data, - timelineId, + globalFilters, pageSize, + timelineId, }: { browserFields: BrowserFields; /** each row of data is represented as one TimelineNonEcsData[] */ data: TimelineNonEcsData[][]; - timelineId: string; + globalFilters?: Filter[]; pageSize: number; + timelineId: string; }) => (props: EuiDataGridColumnCellActionProps) => ReactNode; /** The specification of a column header */ diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx index 86f42d2f68f78..d67cc746f352f 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/index.tsx @@ -74,6 +74,7 @@ import type { EuiTheme } from '../../../../../../../src/plugins/kibana_react/com import { ViewSelection } from '../event_rendered_view/selector'; import { EventRenderedView } from '../event_rendered_view'; import { useDataGridHeightHack } from './height_hack'; +import { Filter } from '../../../../../../../src/plugins/data/public'; const StatefulAlertStatusBulkActions = lazy( () => import('../toolbar/bulk_actions/alert_status_bulk_actions') @@ -86,6 +87,7 @@ interface OwnProps { bulkActions?: BulkActionsProp; data: TimelineItem[]; defaultCellActions?: TGridCellAction[]; + filters?: Filter[]; filterQuery: string; filterStatus?: AlertStatus; id: string; @@ -300,15 +302,18 @@ export const BodyComponent = React.memo( data, defaultCellActions, filterQuery, + filters, filterStatus, + hasAlertsCrud, + hasAlertsCrudPermissions, id, indexNames, isEventViewer = false, + isLoading, isSelectAllChecked, itemsPerPageOptions, leadingControlColumns = EMPTY_CONTROL_COLUMNS, loadingEventIds, - isLoading, loadPage, onRuleChange, pageSize, @@ -322,11 +327,9 @@ export const BodyComponent = React.memo( tableView = 'gridView', tabType, totalItems, + totalSelectAllAlerts, trailingControlColumns = EMPTY_CONTROL_COLUMNS, unit = defaultUnit, - hasAlertsCrud, - hasAlertsCrudPermissions, - totalSelectAllAlerts, }) => { const dispatch = useDispatch(); const getManageTimeline = useMemo(() => tGridSelectors.getManageTimelineById(), []); @@ -641,10 +644,11 @@ export const BodyComponent = React.memo( columnHeaders.map((header) => { const buildAction = (tGridCellAction: TGridCellAction) => tGridCellAction({ - data: data.map((row) => row.data), browserFields, - timelineId: id, + data: data.map((row) => row.data), + globalFilters: filters, pageSize, + timelineId: id, }); return { @@ -653,7 +657,7 @@ export const BodyComponent = React.memo( header.tGridCellActions?.map(buildAction) ?? defaultCellActions?.map(buildAction), }; }), - [browserFields, columnHeaders, data, defaultCellActions, id, pageSize] + [browserFields, columnHeaders, data, defaultCellActions, id, pageSize, filters] ); const renderTGridCellValue = useMemo(() => { diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx index fb37bab7668b9..b649dc36abe0a 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx @@ -347,30 +347,31 @@ const TGridIntegratedComponent: React.FC = ({ > From 3907d53df53ff128a1496b973d15e129abe2419e Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 22 Sep 2021 13:22:38 +0200 Subject: [PATCH 55/69] persistable state docs (#105202) --- dev_docs/key_concepts/persistable_state.mdx | 83 +++++++++++++++++++ examples/embeddable_examples/kibana.json | 2 +- .../migrations_embeddable_factory.ts | 11 +++ .../server/merge_migration_function_maps.ts | 25 ++++++ .../server/searchable_list_saved_object.ts | 21 +++-- src/plugins/kibana_utils/server/index.ts | 1 + 6 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 dev_docs/key_concepts/persistable_state.mdx create mode 100644 examples/embeddable_examples/server/merge_migration_function_maps.ts diff --git a/dev_docs/key_concepts/persistable_state.mdx b/dev_docs/key_concepts/persistable_state.mdx new file mode 100644 index 0000000000000..77c7e41ff2e53 --- /dev/null +++ b/dev_docs/key_concepts/persistable_state.mdx @@ -0,0 +1,83 @@ +--- +id: kibDevDocsPersistableStateIntro +slug: /kibana-dev-docs/persistable-state-intro +title: Persistable State +summary: Persitable state is a key concept to understand when building a Kibana plugin. +date: 2021-02-02 +tags: ['kibana','dev', 'contributor', 'api docs'] +--- + + “Persistable state” is developer-defined state that supports being persisted by a plugin other than the one defining it. Persistable State needs to be serializable and the owner can/should provide utilities to migrate it, extract and inject any it may contain, as well as telemetry collection utilities. + +## Exposing state that can be persisted + +Any plugin that exposes state that another plugin might persist should implement interface on their `setup` contract. This will allow plugins persisting the state to easily access migrations and other utilities. + +Example: Data plugin allows you to generate filters. Those filters can be persisted by applications in their saved +objects or in the URL. In order to allow apps to migrate the filters in case the structure changes in the future, the Data plugin implements `PersistableStateService` on . + +note: There is currently no obvious way for a plugin to know which state is safe to persist. The developer must manually look for a matching `PersistableStateService`, or ad-hoc provided migration utilities (as is the case with Rule Type Parameters). +In the future, we hope to make it easier for producers of state to understand when they need to write a migration with changes, and also make it easier for consumers of such state, to understand whether it is safe to persist. + +## Exposing state that can be persisted but is not owned by plugin exposing it (registry) + +Any plugin that owns collection of items (registry) whose state/configuration can be persisted should implement `PersistableStateService` +interface on their `setup` contract and each item in the collection should implement interface. + +Example: Embeddable plugin owns the registry of embeddable factories to which other plugins can register new embeddable factories. Dashboard plugin +stores a bunch of embeddable panels input in its saved object and URL. Embeddable plugin setup contract implements `PersistableStateService` +interface and each `EmbeddableFactory` needs to implement `PersistableStateDefinition` interface. + +Embeddable plugin exposes this interfaces: +``` +// EmbeddableInput implements Serializable + +export interface EmbeddableRegistryDefinition extends PersistableStateDefinition { + id: string; + ... +} + +export interface EmbeddableSetup extends PersistableStateService; +``` + +Note: if your plugin doesn't expose the state (it is the only one storing state), the plugin doesn't need to implement the `PersistableStateService` interface. +If the state your plugin is storing can be provided by other plugins (your plugin owns a registry) items in that registry still need to implement `PersistableStateDefinition` interface. + +## Storing persistable state as part of saved object + +Any plugin that stores any persistable state as part of their saved object should make sure that its saved object migration +and reference extraction and injection methods correctly use the matching `PersistableStateService` implementation for the state they are storing. + +Take a look at [example saved object](https://github.com/elastic/kibana/blob/master/examples/embeddable_examples/server/searchable_list_saved_object.ts#L32) which stores an embeddable state. Note how the `migrations`, `extractReferences` and `injectReferences` are defined. + +## Storing persistable state as part of URL + +When storing persistable state as part of URL you must make sure your URL is versioned. When loading the state `migrateToLatest` method +of `PersistableStateService` should be called, which will migrate the state from its original version to latest. + +note: Currently there is no recommended way on how to store version in url and its up to every application to decide on how to implement that. + +## Available state operations + +### Extraction/Injection of References + +In order to support import and export, and space-sharing capabilities, Saved Objects need to explicitly list any references they contain to other Saved Objects. +To support persisting your state in saved objects owned by another plugin, the and methods of Persistable State interface should be implemented. + + + +[See example embeddable providing extract/inject functions](https://github.com/elastic/kibana/blob/master/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts) + +### Migrations and Backward compatibility + +As your plugin evolves, you may need to change your state in a breaking way. If that happens, you should write a migration to upgrade the state that existed prior to the change. + +. + +[See an example saved object storing embeddable state implementing saved object migration function](https://github.com/elastic/kibana/blob/master/examples/embeddable_examples/server/searchable_list_saved_object.ts) + +[See example embeddable providing migration functions](https://github.com/elastic/kibana/blob/master/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts) + +## Telemetry + +You might want to collect statistics about how your state is used. If that is the case you should implement the telemetry method of Persistable State interface. diff --git a/examples/embeddable_examples/kibana.json b/examples/embeddable_examples/kibana.json index d725a5c94a9c8..103857804b5d4 100644 --- a/examples/embeddable_examples/kibana.json +++ b/examples/embeddable_examples/kibana.json @@ -9,7 +9,7 @@ "githubTeam": "kibana-app-services" }, "description": "Example app that shows how to register custom embeddables", - "requiredPlugins": ["embeddable", "uiActions", "savedObjects", "dashboard"], + "requiredPlugins": ["embeddable", "uiActions", "savedObjects", "dashboard", "kibanaUtils"], "optionalPlugins": [], "extraPublicDirs": ["public/todo", "public/hello_world", "public/todo/todo_ref_embeddable"], "requiredBundles": ["kibanaReact"] diff --git a/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts b/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts index c1ceaaca3e466..61e6bfa56ec47 100644 --- a/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts +++ b/examples/embeddable_examples/public/migrations/migrations_embeddable_factory.ts @@ -7,6 +7,7 @@ */ import { i18n } from '@kbn/i18n'; +import { EmbeddableStateWithType } from '../../../../src/plugins/embeddable/common'; import { IContainer, EmbeddableInput, @@ -35,6 +36,16 @@ export class SimpleEmbeddableFactoryDefinition '7.3.0': migration730, }; + public extract(state: EmbeddableStateWithType) { + // this embeddable does not store references to other saved objects + return { state, references: [] }; + } + + public inject(state: EmbeddableStateWithType) { + // this embeddable does not store references to other saved objects + return state; + } + /** * In our simple example, we let everyone have permissions to edit this. Most * embeddables should check the UI Capabilities service to be sure of diff --git a/examples/embeddable_examples/server/merge_migration_function_maps.ts b/examples/embeddable_examples/server/merge_migration_function_maps.ts new file mode 100644 index 0000000000000..01a46949e6bbf --- /dev/null +++ b/examples/embeddable_examples/server/merge_migration_function_maps.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mergeWith } from 'lodash'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { MigrateFunctionsObject, MigrateFunction } from '../../../src/plugins/kibana_utils/common'; + +export const mergeMigrationFunctionMaps = ( + obj1: MigrateFunctionsObject, + obj2: MigrateFunctionsObject +) => { + const customizer = (objValue: MigrateFunction, srcValue: MigrateFunction) => { + if (!srcValue || !objValue) { + return srcValue || objValue; + } + return (state: SerializableRecord) => objValue(srcValue(state)); + }; + + return mergeWith({ ...obj1 }, obj2, customizer); +}; diff --git a/examples/embeddable_examples/server/searchable_list_saved_object.ts b/examples/embeddable_examples/server/searchable_list_saved_object.ts index ac4656c7c2b77..a3b12a05323f0 100644 --- a/examples/embeddable_examples/server/searchable_list_saved_object.ts +++ b/examples/embeddable_examples/server/searchable_list_saved_object.ts @@ -9,9 +9,12 @@ import { mapValues } from 'lodash'; import { SavedObjectsType, SavedObjectUnsanitizedDoc } from 'kibana/server'; import { EmbeddableSetup } from '../../../src/plugins/embeddable/server'; +// NOTE: this should rather be imported from 'plugins/kibana_utils/server' but examples at the moment don't +// allow static imports from plugins so this code was duplicated +import { mergeMigrationFunctionMaps } from './merge_migration_function_maps'; export const searchableListSavedObject = (embeddable: EmbeddableSetup) => { - return { + const searchableListSO: SavedObjectsType = { name: 'searchableList', hidden: false, namespaceType: 'single', @@ -30,14 +33,22 @@ export const searchableListSavedObject = (embeddable: EmbeddableSetup) => { }, }, migrations: () => { - // we assume all the migration will be done by embeddables service and that saved object holds no extra state besides that of searchable list embeddable input\ - // if saved object would hold additional information we would need to merge the response from embeddables.getAllMigrations with our custom migrations. - return mapValues(embeddable.getAllMigrations(), (migrate) => { + // there are no migrations defined for the saved object at the moment, possibly they would be added in the future + const searchableListSavedObjectMigrations = {}; + + // we don't know if embeddables have any migrations defined so we need to fetch them and map the received functions so we pass + // them the correct input and that we correctly map the response + const embeddableMigrations = mapValues(embeddable.getAllMigrations(), (migrate) => { return (state: SavedObjectUnsanitizedDoc) => ({ ...state, attributes: migrate(state.attributes), }); }); + + // we merge our and embeddable migrations and return + return mergeMigrationFunctionMaps(searchableListSavedObjectMigrations, embeddableMigrations); }, - } as SavedObjectsType; + }; + + return searchableListSO; }; diff --git a/src/plugins/kibana_utils/server/index.ts b/src/plugins/kibana_utils/server/index.ts index 483c5aa92b45e..42847042be151 100644 --- a/src/plugins/kibana_utils/server/index.ts +++ b/src/plugins/kibana_utils/server/index.ts @@ -15,6 +15,7 @@ export { Get, Set, url, + mergeMigrationFunctionMaps, } from '../common'; export { KbnServerError, reportServerError, getKbnServerError } from './report_server_error'; From 11700a7d7ad29dd249a86f0d52cab556caa9b67b Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Wed, 22 Sep 2021 12:35:31 +0100 Subject: [PATCH 56/69] [Security Solution] Update data field for JA3 fingerprint (#112571) * update data field for JA3 fingerprint * update unit test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/factory/network/tls/__mocks__/index.ts | 4 ++-- .../factory/network/tls/query.tls_network.dsl.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts index 8616a2ef14856..41cf691e00f99 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts @@ -81,7 +81,7 @@ export const formattedSearchStrategyResponse = { issuers: { terms: { field: 'tls.server.issuer' } }, subjects: { terms: { field: 'tls.server.subject' } }, not_after: { terms: { field: 'tls.server.not_after' } }, - ja3: { terms: { field: 'tls.server.ja3s' } }, + ja3: { terms: { field: 'tls.client.ja3s' } }, }, }, }, @@ -136,7 +136,7 @@ export const expectedDsl = { issuers: { terms: { field: 'tls.server.issuer' } }, subjects: { terms: { field: 'tls.server.subject' } }, not_after: { terms: { field: 'tls.server.not_after' } }, - ja3: { terms: { field: 'tls.server.ja3s' } }, + ja3: { terms: { field: 'tls.client.ja3s' } }, }, }, }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts index be60b33ae2d22..7f3f649ed965a 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts @@ -47,7 +47,7 @@ const getAggs = (querySize: number, sort: SortField) => ({ }, ja3: { terms: { - field: 'tls.server.ja3s', + field: 'tls.client.ja3s', }, }, }, From 525d3a59206c4dabe1b6bc822e848c8f534350c4 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 22 Sep 2021 07:04:15 -0500 Subject: [PATCH 57/69] [data views] remove some any instances from service (#112632) * remove some any instances from data view * lint fix * simplify scripted field type --- .../data_views/data_views/_pattern_cache.ts | 4 +-- .../common/data_views/data_views/data_view.ts | 32 +++++++++---------- .../data_views/data_views/data_views.ts | 6 ++-- .../data/common/data_views/lib/get_title.ts | 9 +++--- .../data_views/lib/validate_index_pattern.ts | 2 +- .../application/services/use_es_doc_search.ts | 2 +- .../field_validators/index_pattern_field.ts | 6 ++-- 7 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/plugins/data/common/data_views/data_views/_pattern_cache.ts b/src/plugins/data/common/data_views/data_views/_pattern_cache.ts index f304d0e93d79c..19db5b21e5934 100644 --- a/src/plugins/data/common/data_views/data_views/_pattern_cache.ts +++ b/src/plugins/data/common/data_views/data_views/_pattern_cache.ts @@ -16,12 +16,12 @@ export interface DataViewCache { } export function createDataViewCache(): DataViewCache { - const vals: Record = {}; + const vals: Record> = {}; const cache: DataViewCache = { get: (id: string) => { return vals[id]; }, - set: (id: string, prom: any) => { + set: (id: string, prom: Promise) => { vals[id] = prom; return prom; }, diff --git a/src/plugins/data/common/data_views/data_views/data_view.ts b/src/plugins/data/common/data_views/data_views/data_view.ts index e08d1e62bae06..596887e83ec05 100644 --- a/src/plugins/data/common/data_views/data_views/data_view.ts +++ b/src/plugins/data/common/data_views/data_views/data_view.ts @@ -158,7 +158,7 @@ export class DataView implements IIndexPattern { }; getComputedFields() { - const scriptFields: any = {}; + const scriptFields: Record = {}; if (!this.fields) { return { storedFields: ['*'], @@ -171,23 +171,21 @@ export class DataView implements IIndexPattern { // Date value returned in "_source" could be in any number of formats // Use a docvalue for each date field to ensure standardized formats when working with date fields // indexPattern.flattenHit will override "_source" values when the same field is also defined in "fields" - const docvalueFields = reject(this.fields.getByType('date'), 'scripted').map( - (dateField: any) => { - return { - field: dateField.name, - format: - dateField.esTypes && dateField.esTypes.indexOf('date_nanos') !== -1 - ? 'strict_date_time' - : 'date_time', - }; - } - ); + const docvalueFields = reject(this.fields.getByType('date'), 'scripted').map((dateField) => { + return { + field: dateField.name, + format: + dateField.esTypes && dateField.esTypes.indexOf('date_nanos') !== -1 + ? 'strict_date_time' + : 'date_time', + }; + }); each(this.getScriptedFields(), function (field) { scriptFields[field.name] = { script: { - source: field.script, - lang: field.lang, + source: field.script as string, + lang: field.lang as string, }, }; }); @@ -227,7 +225,7 @@ export class DataView implements IIndexPattern { */ getSourceFiltering() { return { - excludes: (this.sourceFilters && this.sourceFilters.map((filter: any) => filter.value)) || [], + excludes: (this.sourceFilters && this.sourceFilters.map((filter) => filter.value)) || [], }; } @@ -299,8 +297,8 @@ export class DataView implements IIndexPattern { } isTimeNanosBased(): boolean { - const timeField: any = this.getTimeField(); - return timeField && timeField.esTypes && timeField.esTypes.indexOf('date_nanos') !== -1; + const timeField = this.getTimeField(); + return !!(timeField && timeField.esTypes && timeField.esTypes.indexOf('date_nanos') !== -1); } getTimeField() { diff --git a/src/plugins/data/common/data_views/data_views/data_views.ts b/src/plugins/data/common/data_views/data_views/data_views.ts index 1284f00436324..f903c65d95d2a 100644 --- a/src/plugins/data/common/data_views/data_views/data_views.ts +++ b/src/plugins/data/common/data_views/data_views/data_views.ts @@ -289,7 +289,7 @@ export class DataViewsService { indexPattern.fields.replaceAll(fieldsWithSavedAttrs); } catch (err) { if (err instanceof DataViewMissingIndices) { - this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' }); + this.onNotification({ title: err.message, color: 'danger', iconType: 'alert' }); } this.onError(err, { @@ -334,7 +334,7 @@ export class DataViewsService { return this.fieldArrayToMap(updatedFieldList, fieldAttrs); } catch (err) { if (err instanceof DataViewMissingIndices) { - this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' }); + this.onNotification({ title: err.message, color: 'danger', iconType: 'alert' }); return {}; } @@ -475,7 +475,7 @@ export class DataViewsService { } catch (err) { if (err instanceof DataViewMissingIndices) { this.onNotification({ - title: (err as any).message, + title: err.message, color: 'danger', iconType: 'alert', }); diff --git a/src/plugins/data/common/data_views/lib/get_title.ts b/src/plugins/data/common/data_views/lib/get_title.ts index efebbc302f22c..94185eae46893 100644 --- a/src/plugins/data/common/data_views/lib/get_title.ts +++ b/src/plugins/data/common/data_views/lib/get_title.ts @@ -6,17 +6,18 @@ * Side Public License, v 1. */ -import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../core/public'; +import { SavedObjectsClientContract } from '../../../../../core/public'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../../constants'; +import { DataViewAttributes } from '../types'; export async function getTitle( client: SavedObjectsClientContract, indexPatternId: string -): Promise> { - const savedObject = (await client.get( +): Promise { + const savedObject = await client.get( DATA_VIEW_SAVED_OBJECT_TYPE, indexPatternId - )) as SimpleSavedObject; + ); if (savedObject.error) { throw new Error(`Unable to get index-pattern title: ${savedObject.error.message}`); diff --git a/src/plugins/data/common/data_views/lib/validate_index_pattern.ts b/src/plugins/data/common/data_views/lib/validate_index_pattern.ts index 454d0bc1a0c6e..f86ba28e7cde4 100644 --- a/src/plugins/data/common/data_views/lib/validate_index_pattern.ts +++ b/src/plugins/data/common/data_views/lib/validate_index_pattern.ts @@ -24,7 +24,7 @@ function findIllegalCharacters(indexPattern: string): string[] { } export function validateDataView(indexPattern: string) { - const errors: Record = {}; + const errors: { [ILLEGAL_CHARACTERS_KEY]?: string[]; [CONTAINS_SPACES_KEY]?: boolean } = {}; const illegalCharacters = findIllegalCharacters(indexPattern); diff --git a/src/plugins/discover/public/application/services/use_es_doc_search.ts b/src/plugins/discover/public/application/services/use_es_doc_search.ts index a2f0cd6f8442b..c5216c483fd10 100644 --- a/src/plugins/discover/public/application/services/use_es_doc_search.ts +++ b/src/plugins/discover/public/application/services/use_es_doc_search.ts @@ -37,7 +37,7 @@ export function buildSearchBody( }, }, stored_fields: computedFields.storedFields, - script_fields: computedFields.scriptFields, + script_fields: computedFields.scriptFields as Record, version: true, }, }; diff --git a/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts b/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts index 5aadefa6005fa..1b6667fce41ab 100644 --- a/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts +++ b/src/plugins/es_ui_shared/static/forms/helpers/field_validators/index_pattern_field.ts @@ -37,7 +37,7 @@ export const indexPatternField = // Validate illegal characters const errors = indexPatterns.validate(value); - if (errors[indexPatterns.ILLEGAL_CHARACTERS_KEY]) { + if (errors.ILLEGAL_CHARACTERS) { return { code: 'ERR_FIELD_FORMAT', formatType: 'INDEX_PATTERN', @@ -45,8 +45,8 @@ export const indexPatternField = defaultMessage: 'The index pattern contains the invalid {characterListLength, plural, one {character} other {characters}} { characterList }.', values: { - characterList: errors[indexPatterns.ILLEGAL_CHARACTERS_KEY].join(' '), - characterListLength: errors[indexPatterns.ILLEGAL_CHARACTERS_KEY].length, + characterList: errors.ILLEGAL_CHARACTERS.join(' '), + characterListLength: errors.ILLEGAL_CHARACTERS.length, }, }), }; From 70f635b14d8d62d62aec2aacc6a9cf79e60f81fd Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Wed, 22 Sep 2021 08:59:49 -0400 Subject: [PATCH 58/69] [Cases] Migrate user actions connector ID V2 (#112710) * Making progress * Fleshing out the extraction logic * Finishing migration logic and starting more tests * Finishing migration unit tests * Making progress on services * Finishing transform to es schema * Finishing transform functionality and unit tests * reverting migration data updates * Cleaning up type errors * fixing test error * Working migration tests * Refactoring retrieval of connector fields * Refactoring connector id in and tests in frontend * Fixing tests and finished refactoring parse string * Fixing integration test * Fixing integration tests * Removing some duplicate code and updating test name * Fixing create connector user action bug * Addressing feedback and logging error * Moving parsing function to common * Fixing type errors * Fixing type errors * Addressing feedback * Fixing lint errors * Adjusting import for user action changes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/common/api/cases/case.ts | 16 +- .../cases/common/api/cases/user_actions.ts | 3 +- .../cases/common/api/connectors/index.ts | 12 +- x-pack/plugins/cases/common/index.ts | 1 + x-pack/plugins/cases/common/ui/types.ts | 2 + .../cases/common/utils/user_actions.ts | 18 + .../cases/public/common/user_actions/index.ts | 8 + .../common/user_actions/parsers.test.ts | 86 + .../public/common/user_actions/parsers.ts | 77 + .../components/edit_connector/helpers.test.ts | 187 ++ .../components/edit_connector/helpers.ts | 30 +- .../user_action_tree/helpers.test.tsx | 108 +- .../components/user_action_tree/helpers.tsx | 53 +- .../components/user_action_tree/index.tsx | 10 +- .../plugins/cases/public/containers/mock.ts | 123 +- .../use_get_case_user_actions.test.tsx | 237 +- .../containers/use_get_case_user_actions.tsx | 51 +- .../plugins/cases/public/containers/utils.ts | 8 - .../cases/server/client/attachments/add.ts | 6 +- .../cases/server/client/attachments/update.ts | 9 +- .../cases/server/client/cases/create.ts | 2 +- .../cases/server/client/cases/delete.ts | 2 +- .../plugins/cases/server/client/cases/mock.ts | 18 +- .../plugins/cases/server/client/cases/push.ts | 2 +- .../cases/server/client/cases/utils.test.ts | 6 +- .../cases/server/client/cases/utils.ts | 30 +- .../server/client/user_actions/get.test.ts | 106 + .../cases/server/client/user_actions/get.ts | 47 +- .../plugins/cases/server/common/constants.ts | 29 + .../migrations/cases.test.ts | 570 ++-- .../saved_object_types/migrations/cases.ts | 14 +- .../migrations/configuration.test.ts | 142 +- .../migrations/configuration.ts | 9 +- .../saved_object_types/migrations/index.ts | 57 +- .../migrations/user_actions.test.ts | 562 ++++ .../migrations/user_actions.ts | 159 + .../migrations/utils.test.ts | 229 -- .../saved_object_types/migrations/utils.ts | 73 - .../cases/server/services/cases/index.test.ts | 8 +- .../cases/server/services/test_utils.ts | 17 +- .../services/user_actions/helpers.test.ts | 332 ++ .../server/services/user_actions/helpers.ts | 192 +- .../services/user_actions/index.test.ts | 557 ++++ .../server/services/user_actions/index.ts | 101 +- .../services/user_actions/transform.test.ts | 1246 +++++++ .../server/services/user_actions/transform.ts | 320 ++ .../server/services/user_actions/types.ts | 14 + .../tests/common/cases/delete_cases.ts | 4 +- .../tests/common/cases/import_export.ts | 85 +- .../tests/common/cases/patch_cases.ts | 4 + .../tests/common/cases/post_case.ts | 7 +- .../tests/common/comments/post_comment.ts | 2 + .../user_actions/get_all_user_actions.ts | 19 +- .../tests/common/user_actions/migrations.ts | 149 +- .../tests/trial/cases/push_case.ts | 3 +- .../user_actions/get_all_user_actions.ts | 4 +- .../migrations/7.13_user_actions/data.json.gz | Bin 0 -> 2078 bytes .../7.13_user_actions/mappings.json | 2954 +++++++++++++++++ 58 files changed, 7949 insertions(+), 1171 deletions(-) create mode 100644 x-pack/plugins/cases/common/utils/user_actions.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/index.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.test.ts create mode 100644 x-pack/plugins/cases/public/common/user_actions/parsers.ts create mode 100644 x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts create mode 100644 x-pack/plugins/cases/server/client/user_actions/get.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts create mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts delete mode 100644 x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/helpers.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/index.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.test.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/transform.ts create mode 100644 x-pack/plugins/cases/server/services/user_actions/types.ts create mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz create mode 100644 x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/mappings.json diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index 37a491cdad4c0..05a053307b29c 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -87,8 +87,11 @@ const CaseBasicRt = rt.type({ owner: rt.string, }); -export const CaseExternalServiceBasicRt = rt.type({ - connector_id: rt.union([rt.string, rt.null]), +/** + * This represents the push to service UserAction. It lacks the connector_id because that is stored in a different field + * within the user action object in the API response. + */ +export const CaseUserActionExternalServiceRt = rt.type({ connector_name: rt.string, external_id: rt.string, external_title: rt.string, @@ -97,7 +100,14 @@ export const CaseExternalServiceBasicRt = rt.type({ pushed_by: UserRT, }); -const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); +export const CaseExternalServiceBasicRt = rt.intersection([ + rt.type({ + connector_id: rt.union([rt.string, rt.null]), + }), + CaseUserActionExternalServiceRt, +]); + +export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]); export const CaseAttributesRt = rt.intersection([ CaseBasicRt, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index 03912c550d77a..e86ce5248a6f9 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -34,7 +34,6 @@ const UserActionRt = rt.union([ rt.literal('push-to-service'), ]); -// TO DO change state to status const CaseUserActionBasicRT = rt.type({ action_field: UserActionFieldRt, action: UserActionRt, @@ -51,6 +50,8 @@ const CaseUserActionResponseRT = rt.intersection([ action_id: rt.string, case_id: rt.string, comment_id: rt.union([rt.string, rt.null]), + new_val_connector_id: rt.union([rt.string, rt.null]), + old_val_connector_id: rt.union([rt.string, rt.null]), }), rt.partial({ sub_case_id: rt.string }), ]); diff --git a/x-pack/plugins/cases/common/api/connectors/index.ts b/x-pack/plugins/cases/common/api/connectors/index.ts index 77af90b5d08cb..2b3483b4f6184 100644 --- a/x-pack/plugins/cases/common/api/connectors/index.ts +++ b/x-pack/plugins/cases/common/api/connectors/index.ts @@ -84,14 +84,22 @@ export const ConnectorTypeFieldsRt = rt.union([ ConnectorSwimlaneTypeFieldsRt, ]); +/** + * This type represents the connector's format when it is encoded within a user action. + */ +export const CaseUserActionConnectorRt = rt.intersection([ + rt.type({ name: rt.string }), + ConnectorTypeFieldsRt, +]); + export const CaseConnectorRt = rt.intersection([ rt.type({ id: rt.string, - name: rt.string, }), - ConnectorTypeFieldsRt, + CaseUserActionConnectorRt, ]); +export type CaseUserActionConnector = rt.TypeOf; export type CaseConnector = rt.TypeOf; export type ConnectorTypeFields = rt.TypeOf; export type ConnectorJiraTypeFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/index.ts b/x-pack/plugins/cases/common/index.ts index 5305318cc9aa6..d38b1a779981c 100644 --- a/x-pack/plugins/cases/common/index.ts +++ b/x-pack/plugins/cases/common/index.ts @@ -12,3 +12,4 @@ export * from './constants'; export * from './api'; export * from './ui/types'; export * from './utils/connectors_api'; +export * from './utils/user_actions'; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index bf4ec0da6ee56..c89c3eb08263b 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -66,7 +66,9 @@ export interface CaseUserActions { caseId: string; commentId: string | null; newValue: string | null; + newValConnectorId: string | null; oldValue: string | null; + oldValConnectorId: string | null; } export interface CaseExternalService { diff --git a/x-pack/plugins/cases/common/utils/user_actions.ts b/x-pack/plugins/cases/common/utils/user_actions.ts new file mode 100644 index 0000000000000..7de0d7066eaed --- /dev/null +++ b/x-pack/plugins/cases/common/utils/user_actions.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export function isCreateConnector(action?: string, actionFields?: string[]): boolean { + return action === 'create' && actionFields != null && actionFields.includes('connector'); +} + +export function isUpdateConnector(action?: string, actionFields?: string[]): boolean { + return action === 'update' && actionFields != null && actionFields.includes('connector'); +} + +export function isPush(action?: string, actionFields?: string[]): boolean { + return action === 'push-to-service' && actionFields != null && actionFields.includes('pushed'); +} diff --git a/x-pack/plugins/cases/public/common/user_actions/index.ts b/x-pack/plugins/cases/public/common/user_actions/index.ts new file mode 100644 index 0000000000000..507455f7102a7 --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './parsers'; diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts new file mode 100644 index 0000000000000..c6d13cc41686c --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/parsers.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ConnectorTypes, noneConnectorId } from '../../../common'; +import { parseStringAsConnector, parseStringAsExternalService } from './parsers'; + +describe('user actions utility functions', () => { + describe('parseStringAsConnector', () => { + it('return null if the data is null', () => { + expect(parseStringAsConnector('', null)).toBeNull(); + }); + + it('return null if the data is not a json object', () => { + expect(parseStringAsConnector('', 'blah')).toBeNull(); + }); + + it('return null if the data is not a valid connector', () => { + expect(parseStringAsConnector('', JSON.stringify({ a: '1' }))).toBeNull(); + }); + + it('return null if id is null but the data is a connector other than none', () => { + expect( + parseStringAsConnector( + null, + JSON.stringify({ type: ConnectorTypes.jira, name: '', fields: null }) + ) + ).toBeNull(); + }); + + it('return the id as the none connector if the data is the none connector', () => { + expect( + parseStringAsConnector( + null, + JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }) + ) + ).toEqual({ id: noneConnectorId, type: ConnectorTypes.none, name: '', fields: null }); + }); + + it('returns a decoded connector with the specified id', () => { + expect( + parseStringAsConnector( + 'a', + JSON.stringify({ type: ConnectorTypes.jira, name: 'hi', fields: null }) + ) + ).toEqual({ id: 'a', type: ConnectorTypes.jira, name: 'hi', fields: null }); + }); + }); + + describe('parseStringAsExternalService', () => { + it('returns null when the data is null', () => { + expect(parseStringAsExternalService('', null)).toBeNull(); + }); + + it('returns null when the data is not valid json', () => { + expect(parseStringAsExternalService('', 'blah')).toBeNull(); + }); + + it('returns null when the data is not a valid external service object', () => { + expect(parseStringAsExternalService('', JSON.stringify({ a: '1' }))).toBeNull(); + }); + + it('returns the decoded external service with the connector_id field added', () => { + const externalServiceInfo = { + connector_name: 'name', + external_id: '1', + external_title: 'title', + external_url: 'abc', + pushed_at: '1', + pushed_by: { + username: 'a', + email: 'a@a.com', + full_name: 'a', + }, + }; + + expect(parseStringAsExternalService('500', JSON.stringify(externalServiceInfo))).toEqual({ + ...externalServiceInfo, + connector_id: '500', + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/common/user_actions/parsers.ts b/x-pack/plugins/cases/public/common/user_actions/parsers.ts new file mode 100644 index 0000000000000..dfea22443aa51 --- /dev/null +++ b/x-pack/plugins/cases/public/common/user_actions/parsers.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + CaseUserActionConnectorRt, + CaseConnector, + ConnectorTypes, + noneConnectorId, + CaseFullExternalService, + CaseUserActionExternalServiceRt, +} from '../../../common'; + +export const parseStringAsConnector = ( + id: string | null, + encodedData: string | null +): CaseConnector | null => { + if (encodedData == null) { + return null; + } + + const decodedConnector = parseString(encodedData); + + if (!CaseUserActionConnectorRt.is(decodedConnector)) { + return null; + } + + if (id == null && decodedConnector.type === ConnectorTypes.none) { + return { + ...decodedConnector, + id: noneConnectorId, + }; + } else if (id == null) { + return null; + } else { + // id does not equal null or undefined and the connector type does not equal none + // so return the connector with its id + return { + ...decodedConnector, + id, + }; + } +}; + +const parseString = (params: string | null): unknown | null => { + if (params == null) { + return null; + } + + try { + return JSON.parse(params); + } catch { + return null; + } +}; + +export const parseStringAsExternalService = ( + id: string | null, + encodedData: string | null +): CaseFullExternalService => { + if (encodedData == null) { + return null; + } + + const decodedExternalService = parseString(encodedData); + if (!CaseUserActionExternalServiceRt.is(decodedExternalService)) { + return null; + } + + return { + ...decodedExternalService, + connector_id: id, + }; +}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts new file mode 100644 index 0000000000000..e20d6b37258bc --- /dev/null +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.test.ts @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseUserActionConnector, ConnectorTypes } from '../../../common'; +import { CaseUserActions } from '../../containers/types'; +import { getConnectorFieldsFromUserActions } from './helpers'; + +describe('helpers', () => { + describe('getConnectorFieldsFromUserActions', () => { + it('returns null when it cannot find the connector id', () => { + expect(getConnectorFieldsFromUserActions('a', [])).toBeNull(); + }); + + it('returns null when the value fields are not valid encoded fields', () => { + expect( + getConnectorFieldsFromUserActions('a', [createUserAction({ newValue: 'a', oldValue: 'a' })]) + ).toBeNull(); + }); + + it('returns null when it cannot find the connector id in a non empty array', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: JSON.stringify({ a: '1' }), + oldValue: JSON.stringify({ a: '1' }), + }), + ]) + ).toBeNull(); + }); + + it('returns the fields when it finds the connector id in the new value', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: JSON.stringify({ a: '1' }), + newValConnectorId: 'a', + }), + ]) + ).toEqual(defaultJiraFields); + }); + + it('returns the fields when it finds the connector id in the new value and the old value is null', () => { + expect( + getConnectorFieldsFromUserActions('a', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + newValConnectorId: 'a', + }), + ]) + ).toEqual(defaultJiraFields); + }); + + it('returns the fields when it finds the connector id in the old value', () => { + const expectedFields = { ...defaultJiraFields, issueType: '5' }; + + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector({ + fields: expectedFields, + }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + }), + ]) + ).toEqual(expectedFields); + }); + + it('returns the fields when it finds the connector id in the second user action', () => { + const expectedFields = { ...defaultJiraFields, issueType: '5' }; + + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector(), + newValConnectorId: 'b', + oldValConnectorId: 'a', + }), + createUserAction({ + newValue: createEncodedJiraConnector(), + oldValue: createEncodedJiraConnector({ fields: expectedFields }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + }), + ]) + ).toEqual(expectedFields); + }); + + it('ignores a parse failure and finds the right user action', () => { + expect( + getConnectorFieldsFromUserActions('none', [ + createUserAction({ + newValue: 'b', + newValConnectorId: null, + }), + createUserAction({ + newValue: createEncodedJiraConnector({ + type: ConnectorTypes.none, + name: '', + fields: null, + }), + newValConnectorId: null, + }), + ]) + ).toBeNull(); + }); + + it('returns null when the id matches but the encoded value is null', () => { + expect( + getConnectorFieldsFromUserActions('b', [ + createUserAction({ + newValue: null, + newValConnectorId: 'b', + }), + ]) + ).toBeNull(); + }); + + it('returns null when the action fields is not of length 1', () => { + expect( + getConnectorFieldsFromUserActions('id-to-find', [ + createUserAction({ + newValue: JSON.stringify({ a: '1', fields: { hello: '1' } }), + oldValue: JSON.stringify({ a: '1', fields: { hi: '2' } }), + newValConnectorId: 'b', + oldValConnectorId: 'id-to-find', + actionField: ['connector', 'connector'], + }), + ]) + ).toBeNull(); + }); + + it('matches the none connector the searched for id is none', () => { + expect( + getConnectorFieldsFromUserActions('none', [ + createUserAction({ + newValue: createEncodedJiraConnector({ + type: ConnectorTypes.none, + name: '', + fields: null, + }), + newValConnectorId: null, + }), + ]) + ).toBeNull(); + }); + }); +}); + +function createUserAction(fields: Partial): CaseUserActions { + return { + action: 'update', + actionAt: '', + actionBy: {}, + actionField: ['connector'], + actionId: '', + caseId: '', + commentId: '', + newValConnectorId: null, + oldValConnectorId: null, + newValue: null, + oldValue: null, + ...fields, + }; +} + +function createEncodedJiraConnector(fields?: Partial): string { + return JSON.stringify({ + type: ConnectorTypes.jira, + name: 'name', + fields: defaultJiraFields, + ...fields, + }); +} + +const defaultJiraFields = { + issueType: '1', + parent: null, + priority: null, +}; diff --git a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts index 36eb3f58c8aaf..b97035c458aca 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/helpers.ts +++ b/x-pack/plugins/cases/public/components/edit_connector/helpers.ts @@ -5,23 +5,33 @@ * 2.0. */ +import { ConnectorTypeFields } from '../../../common'; import { CaseUserActions } from '../../containers/types'; +import { parseStringAsConnector } from '../../common/user_actions'; -export const getConnectorFieldsFromUserActions = (id: string, userActions: CaseUserActions[]) => { +export const getConnectorFieldsFromUserActions = ( + id: string, + userActions: CaseUserActions[] +): ConnectorTypeFields['fields'] => { try { for (const action of [...userActions].reverse()) { if (action.actionField.length === 1 && action.actionField[0] === 'connector') { - if (action.oldValue && action.newValue) { - const oldValue = JSON.parse(action.oldValue); - const newValue = JSON.parse(action.newValue); + const parsedNewConnector = parseStringAsConnector( + action.newValConnectorId, + action.newValue + ); - if (newValue.id === id) { - return newValue.fields; - } + if (parsedNewConnector && id === parsedNewConnector.id) { + return parsedNewConnector.fields; + } + + const parsedOldConnector = parseStringAsConnector( + action.oldValConnectorId, + action.oldValue + ); - if (oldValue.id === id) { - return oldValue.fields; - } + if (parsedOldConnector && id === parsedOldConnector.id) { + return parsedOldConnector.fields; } } } diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx index b49a010cff38f..841f0d36bbf17 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CaseStatuses } from '../../../common'; +import { CaseStatuses, ConnectorTypes } from '../../../common'; import { basicPush, getUserAction } from '../../containers/mock'; import { getLabelTitle, @@ -129,7 +129,7 @@ describe('User action tree helpers', () => { `${i18n.PUSHED_NEW_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue).external_url + JSON.parse(action.newValue!).external_url ); }); @@ -142,50 +142,74 @@ describe('User action tree helpers', () => { `${i18n.UPDATE_INCIDENT} ${basicPush.connectorName}` ); expect(wrapper.find(`[data-test-subj="pushed-value"]`).first().prop('href')).toEqual( - JSON.parse(action.newValue).external_url + JSON.parse(action.newValue!).external_url ); }); - it('label title generated for update connector - change connector', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'resilient-2' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, - }); - - expect(result).toEqual('selected My Connector 2 as incident management system'); - }); - - it('label title generated for update connector - change connector to none', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'none' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, + describe('getConnectorLabelTitle', () => { + it('returns an empty string when the encoded old value is null', () => { + const result = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { oldValue: null }), + connectors, + }); + + expect(result).toEqual(''); + }); + + it('returns an empty string when the encoded new value is null', () => { + const result = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { newValue: null }), + connectors, + }); + + expect(result).toEqual(''); + }); + + it('returns the change connector label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ + type: ConnectorTypes.serviceNowITSM, + name: 'a', + fields: null, + }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.resilient, name: 'a', fields: null }), + newValConnectorId: 'resilient-2', + }), + connectors, + }); + + expect(result).toEqual('selected My Connector 2 as incident management system'); + }); + + it('returns the removed connector label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null }), + newValConnectorId: 'none', + }), + connectors, + }); + + expect(result).toEqual('removed external incident management system'); + }); + + it('returns the connector fields changed label', () => { + const result: string | JSX.Element = getConnectorLabelTitle({ + action: getUserAction(['connector'], 'update', { + oldValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + oldValConnectorId: 'servicenow-1', + newValue: JSON.stringify({ type: ConnectorTypes.serviceNowITSM, name: '', fields: null }), + newValConnectorId: 'servicenow-1', + }), + connectors, + }); + + expect(result).toEqual('changed connector field'); }); - - expect(result).toEqual('removed external incident management system'); - }); - - it('label title generated for update connector - field change', () => { - const action = { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: 'servicenow-1' }), - newValue: JSON.stringify({ id: 'servicenow-1' }), - }; - const result: string | JSX.Element = getConnectorLabelTitle({ - action, - connectors, - }); - - expect(result).toEqual('changed connector field'); }); describe('toStringArray', () => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx index 744b14926b358..2eb44f91190c6 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/helpers.tsx @@ -23,10 +23,11 @@ import { CommentType, Comment, CommentRequestActionsType, + noneConnectorId, } from '../../../common'; import { CaseUserActions } from '../../containers/types'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseString } from '../../containers/utils'; +import { parseStringAsConnector, parseStringAsExternalService } from '../../common/user_actions'; import { Tags } from '../tag_list/tags'; import { UserActionUsernameWithAvatar } from './user_action_username_with_avatar'; import { UserActionTimestamp } from './user_action_timestamp'; @@ -97,23 +98,27 @@ export const getConnectorLabelTitle = ({ action: CaseUserActions; connectors: ActionConnector[]; }) => { - const oldValue = parseString(`${action.oldValue}`); - const newValue = parseString(`${action.newValue}`); + const oldConnector = parseStringAsConnector(action.oldValConnectorId, action.oldValue); + const newConnector = parseStringAsConnector(action.newValConnectorId, action.newValue); - if (oldValue === null || newValue === null) { + if (!oldConnector || !newConnector) { return ''; } - // Connector changed - if (oldValue.id !== newValue.id) { - const newConnector = connectors.find((c) => c.id === newValue.id); - return newValue.id != null && newValue.id !== 'none' && newConnector != null - ? i18n.SELECTED_THIRD_PARTY(newConnector.name) - : i18n.REMOVED_THIRD_PARTY; - } else { - // Field changed + // if the ids are the same, assume we just changed the fields + if (oldConnector.id === newConnector.id) { return i18n.CHANGED_CONNECTOR_FIELD; } + + // ids are not the same so check and see if the id is a valid connector and then return its name + // if the connector id is the none connector value then it must have been removed + const newConnectorActionInfo = connectors.find((c) => c.id === newConnector.id); + if (newConnector.id !== noneConnectorId && newConnectorActionInfo != null) { + return i18n.SELECTED_THIRD_PARTY(newConnectorActionInfo.name); + } + + // it wasn't a valid connector or it was the none connector, so it must have been removed + return i18n.REMOVED_THIRD_PARTY; }; const getTagsLabelTitle = (action: CaseUserActions) => { @@ -133,7 +138,8 @@ const getTagsLabelTitle = (action: CaseUserActions) => { }; export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: boolean) => { - const pushedVal = JSON.parse(action.newValue ?? '') as CaseFullExternalService; + const externalService = parseStringAsExternalService(action.newValConnectorId, action.newValue); + return ( {`${firstPush ? i18n.PUSHED_NEW_INCIDENT : i18n.UPDATE_INCIDENT} ${ - pushedVal?.connector_name + externalService?.connector_name }`} - - {pushedVal?.external_title} + + {externalService?.external_title} @@ -157,20 +163,19 @@ export const getPushedServiceLabelTitle = (action: CaseUserActions, firstPush: b export const getPushInfo = ( caseServices: CaseServices, - // a JSON parse failure will result in null for parsedValue - parsedValue: { connector_id: string | null; connector_name: string } | null, + externalService: CaseFullExternalService | undefined, index: number ) => - parsedValue != null && parsedValue.connector_id != null + externalService != null && externalService.connector_id != null ? { - firstPush: caseServices[parsedValue.connector_id]?.firstPushIndex === index, - parsedConnectorId: parsedValue.connector_id, - parsedConnectorName: parsedValue.connector_name, + firstPush: caseServices[externalService.connector_id]?.firstPushIndex === index, + parsedConnectorId: externalService.connector_id, + parsedConnectorName: externalService.connector_name, } : { firstPush: false, - parsedConnectorId: 'none', - parsedConnectorName: 'none', + parsedConnectorId: noneConnectorId, + parsedConnectorName: noneConnectorId, }; const getUpdateActionIcon = (actionField: string): string => { diff --git a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx index 784817229caf9..7ea415324194c 100644 --- a/x-pack/plugins/cases/public/components/user_action_tree/index.tsx +++ b/x-pack/plugins/cases/public/components/user_action_tree/index.tsx @@ -35,7 +35,7 @@ import { Ecs, } from '../../../common'; import { CaseServices } from '../../containers/use_get_case_user_actions'; -import { parseString } from '../../containers/utils'; +import { parseStringAsExternalService } from '../../common/user_actions'; import { OnUpdateFields } from '../case_view'; import { getConnectorLabelTitle, @@ -512,10 +512,14 @@ export const UserActionTree = React.memo( // Pushed information if (action.actionField.length === 1 && action.actionField[0] === 'pushed') { - const parsedValue = parseString(`${action.newValue}`); + const parsedExternalService = parseStringAsExternalService( + action.newValConnectorId, + action.newValue + ); + const { firstPush, parsedConnectorId, parsedConnectorName } = getPushInfo( caseServices, - parsedValue, + parsedExternalService, index ); diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index c955bb34240e2..fcd564969d486 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -9,6 +9,7 @@ import { ActionLicense, AllCases, Case, CasesStatus, CaseUserActions, Comment } import { AssociationType, + CaseUserActionConnector, CaseResponse, CasesFindResponse, CasesResponse, @@ -19,6 +20,9 @@ import { CommentResponse, CommentType, ConnectorTypes, + isCreateConnector, + isPush, + isUpdateConnector, SECURITY_SOLUTION_OWNER, UserAction, UserActionField, @@ -240,7 +244,9 @@ export const pushedCase: Case = { const basicAction = { actionAt: basicCreatedAt, actionBy: elasticUser, + oldValConnectorId: null, oldValue: null, + newValConnectorId: null, newValue: 'what a cool value', caseId: basicCaseId, commentId: null, @@ -308,12 +314,7 @@ export const basicCaseSnake: CaseResponse = { closed_at: null, closed_by: null, comments: [basicCommentSnake], - connector: { - id: 'none', - name: 'My Connector', - type: ConnectorTypes.none, - fields: null, - }, + connector: { id: 'none', name: 'My Connector', type: ConnectorTypes.none, fields: null }, created_at: basicCreatedAt, created_by: elasticUserSnake, external_service: null, @@ -328,8 +329,8 @@ export const casesStatusSnake: CasesStatusResponse = { count_open_cases: 20, }; +export const pushConnectorId = '123'; export const pushSnake = { - connector_id: '123', connector_name: 'connector name', external_id: 'external_id', external_title: 'external title', @@ -350,7 +351,7 @@ export const pushedCaseSnake = { type: ConnectorTypes.jira, fields: null, }, - external_service: basicPushSnake, + external_service: { ...basicPushSnake, connector_id: pushConnectorId }, }; export const reporters: string[] = ['alexis', 'kim', 'maria', 'steph']; @@ -385,17 +386,20 @@ const basicActionSnake = { comment_id: null, owner: SECURITY_SOLUTION_OWNER, }; -export const getUserActionSnake = (af: UserActionField, a: UserAction) => ({ - ...basicActionSnake, - action_id: `${af[0]}-${a}`, - action_field: af, - action: a, - comment_id: af[0] === 'comment' ? basicCommentId : null, - new_value: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); +export const getUserActionSnake = (af: UserActionField, a: UserAction) => { + const isPushToService = a === 'push-to-service' && af[0] === 'pushed'; + + return { + ...basicActionSnake, + action_id: `${af[0]}-${a}`, + action_field: af, + action: a, + comment_id: af[0] === 'comment' ? basicCommentId : null, + new_value: isPushToService ? JSON.stringify(basicPushSnake) : basicAction.newValue, + new_val_connector_id: isPushToService ? pushConnectorId : null, + old_val_connector_id: null, + }; +}; export const caseUserActionsSnake: CaseUserActionsResponse = [ getUserActionSnake(['description'], 'create'), @@ -405,17 +409,76 @@ export const caseUserActionsSnake: CaseUserActionsResponse = [ // user actions -export const getUserAction = (af: UserActionField, a: UserAction) => ({ - ...basicAction, - actionId: `${af[0]}-${a}`, - actionField: af, - action: a, - commentId: af[0] === 'comment' ? basicCommentId : null, - newValue: - a === 'push-to-service' && af[0] === 'pushed' - ? JSON.stringify(basicPushSnake) - : basicAction.newValue, -}); +export const getUserAction = ( + af: UserActionField, + a: UserAction, + overrides?: Partial +): CaseUserActions => { + return { + ...basicAction, + actionId: `${af[0]}-${a}`, + actionField: af, + action: a, + commentId: af[0] === 'comment' ? basicCommentId : null, + ...getValues(a, af, overrides), + }; +}; + +const getValues = ( + userAction: UserAction, + actionFields: UserActionField, + overrides?: Partial +): Partial => { + if (isCreateConnector(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined ? JSON.stringify(basicCaseSnake) : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: null, + oldValConnectorId: null, + }; + } else if (isUpdateConnector(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined + ? JSON.stringify({ name: 'My Connector', type: ConnectorTypes.none, fields: null }) + : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: + overrides?.oldValue === undefined + ? JSON.stringify({ name: 'My Connector2', type: ConnectorTypes.none, fields: null }) + : overrides.oldValue, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } else if (isPush(userAction, actionFields)) { + return { + newValue: + overrides?.newValue === undefined ? JSON.stringify(basicPushSnake) : overrides?.newValue, + newValConnectorId: + overrides?.newValConnectorId === undefined ? pushConnectorId : overrides.newValConnectorId, + oldValue: overrides?.oldValue ?? null, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } else { + return { + newValue: overrides?.newValue === undefined ? basicAction.newValue : overrides.newValue, + newValConnectorId: overrides?.newValConnectorId ?? null, + oldValue: overrides?.oldValue ?? null, + oldValConnectorId: overrides?.oldValConnectorId ?? null, + }; + } +}; + +export const getJiraConnectorWithoutId = (overrides?: Partial) => { + return JSON.stringify({ + name: 'jira1', + type: ConnectorTypes.jira, + ...jiraFields, + ...overrides, + }); +}; + +export const jiraFields = { fields: { issueType: '10006', priority: null, parent: null } }; export const getAlertUserAction = () => ({ ...basicAction, diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx index 62b4cf92434cd..e7e46fa46c7cc 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx @@ -18,7 +18,9 @@ import { basicPushSnake, caseUserActions, elasticUser, + getJiraConnectorWithoutId, getUserAction, + jiraFields, } from './mock'; import * as api from './api'; @@ -299,15 +301,14 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, @@ -346,15 +347,14 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, @@ -392,11 +392,7 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -418,11 +414,7 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123To456UserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -444,16 +436,8 @@ describe('useGetCaseUserActions', () => { const userActions = [ ...caseUserActions, getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - }, + createChangeConnector123To456UserAction(), + createChangeConnector456To123UserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -474,22 +458,10 @@ describe('useGetCaseUserActions', () => { it('Change fields and connector after push - hasDataToPush: true', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createChangeConnector456To123PriorityLowUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -510,22 +482,10 @@ describe('useGetCaseUserActions', () => { it('Change only connector after push - hasDataToPush: false', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createChangeConnector456To123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -547,45 +507,24 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { newValue: JSON.stringify(push456), - }; + newValConnectorId: '456', + }); const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), pushAction123, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), pushAction456, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'Low' } }), - }, + createChangeConnector456To123PriorityLowUserAction(), + createChangeConnector123LowPriorityTo456UserAction(), + createChangeConnector456To123PriorityLowUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -617,34 +556,22 @@ describe('useGetCaseUserActions', () => { const pushAction123 = getUserAction(['pushed'], 'push-to-service'); const push456 = { ...basicPushSnake, - connector_id: '456', connector_name: 'other connector name', external_id: 'other_external_id', }; - const pushAction456 = { - ...getUserAction(['pushed'], 'push-to-service'), + const pushAction456 = getUserAction(['pushed'], 'push-to-service', { + newValConnectorId: '456', newValue: JSON.stringify(push456), - }; + }); + const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), pushAction123, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), pushAction456, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createChangeConnector456To123HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -675,22 +602,10 @@ describe('useGetCaseUserActions', () => { it('Changing other connectors fields does not count as an update', () => { const userActions = [ ...caseUserActions, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: null } }), - newValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - }, + createUpdateConnectorFields123HighPriorityUserAction(), getUserAction(['pushed'], 'push-to-service'), - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '123', fields: { issueType: '10006', priority: 'High' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - }, - { - ...getUserAction(['connector'], 'update'), - oldValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '6' } }), - newValue: JSON.stringify({ id: '456', fields: { issueTypes: ['10'], severity: '3' } }), - }, + createChangeConnector123HighPriorityTo456UserAction(), + createUpdateConnectorFields456HighPriorityUserAction(), ]; const result = getPushedInfo(userActions, '123'); @@ -709,3 +624,83 @@ describe('useGetCaseUserActions', () => { }); }); }); + +const jira123HighPriorityFields = { + fields: { ...jiraFields.fields, priority: 'High' }, +}; + +const jira123LowPriorityFields = { + fields: { ...jiraFields.fields, priority: 'Low' }, +}; + +const jira456Fields = { + fields: { issueType: '10', parent: null, priority: null }, +}; + +const jira456HighPriorityFields = { + fields: { ...jira456Fields.fields, priority: 'High' }, +}; + +const createUpdateConnectorFields123HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(), + newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + oldValConnectorId: '123', + newValConnectorId: '123', + }); + +const createUpdateConnectorFields456HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + newValue: getJiraConnectorWithoutId(jira456HighPriorityFields), + oldValConnectorId: '456', + newValConnectorId: '456', + }); + +const createChangeConnector123HighPriorityTo456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector123To456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector123LowPriorityTo456UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira123LowPriorityFields), + oldValConnectorId: '123', + newValue: getJiraConnectorWithoutId(jira456Fields), + newValConnectorId: '456', + }); + +const createChangeConnector456To123UserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(), + newValConnectorId: '123', + }); + +const createChangeConnector456To123HighPriorityUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(jira123HighPriorityFields), + newValConnectorId: '123', + }); + +const createChangeConnector456To123PriorityLowUserAction = () => + getUserAction(['connector'], 'update', { + oldValue: getJiraConnectorWithoutId(jira456Fields), + oldValConnectorId: '456', + newValue: getJiraConnectorWithoutId(jira123LowPriorityFields), + newValConnectorId: '123', + }); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx index e481519ba19a3..36d600c3f1c9d 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx @@ -18,7 +18,8 @@ import { } from '../../common'; import { getCaseUserActions, getSubCaseUserActions } from './api'; import * as i18n from './translations'; -import { convertToCamelCase, parseString } from './utils'; +import { convertToCamelCase } from './utils'; +import { parseStringAsConnector, parseStringAsExternalService } from '../common/user_actions'; import { useToasts } from '../common/lib/kibana'; export interface CaseService extends CaseExternalService { @@ -58,8 +59,24 @@ export interface UseGetCaseUserActions extends CaseUserActionsState { ) => Promise; } -const getExternalService = (value: string): CaseExternalService | null => - convertToCamelCase(parseString(`${value}`)); +const unknownExternalServiceConnectorId = 'unknown'; + +const getExternalService = ( + connectorId: string | null, + encodedValue: string | null +): CaseExternalService | null => { + const decodedValue = parseStringAsExternalService(connectorId, encodedValue); + + if (decodedValue == null) { + return null; + } + return { + ...convertToCamelCase(decodedValue), + // if in the rare case that the connector id is null we'll set it to unknown if we need to reference it in the UI + // anywhere. The id would only ever be null if a migration failed or some logic error within the backend occurred + connectorId: connectorId ?? unknownExternalServiceConnectorId, + }; +}; const groupConnectorFields = ( userActions: CaseUserActions[] @@ -69,22 +86,26 @@ const groupConnectorFields = ( return acc; } - const oldValue = parseString(`${mua.oldValue}`); - const newValue = parseString(`${mua.newValue}`); + const oldConnector = parseStringAsConnector(mua.oldValConnectorId, mua.oldValue); + const newConnector = parseStringAsConnector(mua.newValConnectorId, mua.newValue); - if (oldValue == null || newValue == null) { + if (!oldConnector || !newConnector) { return acc; } return { ...acc, - [oldValue.id]: [ - ...(acc[oldValue.id] || []), - ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [oldValue.fields]), + [oldConnector.id]: [ + ...(acc[oldConnector.id] || []), + ...(oldConnector.id === newConnector.id + ? [oldConnector.fields, newConnector.fields] + : [oldConnector.fields]), ], - [newValue.id]: [ - ...(acc[newValue.id] || []), - ...(oldValue.id === newValue.id ? [oldValue.fields, newValue.fields] : [newValue.fields]), + [newConnector.id]: [ + ...(acc[newConnector.id] || []), + ...(oldConnector.id === newConnector.id + ? [oldConnector.fields, newConnector.fields] + : [newConnector.fields]), ], }; }, {} as Record>); @@ -137,9 +158,7 @@ export const getPushedInfo = ( const hasDataToPushForConnector = (connectorId: string): boolean => { const caseUserActionsReversed = [...caseUserActions].reverse(); const lastPushOfConnectorReversedIndex = caseUserActionsReversed.findIndex( - (mua) => - mua.action === 'push-to-service' && - getExternalService(`${mua.newValue}`)?.connectorId === connectorId + (mua) => mua.action === 'push-to-service' && mua.newValConnectorId === connectorId ); if (lastPushOfConnectorReversedIndex === -1) { @@ -190,7 +209,7 @@ export const getPushedInfo = ( return acc; } - const externalService = getExternalService(`${cua.newValue}`); + const externalService = getExternalService(cua.newValConnectorId, cua.newValue); if (externalService === null) { return acc; } diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index de67b1cfbd6fa..b0cc0c72fee78 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -36,14 +36,6 @@ import * as i18n from './translations'; export const getTypedPayload = (a: unknown): T => a as T; -export const parseString = (params: string) => { - try { - return JSON.parse(params); - } catch { - return null; - } -}; - export const convertArrayToCamelCase = (arrayOfSnakes: unknown[]): unknown[] => arrayOfSnakes.reduce((acc: unknown[], value) => { if (isArray(value)) { diff --git a/x-pack/plugins/cases/server/client/attachments/add.ts b/x-pack/plugins/cases/server/client/attachments/add.ts index 507405d58cef1..b84a6bd84c43b 100644 --- a/x-pack/plugins/cases/server/client/attachments/add.ts +++ b/x-pack/plugins/cases/server/client/attachments/add.ts @@ -106,7 +106,7 @@ async function getSubCase({ caseId, subCaseId: newSubCase.id, fields: ['status', 'sub_case'], - newValue: JSON.stringify({ status: newSubCase.attributes.status }), + newValue: { status: newSubCase.attributes.status }, owner: newSubCase.attributes.owner, }), ], @@ -220,7 +220,7 @@ const addGeneratedAlerts = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: JSON.stringify(query), + newValue: query, owner: newComment.attributes.owner, }), ], @@ -408,7 +408,7 @@ export const addComment = async ( subCaseId: updatedCase.subCaseId, commentId: newComment.id, fields: ['comment'], - newValue: JSON.stringify(query), + newValue: query, owner: newComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/attachments/update.ts b/x-pack/plugins/cases/server/client/attachments/update.ts index 9816efd9a8452..b5e9e6c372355 100644 --- a/x-pack/plugins/cases/server/client/attachments/update.ts +++ b/x-pack/plugins/cases/server/client/attachments/update.ts @@ -17,6 +17,7 @@ import { SUB_CASE_SAVED_OBJECT, CaseResponse, CommentPatchRequest, + CommentRequest, } from '../../../common'; import { AttachmentService, CasesService } from '../../services'; import { CasesClientArgs } from '..'; @@ -193,12 +194,12 @@ export async function update( subCaseId: subCaseID, commentId: updatedComment.id, fields: ['comment'], - newValue: JSON.stringify(queryRestAttributes), - oldValue: JSON.stringify( + // casting because typescript is complaining that it's not a Record even though it is + newValue: queryRestAttributes as CommentRequest, + oldValue: // We are interested only in ContextBasicRt attributes // myComment.attribute contains also CommentAttributesBasicRt attributes - pick(Object.keys(queryRestAttributes), myComment.attributes) - ), + pick(Object.keys(queryRestAttributes), myComment.attributes), owner: myComment.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 887990fef8938..488bc523f7796 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -106,7 +106,7 @@ export const create = async ( actionBy: { username, full_name, email }, caseId: newCase.id, fields: ['description', 'status', 'tags', 'title', 'connector', 'settings', OWNER_FIELD], - newValue: JSON.stringify(query), + newValue: query, owner: newCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 80a687a0e72f8..4333535f17a24 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -168,7 +168,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P 'settings', OWNER_FIELD, 'comment', - ...(ENABLE_CASE_CONNECTOR ? ['sub_case'] : []), + ...(ENABLE_CASE_CONNECTOR ? ['sub_case' as const] : []), ], owner: caseInfo.attributes.owner, }) diff --git a/x-pack/plugins/cases/server/client/cases/mock.ts b/x-pack/plugins/cases/server/client/cases/mock.ts index 313d6cd12a6db..22520cea11014 100644 --- a/x-pack/plugins/cases/server/client/cases/mock.ts +++ b/x-pack/plugins/cases/server/client/cases/mock.ts @@ -231,8 +231,10 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"id":"456","name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', + '{"title":"Case SIR","tags":["sir"],"description":"testing sir","connector":{"name":"ServiceNow SN","type":".servicenow-sir","fields":{"category":"Denial of Service","destIp":true,"malwareHash":true,"malwareUrl":true,"priority":"2","sourceIp":true,"subcategory":"45"}},"settings":{"syncAlerts":true}}', + new_val_connector_id: '456', old_value: null, + old_val_connector_id: null, action_id: 'fd830c60-6646-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -248,7 +250,9 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + '{"pushed_at":"2021-02-03T17:41:26.108Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '456', + old_val_connector_id: null, old_value: null, action_id: '0a801750-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -265,6 +269,8 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: '{"type":"alert","alertId":"alert-id-1","index":".siem-signals-default-000008"}', + new_val_connector_id: null, + old_val_connector_id: null, old_value: null, action_id: '7373eb60-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', @@ -282,6 +288,8 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"type":"alert","alertId":"alert-id-2","index":".siem-signals-default-000008"}', old_value: null, + new_val_connector_id: null, + old_val_connector_id: null, action_id: '7abc6410-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-alert-2', @@ -297,8 +305,10 @@ export const userActions: CaseUserActionsResponse = [ username: 'elastic', }, new_value: - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"456","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '456', old_value: null, + old_val_connector_id: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: null, @@ -315,6 +325,8 @@ export const userActions: CaseUserActionsResponse = [ }, new_value: '{"comment":"a comment!","type":"user"}', old_value: null, + new_val_connector_id: null, + old_val_connector_id: null, action_id: '0818e5e0-6648-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', comment_id: 'comment-user-1', diff --git a/x-pack/plugins/cases/server/client/cases/push.ts b/x-pack/plugins/cases/server/client/cases/push.ts index 3048cf01bb3ba..1b090a653546d 100644 --- a/x-pack/plugins/cases/server/client/cases/push.ts +++ b/x-pack/plugins/cases/server/client/cases/push.ts @@ -241,7 +241,7 @@ export const push = async ( actionBy: { username, full_name, email }, caseId, fields: ['pushed'], - newValue: JSON.stringify(externalService), + newValue: externalService, owner: myCase.attributes.owner, }), ], diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index d7c45d3e1e9ae..315e9966d347b 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -799,8 +799,10 @@ describe('utils', () => { username: 'elastic', }, new_value: - // The connector id is 123 - '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_id":"123","connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + // The connector id is 123 + '{"pushed_at":"2021-02-03T17:45:29.400Z","pushed_by":{"username":"elastic","full_name":"Elastic","email":"elastic@elastic.co"},"connector_name":"ServiceNow SN","external_id":"external-id","external_title":"SIR0010037","external_url":"https://dev92273.service-now.com/nav_to.do?uri=sn_si_incident.do?sys_id=external-id"}', + new_val_connector_id: '123', + old_val_connector_id: null, old_value: null, action_id: '9b91d8f0-6647-11eb-a291-51bf6b175a53', case_id: 'fcdedd20-6646-11eb-a291-51bf6b175a53', diff --git a/x-pack/plugins/cases/server/client/cases/utils.ts b/x-pack/plugins/cases/server/client/cases/utils.ts index 359ad4b41ead0..f5cf2fe4b3f51 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.ts @@ -20,6 +20,8 @@ import { CommentRequestUserType, CommentRequestAlertType, CommentRequestActionsType, + CaseUserActionResponse, + isPush, } from '../../../common'; import { ActionsClient } from '../../../../actions/server'; import { CasesClientGetAlertsResponse } from '../../client/alerts/types'; @@ -55,22 +57,36 @@ export const getLatestPushInfo = ( userActions: CaseUserActionsResponse ): { index: number; pushedInfo: CaseFullExternalService } | null => { for (const [index, action] of [...userActions].reverse().entries()) { - if (action.action === 'push-to-service' && action.new_value) + if ( + isPush(action.action, action.action_field) && + isValidNewValue(action) && + connectorId === action.new_val_connector_id + ) { try { const pushedInfo = JSON.parse(action.new_value); - if (pushedInfo.connector_id === connectorId) { - // We returned the index of the element in the userActions array. - // As we traverse the userActions in reverse we need to calculate the index of a normal traversal - return { index: userActions.length - index - 1, pushedInfo }; - } + // We returned the index of the element in the userActions array. + // As we traverse the userActions in reverse we need to calculate the index of a normal traversal + return { + index: userActions.length - index - 1, + pushedInfo: { ...pushedInfo, connector_id: connectorId }, + }; } catch (e) { - // Silence JSON parse errors + // ignore parse failures and check the next user action } + } } return null; }; +type NonNullNewValueAction = Omit & { + new_value: string; + new_val_connector_id: string; +}; + +const isValidNewValue = (userAction: CaseUserActionResponse): userAction is NonNullNewValueAction => + userAction.new_val_connector_id != null && userAction.new_value != null; + const getCommentContent = (comment: CommentResponse): string => { if (comment.type === CommentType.user) { return comment.comment; diff --git a/x-pack/plugins/cases/server/client/user_actions/get.test.ts b/x-pack/plugins/cases/server/client/user_actions/get.test.ts new file mode 100644 index 0000000000000..302e069cde4d1 --- /dev/null +++ b/x-pack/plugins/cases/server/client/user_actions/get.test.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CaseUserActionResponse, SUB_CASE_SAVED_OBJECT } from '../../../common'; +import { SUB_CASE_REF_NAME } from '../../common'; +import { extractAttributesWithoutSubCases } from './get'; + +describe('get', () => { + describe('extractAttributesWithoutSubCases', () => { + it('returns an empty array when given an empty array', () => { + expect( + extractAttributesWithoutSubCases({ ...getFindResponseFields(), saved_objects: [] }) + ).toEqual([]); + }); + + it('filters out saved objects with a sub case reference', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], + id: 'b', + score: 0, + attributes: {} as CaseUserActionResponse, + }, + ], + }) + ).toEqual([]); + }); + + it('filters out saved objects with a sub case reference with other references', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: {} as CaseUserActionResponse, + }, + ], + }) + ).toEqual([]); + }); + + it('keeps saved objects that do not have a sub case reference', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: { field: '1' } as unknown as CaseUserActionResponse, + }, + ], + }) + ).toEqual([{ field: '1' }]); + }); + + it('filters multiple saved objects correctly', () => { + expect( + extractAttributesWithoutSubCases({ + ...getFindResponseFields(), + saved_objects: [ + { + type: 'a', + references: [ + { name: SUB_CASE_REF_NAME, type: 'awesome', id: '1' }, + { name: 'a', type: 'b', id: '5' }, + ], + id: 'b', + score: 0, + attributes: { field: '2' } as unknown as CaseUserActionResponse, + }, + { + type: 'a', + references: [{ name: SUB_CASE_REF_NAME, type: SUB_CASE_SAVED_OBJECT, id: '1' }], + id: 'b', + score: 0, + attributes: { field: '1' } as unknown as CaseUserActionResponse, + }, + ], + }) + ).toEqual([{ field: '2' }]); + }); + }); +}); + +const getFindResponseFields = () => ({ page: 1, per_page: 1, total: 0 }); diff --git a/x-pack/plugins/cases/server/client/user_actions/get.ts b/x-pack/plugins/cases/server/client/user_actions/get.ts index 2a6608014c800..660cf1b6a336e 100644 --- a/x-pack/plugins/cases/server/client/user_actions/get.ts +++ b/x-pack/plugins/cases/server/client/user_actions/get.ts @@ -5,14 +5,14 @@ * 2.0. */ +import { SavedObjectReference, SavedObjectsFindResponse } from 'kibana/server'; import { - CASE_COMMENT_SAVED_OBJECT, - CASE_SAVED_OBJECT, CaseUserActionsResponse, CaseUserActionsResponseRt, SUB_CASE_SAVED_OBJECT, + CaseUserActionResponse, } from '../../../common'; -import { createCaseError, checkEnabledCaseConnectorOrThrow } from '../../common'; +import { createCaseError, checkEnabledCaseConnectorOrThrow, SUB_CASE_REF_NAME } from '../../common'; import { CasesClientArgs } from '..'; import { Operations } from '../../authorization'; import { UserActionGet } from './client'; @@ -40,23 +40,12 @@ export const get = async ( operation: Operations.getUserActions, }); - return CaseUserActionsResponseRt.encode( - userActions.saved_objects.reduce((acc, ua) => { - if (subCaseId == null && ua.references.some((uar) => uar.type === SUB_CASE_SAVED_OBJECT)) { - return acc; - } - return [ - ...acc, - { - ...ua.attributes, - action_id: ua.id, - case_id: ua.references.find((r) => r.type === CASE_SAVED_OBJECT)?.id ?? '', - comment_id: ua.references.find((r) => r.type === CASE_COMMENT_SAVED_OBJECT)?.id ?? null, - sub_case_id: ua.references.find((r) => r.type === SUB_CASE_SAVED_OBJECT)?.id ?? '', - }, - ]; - }, []) - ); + const resultsToEncode = + subCaseId == null + ? extractAttributesWithoutSubCases(userActions) + : extractAttributes(userActions); + + return CaseUserActionsResponseRt.encode(resultsToEncode); } catch (error) { throw createCaseError({ message: `Failed to retrieve user actions case id: ${caseId} sub case id: ${subCaseId}: ${error}`, @@ -65,3 +54,21 @@ export const get = async ( }); } }; + +export function extractAttributesWithoutSubCases( + userActions: SavedObjectsFindResponse +): CaseUserActionsResponse { + // exclude user actions relating to sub cases from the results + const hasSubCaseReference = (references: SavedObjectReference[]) => + references.find((ref) => ref.type === SUB_CASE_SAVED_OBJECT && ref.name === SUB_CASE_REF_NAME); + + return userActions.saved_objects + .filter((so) => !hasSubCaseReference(so.references)) + .map((so) => so.attributes); +} + +function extractAttributes( + userActions: SavedObjectsFindResponse +): CaseUserActionsResponse { + return userActions.saved_objects.map((so) => so.attributes); +} diff --git a/x-pack/plugins/cases/server/common/constants.ts b/x-pack/plugins/cases/server/common/constants.ts index 1f6af310d6ece..eba0a64a5c0be 100644 --- a/x-pack/plugins/cases/server/common/constants.ts +++ b/x-pack/plugins/cases/server/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../common'; + /** * The name of the saved object reference indicating the action connector ID. This is stored in the Saved Object reference * field's name property. @@ -15,3 +17,30 @@ export const CONNECTOR_ID_REFERENCE_NAME = 'connectorId'; * The name of the saved object reference indicating the action connector ID that was used to push a case. */ export const PUSH_CONNECTOR_ID_REFERENCE_NAME = 'pushConnectorId'; + +/** + * The name of the saved object reference indicating the action connector ID that was used for + * adding a connector, or updating the existing connector for a user action's old_value field. + */ +export const USER_ACTION_OLD_ID_REF_NAME = 'oldConnectorId'; + +/** + * The name of the saved object reference indicating the action connector ID that was used for pushing a case, + * for a user action's old_value field. + */ +export const USER_ACTION_OLD_PUSH_ID_REF_NAME = 'oldPushConnectorId'; + +/** + * The name of the saved object reference indicating the caseId reference + */ +export const CASE_REF_NAME = `associated-${CASE_SAVED_OBJECT}`; + +/** + * The name of the saved object reference indicating the commentId reference + */ +export const COMMENT_REF_NAME = `associated-${CASE_COMMENT_SAVED_OBJECT}`; + +/** + * The name of the saved object reference indicating the subCaseId reference + */ +export const SUB_CASE_REF_NAME = `associated-${SUB_CASE_SAVED_OBJECT}`; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts index bca12a86a544e..9020f65ae352c 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.test.ts @@ -30,322 +30,324 @@ const create_7_14_0_case = ({ }, }); -describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector.id is none', () => { - const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); +describe('case migrations', () => { + describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector.id is none', () => { + const caseSavedObject = create_7_14_0_case({ connector: getNoneCaseConnector() }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the connector is undefined', () => { - const caseSavedObject = create_7_14_0_case(); - - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + it('does not create a reference when the connector is undefined', () => { + const caseSavedObject = create_7_14_0_case(); - it('sets the connector to the default none connector if the connector.id is undefined', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - fields: null, - name: ConnectorTypes.jira, - type: ConnectorTypes.jira, - } as ESCaseConnectorWithId, - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - it('does not create a reference when the external_service is null', () => { - const caseSavedObject = create_7_14_0_case({ externalService: null }); + it('sets the connector to the default none connector if the connector.id is undefined', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + fields: null, + name: ConnectorTypes.jira, + type: ConnectorTypes.jira, + } as ESCaseConnectorWithId, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the external_service is null', () => { + const caseSavedObject = create_7_14_0_case({ externalService: null }); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - it('does not create a reference when the external_service is undefined and sets external_service to null', () => { - const caseSavedObject = create_7_14_0_case(); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + it('does not create a reference when the external_service is undefined and sets external_service to null', () => { + const caseSavedObject = create_7_14_0_case(); - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toBeNull(); - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - it('does not create a reference when the external_service.connector_id is none', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: createExternalService({ connector_id: noneConnectorId }), + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toBeNull(); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - }); - - it('preserves the existing references when migrating', () => { - const caseSavedObject = { - ...create_7_14_0_case(), - references: [{ id: '1', name: 'awesome', type: 'hello' }], - }; + it('does not create a reference when the external_service.connector_id is none', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: createExternalService({ connector_id: noneConnectorId }), + }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` Object { - "id": "1", - "name": "awesome", - "type": "hello", - }, - ] - `); - }); + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); - it('creates a connector reference and removes the connector.id field', () => { - const caseSavedObject = create_7_14_0_case({ - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }, + it('preserves the existing references when migrating', () => { + const caseSavedObject = { + ...create_7_14_0_case(), + references: [{ id: '1', name: 'awesome', type: 'hello' }], + }; + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "awesome", + "type": "hello", + }, + ] + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", + it('creates a connector reference and removes the connector.id field', () => { + const caseSavedObject = create_7_14_0_case({ + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, }, - ] - `); - }); + }); - it('creates a push connector reference and removes the connector_id field', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - }); + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(1); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); - it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: null, - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', + it('creates a push connector reference and removes the connector_id field', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - }, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(1); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + it('does not create a reference and preserves the existing external_service fields when connector_id is null', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: null, + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - } - `); - }); + }); - it('migrates both connector and external_service when provided', () => { - const caseSavedObject = create_7_14_0_case({ - externalService: { - connector_id: '100', - connector_name: '.jira', - external_id: '100', - external_title: 'awesome', - external_url: 'http://www.google.com', - pushed_at: '2019-11-25T21:54:48.952Z', - pushed_by: { - full_name: 'elastic', - email: 'testemail@elastic.co', - username: 'elastic', - }, - }, - connector: { - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, - }, + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); }); - const migratedConnector = caseConnectorIdMigration( - caseSavedObject - ) as SavedObjectSanitizedDoc; - - expect(migratedConnector.references.length).toBe(2); - expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); - expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` - Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + it('migrates both connector and external_service when provided', () => { + const caseSavedObject = create_7_14_0_case({ + externalService: { + connector_id: '100', + connector_name: '.jira', + external_id: '100', + external_title: 'awesome', + external_url: 'http://www.google.com', + pushed_at: '2019-11-25T21:54:48.952Z', + pushed_by: { + full_name: 'elastic', + email: 'testemail@elastic.co', + username: 'elastic', + }, }, - } - `); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "connector", - "type": ".jira", - } - `); - expect(migratedConnector.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "123", - "name": "connectorId", - "type": "action", + connector: { + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, }, + }); + + const migratedConnector = caseConnectorIdMigration( + caseSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector.references.length).toBe(2); + expect(migratedConnector.attributes.external_service).not.toHaveProperty('connector_id'); + expect(migratedConnector.attributes.external_service).toMatchInlineSnapshot(` Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "connector", + "type": ".jira", + } + `); + expect(migratedConnector.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "123", + "name": "connectorId", + "type": "action", + }, + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts index bffd4171270ef..80f02fa3bf6a6 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/cases.ts @@ -14,7 +14,11 @@ import { } from '../../../../../../src/core/server'; import { ESConnectorFields } from '../../services'; import { ConnectorTypes, CaseType } from '../../../common'; -import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; +import { + transformConnectorIdToReference, + transformPushConnectorIdToReference, +} from '../../services/user_actions/transform'; +import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../../common'; interface UnsanitizedCaseConnector { connector_id: string; @@ -50,11 +54,13 @@ export const caseConnectorIdMigration = ( // removing the id field since it will be stored in the references instead const { connector, external_service, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = - transformConnectorIdToReference(connector); + const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + connector + ); const { transformedPushConnector, references: pushConnectorReferences } = - transformPushConnectorIdToReference(external_service); + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, external_service); const { references = [] } = doc; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts index 4467b499817a5..9ae0285598dbf 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.test.ts @@ -40,87 +40,89 @@ const create_7_14_0_configSchema = (connector?: ESCaseConnectorWithId) => ({ }, }); -describe('7.15.0 connector ID migration', () => { - it('does not create a reference when the connector ID is none', () => { - const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); +describe('configuration migrations', () => { + describe('7.15.0 connector ID migration', () => { + it('does not create a reference when the connector ID is none', () => { + const configureSavedObject = create_7_14_0_configSchema(getNoneCaseConnector()); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { - const configureSavedObject = create_7_14_0_configSchema(); + it('does not create a reference when the connector is undefined and defaults it to the none connector', () => { + const configureSavedObject = create_7_14_0_configSchema(); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - expect(migratedConnector.references.length).toBe(0); - expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` - Object { - "fields": null, - "name": "none", - "type": ".none", - } - `); - }); - - it('creates a reference using the connector id', () => { - const configureSavedObject = create_7_14_0_configSchema({ - id: '123', - fields: null, - name: 'connector', - type: ConnectorTypes.jira, + expect(migratedConnector.references.length).toBe(0); + expect(migratedConnector.attributes.connector).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + it('creates a reference using the connector id', () => { + const configureSavedObject = create_7_14_0_configSchema({ + id: '123', + fields: null, + name: 'connector', + type: ConnectorTypes.jira, + }); - expect(migratedConnector.references).toEqual([ - { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, - ]); - expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); - }); + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; - it('returns the other attributes and default connector when the connector is undefined', () => { - const configureSavedObject = create_7_14_0_configSchema(); + expect(migratedConnector.references).toEqual([ + { id: '123', type: ACTION_SAVED_OBJECT_TYPE, name: CONNECTOR_ID_REFERENCE_NAME }, + ]); + expect(migratedConnector.attributes.connector).not.toHaveProperty('id'); + }); - const migratedConnector = configureConnectorIdMigration( - configureSavedObject - ) as SavedObjectSanitizedDoc; + it('returns the other attributes and default connector when the connector is undefined', () => { + const configureSavedObject = create_7_14_0_configSchema(); - expect(migratedConnector).toMatchInlineSnapshot(` - Object { - "attributes": Object { - "closure_type": "close-by-pushing", - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - "created_at": "2020-04-09T09:43:51.778Z", - "created_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - "owner": "securitySolution", - "updated_at": "2020-04-09T09:43:51.778Z", - "updated_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", + const migratedConnector = configureConnectorIdMigration( + configureSavedObject + ) as SavedObjectSanitizedDoc; + + expect(migratedConnector).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "closure_type": "close-by-pushing", + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + "created_at": "2020-04-09T09:43:51.778Z", + "created_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + "owner": "securitySolution", + "updated_at": "2020-04-09T09:43:51.778Z", + "updated_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, }, - }, - "id": "1", - "references": Array [], - "type": "cases-configure", - } - `); + "id": "1", + "references": Array [], + "type": "cases-configure", + } + `); + }); }); }); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts index 527d40fca2e35..f9937253e0d2f 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/configuration.ts @@ -13,7 +13,8 @@ import { } from '../../../../../../src/core/server'; import { ConnectorTypes } from '../../../common'; import { addOwnerToSO, SanitizedCaseOwner } from '.'; -import { transformConnectorIdToReference } from './utils'; +import { transformConnectorIdToReference } from '../../services/user_actions/transform'; +import { CONNECTOR_ID_REFERENCE_NAME } from '../../common'; interface UnsanitizedConfigureConnector { connector_id: string; @@ -34,8 +35,10 @@ export const configureConnectorIdMigration = ( ): SavedObjectSanitizedDoc => { // removing the id field since it will be stored in the references instead const { connector, ...restAttributes } = doc.attributes; - const { transformedConnector, references: connectorReferences } = - transformConnectorIdToReference(connector); + const { transformedConnector, references: connectorReferences } = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + connector + ); const { references = [] } = doc; return { diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts index a445131073d19..a4f50fbfcde5b 100644 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/index.ts @@ -5,24 +5,17 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ - import { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, } from '../../../../../../src/core/server'; -import { ConnectorTypes, SECURITY_SOLUTION_OWNER } from '../../../common'; +import { SECURITY_SOLUTION_OWNER } from '../../../common'; export { caseMigrations } from './cases'; export { configureMigrations } from './configuration'; +export { userActionsMigrations } from './user_actions'; export { createCommentsMigrations, CreateCommentsMigrationsDeps } from './comments'; -interface UserActions { - action_field: string[]; - new_value: string; - old_value: string; -} - export interface SanitizedCaseOwner { owner: string; } @@ -38,52 +31,6 @@ export const addOwnerToSO = >( references: doc.references || [], }); -export const userActionsMigrations = { - '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { - const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; - - if ( - action_field == null || - !Array.isArray(action_field) || - action_field[0] !== 'connector_id' - ) { - return { ...doc, references: doc.references || [] }; - } - - return { - ...doc, - attributes: { - ...restAttributes, - action_field: ['connector'], - new_value: - new_value != null - ? JSON.stringify({ - id: new_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : new_value, - old_value: - old_value != null - ? JSON.stringify({ - id: old_value, - name: 'none', - type: ConnectorTypes.none, - fields: null, - }) - : old_value, - }, - references: doc.references || [], - }; - }, - '7.14.0': ( - doc: SavedObjectUnsanitizedDoc> - ): SavedObjectSanitizedDoc => { - return addOwnerToSO(doc); - }, -}; - export const connectorMappingsMigrations = { '7.14.0': ( doc: SavedObjectUnsanitizedDoc> diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts new file mode 100644 index 0000000000000..e71c8db0db694 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.test.ts @@ -0,0 +1,562 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { SavedObjectMigrationContext, SavedObjectSanitizedDoc } from 'kibana/server'; +import { migrationMocks } from 'src/core/server/mocks'; +import { CaseUserActionAttributes, CASE_USER_ACTION_SAVED_OBJECT } from '../../../common'; +import { + createConnectorObject, + createExternalService, + createJiraConnector, +} from '../../services/test_utils'; +import { userActionsConnectorIdMigration } from './user_actions'; + +const create_7_14_0_userAction = ( + params: { + action?: string; + action_field?: string[]; + new_value?: string | null | object; + old_value?: string | null | object; + } = {} +) => { + const { new_value, old_value, ...restParams } = params; + + return { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '1', + attributes: { + ...restParams, + new_value: new_value && typeof new_value === 'object' ? JSON.stringify(new_value) : new_value, + old_value: old_value && typeof old_value === 'object' ? JSON.stringify(old_value) : old_value, + }, + }; +}; + +describe('user action migrations', () => { + describe('7.15.0 connector ID migration', () => { + describe('userActionsConnectorIdMigration', () => { + let context: jest.Mocked; + + beforeEach(() => { + context = migrationMocks.createContext(); + }); + + describe('push user action', () => { + it('extracts the external_service connector_id to references for a new pushed user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedExternalService).not.toHaveProperty('connector_id'); + expect(parsedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extract the external_service connector_id to references for new and old pushed user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: createExternalService({ connector_id: '5' }), + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(migratedUserAction.references).toEqual([ + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the external_service connector_id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: createExternalService(), + old_value: createExternalService({ connector_id: '5' }), + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewExternalService = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid push user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['invalid field'], + new_value: 'hello', + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction.attributes.old_value).toBeNull(); + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": null, + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when it new value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: '{a', + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction.attributes.old_value).toBeNull(); + expect(migratedUserAction.attributes.new_value).toEqual('{a'); + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_field": Array [ + "pushed", + ], + "new_value": "{a", + "old_value": null, + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error new value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'push-to-service', + action_field: ['pushed'], + new_value: '{a', + old_value: null, + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + + describe('update connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedConnector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: { ...createJiraConnector(), id: '5' }, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); + + expect(parsedNewConnector).not.toHaveProperty('id'); + expect(parsedOldConnector).not.toHaveProperty('id'); + + expect(migratedUserAction.references).toEqual([ + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the connector.id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: { ...createJiraConnector(), id: '5' }, + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewConnectorId).not.toHaveProperty('id'); + expect(parsedOldConnectorId).not.toHaveProperty('id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['invalid action'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when old_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: '{}', + old_value: '{b', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_field": Array [ + "connector", + ], + "new_value": "{}", + "old_value": "{b", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error message when old_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'update', + action_field: ['connector'], + new_value: createJiraConnector(), + old_value: '{b', + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + + describe('create connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: null, + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedConnector = JSON.parse(migratedUserAction.attributes.new_value!); + expect(parsedConnector.connector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + + expect(migratedUserAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(migratedUserAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: createConnectorObject({ id: '5' }), + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnector = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(migratedUserAction.attributes.new_value!); + + expect(parsedNewConnector.connector).not.toHaveProperty('id'); + expect(parsedOldConnector.connector).not.toHaveProperty('id'); + + expect(migratedUserAction.references).toEqual([ + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('preserves the existing references after extracting the connector.id field', () => { + const userAction = { + ...create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: createConnectorObject(), + old_value: createConnectorObject({ id: '5' }), + }), + references: [{ id: '500', name: 'someReference', type: 'ref' }], + }; + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + const parsedNewConnectorId = JSON.parse(migratedUserAction.attributes.new_value!); + const parsedOldConnectorId = JSON.parse(migratedUserAction.attributes.old_value!); + + expect(parsedNewConnectorId.connector).not.toHaveProperty('id'); + expect(parsedOldConnectorId.connector).not.toHaveProperty('id'); + expect(migratedUserAction.references).toEqual([ + { id: '500', name: 'someReference', type: 'ref' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['invalid action'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('leaves the object unmodified when new_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: 'new json value', + old_value: 'old value', + }); + + const migratedUserAction = userActionsConnectorIdMigration( + userAction, + context + ) as SavedObjectSanitizedDoc; + + expect(migratedUserAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_field": Array [ + "connector", + ], + "new_value": "new json value", + "old_value": "old value", + }, + "id": "1", + "references": Array [], + "type": "cases-user-actions", + } + `); + }); + + it('logs an error message when new_value is invalid json', () => { + const userAction = create_7_14_0_userAction({ + action: 'create', + action_field: ['connector'], + new_value: 'new json value', + old_value: 'old value', + }); + + userActionsConnectorIdMigration(userAction, context); + + expect(context.log.error).toHaveBeenCalled(); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts new file mode 100644 index 0000000000000..ed6b57ef647f9 --- /dev/null +++ b/x-pack/plugins/cases/server/saved_object_types/migrations/user_actions.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import { addOwnerToSO, SanitizedCaseOwner } from '.'; +import { + SavedObjectUnsanitizedDoc, + SavedObjectSanitizedDoc, + SavedObjectMigrationContext, + LogMeta, +} from '../../../../../../src/core/server'; +import { ConnectorTypes, isCreateConnector, isPush, isUpdateConnector } from '../../../common'; + +import { extractConnectorIdFromJson } from '../../services/user_actions/transform'; +import { UserActionFieldType } from '../../services/user_actions/types'; + +interface UserActions { + action_field: string[]; + new_value: string; + old_value: string; +} + +interface UserActionUnmigratedConnectorDocument { + action?: string; + action_field?: string[]; + new_value?: string | null; + old_value?: string | null; +} + +interface UserActionLogMeta extends LogMeta { + migrations: { userAction: { id: string } }; +} + +export function userActionsConnectorIdMigration( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext +): SavedObjectSanitizedDoc { + const originalDocWithReferences = { ...doc, references: doc.references ?? [] }; + + if (!isConnectorUserAction(doc.attributes.action, doc.attributes.action_field)) { + return originalDocWithReferences; + } + + try { + return formatDocumentWithConnectorReferences(doc); + } catch (error) { + logError(doc.id, context, error); + + return originalDocWithReferences; + } +} + +function isConnectorUserAction(action?: string, actionFields?: string[]): boolean { + return ( + isCreateConnector(action, actionFields) || + isUpdateConnector(action, actionFields) || + isPush(action, actionFields) + ); +} + +function formatDocumentWithConnectorReferences( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc { + const { new_value, old_value, action, action_field, ...restAttributes } = doc.attributes; + const { references = [] } = doc; + + const { transformedActionDetails: transformedNewValue, references: newValueConnectorRefs } = + extractConnectorIdFromJson({ + action, + actionFields: action_field, + actionDetails: new_value, + fieldType: UserActionFieldType.New, + }); + + const { transformedActionDetails: transformedOldValue, references: oldValueConnectorRefs } = + extractConnectorIdFromJson({ + action, + actionFields: action_field, + actionDetails: old_value, + fieldType: UserActionFieldType.Old, + }); + + return { + ...doc, + attributes: { + ...restAttributes, + action, + action_field, + new_value: transformedNewValue, + old_value: transformedOldValue, + }, + references: [...references, ...newValueConnectorRefs, ...oldValueConnectorRefs], + }; +} + +function logError(id: string, context: SavedObjectMigrationContext, error: Error) { + context.log.error( + `Failed to migrate user action connector doc id: ${id} version: ${context.migrationVersion} error: ${error.message}`, + { + migrations: { + userAction: { + id, + }, + }, + } + ); +} + +export const userActionsMigrations = { + '7.10.0': (doc: SavedObjectUnsanitizedDoc): SavedObjectSanitizedDoc => { + const { action_field, new_value, old_value, ...restAttributes } = doc.attributes; + + if ( + action_field == null || + !Array.isArray(action_field) || + action_field[0] !== 'connector_id' + ) { + return { ...doc, references: doc.references || [] }; + } + + return { + ...doc, + attributes: { + ...restAttributes, + action_field: ['connector'], + new_value: + new_value != null + ? JSON.stringify({ + id: new_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : new_value, + old_value: + old_value != null + ? JSON.stringify({ + id: old_value, + name: 'none', + type: ConnectorTypes.none, + fields: null, + }) + : old_value, + }, + references: doc.references || [], + }; + }, + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc> + ): SavedObjectSanitizedDoc => { + return addOwnerToSO(doc); + }, + '7.16.0': userActionsConnectorIdMigration, +}; diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts deleted file mode 100644 index f591bef6b3236..0000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { noneConnectorId } from '../../../common'; -import { createExternalService, createJiraConnector } from '../../services/test_utils'; -import { transformConnectorIdToReference, transformPushConnectorIdToReference } from './utils'; - -describe('migration utils', () => { - describe('transformConnectorIdToReference', () => { - it('returns the default none connector when the connector is undefined', () => { - expect(transformConnectorIdToReference().transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is undefined', () => { - expect(transformConnectorIdToReference({ id: undefined }).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none', () => { - expect(transformConnectorIdToReference({ id: noneConnectorId }).transformedConnector) - .toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns the default none connector when the id is none and other fields are defined', () => { - expect( - transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) - .transformedConnector - ).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": null, - "name": "none", - "type": ".none", - }, - } - `); - }); - - it('returns an empty array of references when the connector is undefined', () => { - expect(transformConnectorIdToReference().references.length).toBe(0); - }); - - it('returns an empty array of references when the id is undefined', () => { - expect(transformConnectorIdToReference({ id: undefined }).references.length).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector', () => { - expect(transformConnectorIdToReference({ id: noneConnectorId }).references.length).toBe(0); - }); - - it('returns an empty array of references when the id is the none connector and other fields are defined', () => { - expect( - transformConnectorIdToReference({ ...createJiraConnector(), id: noneConnectorId }) - .references.length - ).toBe(0); - }); - - it('returns a jira connector', () => { - const transformedFields = transformConnectorIdToReference(createJiraConnector()); - expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` - Object { - "connector": Object { - "fields": Object { - "issueType": "bug", - "parent": "2", - "priority": "high", - }, - "name": ".jira", - "type": ".jira", - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "1", - "name": "connectorId", - "type": "action", - }, - ] - `); - }); - }); - - describe('transformPushConnectorIdToReference', () => { - it('sets external_service to null when it is undefined', () => { - expect(transformPushConnectorIdToReference().transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('sets external_service to null when it is null', () => { - expect(transformPushConnectorIdToReference(null).transformedPushConnector) - .toMatchInlineSnapshot(` - Object { - "external_service": null, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is null', () => { - expect(transformPushConnectorIdToReference({ connector_id: null }).transformedPushConnector) - .toMatchInlineSnapshot(` - Object { - "external_service": Object {}, - } - `); - }); - - it('returns an object when external_service is defined but connector_id is none', () => { - const otherFields = { otherField: 'hi' }; - - expect( - transformPushConnectorIdToReference({ ...otherFields, connector_id: noneConnectorId }) - .transformedPushConnector - ).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "otherField": "hi", - }, - } - `); - }); - - it('returns an empty array of references when the external_service is undefined', () => { - expect(transformPushConnectorIdToReference().references.length).toBe(0); - }); - - it('returns an empty array of references when the external_service is null', () => { - expect(transformPushConnectorIdToReference(null).references.length).toBe(0); - }); - - it('returns an empty array of references when the connector_id is undefined', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is null', () => { - expect( - transformPushConnectorIdToReference({ connector_id: undefined }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector', () => { - expect( - transformPushConnectorIdToReference({ connector_id: noneConnectorId }).references.length - ).toBe(0); - }); - - it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { - expect( - transformPushConnectorIdToReference({ - ...createExternalService(), - connector_id: noneConnectorId, - }).references.length - ).toBe(0); - }); - - it('returns the external_service connector', () => { - const transformedFields = transformPushConnectorIdToReference(createExternalService()); - expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` - Object { - "external_service": Object { - "connector_name": ".jira", - "external_id": "100", - "external_title": "awesome", - "external_url": "http://www.google.com", - "pushed_at": "2019-11-25T21:54:48.952Z", - "pushed_by": Object { - "email": "testemail@elastic.co", - "full_name": "elastic", - "username": "elastic", - }, - }, - } - `); - expect(transformedFields.references).toMatchInlineSnapshot(` - Array [ - Object { - "id": "100", - "name": "pushConnectorId", - "type": "action", - }, - ] - `); - }); - }); -}); diff --git a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts b/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts deleted file mode 100644 index 0100a04cde679..0000000000000 --- a/x-pack/plugins/cases/server/saved_object_types/migrations/utils.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* eslint-disable @typescript-eslint/naming-convention */ - -import { noneConnectorId } from '../../../common'; -import { SavedObjectReference } from '../../../../../../src/core/server'; -import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; -import { - getNoneCaseConnector, - CONNECTOR_ID_REFERENCE_NAME, - PUSH_CONNECTOR_ID_REFERENCE_NAME, -} from '../../common'; - -export const transformConnectorIdToReference = (connector?: { - id?: string; -}): { transformedConnector: Record; references: SavedObjectReference[] } => { - const { id: connectorId, ...restConnector } = connector ?? {}; - - const references = createConnectorReference( - connectorId, - ACTION_SAVED_OBJECT_TYPE, - CONNECTOR_ID_REFERENCE_NAME - ); - - const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); - const connectorFieldsToReturn = - connector && references.length > 0 ? restConnector : restNoneConnector; - - return { - transformedConnector: { - connector: connectorFieldsToReturn, - }, - references, - }; -}; - -const createConnectorReference = ( - id: string | null | undefined, - type: string, - name: string -): SavedObjectReference[] => { - return id && id !== noneConnectorId - ? [ - { - id, - type, - name, - }, - ] - : []; -}; - -export const transformPushConnectorIdToReference = ( - external_service?: { connector_id?: string | null } | null -): { transformedPushConnector: Record; references: SavedObjectReference[] } => { - const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; - - const references = createConnectorReference( - pushConnectorId, - ACTION_SAVED_OBJECT_TYPE, - PUSH_CONNECTOR_ID_REFERENCE_NAME - ); - - return { - transformedPushConnector: { external_service: external_service ? restExternalService : null }, - references, - }; -}; diff --git a/x-pack/plugins/cases/server/services/cases/index.test.ts b/x-pack/plugins/cases/server/services/cases/index.test.ts index 18f4ff867cfa9..8c71abe5bff4f 100644 --- a/x-pack/plugins/cases/server/services/cases/index.test.ts +++ b/x-pack/plugins/cases/server/services/cases/index.test.ts @@ -40,6 +40,7 @@ import { createSavedObjectReferences, createCaseSavedObjectResponse, basicCaseFields, + createSOFindResponse, } from '../test_utils'; import { ESCaseAttributes } from './types'; @@ -87,13 +88,6 @@ const createFindSO = ( score: 0, }); -const createSOFindResponse = (savedObjects: Array>) => ({ - saved_objects: savedObjects, - total: savedObjects.length, - per_page: savedObjects.length, - page: 1, -}); - const createCaseUpdateParams = ( connector?: CaseConnector, externalService?: CaseFullExternalService diff --git a/x-pack/plugins/cases/server/services/test_utils.ts b/x-pack/plugins/cases/server/services/test_utils.ts index b712ea07f9c71..07743eda61212 100644 --- a/x-pack/plugins/cases/server/services/test_utils.ts +++ b/x-pack/plugins/cases/server/services/test_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectReference } from 'kibana/server'; +import { SavedObject, SavedObjectReference, SavedObjectsFindResult } from 'kibana/server'; import { ESConnectorFields } from '.'; import { CONNECTOR_ID_REFERENCE_NAME, PUSH_CONNECTOR_ID_REFERENCE_NAME } from '../common'; import { @@ -54,7 +54,7 @@ export const createESJiraConnector = ( { key: 'parent', value: '2' }, ], type: ConnectorTypes.jira, - ...(overrides && { ...overrides }), + ...overrides, }; }; @@ -94,7 +94,7 @@ export const createExternalService = ( email: 'testemail@elastic.co', username: 'elastic', }, - ...(overrides && { ...overrides }), + ...overrides, }); export const basicCaseFields = { @@ -198,3 +198,14 @@ export const createSavedObjectReferences = ({ ] : []), ]; + +export const createConnectorObject = (overrides?: Partial) => ({ + connector: { ...createJiraConnector(), ...overrides }, +}); + +export const createSOFindResponse = (savedObjects: Array>) => ({ + saved_objects: savedObjects, + total: savedObjects.length, + per_page: savedObjects.length, + page: 1, +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts new file mode 100644 index 0000000000000..7bcbaf58d0f6e --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.test.ts @@ -0,0 +1,332 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserActionField } from '../../../common'; +import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; +import { buildCaseUserActionItem } from './helpers'; + +const defaultFields = () => ({ + actionAt: 'now', + actionBy: { + email: 'a', + full_name: 'j', + username: '1', + }, + caseId: '300', + owner: 'securitySolution', +}); + +describe('user action helpers', () => { + describe('buildCaseUserActionItem', () => { + describe('push user action', () => { + it('extracts the external_service connector_id to references for a new pushed user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + }); + + const parsedExternalService = JSON.parse(userAction.attributes.new_value!); + expect(parsedExternalService).not.toHaveProperty('connector_id'); + expect(parsedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extract the external_service connector_id to references for new and old pushed user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + oldValue: createExternalService({ connector_id: '5' }), + }); + + const parsedNewExternalService = JSON.parse(userAction.attributes.new_value!); + const parsedOldExternalService = JSON.parse(userAction.attributes.old_value!); + + expect(parsedNewExternalService).not.toHaveProperty('connector_id'); + expect(parsedOldExternalService).not.toHaveProperty('connector_id'); + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '100', name: 'pushConnectorId', type: 'action' }, + { id: '5', name: 'oldPushConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid push user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'push-to-service', + fields: ['invalid field'] as unknown as UserActionField, + newValue: 'hello' as unknown as Record, + }); + + expect(userAction.attributes.old_value).toBeNull(); + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "push-to-service", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": null, + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + + describe('update connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + }); + + const parsedConnector = JSON.parse(userAction.attributes.new_value!); + expect(parsedConnector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + oldValue: { ...createJiraConnector(), id: '5' }, + }); + + const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); + + expect(parsedNewConnector).not.toHaveProperty('id'); + expect(parsedOldConnector).not.toHaveProperty('id'); + + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'update', + fields: ['invalid field'] as unknown as UserActionField, + newValue: 'hello' as unknown as Record, + oldValue: 'old value' as unknown as Record, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "update", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid field", + ], + "new_value": "hello", + "old_value": "old value", + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + + describe('create connector user action', () => { + it('extracts the connector id to references for a new create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + }); + + const parsedConnector = JSON.parse(userAction.attributes.new_value!); + expect(parsedConnector.connector).not.toHaveProperty('id'); + expect(parsedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + + expect(userAction.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + + expect(userAction.attributes.old_value).toBeNull(); + }); + + it('extracts the connector id to references for a new and old create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + oldValue: createConnectorObject({ id: '5' }), + }); + + const parsedNewConnector = JSON.parse(userAction.attributes.new_value!); + const parsedOldConnector = JSON.parse(userAction.attributes.new_value!); + + expect(parsedNewConnector.connector).not.toHaveProperty('id'); + expect(parsedOldConnector.connector).not.toHaveProperty('id'); + + expect(userAction.references).toEqual([ + { id: '300', name: 'associated-cases', type: 'cases' }, + { id: '1', name: 'connectorId', type: 'action' }, + { id: '5', name: 'oldConnectorId', type: 'action' }, + ]); + }); + + it('leaves the object unmodified when it is not a valid create connector user action', () => { + const userAction = buildCaseUserActionItem({ + ...defaultFields(), + action: 'create', + fields: ['invalid action'] as unknown as UserActionField, + newValue: 'new json value' as unknown as Record, + oldValue: 'old value' as unknown as Record, + }); + + expect(userAction).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "action": "create", + "action_at": "now", + "action_by": Object { + "email": "a", + "full_name": "j", + "username": "1", + }, + "action_field": Array [ + "invalid action", + ], + "new_value": "new json value", + "old_value": "old value", + "owner": "securitySolution", + }, + "references": Array [ + Object { + "id": "300", + "name": "associated-cases", + "type": "cases", + }, + ], + } + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index 223e731aa8d9b..e91b69f0995bd 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SavedObject, SavedObjectsUpdateResponse } from 'kibana/server'; +import { SavedObject, SavedObjectReference, SavedObjectsUpdateResponse } from 'kibana/server'; import { get, isPlainObject, isString } from 'lodash'; import deepEqual from 'fast-deep-equal'; @@ -23,8 +23,68 @@ import { } from '../../../common'; import { isTwoArraysDifference } from '../../client/utils'; import { UserActionItem } from '.'; +import { extractConnectorId } from './transform'; +import { UserActionFieldType } from './types'; +import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; -export const transformNewUserAction = ({ +interface BuildCaseUserActionParams { + action: UserAction; + actionAt: string; + actionBy: User; + caseId: string; + owner: string; + fields: UserActionField; + newValue?: Record | string | null; + oldValue?: Record | string | null; + subCaseId?: string; +} + +export const buildCaseUserActionItem = ({ + action, + actionAt, + actionBy, + caseId, + fields, + newValue, + oldValue, + subCaseId, + owner, +}: BuildCaseUserActionParams): UserActionItem => { + const { transformedActionDetails: transformedNewValue, references: newValueReferences } = + extractConnectorId({ + action, + actionFields: fields, + actionDetails: newValue, + fieldType: UserActionFieldType.New, + }); + + const { transformedActionDetails: transformedOldValue, references: oldValueReferences } = + extractConnectorId({ + action, + actionFields: fields, + actionDetails: oldValue, + fieldType: UserActionFieldType.Old, + }); + + return { + attributes: transformNewUserAction({ + actionField: fields, + action, + actionAt, + owner, + ...actionBy, + newValue: transformedNewValue, + oldValue: transformedOldValue, + }), + references: [ + ...createCaseReferences(caseId, subCaseId), + ...newValueReferences, + ...oldValueReferences, + ], + }; +}; + +const transformNewUserAction = ({ actionField, action, actionAt, @@ -55,103 +115,43 @@ export const transformNewUserAction = ({ owner, }); -interface BuildCaseUserAction { - action: UserAction; - actionAt: string; - actionBy: User; - caseId: string; - owner: string; - fields: UserActionField | unknown[]; - newValue?: string | unknown; - oldValue?: string | unknown; - subCaseId?: string; -} +const createCaseReferences = (caseId: string, subCaseId?: string): SavedObjectReference[] => [ + { + type: CASE_SAVED_OBJECT, + name: CASE_REF_NAME, + id: caseId, + }, + ...(subCaseId + ? [ + { + type: SUB_CASE_SAVED_OBJECT, + name: SUB_CASE_REF_NAME, + id: subCaseId, + }, + ] + : []), +]; -interface BuildCommentUserActionItem extends BuildCaseUserAction { +interface BuildCommentUserActionItem extends BuildCaseUserActionParams { commentId: string; } -export const buildCommentUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - commentId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCommentUserActionItem): UserActionItem => ({ - attributes: transformNewUserAction({ - actionField: fields as UserActionField, - action, - actionAt, - owner, - ...actionBy, - newValue: newValue as string, - oldValue: oldValue as string, - }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: caseId, - }, - { - type: CASE_COMMENT_SAVED_OBJECT, - name: `associated-${CASE_COMMENT_SAVED_OBJECT}`, - id: commentId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - id: subCaseId, - name: `associated-${SUB_CASE_SAVED_OBJECT}`, - }, - ] - : []), - ], -}); +export const buildCommentUserActionItem = (params: BuildCommentUserActionItem): UserActionItem => { + const { commentId } = params; + const { attributes, references } = buildCaseUserActionItem(params); -export const buildCaseUserActionItem = ({ - action, - actionAt, - actionBy, - caseId, - fields, - newValue, - oldValue, - subCaseId, - owner, -}: BuildCaseUserAction): UserActionItem => ({ - attributes: transformNewUserAction({ - actionField: fields as UserActionField, - action, - actionAt, - owner, - ...actionBy, - newValue: newValue as string, - oldValue: oldValue as string, - }), - references: [ - { - type: CASE_SAVED_OBJECT, - name: `associated-${CASE_SAVED_OBJECT}`, - id: caseId, - }, - ...(subCaseId - ? [ - { - type: SUB_CASE_SAVED_OBJECT, - name: `associated-${SUB_CASE_SAVED_OBJECT}`, - id: subCaseId, - }, - ] - : []), - ], -}); + return { + attributes, + references: [ + ...references, + { + type: CASE_COMMENT_SAVED_OBJECT, + name: COMMENT_REF_NAME, + id: commentId, + }, + ], + }; +}; const userActionFieldsAllowed: UserActionField = [ 'comment', @@ -278,8 +278,8 @@ const buildGenericCaseUserActions = ({ caseId, subCaseId, fields: [field], - newValue: JSON.stringify(updatedValue), - oldValue: JSON.stringify(origValue), + newValue: updatedValue, + oldValue: origValue, owner: originalItem.attributes.owner, }), ]; diff --git a/x-pack/plugins/cases/server/services/user_actions/index.test.ts b/x-pack/plugins/cases/server/services/user_actions/index.test.ts new file mode 100644 index 0000000000000..c4a350f4ac015 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/index.test.ts @@ -0,0 +1,557 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObject, SavedObjectsFindResult } from 'kibana/server'; +import { transformFindResponseToExternalModel, UserActionItem } from '.'; +import { + CaseUserActionAttributes, + CASE_USER_ACTION_SAVED_OBJECT, + UserAction, + UserActionField, +} from '../../../common'; + +import { + createConnectorObject, + createExternalService, + createJiraConnector, + createSOFindResponse, +} from '../test_utils'; +import { buildCaseUserActionItem, buildCommentUserActionItem } from './helpers'; + +const createConnectorUserAction = ( + subCaseId?: string, + overrides?: Partial +): SavedObject => { + return { + ...createUserActionSO({ + action: 'create', + fields: ['connector'], + newValue: createConnectorObject(), + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const updateConnectorUserAction = ({ + subCaseId, + overrides, + oldValue, +}: { + subCaseId?: string; + overrides?: Partial; + oldValue?: string | null | Record; +} = {}): SavedObject => { + return { + ...createUserActionSO({ + action: 'update', + fields: ['connector'], + newValue: createJiraConnector(), + oldValue, + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const pushConnectorUserAction = ({ + subCaseId, + overrides, + oldValue, +}: { + subCaseId?: string; + overrides?: Partial; + oldValue?: string | null | Record; +} = {}): SavedObject => { + return { + ...createUserActionSO({ + action: 'push-to-service', + fields: ['pushed'], + newValue: createExternalService(), + oldValue, + subCaseId, + }), + ...(overrides && { ...overrides }), + }; +}; + +const createUserActionFindSO = ( + userAction: SavedObject +): SavedObjectsFindResult => ({ + ...userAction, + score: 0, +}); + +const createUserActionSO = ({ + action, + fields, + subCaseId, + newValue, + oldValue, + attributesOverrides, + commentId, +}: { + action: UserAction; + fields: UserActionField; + subCaseId?: string; + newValue?: string | null | Record; + oldValue?: string | null | Record; + attributesOverrides?: Partial; + commentId?: string; +}): SavedObject => { + const defaultParams = { + action, + actionAt: 'abc', + actionBy: { + email: 'a', + username: 'b', + full_name: 'abc', + }, + caseId: '1', + subCaseId, + fields, + newValue, + oldValue, + owner: 'securitySolution', + }; + + let userAction: UserActionItem; + + if (commentId) { + userAction = buildCommentUserActionItem({ + commentId, + ...defaultParams, + }); + } else { + userAction = buildCaseUserActionItem(defaultParams); + } + + return { + type: CASE_USER_ACTION_SAVED_OBJECT, + id: '100', + attributes: { + ...userAction.attributes, + ...(attributesOverrides && { ...attributesOverrides }), + }, + references: userAction.references, + }; +}; + +describe('CaseUserActionService', () => { + describe('transformFindResponseToExternalModel', () => { + it('does not populate the ids when the response is an empty array', () => { + expect(transformFindResponseToExternalModel(createSOFindResponse([]))).toMatchInlineSnapshot(` + Object { + "page": 1, + "per_page": 0, + "saved_objects": Array [], + "total": 0, + } + `); + }); + + it('preserves the saved object fields and attributes when inject the ids', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(createConnectorUserAction())]) + ); + + expect(transformed).toMatchInlineSnapshot(` + Object { + "page": 1, + "per_page": 1, + "saved_objects": Array [ + Object { + "attributes": Object { + "action": "create", + "action_at": "abc", + "action_by": Object { + "email": "a", + "full_name": "abc", + "username": "b", + }, + "action_field": Array [ + "connector", + ], + "action_id": "100", + "case_id": "1", + "comment_id": null, + "new_val_connector_id": "1", + "new_value": "{\\"connector\\":{\\"name\\":\\".jira\\",\\"type\\":\\".jira\\",\\"fields\\":{\\"issueType\\":\\"bug\\",\\"priority\\":\\"high\\",\\"parent\\":\\"2\\"}}}", + "old_val_connector_id": null, + "old_value": null, + "owner": "securitySolution", + "sub_case_id": "", + }, + "id": "100", + "references": Array [ + Object { + "id": "1", + "name": "associated-cases", + "type": "cases", + }, + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ], + "score": 0, + "type": "cases-user-actions", + }, + ], + "total": 1, + } + `); + }); + + it('populates the new_val_connector_id for multiple user actions', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(createConnectorUserAction()), + createUserActionFindSO(createConnectorUserAction()), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + expect(transformed.saved_objects[1].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id for multiple user actions', () => { + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO( + createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }) + ), + createUserActionFindSO( + createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject({ id: '10' }), + }) + ), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + expect(transformed.saved_objects[1].attributes.old_val_connector_id).toEqual('10'); + }); + + describe('reference ids', () => { + it('sets case_id to an empty string when it cannot find the reference', () => { + const userAction = { + ...createConnectorUserAction(), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.case_id).toEqual(''); + }); + + it('sets comment_id to null when it cannot find the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], commentId: '5' }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); + }); + + it('sets sub_case_id to an empty string when it cannot find the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toBeNull(); + }); + + it('sets case_id correctly when it finds the reference', () => { + const userAction = createConnectorUserAction(); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.case_id).toEqual('1'); + }); + + it('sets comment_id correctly when it finds the reference', () => { + const userAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + commentId: '5', + }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.comment_id).toEqual('5'); + }); + + it('sets sub_case_id correctly when it finds the reference', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + }; + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.sub_case_id).toEqual('5'); + }); + + it('sets action_id correctly to the saved object id', () => { + const userAction = { + ...createUserActionSO({ action: 'create', fields: ['connector'], subCaseId: '5' }), + }; + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.action_id).toEqual('100'); + }); + }); + + describe('create connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...createConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { ...createConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = createConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = createConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = createUserActionSO({ + action: 'create', + fields: ['connector'], + oldValue: createConnectorObject(), + }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + }); + }); + + describe('update connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...updateConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { + ...updateConnectorUserAction({ oldValue: createJiraConnector() }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = updateConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = updateConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('1'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = updateConnectorUserAction({ oldValue: createJiraConnector() }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('1'); + }); + }); + + describe('push connector', () => { + it('does not populate the new_val_connector_id when it cannot find the reference', () => { + const userAction = { ...pushConnectorUserAction(), references: [] }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when it cannot find the reference', () => { + const userAction = { + ...pushConnectorUserAction({ oldValue: createExternalService() }), + references: [], + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('does not populate the new_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = pushConnectorUserAction(); + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toBeNull(); + }); + + it('does not populate the old_val_connector_id when the reference exists but the action and fields are invalid', () => { + const validUserAction = pushConnectorUserAction({ oldValue: createExternalService() }); + + const invalidUserAction = { + ...validUserAction, + attributes: { ...validUserAction.attributes, action: 'invalid' }, + }; + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([ + createUserActionFindSO(invalidUserAction as SavedObject), + ]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toBeNull(); + }); + + it('populates the new_val_connector_id', () => { + const userAction = pushConnectorUserAction(); + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.new_val_connector_id).toEqual('100'); + }); + + it('populates the old_val_connector_id', () => { + const userAction = pushConnectorUserAction({ oldValue: createExternalService() }); + + const transformed = transformFindResponseToExternalModel( + createSOFindResponse([createUserActionFindSO(userAction)]) + ); + + expect(transformed.saved_objects[0].attributes.old_val_connector_id).toEqual('100'); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index b702448165554..4f158862e3d63 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { Logger, SavedObjectReference } from 'kibana/server'; +import { + Logger, + SavedObjectReference, + SavedObjectsFindResponse, + SavedObjectsFindResult, +} from 'kibana/server'; import { CASE_SAVED_OBJECT, @@ -13,8 +18,17 @@ import { CaseUserActionAttributes, MAX_DOCS_PER_PAGE, SUB_CASE_SAVED_OBJECT, + CaseUserActionResponse, + CASE_COMMENT_SAVED_OBJECT, + isCreateConnector, + isPush, + isUpdateConnector, } from '../../../common'; import { ClientArgs } from '..'; +import { UserActionFieldType } from './types'; +import { CASE_REF_NAME, COMMENT_REF_NAME, SUB_CASE_REF_NAME } from '../../common'; +import { ConnectorIdReferenceName, PushConnectorIdReferenceName } from './transform'; +import { findConnectorIdReference } from '../transform'; interface GetCaseUserActionArgs extends ClientArgs { caseId: string; @@ -33,12 +47,16 @@ interface PostCaseUserActionArgs extends ClientArgs { export class CaseUserActionService { constructor(private readonly log: Logger) {} - public async getAll({ unsecuredSavedObjectsClient, caseId, subCaseId }: GetCaseUserActionArgs) { + public async getAll({ + unsecuredSavedObjectsClient, + caseId, + subCaseId, + }: GetCaseUserActionArgs): Promise> { try { const id = subCaseId ?? caseId; const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT; - return await unsecuredSavedObjectsClient.find({ + const userActions = await unsecuredSavedObjectsClient.find({ type: CASE_USER_ACTION_SAVED_OBJECT, hasReference: { type, id }, page: 1, @@ -46,17 +64,22 @@ export class CaseUserActionService { sortField: 'action_at', sortOrder: 'asc', }); + + return transformFindResponseToExternalModel(userActions); } catch (error) { this.log.error(`Error on GET case user action case id: ${caseId}: ${error}`); throw error; } } - public async bulkCreate({ unsecuredSavedObjectsClient, actions }: PostCaseUserActionArgs) { + public async bulkCreate({ + unsecuredSavedObjectsClient, + actions, + }: PostCaseUserActionArgs): Promise { try { this.log.debug(`Attempting to POST a new case user action`); - return await unsecuredSavedObjectsClient.bulkCreate( + await unsecuredSavedObjectsClient.bulkCreate( actions.map((action) => ({ type: CASE_USER_ACTION_SAVED_OBJECT, ...action })) ); } catch (error) { @@ -65,3 +88,71 @@ export class CaseUserActionService { } } } + +export function transformFindResponseToExternalModel( + userActions: SavedObjectsFindResponse +): SavedObjectsFindResponse { + return { + ...userActions, + saved_objects: userActions.saved_objects.map((so) => ({ + ...so, + ...transformToExternalModel(so), + })), + }; +} + +function transformToExternalModel( + userAction: SavedObjectsFindResult +): SavedObjectsFindResult { + const { references } = userAction; + + const newValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.New, userAction); + const oldValueConnectorId = getConnectorIdFromReferences(UserActionFieldType.Old, userAction); + + const caseId = findReferenceId(CASE_REF_NAME, CASE_SAVED_OBJECT, references) ?? ''; + const commentId = + findReferenceId(COMMENT_REF_NAME, CASE_COMMENT_SAVED_OBJECT, references) ?? null; + const subCaseId = findReferenceId(SUB_CASE_REF_NAME, SUB_CASE_SAVED_OBJECT, references) ?? ''; + + return { + ...userAction, + attributes: { + ...userAction.attributes, + action_id: userAction.id, + case_id: caseId, + comment_id: commentId, + sub_case_id: subCaseId, + new_val_connector_id: newValueConnectorId, + old_val_connector_id: oldValueConnectorId, + }, + }; +} + +function getConnectorIdFromReferences( + fieldType: UserActionFieldType, + userAction: SavedObjectsFindResult +): string | null { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + attributes: { action, action_field }, + references, + } = userAction; + + if (isCreateConnector(action, action_field) || isUpdateConnector(action, action_field)) { + return findConnectorIdReference(ConnectorIdReferenceName[fieldType], references)?.id ?? null; + } else if (isPush(action, action_field)) { + return ( + findConnectorIdReference(PushConnectorIdReferenceName[fieldType], references)?.id ?? null + ); + } + + return null; +} + +function findReferenceId( + name: string, + type: string, + references: SavedObjectReference[] +): string | undefined { + return references.find((ref) => ref.name === name && ref.type === type)?.id; +} diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.test.ts b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts new file mode 100644 index 0000000000000..2d28770617094 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/transform.test.ts @@ -0,0 +1,1246 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { noneConnectorId } from '../../../common'; +import { + CONNECTOR_ID_REFERENCE_NAME, + getNoneCaseConnector, + PUSH_CONNECTOR_ID_REFERENCE_NAME, + USER_ACTION_OLD_ID_REF_NAME, + USER_ACTION_OLD_PUSH_ID_REF_NAME, +} from '../../common'; +import { createConnectorObject, createExternalService, createJiraConnector } from '../test_utils'; +import { + extractConnectorIdHelper, + extractConnectorIdFromJson, + extractConnectorId, + transformConnectorIdToReference, + transformPushConnectorIdToReference, +} from './transform'; +import { UserActionFieldType } from './types'; + +describe('user action transform utils', () => { + describe('transformConnectorIdToReference', () => { + it('returns the default none connector when the connector is undefined', () => { + expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).transformedConnector) + .toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is undefined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) + .transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns the default none connector when the id is none and other fields are defined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { + ...createJiraConnector(), + id: noneConnectorId, + }).transformedConnector + ).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('returns an empty array of references when the connector is undefined', () => { + expect(transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME).references.length).toBe( + 0 + ); + }); + + it('returns an empty array of references when the id is undefined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: undefined }).references + .length + ).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { id: noneConnectorId }) + .references.length + ).toBe(0); + }); + + it('returns an empty array of references when the id is the none connector and other fields are defined', () => { + expect( + transformConnectorIdToReference(CONNECTOR_ID_REFERENCE_NAME, { + ...createJiraConnector(), + id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns a jira connector', () => { + const transformedFields = transformConnectorIdToReference( + CONNECTOR_ID_REFERENCE_NAME, + createJiraConnector() + ); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('returns a jira connector with the user action reference name', () => { + const transformedFields = transformConnectorIdToReference( + USER_ACTION_OLD_ID_REF_NAME, + createJiraConnector() + ); + expect(transformedFields.transformedConnector).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('transformPushConnectorIdToReference', () => { + it('sets external_service to null when it is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('sets external_service to null when it is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null) + .transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": null, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: null, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object {}, + } + `); + }); + + it('returns an object when external_service is defined but connector_id is none', () => { + const otherFields = { otherField: 'hi' }; + + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + ...otherFields, + connector_id: noneConnectorId, + }).transformedPushConnector + ).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "otherField": "hi", + }, + } + `); + }); + + it('returns an empty array of references when the external_service is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the external_service is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, null).references + .length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is undefined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is null', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: undefined, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns an empty array of references when the connector_id is the none connector and other fields are defined', () => { + expect( + transformPushConnectorIdToReference(PUSH_CONNECTOR_ID_REFERENCE_NAME, { + ...createExternalService(), + connector_id: noneConnectorId, + }).references.length + ).toBe(0); + }); + + it('returns the external_service connector', () => { + const transformedFields = transformPushConnectorIdToReference( + PUSH_CONNECTOR_ID_REFERENCE_NAME, + createExternalService() + ); + expect(transformedFields.transformedPushConnector).toMatchInlineSnapshot(` + Object { + "external_service": Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + }, + } + `); + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns the external_service connector with a user actions reference name', () => { + const transformedFields = transformPushConnectorIdToReference( + USER_ACTION_OLD_PUSH_ID_REF_NAME, + createExternalService() + ); + + expect(transformedFields.references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('extractConnectorIdHelper', () => { + it('throws an error when action details has a circular reference', () => { + const circularRef = { prop: {} }; + circularRef.prop = circularRef; + + expect(() => { + extractConnectorIdHelper({ + action: 'a', + actionFields: [], + actionDetails: circularRef, + fieldType: UserActionFieldType.New, + }); + }).toThrow(); + }); + + describe('create action', () => { + it('returns no references and untransformed json when actionDetails is not a valid connector', () => { + expect( + extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is create and action fields does not contain connector', () => { + expect( + extractConnectorIdHelper({ + action: 'create', + actionFields: ['', 'something', 'onnector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns the stringified json without the id', () => { + const jiraConnector = createConnectorObject(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails)).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + }); + + it('removes the connector.id when the connector is none', () => { + const connector = { connector: getNoneCaseConnector() }; + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + const parsedJson = JSON.parse(transformedActionDetails); + + expect(parsedJson.connector).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": null, + "name": "none", + "type": ".none", + }, + } + `); + }); + + it('does not return a reference when the connector is none', () => { + const connector = { connector: getNoneCaseConnector() }; + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toEqual([]); + }); + + it('returns a reference to the connector.id', () => { + const connector = createConnectorObject(); + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('returns an old reference name to the connector.id', () => { + const connector = createConnectorObject(); + + const { references } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns the transformed connector and the description', () => { + const details = { ...createConnectorObject(), description: 'a description' }; + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'create', + actionFields: ['connector'], + actionDetails: details, + fieldType: UserActionFieldType.Old, + })!; + + const parsedJson = JSON.parse(transformedActionDetails); + + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + "description": "a description", + } + `); + }); + }); + + describe('update action', () => { + it('returns no references and untransformed json when actionDetails is not a valid connector', () => { + expect( + extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is update and action fields does not contain connector', () => { + expect( + extractConnectorIdHelper({ + action: 'update', + actionFields: ['', 'something', 'onnector'], + actionDetails: 5, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "5", + } + `); + }); + + it('returns the stringified json without the id', () => { + const jiraConnector = createJiraConnector(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails!); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + }); + + it('returns the stringified json without the id when the connector is none', () => { + const connector = getNoneCaseConnector(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": null, + "name": "none", + "type": ".none", + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('does not return a reference when the connector is none', () => { + const connector = getNoneCaseConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: connector, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toEqual([]); + }); + + it('returns an old reference name to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdHelper({ + action: 'update', + actionFields: ['connector'], + actionDetails: jiraConnector, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns no references and untransformed json when actionDetails is not a valid external_service', () => { + expect( + extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns no references and untransformed json when the action is push-to-service and action fields does not contain pushed', () => { + expect( + extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['', 'something', 'ushed'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }) + ).toMatchInlineSnapshot(` + Object { + "references": Array [], + "transformedActionDetails": "{\\"a\\":\\"hello\\"}", + } + `); + }); + + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns an old reference name to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdHelper({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); + + describe('extractConnectorId', () => { + it('returns null when the action details has a circular reference', () => { + const circularRef = { prop: {} }; + circularRef.prop = circularRef; + + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: circularRef, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeNull(); + expect(references).toEqual([]); + }); + + describe('fails to extract the id', () => { + it('returns a null transformed action details when it is initially null', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: null, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeNull(); + expect(references).toEqual([]); + }); + + it('returns an undefined transformed action details when it is initially undefined', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: undefined, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails).toBeUndefined(); + expect(references).toEqual([]); + }); + + it('returns a json encoded string and empty references when the action is not a valid connector', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: { a: 'hello' }, + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails!)).toEqual({ a: 'hello' }); + expect(references).toEqual([]); + }); + + it('returns a json encoded string and empty references when the action details is an invalid object', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'a', + actionFields: ['a'], + actionDetails: 5 as unknown as Record, + fieldType: UserActionFieldType.New, + }); + + expect(transformedActionDetails!).toEqual('5'); + expect(references).toEqual([]); + }); + }); + + describe('create', () => { + it('extracts the connector.id from a new create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'create', + actionFields: ['connector'], + actionDetails: createConnectorObject(), + fieldType: UserActionFieldType.New, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('extracts the connector.id from an old create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'create', + actionFields: ['connector'], + actionDetails: createConnectorObject(), + fieldType: UserActionFieldType.Old, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('update', () => { + it('extracts the connector.id from a new create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'update', + actionFields: ['connector'], + actionDetails: createJiraConnector(), + fieldType: UserActionFieldType.New, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + + it('extracts the connector.id from an old create jira connector to the references', () => { + const { transformedActionDetails, references } = extractConnectorId({ + action: 'update', + actionFields: ['connector'], + actionDetails: createJiraConnector(), + fieldType: UserActionFieldType.Old, + }); + + const parsedJson = JSON.parse(transformedActionDetails!); + + expect(parsedJson).not.toHaveProperty('id'); + expect(parsedJson).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "oldConnectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails!); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + + it('returns a reference to the old action details connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorId({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: externalService, + fieldType: UserActionFieldType.Old, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "oldPushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); + + describe('extractConnectorIdFromJson', () => { + describe('fails to extract the id', () => { + it('returns no references and null transformed json when action is undefined', () => { + expect( + extractConnectorIdFromJson({ + actionFields: [], + actionDetails: undefined, + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionFields is undefined', () => { + expect( + extractConnectorIdFromJson({ action: 'a', fieldType: UserActionFieldType.New }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionDetails is undefined', () => { + expect( + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: undefined, + references: [], + }); + }); + + it('returns no references and undefined transformed json when actionDetails is null', () => { + expect( + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + actionDetails: null, + fieldType: UserActionFieldType.New, + }) + ).toEqual({ + transformedActionDetails: null, + references: [], + }); + }); + + it('throws an error when actionDetails is invalid json', () => { + expect(() => + extractConnectorIdFromJson({ + action: 'a', + actionFields: [], + actionDetails: '{a', + fieldType: UserActionFieldType.New, + }) + ).toThrow(); + }); + }); + + describe('create action', () => { + it('returns the stringified json without the id', () => { + const jiraConnector = createConnectorObject(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'create', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + }); + + expect(JSON.parse(transformedActionDetails!)).toMatchInlineSnapshot(` + Object { + "connector": Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + }, + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createConnectorObject(); + + const { references } = extractConnectorIdFromJson({ + action: 'create', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('update action', () => { + it('returns the stringified json without the id', () => { + const jiraConnector = createJiraConnector(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'update', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + }); + + const transformedConnetor = JSON.parse(transformedActionDetails!); + expect(transformedConnetor).not.toHaveProperty('id'); + expect(transformedConnetor).toMatchInlineSnapshot(` + Object { + "fields": Object { + "issueType": "bug", + "parent": "2", + "priority": "high", + }, + "name": ".jira", + "type": ".jira", + } + `); + }); + + it('returns a reference to the connector.id', () => { + const jiraConnector = createJiraConnector(); + + const { references } = extractConnectorIdFromJson({ + action: 'update', + actionFields: ['connector'], + actionDetails: JSON.stringify(jiraConnector), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + "name": "connectorId", + "type": "action", + }, + ] + `); + }); + }); + + describe('push action', () => { + it('returns the stringified json without the connector_id', () => { + const externalService = createExternalService(); + + const { transformedActionDetails } = extractConnectorIdFromJson({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: JSON.stringify(externalService), + fieldType: UserActionFieldType.New, + }); + + const transformedExternalService = JSON.parse(transformedActionDetails!); + expect(transformedExternalService).not.toHaveProperty('connector_id'); + expect(transformedExternalService).toMatchInlineSnapshot(` + Object { + "connector_name": ".jira", + "external_id": "100", + "external_title": "awesome", + "external_url": "http://www.google.com", + "pushed_at": "2019-11-25T21:54:48.952Z", + "pushed_by": Object { + "email": "testemail@elastic.co", + "full_name": "elastic", + "username": "elastic", + }, + } + `); + }); + + it('returns a reference to the connector_id', () => { + const externalService = createExternalService(); + + const { references } = extractConnectorIdFromJson({ + action: 'push-to-service', + actionFields: ['pushed'], + actionDetails: JSON.stringify(externalService), + fieldType: UserActionFieldType.New, + })!; + + expect(references).toMatchInlineSnapshot(` + Array [ + Object { + "id": "100", + "name": "pushConnectorId", + "type": "action", + }, + ] + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts new file mode 100644 index 0000000000000..93595374208a3 --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/transform.ts @@ -0,0 +1,320 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as rt from 'io-ts'; +import { isString } from 'lodash'; + +import { SavedObjectReference } from '../../../../../../src/core/server'; +import { + CaseAttributes, + CaseConnector, + CaseConnectorRt, + CaseExternalServiceBasicRt, + isCreateConnector, + isPush, + isUpdateConnector, + noneConnectorId, +} from '../../../common'; +import { + CONNECTOR_ID_REFERENCE_NAME, + getNoneCaseConnector, + PUSH_CONNECTOR_ID_REFERENCE_NAME, + USER_ACTION_OLD_ID_REF_NAME, + USER_ACTION_OLD_PUSH_ID_REF_NAME, +} from '../../common'; +import { ACTION_SAVED_OBJECT_TYPE } from '../../../../actions/server'; +import { UserActionFieldType } from './types'; + +/** + * Extracts the connector id from a json encoded string and formats it as a saved object reference. This will remove + * the field it extracted the connector id from. + */ +export function extractConnectorIdFromJson({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action?: string; + actionFields?: string[]; + actionDetails?: string | null; + fieldType: UserActionFieldType; +}): { transformedActionDetails?: string | null; references: SavedObjectReference[] } { + if (!action || !actionFields || !actionDetails) { + return { transformedActionDetails: actionDetails, references: [] }; + } + + const decodedJson = JSON.parse(actionDetails); + + return extractConnectorIdHelper({ + action, + actionFields, + actionDetails: decodedJson, + fieldType, + }); +} + +/** + * Extracts the connector id from an unencoded object and formats it as a saved object reference. + * This will remove the field it extracted the connector id from. + */ +export function extractConnectorId({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action: string; + actionFields: string[]; + actionDetails?: Record | string | null; + fieldType: UserActionFieldType; +}): { + transformedActionDetails?: string | null; + references: SavedObjectReference[]; +} { + if (!actionDetails || isString(actionDetails)) { + // the action was null, undefined, or a regular string so just return it unmodified and not encoded + return { transformedActionDetails: actionDetails, references: [] }; + } + + try { + return extractConnectorIdHelper({ + action, + actionFields, + actionDetails, + fieldType, + }); + } catch (error) { + return { transformedActionDetails: encodeActionDetails(actionDetails), references: [] }; + } +} + +function encodeActionDetails(actionDetails: Record): string | null { + try { + return JSON.stringify(actionDetails); + } catch (error) { + return null; + } +} + +/** + * Internal helper function for extracting the connector id. This is only exported for usage in unit tests. + * This function handles encoding the transformed fields as a json string + */ +export function extractConnectorIdHelper({ + action, + actionFields, + actionDetails, + fieldType, +}: { + action: string; + actionFields: string[]; + actionDetails: unknown; + fieldType: UserActionFieldType; +}): { transformedActionDetails: string; references: SavedObjectReference[] } { + let transformedActionDetails: unknown = actionDetails; + let referencesToReturn: SavedObjectReference[] = []; + + try { + if (isCreateCaseConnector(action, actionFields, actionDetails)) { + const { transformedActionDetails: transformedConnectorPortion, references } = + transformConnectorFromCreateAndUpdateAction(actionDetails.connector, fieldType); + + // the above call only transforms the connector portion of the action details so let's add back + // the rest of the details and we'll overwrite the connector portion when the transformed one + transformedActionDetails = { + ...actionDetails, + ...transformedConnectorPortion, + }; + referencesToReturn = references; + } else if (isUpdateCaseConnector(action, actionFields, actionDetails)) { + const { + transformedActionDetails: { connector: transformedConnector }, + references, + } = transformConnectorFromCreateAndUpdateAction(actionDetails, fieldType); + + transformedActionDetails = transformedConnector; + referencesToReturn = references; + } else if (isPushConnector(action, actionFields, actionDetails)) { + ({ transformedActionDetails, references: referencesToReturn } = + transformConnectorFromPushAction(actionDetails, fieldType)); + } + } catch (error) { + // ignore any errors, we'll just return whatever was passed in for action details in that case + } + + return { + transformedActionDetails: JSON.stringify(transformedActionDetails), + references: referencesToReturn, + }; +} + +function isCreateCaseConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is { connector: CaseConnector } { + try { + const unsafeCase = actionDetails as CaseAttributes; + + return ( + isCreateConnector(action, actionFields) && + unsafeCase.connector !== undefined && + CaseConnectorRt.is(unsafeCase.connector) + ); + } catch { + return false; + } +} + +export const ConnectorIdReferenceName: Record = { + [UserActionFieldType.New]: CONNECTOR_ID_REFERENCE_NAME, + [UserActionFieldType.Old]: USER_ACTION_OLD_ID_REF_NAME, +}; + +function transformConnectorFromCreateAndUpdateAction( + connector: CaseConnector, + fieldType: UserActionFieldType +): { + transformedActionDetails: { connector: unknown }; + references: SavedObjectReference[]; +} { + const { transformedConnector, references } = transformConnectorIdToReference( + ConnectorIdReferenceName[fieldType], + connector + ); + + return { + transformedActionDetails: transformedConnector, + references, + }; +} + +type ConnectorIdRefNameType = + | typeof CONNECTOR_ID_REFERENCE_NAME + | typeof USER_ACTION_OLD_ID_REF_NAME; + +export const transformConnectorIdToReference = ( + referenceName: ConnectorIdRefNameType, + connector?: { + id?: string; + } +): { + transformedConnector: { connector: unknown }; + references: SavedObjectReference[]; +} => { + const { id: connectorId, ...restConnector } = connector ?? {}; + + const references = createConnectorReference(connectorId, ACTION_SAVED_OBJECT_TYPE, referenceName); + + const { id: ignoreNoneId, ...restNoneConnector } = getNoneCaseConnector(); + const connectorFieldsToReturn = + connector && isConnectorIdValid(connectorId) ? restConnector : restNoneConnector; + + return { + transformedConnector: { + connector: connectorFieldsToReturn, + }, + references, + }; +}; + +const createConnectorReference = ( + id: string | null | undefined, + type: string, + name: string +): SavedObjectReference[] => { + return isConnectorIdValid(id) + ? [ + { + id, + type, + name, + }, + ] + : []; +}; + +const isConnectorIdValid = (id: string | null | undefined): id is string => + id != null && id !== noneConnectorId; + +function isUpdateCaseConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is CaseConnector { + try { + return isUpdateConnector(action, actionFields) && CaseConnectorRt.is(actionDetails); + } catch { + return false; + } +} + +type CaseExternalService = rt.TypeOf; + +function isPushConnector( + action: string, + actionFields: string[], + actionDetails: unknown +): actionDetails is CaseExternalService { + try { + return isPush(action, actionFields) && CaseExternalServiceBasicRt.is(actionDetails); + } catch { + return false; + } +} + +export const PushConnectorIdReferenceName: Record = + { + [UserActionFieldType.New]: PUSH_CONNECTOR_ID_REFERENCE_NAME, + [UserActionFieldType.Old]: USER_ACTION_OLD_PUSH_ID_REF_NAME, + }; + +function transformConnectorFromPushAction( + externalService: CaseExternalService, + fieldType: UserActionFieldType +): { + transformedActionDetails: {} | null; + references: SavedObjectReference[]; +} { + const { transformedPushConnector, references } = transformPushConnectorIdToReference( + PushConnectorIdReferenceName[fieldType], + externalService + ); + + return { + transformedActionDetails: transformedPushConnector.external_service, + references, + }; +} + +type PushConnectorIdRefNameType = + | typeof PUSH_CONNECTOR_ID_REFERENCE_NAME + | typeof USER_ACTION_OLD_PUSH_ID_REF_NAME; + +export const transformPushConnectorIdToReference = ( + referenceName: PushConnectorIdRefNameType, + external_service?: { connector_id?: string | null } | null +): { + transformedPushConnector: { external_service: {} | null }; + references: SavedObjectReference[]; +} => { + const { connector_id: pushConnectorId, ...restExternalService } = external_service ?? {}; + + const references = createConnectorReference( + pushConnectorId, + ACTION_SAVED_OBJECT_TYPE, + referenceName + ); + + return { + transformedPushConnector: { external_service: external_service ? restExternalService : null }, + references, + }; +}; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts new file mode 100644 index 0000000000000..3c67535255ecc --- /dev/null +++ b/x-pack/plugins/cases/server/services/user_actions/types.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Indicates whether which user action field is being parsed, the new_value or the old_value. + */ +export enum UserActionFieldType { + New = 'New', + Old = 'Old', +} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 964e9135aba7b..68f0ba43d889b 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should create a user action when creating a case', async () => { + it('should create a user action when deleting a case', async () => { const postedCase = await createCase(supertest, getPostCaseRequest()); await deleteCases({ supertest, caseIDs: [postedCase.id] }); const userActions = await getCaseUserActions({ supertest, caseID: postedCase.id }); @@ -106,6 +106,8 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, old_value: null, new_value: null, + new_val_connector_id: null, + old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts index f149f4b5d13a8..dd1c2e810f150 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/import_export.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { join } from 'path'; import { SavedObject } from 'kibana/server'; +import supertest from 'supertest'; import { ObjectRemover as ActionsRemover } from '../../../../../alerting_api_integration/common/lib'; import { deleteAllCaseItems, @@ -29,15 +30,16 @@ import { CaseUserActionAttributes, CASE_COMMENT_SAVED_OBJECT, CasePostRequest, + CaseUserActionResponse, } from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { - const supertest = getService('supertest'); + const supertestService = getService('supertest'); const es = getService('es'); describe('import and export cases', () => { - const actionsRemover = new ActionsRemover(supertest); + const actionsRemover = new ActionsRemover(supertestService); afterEach(async () => { await deleteAllCaseItems(es); @@ -46,14 +48,14 @@ export default ({ getService }: FtrProviderContext): void => { it('exports a case with its associated user actions and comments', async () => { const caseRequest = getPostCaseRequest(); - const postedCase = await createCase(supertest, caseRequest); + const postedCase = await createCase(supertestService, caseRequest); await createComment({ - supertest, + supertest: supertestService, caseId: postedCase.id, params: postCommentUserReq, }); - const { text } = await supertest + const { text } = await supertestService .post(`/api/saved_objects/_export`) .send({ type: ['cases'], @@ -72,7 +74,7 @@ export default ({ getService }: FtrProviderContext): void => { }); it('imports a case with a comment and user actions', async () => { - await supertest + await supertestService .post('/api/saved_objects/_import') .query({ overwrite: true }) .attach( @@ -85,12 +87,12 @@ export default ({ getService }: FtrProviderContext): void => { .set('kbn-xsrf', 'true') .expect(200); - const findResponse = await findCases({ supertest, query: {} }); + const findResponse = await findCases({ supertest: supertestService, query: {} }); expect(findResponse.total).to.eql(1); expect(findResponse.cases[0].title).to.eql('A case to export'); expect(findResponse.cases[0].description).to.eql('a description'); - const { body: commentsResponse }: { body: CommentsResponse } = await supertest + const { body: commentsResponse }: { body: CommentsResponse } = await supertestService .get(`${CASES_URL}/${findResponse.cases[0].id}/comments/_find`) .send() .expect(200); @@ -99,7 +101,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(comment.comment).to.eql('A comment for my case'); const userActions = await getCaseUserActions({ - supertest, + supertest: supertestService, caseID: findResponse.cases[0].id, }); @@ -118,7 +120,7 @@ export default ({ getService }: FtrProviderContext): void => { }); it('imports a case with a connector', async () => { - await supertest + await supertestService .post('/api/saved_objects/_import') .query({ overwrite: true }) .attach( @@ -133,35 +135,56 @@ export default ({ getService }: FtrProviderContext): void => { actionsRemover.add('default', '1cd34740-06ad-11ec-babc-0b08808e8e01', 'action', 'actions'); - const findResponse = await findCases({ supertest, query: {} }); - expect(findResponse.total).to.eql(1); - expect(findResponse.cases[0].title).to.eql('A case with a connector'); - expect(findResponse.cases[0].description).to.eql('super description'); + await expectImportToHaveOneCase(supertestService); const userActions = await getCaseUserActions({ - supertest, - caseID: findResponse.cases[0].id, + supertest: supertestService, + caseID: '2e85c3f0-06ad-11ec-babc-0b08808e8e01', }); - expect(userActions).to.have.length(3); - expect(userActions[0].action).to.eql('create'); - expect(includesAllCreateCaseActionFields(userActions[0].action_field)).to.eql(true); - expect(userActions[1].action).to.eql('push-to-service'); - expect(userActions[1].action_field).to.eql(['pushed']); - expect(userActions[1].old_value).to.eql(null); + expectImportToHaveCreateCaseUserAction(userActions[0]); + expectImportToHavePushUserAction(userActions[1]); + expectImportToHaveUpdateConnector(userActions[2]); + }); + }); +}; - const parsedPushNewValue = JSON.parse(userActions[1].new_value!); - expect(parsedPushNewValue.connector_name).to.eql('A jira connector'); - expect(parsedPushNewValue.connector_id).to.eql('1cd34740-06ad-11ec-babc-0b08808e8e01'); +const expectImportToHaveOneCase = async (supertestService: supertest.SuperTest) => { + const findResponse = await findCases({ supertest: supertestService, query: {} }); + expect(findResponse.total).to.eql(1); + expect(findResponse.cases[0].title).to.eql('A case with a connector'); + expect(findResponse.cases[0].description).to.eql('super description'); +}; - expect(userActions[2].action).to.eql('update'); - expect(userActions[2].action_field).to.eql(['connector']); +const expectImportToHaveCreateCaseUserAction = (userAction: CaseUserActionResponse) => { + expect(userAction.action).to.eql('create'); + expect(includesAllCreateCaseActionFields(userAction.action_field)).to.eql(true); +}; - const parsedUpdateNewValue = JSON.parse(userActions[2].new_value!); - expect(parsedUpdateNewValue.id).to.eql('none'); - }); - }); +const expectImportToHavePushUserAction = (userAction: CaseUserActionResponse) => { + expect(userAction.action).to.eql('push-to-service'); + expect(userAction.action_field).to.eql(['pushed']); + expect(userAction.old_value).to.eql(null); + + const parsedPushNewValue = JSON.parse(userAction.new_value!); + expect(parsedPushNewValue.connector_name).to.eql('A jira connector'); + expect(parsedPushNewValue).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.eql('1cd34740-06ad-11ec-babc-0b08808e8e01'); +}; + +const expectImportToHaveUpdateConnector = (userAction: CaseUserActionResponse) => { + expect(userAction.action).to.eql('update'); + expect(userAction.action_field).to.eql(['connector']); + + const parsedUpdateNewValue = JSON.parse(userAction.new_value!); + expect(parsedUpdateNewValue).to.not.have.property('id'); + // the new val connector id is null because it is the none connector + expect(userAction.new_val_connector_id).to.eql(null); + + const parsedUpdateOldValue = JSON.parse(userAction.old_value!); + expect(parsedUpdateOldValue).to.not.have.property('id'); + expect(userAction.old_val_connector_id).to.eql('1cd34740-06ad-11ec-babc-0b08808e8e01'); }; const ndjsonToObject = (input: string) => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 63b2f2e9b90ed..d7c506a6b69d2 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -126,6 +126,8 @@ export default ({ getService }: FtrProviderContext): void => { action: 'update', action_by: defaultUser, new_value: CaseStatuses.closed, + new_val_connector_id: null, + old_val_connector_id: null, old_value: CaseStatuses.open, case_id: `${postedCase.id}`, comment_id: null, @@ -165,6 +167,8 @@ export default ({ getService }: FtrProviderContext): void => { action_by: defaultUser, new_value: CaseStatuses['in-progress'], old_value: CaseStatuses.open, + old_val_connector_id: null, + new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts index 96709ee7c309d..13408c5d309d9 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -114,6 +114,8 @@ export default ({ getService }: FtrProviderContext): void => { const { new_value, ...rest } = creationUserAction as CaseUserActionResponse; const parsedNewValue = JSON.parse(new_value!); + const { id: connectorId, ...restCaseConnector } = postedCase.connector; + expect(rest).to.eql({ action_field: [ 'description', @@ -127,6 +129,9 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, old_value: null, + old_val_connector_id: null, + // the connector id will be null here because it the connector is none + new_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -138,7 +143,7 @@ export default ({ getService }: FtrProviderContext): void => { description: postedCase.description, title: postedCase.title, tags: postedCase.tags, - connector: postedCase.connector, + connector: restCaseConnector, settings: postedCase.settings, owner: postedCase.owner, }); diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts index f4c31c052cddd..942293437b03f 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/comments/post_comment.ts @@ -148,7 +148,9 @@ export default ({ getService }: FtrProviderContext): void => { action: 'create', action_by: defaultUser, new_value: `{"comment":"${postCommentUserReq.comment}","type":"${postCommentUserReq.type}","owner":"securitySolutionFixture"}`, + new_val_connector_id: null, old_value: null, + old_val_connector_id: null, case_id: `${postedCase.id}`, comment_id: `${patchedCase.comments![0].id}`, sub_case_id: '', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts index 35ebb1a4bf7b1..4cae10510d28e 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/get_all_user_actions.ts @@ -48,6 +48,15 @@ export default ({ getService }: FtrProviderContext): void => { }); it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector', 'settings, owner]`, async () => { + const { id: connectorId, ...restConnector } = userActionPostResp.connector; + + const userActionNewValueNoId = { + ...userActionPostResp, + connector: { + ...restConnector, + }, + }; + const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -73,7 +82,10 @@ export default ({ getService }: FtrProviderContext): void => { ]); expect(body[0].action).to.eql('create'); expect(body[0].old_value).to.eql(null); - expect(JSON.parse(body[0].new_value)).to.eql(userActionPostResp); + expect(body[0].old_val_connector_id).to.eql(null); + // this will be null because it is for the none connector + expect(body[0].new_val_connector_id).to.eql(null); + expect(JSON.parse(body[0].new_value)).to.eql(userActionNewValueNoId); }); it(`on close case, user action: 'update' should be called with actionFields: ['status']`, async () => { @@ -147,18 +159,19 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.length).to.eql(2); expect(body[1].action_field).to.eql(['connector']); expect(body[1].action).to.eql('update'); + // this is null because it is the none connector + expect(body[1].old_val_connector_id).to.eql(null); expect(JSON.parse(body[1].old_value)).to.eql({ - id: 'none', name: 'none', type: '.none', fields: null, }); expect(JSON.parse(body[1].new_value)).to.eql({ - id: '123', name: 'Connector', type: '.jira', fields: { issueType: 'Task', priority: 'High', parent: null }, }); + expect(body[1].new_val_connector_id).to.eql('123'); }); it(`on update tags, user action: 'add' and 'delete' should be called with actionFields: ['tags']`, async () => { diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts index b4c2dca47bf5f..f9e66880c5230 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/common/user_actions/migrations.ts @@ -12,6 +12,10 @@ import { SECURITY_SOLUTION_OWNER, } from '../../../../../../plugins/cases/common/constants'; import { getCaseUserActions } from '../../../../common/lib/utils'; +import { + CaseUserActionResponse, + CaseUserActionsResponse, +} from '../../../../../../plugins/cases/common'; // eslint-disable-next-line import/no-default-export export default function createGetTests({ getService }: FtrProviderContext) { @@ -41,14 +45,18 @@ export default function createGetTests({ getService }: FtrProviderContext) { expect(connectorUserAction.action_field.length).eql(1); expect(connectorUserAction.action_field[0]).eql('connector'); + expect(connectorUserAction.old_val_connector_id).to.eql( + 'c1900ac0-017f-11eb-93f8-d161651bf509' + ); expect(oldValue).to.eql({ - id: 'c1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, }); + expect(connectorUserAction.new_val_connector_id).to.eql( + 'b1900ac0-017f-11eb-93f8-d161651bf509' + ); expect(newValue).to.eql({ - id: 'b1900ac0-017f-11eb-93f8-d161651bf509', name: 'none', type: '.none', fields: null, @@ -77,5 +85,142 @@ export default function createGetTests({ getService }: FtrProviderContext) { } }); }); + + describe('7.13 connector id extraction', () => { + let userActions: CaseUserActionsResponse; + + before(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' + ); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions' + ); + }); + + describe('none connector case', () => { + it('removes the connector id from the case create user action and sets the ids to null', async () => { + userActions = await getCaseUserActions({ + supertest, + caseID: 'aa8ac630-005e-11ec-91f1-6daf2ab59fb5', + }); + + const userAction = getUserActionById( + userActions, + 'ab43b5f0-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.description).to.be('a description'); + expect(newValDecoded.title).to.be('a case'); + expect(newValDecoded.connector).not.have.property('id'); + // the connector id should be none so it should be removed + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('sets the connector ids to null for a create user action with null new and old values', async () => { + const userAction = getUserActionById( + userActions, + 'b3094de0-005e-11ec-91f1-6daf2ab59fb5' + )!; + + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + }); + + describe('case with many user actions', () => { + before(async () => { + userActions = await getCaseUserActions({ + supertest, + caseID: 'e6fa9370-005e-11ec-91f1-6daf2ab59fb5', + }); + }); + + it('removes the connector id field for a created case user action', async () => { + const userAction = getUserActionById( + userActions, + 'e7882d70-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.description).to.be('a description'); + expect(newValDecoded.title).to.be('a case'); + + expect(newValDecoded.connector).to.not.have.property('id'); + expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('removes the connector id from the external service new value', async () => { + const userAction = getUserActionById( + userActions, + 'e9471b80-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.connector_name).to.be('a jira connector'); + expect(newValDecoded).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('sets the connector ids to null for a comment user action', async () => { + const userAction = getUserActionById( + userActions, + 'efe9de50-005e-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + expect(newValDecoded.comment).to.be('a comment'); + expect(userAction.new_val_connector_id).to.be(null); + expect(userAction.old_val_connector_id).to.be(null); + }); + + it('removes the connector id for an update connector action', async () => { + const userAction = getUserActionById( + userActions, + '16cd9e30-005f-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + const oldValDecoded = JSON.parse(userAction.old_value!); + + expect(newValDecoded.name).to.be('a different jira connector'); + expect(oldValDecoded.name).to.be('a jira connector'); + + expect(newValDecoded).to.not.have.property('id'); + expect(oldValDecoded).to.not.have.property('id'); + expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be('d92243b0-005e-11ec-91f1-6daf2ab59fb5'); + }); + + it('removes the connector id from the external service new value for second push', async () => { + const userAction = getUserActionById( + userActions, + '1ea33bb0-005f-11ec-91f1-6daf2ab59fb5' + )!; + + const newValDecoded = JSON.parse(userAction.new_value!); + + expect(newValDecoded.connector_name).to.be('a different jira connector'); + + expect(newValDecoded).to.not.have.property('connector_id'); + expect(userAction.new_val_connector_id).to.be('0a572860-005f-11ec-91f1-6daf2ab59fb5'); + expect(userAction.old_val_connector_id).to.be(null); + }); + }); + }); }); } + +function getUserActionById( + userActions: CaseUserActionsResponse, + id: string +): CaseUserActionResponse | undefined { + return userActions.find((userAction) => userAction.action_id === id); +} diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 94fe494fc7cc4..0ea66d35b63b8 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -275,6 +275,8 @@ export default ({ getService }: FtrProviderContext): void => { action: 'push-to-service', action_by: defaultUser, old_value: null, + old_val_connector_id: null, + new_val_connector_id: connector.id, case_id: `${postedCase.id}`, comment_id: null, sub_case_id: '', @@ -284,7 +286,6 @@ export default ({ getService }: FtrProviderContext): void => { expect(parsedNewValue).to.eql({ pushed_at: pushedCase.external_service!.pushed_at, pushed_by: defaultUser, - connector_id: connector.id, connector_name: connector.name, external_id: '123', external_title: 'INC01', diff --git a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts index 79af6bb279a3d..255a2a4ce28b5 100644 --- a/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/security_and_spaces/tests/trial/cases/user_actions/get_all_user_actions.ts @@ -108,8 +108,10 @@ export default ({ getService }: FtrProviderContext): void => { expect(body[1].action_field).to.eql(['pushed']); expect(body[1].action).to.eql('push-to-service'); expect(body[1].old_value).to.eql(null); + expect(body[1].old_val_connector_id).to.eql(null); + expect(body[1].new_val_connector_id).to.eql(configure.connector.id); const newValue = JSON.parse(body[1].new_value); - expect(newValue.connector_id).to.eql(configure.connector.id); + expect(newValue).to.not.have.property('connector_id'); expect(newValue.pushed_by).to.eql(defaultUser); }); }); diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz b/x-pack/test/functional/es_archives/cases/migrations/7.13_user_actions/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..5f73dfd89d1660ef207e6ec26ce7198f2a722616 GIT binary patch literal 2078 zcmV+(2;ui1iwFpikRD+G17u-zVJ>QOZ*BnXTuXDKI1s+)S5TY-LE>SvxgZy+IT5F>ahZaIhWE-j?DT+?5t5lioJNZyKPi$UR$bs&%<`)2h3okV(a3a zL#Oy&DHWJDNxNT|jw-rg7~^}oGPm^>`SOTBQ4}aL)$g~xMn9kcdl7$V`cR|B4O~BP zqt_0h|8$>z;4rF*UHSD`?VD}BV?wB@n?Bo~_4Cj^nGOj*-2e&YwlJ-7)stzXQl$6l zh6Wf#xgLm6fk0B05?J0t#ZnXtXtJuQ4`NG?L_HWCza&<@-~_JN!GM!|m2&)MuBmhx9q}HCvC}gmQ#_ z+fhiAP6!kL7MvE58|{)2@BjUW$f9tX#Q|M!@kbOLLhJ4>F>@&*`saF2GK$DLhc5DD z4@Y;Kz<&wxUF&#wWg|CggBy49oDxweD&-< zb~uP=O%%kqy?2D7n6?o817_4HLQi#A6T|p`gHoi&YV&;Kh!&9DU`EQUkpQ>*>^d>p zRcqse!#BjXO(=RyYKS+rwIfCl;J@9c08#t<4+mjpmkSxFvQ~^M?wf6@-Y9cWv>TKa zu`x}6x0rysjjNkcEPS!H={QAkk41(0LpobFS25I@I_w+z$nQ5dZ){!e%NyRD_2&{@ zND0~Wv59lv5O~qI{Q!pV-rF3!&^BRRE7IabxCFx4Jx^+cnue!l zJ|JK(w>}RR!qB%}icb;?Te-?HnAYk5A6wi@%Om%3Gnt)hGE~fmCjQn@F_nTKW4MH^O>YEbb(vmNHhKZMDpBhSK34k%}} z16_HGrVj!F1sK!E{n*qNV6(h{xStZ5QshfHTiojakdfp|8LD-HbS4LYl*jGQQxsHj z!VB=0BB2Tjia4bPa*30UROFOT$R9W+8NjqyjU)w({mj!hArW} zQJfE0UYX=hCb>5jcO!7gI@3y)b*7W$sRJ22D+)~AP4dz|^A+@W_Od={mc5oI0DI-E zzZ?qdvI>F<+l@p02 zaX5v>QMt(~d?G+WQ&tye2or%}J0?!wI&9AhQ0W5o2Pj~v#CY_Xo~g@Vt~W$w5tM@T z1zZ}PJ64aN9Cw?bKr~e>^_Txb<0w8>igODIIjNCXWIszp2WS?80jGJr$O0xts=C=+ z0AP-0BLRIkyO?XL)=AmOVkxF$BYWd_D;wE~z}Nxr4lAb1^Z{+a&N!<1|<$U5Iw zRxBaS99|>5;$A8+ zg$Yj)35^VZxS__-2xA(;gQaD+1~znb0krJUJOG&Z8TzH+JZagnc>ptcZO)~I=bF+m z$x-a9QQ19?V Date: Wed, 22 Sep 2021 09:00:52 -0400 Subject: [PATCH 59/69] Moving title to text and hiding user actions and comments (#112745) --- x-pack/plugins/cases/server/saved_object_types/cases.ts | 2 +- x-pack/plugins/cases/server/saved_object_types/comments.ts | 1 + x-pack/plugins/cases/server/saved_object_types/user_actions.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/cases/server/saved_object_types/cases.ts b/x-pack/plugins/cases/server/saved_object_types/cases.ts index a362d77c06626..74c6a053e95c0 100644 --- a/x-pack/plugins/cases/server/saved_object_types/cases.ts +++ b/x-pack/plugins/cases/server/saved_object_types/cases.ts @@ -117,7 +117,7 @@ export const createCaseSavedObjectType = ( type: 'keyword', }, title: { - type: 'keyword', + type: 'text', }, status: { type: 'keyword', diff --git a/x-pack/plugins/cases/server/saved_object_types/comments.ts b/x-pack/plugins/cases/server/saved_object_types/comments.ts index af14123eca580..64e75ad26ae28 100644 --- a/x-pack/plugins/cases/server/saved_object_types/comments.ts +++ b/x-pack/plugins/cases/server/saved_object_types/comments.ts @@ -112,5 +112,6 @@ export const createCaseCommentSavedObjectType = ({ migrations: createCommentsMigrations(migrationDeps), management: { importableAndExportable: true, + visibleInManagement: false, }, }); diff --git a/x-pack/plugins/cases/server/saved_object_types/user_actions.ts b/x-pack/plugins/cases/server/saved_object_types/user_actions.ts index 883105982bcb3..7ef7c639ed9db 100644 --- a/x-pack/plugins/cases/server/saved_object_types/user_actions.ts +++ b/x-pack/plugins/cases/server/saved_object_types/user_actions.ts @@ -51,5 +51,6 @@ export const caseUserActionSavedObjectType: SavedObjectsType = { migrations: userActionsMigrations, management: { importableAndExportable: true, + visibleInManagement: false, }, }; From e39a9d495bda9b2e8fbc9e8fba8d33a9d0f6de55 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Wed, 22 Sep 2021 15:04:58 +0200 Subject: [PATCH 60/69] Fix unhandled promise rejection in socket tests (#112806) --- src/core/server/http/router/socket.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/server/http/router/socket.test.ts b/src/core/server/http/router/socket.test.ts index 60c91786767a6..389c08825d51b 100644 --- a/src/core/server/http/router/socket.test.ts +++ b/src/core/server/http/router/socket.test.ts @@ -92,7 +92,7 @@ describe('KibanaSocket', () => { }); const socket = new KibanaSocket(tlsSocket); - expect(socket.renegotiate({})).resolves.toBe(result); + await expect(socket.renegotiate({})).rejects.toBe(result); expect(spy).toBeCalledTimes(1); }); From 93cc4fcd9b9a2e7e7611c0b082b68c494308820a Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Wed, 22 Sep 2021 09:09:34 -0400 Subject: [PATCH 61/69] [Security Solution][Timeline] Pinned events migrations (#112360) * Starting migration class * Fleshing out migrator * Adding migration tests * Refactoring * Adding migrator to each client * gzipping file * Fixing cypress tests * Cleaning up types and adding additional test * Starting notes migrations * Finishing notes references migration * gzipping data.json * Fixing unit tests * Updating the archive and fixing spelling * Starting pinned events * Fixing more conflicts * Finishing pinned events * fixing pinned events not showing bug * Fixing lint errors Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../types/timeline/pinned_event/index.ts | 6 + .../pinned_events/field_migrator.ts | 18 ++ .../saved_object/pinned_events/index.ts | 208 +++++++++++------- .../migrations/notes.test.ts | 40 ---- .../saved_object_mappings/migrations/notes.ts | 36 +-- .../migrations/pinned_events.ts | 13 ++ .../migrations/{index.ts => types.ts} | 5 +- .../migrations/utils.test.ts | 32 ++- .../saved_object_mappings/migrations/utils.ts | 23 ++ .../timeline/saved_object_mappings/notes.ts | 2 +- .../saved_object_mappings/pinned_events.ts | 5 +- .../saved_object_mappings/timelines.ts | 2 +- .../security_solution/timeline_migrations.ts | 126 +++++++---- .../timelines/7.15.0/data.json.gz | Bin 3118 -> 3118 bytes 14 files changed, 312 insertions(+), 204 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/field_migrator.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/pinned_events.ts rename x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/{index.ts => types.ts} (72%) diff --git a/x-pack/plugins/security_solution/common/types/timeline/pinned_event/index.ts b/x-pack/plugins/security_solution/common/types/timeline/pinned_event/index.ts index dbb19df7a6b05..df230615818ac 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/pinned_event/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/pinned_event/index.ts @@ -30,6 +30,12 @@ export const SavedPinnedEventRuntimeType = runtimeTypes.intersection([ export interface SavedPinnedEvent extends runtimeTypes.TypeOf {} +/** + * This type represents a pinned event type stored in a saved object that does not include any fields that reference + * other saved objects. + */ +export type PinnedEventWithoutExternalRefs = Omit; + /** * Note Saved object type with metadata */ diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/field_migrator.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/field_migrator.ts new file mode 100644 index 0000000000000..5939676c2a924 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/field_migrator.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TIMELINE_ID_REF_NAME } from '../../constants'; +import { timelineSavedObjectType } from '../../saved_object_mappings'; +import { FieldMigrator } from '../../utils/migrator'; + +/** + * A migrator to handle moving specific fields that reference the timeline saved object to the references field within a note saved + * object. + */ +export const pinnedEventFieldsMigrator = new FieldMigrator([ + { path: 'timelineId', type: timelineSavedObjectType, name: TIMELINE_ID_REF_NAME }, +]); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts index b3d262b13cbf3..260531e1106bf 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts @@ -19,13 +19,13 @@ import { PinnedEventSavedObjectRuntimeType, SavedPinnedEvent, PinnedEvent as PinnedEventResponse, + PinnedEventWithoutExternalRefs, } from '../../../../../common/types/timeline/pinned_event'; -import { PageInfoNote, SortNote } from '../../../../../common/types/timeline/note'; import { FrameworkRequest } from '../../../framework'; -import { pickSavedTimeline } from '../../saved_object/timelines'; -import { convertSavedObjectToSavedTimeline } from '../timelines'; +import { createTimeline } from '../../saved_object/timelines'; import { pinnedEventSavedObjectType } from '../../saved_object_mappings/pinned_events'; +import { pinnedEventFieldsMigrator } from './field_migrator'; import { timelineSavedObjectType } from '../../saved_object_mappings'; export interface PinnedEvent { @@ -46,13 +46,6 @@ export interface PinnedEvent { timelineId: string ) => Promise; - getAllPinnedEvents: ( - request: FrameworkRequest, - pageInfo: PageInfoNote | null, - search: string | null, - sort: SortNote | null - ) => Promise; - persistPinnedEventOnTimeline: ( request: FrameworkRequest, pinnedEventId: string | null, // pinned event saved object id @@ -117,26 +110,7 @@ export const getAllPinnedEventsByTimelineId = async ( ): Promise => { const options: SavedObjectsFindOptions = { type: pinnedEventSavedObjectType, - search: timelineId, - searchFields: ['timelineId'], - }; - return getAllSavedPinnedEvents(request, options); -}; - -export const getAllPinnedEvents = async ( - request: FrameworkRequest, - pageInfo: PageInfoNote | null, - search: string | null, - sort: SortNote | null -): Promise => { - const options: SavedObjectsFindOptions = { - type: pinnedEventSavedObjectType, - perPage: pageInfo != null ? pageInfo.pageSize : undefined, - page: pageInfo != null ? pageInfo.pageIndex : undefined, - search: search != null ? search : undefined, - searchFields: ['timelineId', 'eventId'], - sortField: sort != null ? sort.sortField : undefined, - sortOrder: sort != null ? sort.sortOrder : undefined, + hasReference: { type: timelineSavedObjectType, id: timelineId }, }; return getAllSavedPinnedEvents(request, options); }; @@ -147,51 +121,35 @@ export const persistPinnedEventOnTimeline = async ( eventId: string, timelineId: string | null ): Promise => { - const savedObjectsClient = request.context.core.savedObjects.client; - try { - if (pinnedEventId == null) { - const timelineVersionSavedObject = - timelineId == null - ? await (async () => { - const timelineResult = convertSavedObjectToSavedTimeline( - await savedObjectsClient.create( - timelineSavedObjectType, - pickSavedTimeline(null, {}, request.user || null) - ) - ); - timelineId = timelineResult.savedObjectId; // eslint-disable-line no-param-reassign - return timelineResult.version; - })() - : null; - - if (timelineId != null) { - const allPinnedEventId = await getAllPinnedEventsByTimelineId(request, timelineId); - const isPinnedAlreadyExisting = allPinnedEventId.filter( - (pinnedEvent) => pinnedEvent.eventId === eventId - ); - - if (isPinnedAlreadyExisting.length === 0) { - const savedPinnedEvent: SavedPinnedEvent = { - eventId, - timelineId, - }; - // create Pinned Event on Timeline - return convertSavedObjectToSavedPinnedEvent( - await savedObjectsClient.create( - pinnedEventSavedObjectType, - pickSavedPinnedEvent(pinnedEventId, savedPinnedEvent, request.user || null) - ), - timelineVersionSavedObject != null ? timelineVersionSavedObject : undefined - ); - } - return isPinnedAlreadyExisting[0]; - } - throw new Error('You can NOT pinned event without a timelineID'); + if (pinnedEventId != null) { + // Delete Pinned Event on Timeline + await deletePinnedEventOnTimeline(request, [pinnedEventId]); + return null; } - // Delete Pinned Event on Timeline - await deletePinnedEventOnTimeline(request, [pinnedEventId]); - return null; + + const { timelineId: validatedTimelineId, timelineVersion } = await getValidTimelineIdAndVersion( + request, + timelineId + ); + + const pinnedEvents = await getPinnedEventsInTimelineWithEventId( + request, + validatedTimelineId, + eventId + ); + + // we already had this event pinned so let's just return the one we already had + if (pinnedEvents.length > 0) { + return pinnedEvents[0]; + } + + return await createPinnedEvent({ + request, + eventId, + timelineId: validatedTimelineId, + timelineVersion, + }); } catch (err) { if (getOr(null, 'output.statusCode', err) === 404) { /* @@ -215,11 +173,91 @@ export const persistPinnedEventOnTimeline = async ( } }; +const getValidTimelineIdAndVersion = async ( + request: FrameworkRequest, + timelineId: string | null +): Promise<{ timelineId: string; timelineVersion?: string }> => { + if (timelineId != null) { + return { + timelineId, + }; + } + + const savedObjectsClient = request.context.core.savedObjects.client; + + // create timeline because it didn't exist + const { timeline: timelineResult } = await createTimeline({ + timelineId: null, + timeline: {}, + savedObjectsClient, + userInfo: request.user, + }); + + return { + timelineId: timelineResult.savedObjectId, + timelineVersion: timelineResult.version, + }; +}; + +const getPinnedEventsInTimelineWithEventId = async ( + request: FrameworkRequest, + timelineId: string, + eventId: string +): Promise => { + const allPinnedEventId = await getAllPinnedEventsByTimelineId(request, timelineId); + const pinnedEvents = allPinnedEventId.filter((pinnedEvent) => pinnedEvent.eventId === eventId); + + return pinnedEvents; +}; + +const createPinnedEvent = async ({ + request, + eventId, + timelineId, + timelineVersion, +}: { + request: FrameworkRequest; + eventId: string; + timelineId: string; + timelineVersion?: string; +}) => { + const savedObjectsClient = request.context.core.savedObjects.client; + + const savedPinnedEvent: SavedPinnedEvent = { + eventId, + timelineId, + }; + + const pinnedEventWithCreator = pickSavedPinnedEvent(null, savedPinnedEvent, request.user); + + const { transformedFields: migratedAttributes, references } = + pinnedEventFieldsMigrator.extractFieldsToReferences({ + data: pinnedEventWithCreator, + }); + + const createdPinnedEvent = await savedObjectsClient.create( + pinnedEventSavedObjectType, + migratedAttributes, + { references } + ); + + const repopulatedSavedObject = + pinnedEventFieldsMigrator.populateFieldsFromReferences(createdPinnedEvent); + + // create Pinned Event on Timeline + return convertSavedObjectToSavedPinnedEvent(repopulatedSavedObject, timelineVersion); +}; + const getSavedPinnedEvent = async (request: FrameworkRequest, pinnedEventId: string) => { const savedObjectsClient = request.context.core.savedObjects.client; - const savedObject = await savedObjectsClient.get(pinnedEventSavedObjectType, pinnedEventId); + const savedObject = await savedObjectsClient.get( + pinnedEventSavedObjectType, + pinnedEventId + ); + + const populatedPinnedEvent = pinnedEventFieldsMigrator.populateFieldsFromReferences(savedObject); - return convertSavedObjectToSavedPinnedEvent(savedObject); + return convertSavedObjectToSavedPinnedEvent(populatedPinnedEvent); }; const getAllSavedPinnedEvents = async ( @@ -227,11 +265,14 @@ const getAllSavedPinnedEvents = async ( options: SavedObjectsFindOptions ) => { const savedObjectsClient = request.context.core.savedObjects.client; - const savedObjects = await savedObjectsClient.find(options); + const savedObjects = await savedObjectsClient.find(options); - return savedObjects.saved_objects.map((savedObject) => - convertSavedObjectToSavedPinnedEvent(savedObject) - ); + return savedObjects.saved_objects.map((savedObject) => { + const populatedPinnedEvent = + pinnedEventFieldsMigrator.populateFieldsFromReferences(savedObject); + + return convertSavedObjectToSavedPinnedEvent(populatedPinnedEvent); + }); }; export const savePinnedEvents = ( @@ -284,11 +325,10 @@ export const pickSavedPinnedEvent = ( if (pinnedEventId == null) { savedPinnedEvent.created = dateNow; savedPinnedEvent.createdBy = userInfo?.username ?? UNAUTHENTICATED_USER; - savedPinnedEvent.updated = dateNow; - savedPinnedEvent.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER; - } else if (pinnedEventId != null) { - savedPinnedEvent.updated = dateNow; - savedPinnedEvent.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER; } + + savedPinnedEvent.updated = dateNow; + savedPinnedEvent.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER; + return savedPinnedEvent; }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.test.ts deleted file mode 100644 index b9649896c25a6..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TIMELINE_ID_REF_NAME } from '../../constants'; -import { migrateNoteTimelineIdToReferences, TimelineId } from './notes'; - -describe('notes migrations', () => { - describe('7.16.0 timelineId', () => { - it('removes the timelineId from the migrated document', () => { - const migratedDoc = migrateNoteTimelineIdToReferences({ - id: '1', - type: 'awesome', - attributes: { timelineId: '123' }, - }); - - expect(migratedDoc.attributes).toEqual({}); - expect(migratedDoc.references).toEqual([ - // importing the timeline saved object type from the timeline saved object causes a circular import and causes the jest tests to fail - { id: '123', name: TIMELINE_ID_REF_NAME, type: 'siem-ui-timeline' }, - ]); - }); - - it('preserves additional fields when migrating timeline id', () => { - const migratedDoc = migrateNoteTimelineIdToReferences({ - id: '1', - type: 'awesome', - attributes: { awesome: 'yes', timelineId: '123' } as unknown as TimelineId, - }); - - expect(migratedDoc.attributes).toEqual({ awesome: 'yes' }); - expect(migratedDoc.references).toEqual([ - { id: '123', name: TIMELINE_ID_REF_NAME, type: 'siem-ui-timeline' }, - ]); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.ts index a8d753e916afb..76773b7fcd518 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/notes.ts @@ -5,39 +5,9 @@ * 2.0. */ -import { - SavedObjectMigrationMap, - SavedObjectSanitizedDoc, - SavedObjectUnsanitizedDoc, -} from 'kibana/server'; -import { timelineSavedObjectType } from '..'; -import { TIMELINE_ID_REF_NAME } from '../../constants'; -import { createMigratedDoc, createReference } from './utils'; - -export interface TimelineId { - timelineId?: string | null; -} - -export const migrateNoteTimelineIdToReferences = ( - doc: SavedObjectUnsanitizedDoc -): SavedObjectSanitizedDoc => { - const { timelineId, ...restAttributes } = doc.attributes; - - const { references: docReferences = [] } = doc; - const timelineIdReferences = createReference( - timelineId, - TIMELINE_ID_REF_NAME, - timelineSavedObjectType - ); - - return createMigratedDoc({ - doc, - attributes: restAttributes, - docReferences, - migratedReferences: timelineIdReferences, - }); -}; +import { SavedObjectMigrationMap } from 'kibana/server'; +import { migrateTimelineIdToReferences } from './utils'; export const notesMigrations: SavedObjectMigrationMap = { - '7.16.0': migrateNoteTimelineIdToReferences, + '7.16.0': migrateTimelineIdToReferences, }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/pinned_events.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/pinned_events.ts new file mode 100644 index 0000000000000..4d21190d9381c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/pinned_events.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectMigrationMap } from 'kibana/server'; +import { migrateTimelineIdToReferences } from './utils'; + +export const pinnedEventsMigrations: SavedObjectMigrationMap = { + '7.16.0': migrateTimelineIdToReferences, +}; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/types.ts similarity index 72% rename from x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/index.ts rename to x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/types.ts index e4c8858321e14..7c62310a99aa6 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/types.ts @@ -5,5 +5,6 @@ * 2.0. */ -export { timelinesMigrations } from './timelines'; -export { notesMigrations } from './notes'; +export interface TimelineId { + timelineId?: string | null; +} diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.test.ts index 02e3fca996d5d..329f09e85f3a7 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.test.ts @@ -5,9 +5,39 @@ * 2.0. */ -import { createMigratedDoc, createReference } from './utils'; +import { timelineSavedObjectType } from '../timelines'; +import { TIMELINE_ID_REF_NAME } from '../../constants'; +import { TimelineId } from './types'; +import { createMigratedDoc, createReference, migrateTimelineIdToReferences } from './utils'; describe('migration utils', () => { + describe('migrateTimelineIdToReferences', () => { + it('removes the timelineId from the migrated document', () => { + const migratedDoc = migrateTimelineIdToReferences({ + id: '1', + type: 'awesome', + attributes: { timelineId: '123' }, + }); + + expect(migratedDoc.attributes).toEqual({}); + expect(migratedDoc.references).toEqual([ + { id: '123', name: TIMELINE_ID_REF_NAME, type: timelineSavedObjectType }, + ]); + }); + + it('preserves additional fields when migrating timeline id', () => { + const migratedDoc = migrateTimelineIdToReferences({ + id: '1', + type: 'awesome', + attributes: { awesome: 'yes', timelineId: '123' } as unknown as TimelineId, + }); + + expect(migratedDoc.attributes).toEqual({ awesome: 'yes' }); + expect(migratedDoc.references).toEqual([ + { id: '123', name: TIMELINE_ID_REF_NAME, type: timelineSavedObjectType }, + ]); + }); + }); describe('createReference', () => { it('returns an array with a reference when the id is defined', () => { expect(createReference('awesome', 'name', 'type')).toEqual([ diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.ts index ff9b56e6ae2c9..7bd7bc148c263 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/migrations/utils.ts @@ -10,6 +10,9 @@ import { SavedObjectSanitizedDoc, SavedObjectUnsanitizedDoc, } from 'kibana/server'; +import { timelineSavedObjectType } from '../timelines'; +import { TIMELINE_ID_REF_NAME } from '../../constants'; +import { TimelineId } from './types'; export function createReference( id: string | null | undefined, @@ -19,6 +22,26 @@ export function createReference( return id != null ? [{ id, name, type }] : []; } +export const migrateTimelineIdToReferences = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc => { + const { timelineId, ...restAttributes } = doc.attributes; + + const { references: docReferences = [] } = doc; + const timelineIdReferences = createReference( + timelineId, + TIMELINE_ID_REF_NAME, + timelineSavedObjectType + ); + + return createMigratedDoc({ + doc, + attributes: restAttributes, + docReferences, + migratedReferences: timelineIdReferences, + }); +}; + export const createMigratedDoc = ({ doc, attributes, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts index 387f78e5059f4..eda2478e7809d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/notes.ts @@ -6,7 +6,7 @@ */ import { SavedObjectsType } from '../../../../../../../src/core/server'; -import { notesMigrations } from './migrations'; +import { notesMigrations } from './migrations/notes'; export const noteSavedObjectType = 'siem-ui-timeline-note'; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts index fbbffe35a58c0..2f8e72ad763f9 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/pinned_events.ts @@ -6,14 +6,12 @@ */ import { SavedObjectsType } from '../../../../../../../src/core/server'; +import { pinnedEventsMigrations } from './migrations/pinned_events'; export const pinnedEventSavedObjectType = 'siem-ui-timeline-pinned-event'; export const pinnedEventSavedObjectMappings: SavedObjectsType['mappings'] = { properties: { - timelineId: { - type: 'keyword', - }, eventId: { type: 'keyword', }, @@ -37,4 +35,5 @@ export const pinnedEventType: SavedObjectsType = { hidden: false, namespaceType: 'single', mappings: pinnedEventSavedObjectMappings, + migrations: pinnedEventsMigrations, }; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts index 8300f72a162ed..e1e3a454087f9 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings/timelines.ts @@ -6,7 +6,7 @@ */ import { SavedObjectsType } from '../../../../../../../src/core/server'; -import { timelinesMigrations } from './migrations'; +import { timelinesMigrations } from './migrations/timelines'; export const timelineSavedObjectType = 'siem-ui-timeline'; diff --git a/x-pack/test/api_integration/apis/security_solution/timeline_migrations.ts b/x-pack/test/api_integration/apis/security_solution/timeline_migrations.ts index 9863ebb7ba646..1bfefe04239e2 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline_migrations.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline_migrations.ts @@ -6,18 +6,28 @@ */ import expect from '@kbn/expect'; -import { SavedTimeline } from '../../../../plugins/security_solution/common/types/timeline'; -import { SavedNote } from '../../../../plugins/security_solution/common/types/timeline/note'; +import { + noteSavedObjectType, + pinnedEventSavedObjectType, + timelineSavedObjectType, +} from '../../../../plugins/security_solution/server/lib/timeline/saved_object_mappings'; +import { TimelineWithoutExternalRefs } from '../../../../plugins/security_solution/common/types/timeline'; +import { NoteWithoutExternalRefs } from '../../../../plugins/security_solution/common/types/timeline/note'; import { FtrProviderContext } from '../../ftr_provider_context'; import { getSavedObjectFromES } from './utils'; +import { PinnedEventWithoutExternalRefs } from '../../../../plugins/security_solution/common/types/timeline/pinned_event'; interface TimelineWithoutSavedQueryId { - 'siem-ui-timeline': Omit; + [timelineSavedObjectType]: TimelineWithoutExternalRefs; } interface NoteWithoutTimelineId { - 'siem-ui-timeline-note': Omit; + [noteSavedObjectType]: NoteWithoutExternalRefs; +} + +interface PinnedEventWithoutTimelineId { + [pinnedEventSavedObjectType]: PinnedEventWithoutExternalRefs; } export default function ({ getService }: FtrProviderContext) { @@ -28,23 +38,22 @@ export default function ({ getService }: FtrProviderContext) { const es = getService('es'); describe('7.16.0', () => { - describe('notes timelineId', () => { - before(async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/timelines/7.15.0' - ); - }); - - after(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/timelines/7.15.0' - ); - }); + before(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/timelines/7.15.0' + ); + }); + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/timelines/7.15.0' + ); + }); + describe('notes timelineId', () => { it('removes the timelineId in the saved object', async () => { const timelines = await getSavedObjectFromES( es, - 'siem-ui-timeline-note', + noteSavedObjectType, { ids: { values: [ @@ -55,13 +64,13 @@ export default function ({ getService }: FtrProviderContext) { } ); - expect( - timelines.body.hits.hits[0]._source?.['siem-ui-timeline-note'] - ).to.not.have.property('timelineId'); + expect(timelines.body.hits.hits[0]._source?.[noteSavedObjectType]).to.not.have.property( + 'timelineId' + ); - expect( - timelines.body.hits.hits[1]._source?.['siem-ui-timeline-note'] - ).to.not.have.property('timelineId'); + expect(timelines.body.hits.hits[1]._source?.[noteSavedObjectType]).to.not.have.property( + 'timelineId' + ); }); it('preserves the eventId in the saved object after migration', async () => { @@ -87,30 +96,18 @@ export default function ({ getService }: FtrProviderContext) { }); describe('savedQueryId', () => { - before(async () => { - await esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/timelines/7.15.0' - ); - }); - - after(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/timelines/7.15.0' - ); - }); - it('removes the savedQueryId', async () => { const timelines = await getSavedObjectFromES( es, - 'siem-ui-timeline', + timelineSavedObjectType, { ids: { values: ['siem-ui-timeline:8dc70950-1012-11ec-9ad3-2d7c6600c0f7'] }, } ); - expect(timelines.body.hits.hits[0]._source?.['siem-ui-timeline']).to.not.have.property( - 'savedQueryId' - ); + expect( + timelines.body.hits.hits[0]._source?.[timelineSavedObjectType] + ).to.not.have.property('savedQueryId'); }); it('preserves the title in the saved object after migration', async () => { @@ -129,6 +126,57 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body.data.getOneTimeline.savedQueryId).to.be("It's me"); }); }); + + describe('pinned events timelineId', () => { + it('removes the timelineId in the saved object', async () => { + const timelines = await getSavedObjectFromES( + es, + pinnedEventSavedObjectType, + { + ids: { + values: [ + 'siem-ui-timeline-pinned-event:7a9a5540-126e-11ec-83d2-db1096c73738', + 'siem-ui-timeline-pinned-event:98d919b0-126e-11ec-83d2-db1096c73738', + ], + }, + } + ); + + expect( + timelines.body.hits.hits[0]._source?.[pinnedEventSavedObjectType] + ).to.not.have.property('timelineId'); + + expect( + timelines.body.hits.hits[1]._source?.[pinnedEventSavedObjectType] + ).to.not.have.property('timelineId'); + }); + + it('preserves the eventId in the saved object after migration', async () => { + const resp = await supertest + .get('/api/timeline') + .query({ id: '6484cc90-126e-11ec-83d2-db1096c73738' }); + + expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[0].eventId).to.be( + 'DNo00XsBEVtyvU-8LGNe' + ); + expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[1].eventId).to.be( + 'Edo00XsBEVtyvU-8LGNe' + ); + }); + + it('returns the timelineId in the response', async () => { + const resp = await supertest + .get('/api/timeline') + .query({ id: '6484cc90-126e-11ec-83d2-db1096c73738' }); + + expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[0].timelineId).to.be( + '6484cc90-126e-11ec-83d2-db1096c73738' + ); + expect(resp.body.data.getOneTimeline.pinnedEventsSaveObject[1].timelineId).to.be( + '6484cc90-126e-11ec-83d2-db1096c73738' + ); + }); + }); }); }); } diff --git a/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0/data.json.gz b/x-pack/test/functional/es_archives/security_solution/timelines/7.15.0/data.json.gz index e942ef732b22a21382848acce3ba2e8c8e5456fa..91e3e459f826b0e44324906667de1fe6bcf422db 100644 GIT binary patch delta 16 XcmZ1{u}*?rzMF&NzL3*Kc6A;ADYXP_ delta 16 XcmZ1{u}*?rzMF$1`Jux`c6A;AD Date: Wed, 22 Sep 2021 15:12:18 +0200 Subject: [PATCH 62/69] Rename REINDEX_SOURCE_TO_TEMP_INDEX to REINDEX_SOURCE_TO_TEMP_TRANSFORM (#112727) --- .../saved_objects/migrationsv2/README.md | 8 ++--- .../migrations_state_action_machine.ts | 6 ++-- .../migrationsv2/model/model.test.ts | 32 +++++++++---------- .../saved_objects/migrationsv2/model/model.ts | 6 ++-- .../server/saved_objects/migrationsv2/next.ts | 4 +-- .../saved_objects/migrationsv2/types.ts | 6 ++-- 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/core/server/saved_objects/migrationsv2/README.md b/src/core/server/saved_objects/migrationsv2/README.md index 5121e66052f40..a6b8e01a3dc6c 100644 --- a/src/core/server/saved_objects/migrationsv2/README.md +++ b/src/core/server/saved_objects/migrationsv2/README.md @@ -36,7 +36,7 @@ - [REINDEX_SOURCE_TO_TEMP_READ](#reindex_source_to_temp_read) - [Next action](#next-action-11) - [New control state](#new-control-state-11) - - [REINDEX_SOURCE_TO_TEMP_INDEX](#reindex_source_to_temp_index) + - [REINDEX_SOURCE_TO_TEMP_TRANSFORM](#REINDEX_SOURCE_TO_TEMP_TRANSFORM) - [Next action](#next-action-12) - [New control state](#new-control-state-12) - [REINDEX_SOURCE_TO_TEMP_INDEX_BULK](#reindex_source_to_temp_index_bulk) @@ -284,11 +284,11 @@ Read the next batch of outdated documents from the source index by using search ### New control state 1. If the batch contained > 0 documents - → `REINDEX_SOURCE_TO_TEMP_INDEX` + → `REINDEX_SOURCE_TO_TEMP_TRANSFORM` 2. If there are no more documents returned → `REINDEX_SOURCE_TO_TEMP_CLOSE_PIT` -## REINDEX_SOURCE_TO_TEMP_INDEX +## REINDEX_SOURCE_TO_TEMP_TRANSFORM ### Next action `transformRawDocs` @@ -357,7 +357,7 @@ documents. If another instance has a disabled plugin it will reindex that plugin's documents without transforming them. Because this instance doesn't know which plugins were disabled by the instance that performed the -`REINDEX_SOURCE_TO_TEMP_INDEX` step, we need to search for outdated documents +`REINDEX_SOURCE_TO_TEMP_TRANSFORM` step, we need to search for outdated documents and transform them to ensure that everything is up to date. ### New control state diff --git a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts index d4ad724911277..3a5e592a8b9bf 100644 --- a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts +++ b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.ts @@ -13,7 +13,7 @@ import type { ElasticsearchClient } from '../../elasticsearch'; import { getErrorMessage, getRequestDebugMeta } from '../../elasticsearch'; import { Model, Next, stateActionMachine } from './state_action_machine'; import { cleanup } from './migrations_state_machine_cleanup'; -import { ReindexSourceToTempIndex, ReindexSourceToTempIndexBulk, State } from './types'; +import { ReindexSourceToTempTransform, ReindexSourceToTempIndexBulk, State } from './types'; import { SavedObjectsRawDoc } from '../serialization'; interface StateTransitionLogMeta extends LogMeta { @@ -115,7 +115,9 @@ export async function migrationStateActionMachine({ const redactedNewState = { ...newState, ...{ - outdatedDocuments: ((newState as ReindexSourceToTempIndex).outdatedDocuments ?? []).map( + outdatedDocuments: ( + (newState as ReindexSourceToTempTransform).outdatedDocuments ?? [] + ).map( (doc) => ({ _id: doc._id, diff --git a/src/core/server/saved_objects/migrationsv2/model/model.test.ts b/src/core/server/saved_objects/migrationsv2/model/model.test.ts index 033a18b488841..3e48a7147bffd 100644 --- a/src/core/server/saved_objects/migrationsv2/model/model.test.ts +++ b/src/core/server/saved_objects/migrationsv2/model/model.test.ts @@ -20,7 +20,7 @@ import type { ReindexSourceToTempOpenPit, ReindexSourceToTempRead, ReindexSourceToTempClosePit, - ReindexSourceToTempIndex, + ReindexSourceToTempTransform, RefreshTarget, UpdateTargetMappingsState, UpdateTargetMappingsWaitForTaskState, @@ -962,7 +962,7 @@ describe('migrations v2 model', () => { progress: createInitialProgress(), }; - it('REINDEX_SOURCE_TO_TEMP_READ -> REINDEX_SOURCE_TO_TEMP_INDEX if the index has outdated documents to reindex', () => { + it('REINDEX_SOURCE_TO_TEMP_READ -> REINDEX_SOURCE_TO_TEMP_TRANSFORM if the index has outdated documents to reindex', () => { const outdatedDocuments = [{ _id: '1', _source: { type: 'vis' } }]; const lastHitSortValue = [123456]; const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_READ'> = Either.right({ @@ -970,8 +970,8 @@ describe('migrations v2 model', () => { lastHitSortValue, totalHits: 1, }); - const newState = model(state, res) as ReindexSourceToTempIndex; - expect(newState.controlState).toBe('REINDEX_SOURCE_TO_TEMP_INDEX'); + const newState = model(state, res) as ReindexSourceToTempTransform; + expect(newState.controlState).toBe('REINDEX_SOURCE_TO_TEMP_TRANSFORM'); expect(newState.outdatedDocuments).toBe(outdatedDocuments); expect(newState.lastHitSortValue).toBe(lastHitSortValue); expect(newState.progress.processed).toBe(undefined); @@ -1032,16 +1032,16 @@ describe('migrations v2 model', () => { it('REINDEX_SOURCE_TO_TEMP_CLOSE_PIT -> SET_TEMP_WRITE_BLOCK if action succeeded', () => { const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_CLOSE_PIT'> = Either.right({}); - const newState = model(state, res) as ReindexSourceToTempIndex; + const newState = model(state, res) as ReindexSourceToTempTransform; expect(newState.controlState).toBe('SET_TEMP_WRITE_BLOCK'); expect(newState.sourceIndex).toEqual(state.sourceIndex); }); }); - describe('REINDEX_SOURCE_TO_TEMP_INDEX', () => { - const state: ReindexSourceToTempIndex = { + describe('REINDEX_SOURCE_TO_TEMP_TRANSFORM', () => { + const state: ReindexSourceToTempTransform = { ...baseState, - controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX', + controlState: 'REINDEX_SOURCE_TO_TEMP_TRANSFORM', outdatedDocuments: [], versionIndexReadyActions: Option.none, sourceIndex: Option.some('.kibana') as Option.Some, @@ -1059,8 +1059,8 @@ describe('migrations v2 model', () => { }, ] as SavedObjectsRawDoc[]; - it('REINDEX_SOURCE_TO_TEMP_INDEX -> REINDEX_SOURCE_TO_TEMP_INDEX_BULK if action succeeded', () => { - const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_INDEX'> = Either.right({ + it('REINDEX_SOURCE_TO_TEMP_TRANSFORM -> REINDEX_SOURCE_TO_TEMP_INDEX_BULK if action succeeded', () => { + const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_TRANSFORM'> = Either.right({ processedDocs, }); const newState = model(state, res) as ReindexSourceToTempIndexBulk; @@ -1071,7 +1071,7 @@ describe('migrations v2 model', () => { }); it('increments the progress.processed counter', () => { - const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_INDEX'> = Either.right({ + const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_TRANSFORM'> = Either.right({ processedDocs, }); @@ -1089,8 +1089,8 @@ describe('migrations v2 model', () => { expect(newState.progress.processed).toBe(2); }); - it('REINDEX_SOURCE_TO_TEMP_INDEX -> REINDEX_SOURCE_TO_TEMP_READ if action succeeded but we have carried through previous failures', () => { - const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_INDEX'> = Either.right({ + it('REINDEX_SOURCE_TO_TEMP_TRANSFORM -> REINDEX_SOURCE_TO_TEMP_READ if action succeeded but we have carried through previous failures', () => { + const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_TRANSFORM'> = Either.right({ processedDocs, }); const testState = { @@ -1098,15 +1098,15 @@ describe('migrations v2 model', () => { corruptDocumentIds: ['a:b'], transformErrors: [], }; - const newState = model(testState, res) as ReindexSourceToTempIndex; + const newState = model(testState, res) as ReindexSourceToTempTransform; expect(newState.controlState).toEqual('REINDEX_SOURCE_TO_TEMP_READ'); expect(newState.corruptDocumentIds.length).toEqual(1); expect(newState.transformErrors.length).toEqual(0); expect(newState.progress.processed).toBe(0); }); - it('REINDEX_SOURCE_TO_TEMP_INDEX -> REINDEX_SOURCE_TO_TEMP_READ when response is left documents_transform_failed', () => { - const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_INDEX'> = Either.left({ + it('REINDEX_SOURCE_TO_TEMP_TRANSFORM -> REINDEX_SOURCE_TO_TEMP_READ when response is left documents_transform_failed', () => { + const res: ResponseType<'REINDEX_SOURCE_TO_TEMP_TRANSFORM'> = Either.left({ type: 'documents_transform_failed', corruptDocumentIds: ['a:b'], transformErrors: [], diff --git a/src/core/server/saved_objects/migrationsv2/model/model.ts b/src/core/server/saved_objects/migrationsv2/model/model.ts index 8aa3d7b83b295..5d8862e48df1a 100644 --- a/src/core/server/saved_objects/migrationsv2/model/model.ts +++ b/src/core/server/saved_objects/migrationsv2/model/model.ts @@ -446,7 +446,7 @@ export const model = (currentState: State, resW: ResponseType): if (res.right.outdatedDocuments.length > 0) { return { ...stateP, - controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX', + controlState: 'REINDEX_SOURCE_TO_TEMP_TRANSFORM', outdatedDocuments: res.right.outdatedDocuments, lastHitSortValue: res.right.lastHitSortValue, progress, @@ -489,11 +489,11 @@ export const model = (currentState: State, resW: ResponseType): } else { throwBadResponse(stateP, res); } - } else if (stateP.controlState === 'REINDEX_SOURCE_TO_TEMP_INDEX') { + } else if (stateP.controlState === 'REINDEX_SOURCE_TO_TEMP_TRANSFORM') { // We follow a similar control flow as for // outdated document search -> outdated document transform -> transform documents bulk index // collecting issues along the way rather than failing - // REINDEX_SOURCE_TO_TEMP_INDEX handles the document transforms + // REINDEX_SOURCE_TO_TEMP_TRANSFORM handles the document transforms const res = resW as ExcludeRetryableEsError>; // Increment the processed documents, no matter what the results are. diff --git a/src/core/server/saved_objects/migrationsv2/next.ts b/src/core/server/saved_objects/migrationsv2/next.ts index 3f3714552725b..433c0998f7567 100644 --- a/src/core/server/saved_objects/migrationsv2/next.ts +++ b/src/core/server/saved_objects/migrationsv2/next.ts @@ -12,7 +12,7 @@ import type { ReindexSourceToTempOpenPit, ReindexSourceToTempRead, ReindexSourceToTempClosePit, - ReindexSourceToTempIndex, + ReindexSourceToTempTransform, MarkVersionIndexReady, InitState, LegacyCreateReindexTargetState, @@ -105,7 +105,7 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra }), REINDEX_SOURCE_TO_TEMP_CLOSE_PIT: (state: ReindexSourceToTempClosePit) => Actions.closePit({ client, pitId: state.sourceIndexPitId }), - REINDEX_SOURCE_TO_TEMP_INDEX: (state: ReindexSourceToTempIndex) => + REINDEX_SOURCE_TO_TEMP_TRANSFORM: (state: ReindexSourceToTempTransform) => Actions.transformDocs({ transformRawDocs, outdatedDocuments: state.outdatedDocuments }), REINDEX_SOURCE_TO_TEMP_INDEX_BULK: (state: ReindexSourceToTempIndexBulk) => Actions.bulkOverwriteTransformedDocuments({ diff --git a/src/core/server/saved_objects/migrationsv2/types.ts b/src/core/server/saved_objects/migrationsv2/types.ts index 49ce12c53aa1a..4f6419930c6cc 100644 --- a/src/core/server/saved_objects/migrationsv2/types.ts +++ b/src/core/server/saved_objects/migrationsv2/types.ts @@ -233,8 +233,8 @@ export interface ReindexSourceToTempClosePit extends PostInitState { readonly sourceIndexPitId: string; } -export interface ReindexSourceToTempIndex extends PostInitState { - readonly controlState: 'REINDEX_SOURCE_TO_TEMP_INDEX'; +export interface ReindexSourceToTempTransform extends PostInitState { + readonly controlState: 'REINDEX_SOURCE_TO_TEMP_TRANSFORM'; readonly outdatedDocuments: SavedObjectsRawDoc[]; readonly sourceIndexPitId: string; readonly lastHitSortValue: number[] | undefined; @@ -434,7 +434,7 @@ export type State = Readonly< | ReindexSourceToTempOpenPit | ReindexSourceToTempRead | ReindexSourceToTempClosePit - | ReindexSourceToTempIndex + | ReindexSourceToTempTransform | ReindexSourceToTempIndexBulk | SetTempWriteBlock | CloneTempToSource From 84e42e42bc6b7c5063ad828e2f0d02be07157e5d Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 22 Sep 2021 07:17:53 -0600 Subject: [PATCH 63/69] [vega] fix vega map validation errors crashing vega (#112700) * [vega] fix vega map validation errors crashing vega * eslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../vis_types/vega/public/vega_view/vega_map_view/view.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts index cf5bf15d15051..777806d90d9a6 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts +++ b/src/plugins/vis_types/vega/public/vega_view/vega_map_view/view.ts @@ -72,7 +72,7 @@ export class VegaMapView extends VegaBaseView { const { zoom, maxZoom, minZoom } = validateZoomSettings( this._parser.mapConfig, defaults, - this.onWarn + this.onWarn.bind(this) ); const { signals } = this._vegaStateRestorer.restore() || {}; From 1456257a73fbe3a651944deec58f25118142aee2 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 22 Sep 2021 14:31:35 +0100 Subject: [PATCH 64/69] [ML] Fixing jest tests with unhandled promise rejections (#112804) --- .../components/validate_job/validate_job_view.test.js | 2 +- .../explorer/explorer_charts/explorer_charts_container.js | 8 ++++++-- .../bucket_span_estimator/bucket_span_estimator.test.ts | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js index 067b03d938ee8..8ec83d8679e87 100644 --- a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js +++ b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js @@ -41,7 +41,7 @@ function prepareTest(messages) { }; const kibana = { services: { - notifications: { toasts: { addDanger: jest.fn() } }, + notifications: { toasts: { addDanger: jest.fn(), addError: jest.fn() } }, }, }; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js index c714b388c826f..ddb46edc7b921 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js @@ -79,8 +79,12 @@ function ExplorerChartContainer({ let isCancelled = false; const generateLink = async () => { if (!isCancelled && series.functionDescription !== ML_JOB_AGGREGATION.LAT_LONG) { - const singleMetricViewerLink = await getExploreSeriesLink(mlLocator, series, timefilter); - setExplorerSeriesLink(singleMetricViewerLink); + try { + const singleMetricViewerLink = await getExploreSeriesLink(mlLocator, series, timefilter); + setExplorerSeriesLink(singleMetricViewerLink); + } catch (error) { + setExplorerSeriesLink(''); + } } }; generateLink(); diff --git a/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.test.ts b/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.test.ts index fbe0ff650cc2d..6ffb74131bf6e 100644 --- a/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.test.ts +++ b/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.test.ts @@ -12,6 +12,10 @@ import { BucketSpanEstimatorData } from '../../../common/types/job_service'; import { estimateBucketSpanFactory } from './bucket_span_estimator'; +jest.mock('../../lib/log', () => ({ + mlLog: { warn: jest.fn() }, +})); + const callAs = { search: () => Promise.resolve({ body: {} }), cluster: { getSettings: () => Promise.resolve({ body: {} }) }, From 6bf766abb91a4faf9b9379e223510230bf6d5e4e Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 22 Sep 2021 17:28:26 +0300 Subject: [PATCH 65/69] Fixes flakiness in timelion viz functional test (#112805) * Fixes flakiness in timelion viz functional test * Add sleep --- test/functional/apps/visualize/_timelion.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index ea8cb8b13ba49..85dbf7cc5ca96 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -277,17 +277,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('should show field suggestions for split argument when index pattern set', async () => { await monacoEditor.setCodeEditorValue(''); await monacoEditor.typeCodeEditorValue( - '.es(index=logstash-*, timefield=@timestamp ,split=', + '.es(index=logstash-*, timefield=@timestamp, split=', 'timelionCodeEditor' ); + // wait for split fields to load + await common.sleep(300); const suggestions = await timelion.getSuggestionItemsText(); + expect(suggestions.length).not.to.eql(0); expect(suggestions[0].includes('@message.raw')).to.eql(true); }); it('should show field suggestions for metric argument when index pattern set', async () => { await monacoEditor.typeCodeEditorValue( - '.es(index=logstash-*, timefield=@timestamp ,metric=avg:', + '.es(index=logstash-*, timefield=@timestamp, metric=avg:', 'timelionCodeEditor' ); const suggestions = await timelion.getSuggestionItemsText(); From 1adb492e1569f4de110a6ef428044b1d0a90d3d7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 22 Sep 2021 09:37:25 -0500 Subject: [PATCH 66/69] skip flaky suite. #111234 --- test/accessibility/apps/dashboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/accessibility/apps/dashboard.ts b/test/accessibility/apps/dashboard.ts index 5a3ec9d8fc869..c8a7ac566b55c 100644 --- a/test/accessibility/apps/dashboard.ts +++ b/test/accessibility/apps/dashboard.ts @@ -15,7 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const listingTable = getService('listingTable'); - describe('Dashboard', () => { + describe.skip('Dashboard', () => { const dashboardName = 'Dashboard Listing A11y'; const clonedDashboardName = 'Dashboard Listing A11y Copy'; From 4b71c435a9fed42fbcd1c8f4a3d8d5186ac117fb Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 22 Sep 2021 09:50:05 -0500 Subject: [PATCH 67/69] [data views] Rename a bunch of index pattern code references to data views (#112770) * rename a bunch of index pattern references to data views --- .../hooks/use_dashboard_app_state.test.tsx | 4 +-- .../lib/load_saved_dashboard_state.ts | 2 +- ...ndex_pattern.stub.ts => data_view.stub.ts} | 27 +++++++++++++------ ...rn.test.ts.snap => data_view.test.ts.snap} | 0 ...s.test.ts.snap => data_views.test.ts.snap} | 0 ...ndex_pattern.stub.ts => data_view.stub.ts} | 14 +++++----- ...ndex_pattern.test.ts => data_view.test.ts} | 2 +- .../common/data_views/data_views/data_view.ts | 2 +- ...ex_patterns.test.ts => data_views.test.ts} | 2 +- .../data_views/data_views/data_views.ts | 26 ++++++++---------- .../data_views/data_views/flatten_hit.test.ts | 8 +++--- .../data_views/data_views/flatten_hit.ts | 4 +-- .../data_views/data_views/format_hit.ts | 14 +++++----- .../errors/duplicate_index_pattern.ts | 2 +- .../data/common/data_views/field.stub.ts | 10 +++---- ...t.ts.snap => data_view_field.test.ts.snap} | 0 ..._field.test.ts => data_view_field.test.ts} | 2 +- ...ex_pattern_field.ts => data_view_field.ts} | 0 .../common/data_views/fields/field_list.ts | 2 +- .../data/common/data_views/fields/index.ts | 2 +- .../data/common/data_views/lib/index.ts | 3 +-- .../data/common/data_views/lib/is_default.ts | 14 ---------- ...ern.test.ts => validate_data_view.test.ts} | 2 +- ...index_pattern.ts => validate_data_view.ts} | 0 src/plugins/data/common/data_views/mocks.ts | 2 +- src/plugins/data/common/stubs.ts | 2 +- ...ndex_pattern.stub.ts => data_view.stub.ts} | 14 +++++----- ....ts => data_views_api_client.test.mock.ts} | 0 ....test.ts => data_views_api_client.test.ts} | 8 +++--- ...api_client.ts => data_views_api_client.ts} | 4 +-- .../public/data_views/data_views/index.ts | 2 +- src/plugins/data/public/data_views/index.ts | 3 +-- src/plugins/data/public/index.ts | 2 -- src/plugins/data/public/plugin.ts | 8 +++--- src/plugins/data/public/stubs.ts | 2 +- .../components/layout/discover_layout.tsx | 8 ++---- ...ver_index_pattern_management.test.tsx.snap | 2 +- .../apps/main/discover_main_route.tsx | 2 +- .../apps/main/utils/update_search_source.ts | 4 +-- .../public/saved_object/saved_object.test.ts | 8 +++--- src/plugins/visualize/public/plugin.ts | 2 +- x-pack/plugins/lens/public/plugin.ts | 6 ++--- 42 files changed, 100 insertions(+), 121 deletions(-) rename src/plugins/data/common/data_views/{index_pattern.stub.ts => data_view.stub.ts} (59%) rename src/plugins/data/common/data_views/data_views/__snapshots__/{index_pattern.test.ts.snap => data_view.test.ts.snap} (100%) rename src/plugins/data/common/data_views/data_views/__snapshots__/{index_patterns.test.ts.snap => data_views.test.ts.snap} (100%) rename src/plugins/data/common/data_views/data_views/{index_pattern.stub.ts => data_view.stub.ts} (85%) rename src/plugins/data/common/data_views/data_views/{index_pattern.test.ts => data_view.test.ts} (99%) rename src/plugins/data/common/data_views/data_views/{index_patterns.test.ts => data_views.test.ts} (99%) rename src/plugins/data/common/data_views/fields/__snapshots__/{index_pattern_field.test.ts.snap => data_view_field.test.ts.snap} (100%) rename src/plugins/data/common/data_views/fields/{index_pattern_field.test.ts => data_view_field.test.ts} (98%) rename src/plugins/data/common/data_views/fields/{index_pattern_field.ts => data_view_field.ts} (100%) delete mode 100644 src/plugins/data/common/data_views/lib/is_default.ts rename src/plugins/data/common/data_views/lib/{validate_index_pattern.test.ts => validate_data_view.test.ts} (94%) rename src/plugins/data/common/data_views/lib/{validate_index_pattern.ts => validate_data_view.ts} (100%) rename src/plugins/data/public/data_views/data_views/{index_pattern.stub.ts => data_view.stub.ts} (87%) rename src/plugins/data/public/data_views/data_views/{index_patterns_api_client.test.mock.ts => data_views_api_client.test.mock.ts} (100%) rename src/plugins/data/public/data_views/data_views/{index_patterns_api_client.test.ts => data_views_api_client.test.ts} (83%) rename src/plugins/data/public/data_views/data_views/{index_patterns_api_client.ts => data_views_api_client.ts} (95%) diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx index 1ac9d680915c6..c3b4075690261 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_app_state.test.tsx @@ -59,9 +59,7 @@ const createDashboardAppStateServices = () => { const defaults = makeDefaultServices(); const indexPatterns = {} as IndexPatternsContract; const defaultIndexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; - indexPatterns.ensureDefaultIndexPattern = jest - .fn() - .mockImplementation(() => Promise.resolve(true)); + indexPatterns.ensureDefaultDataView = jest.fn().mockImplementation(() => Promise.resolve(true)); indexPatterns.getDefault = jest .fn() .mockImplementation(() => Promise.resolve(defaultIndexPattern)); diff --git a/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts index 04461a46ad0da..3913608c6beff 100644 --- a/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/load_saved_dashboard_state.ts @@ -51,7 +51,7 @@ export const loadSavedDashboardState = async ({ notifications.toasts.addWarning(getDashboard60Warning()); return; } - await indexPatterns.ensureDefaultIndexPattern(); + await indexPatterns.ensureDefaultDataView(); let savedDashboard: DashboardSavedObject | undefined; try { savedDashboard = (await savedDashboards.get(savedDashboardId)) as DashboardSavedObject; diff --git a/src/plugins/data/common/data_views/index_pattern.stub.ts b/src/plugins/data/common/data_views/data_view.stub.ts similarity index 59% rename from src/plugins/data/common/data_views/index_pattern.stub.ts rename to src/plugins/data/common/data_views/data_view.stub.ts index 16624087f83b3..a3279434c7a0b 100644 --- a/src/plugins/data/common/data_views/index_pattern.stub.ts +++ b/src/plugins/data/common/data_views/data_view.stub.ts @@ -7,12 +7,15 @@ */ import { stubFieldSpecMap, stubLogstashFieldSpecMap } from './field.stub'; -import { createStubIndexPattern } from './data_views/index_pattern.stub'; -export { createStubIndexPattern } from './data_views/index_pattern.stub'; +import { createStubDataView } from './data_views/data_view.stub'; +export { + createStubDataView, + createStubDataView as createStubIndexPattern, +} from './data_views/data_view.stub'; import { SavedObject } from '../../../../core/types'; -import { IndexPatternAttributes } from '../types'; +import { DataViewAttributes } from '../types'; -export const stubIndexPattern = createStubIndexPattern({ +export const stubDataView = createStubDataView({ spec: { id: 'logstash-*', fields: stubFieldSpecMap, @@ -21,7 +24,9 @@ export const stubIndexPattern = createStubIndexPattern({ }, }); -export const stubIndexPatternWithoutTimeField = createStubIndexPattern({ +export const stubIndexPattern = stubDataView; + +export const stubDataViewWithoutTimeField = createStubDataView({ spec: { id: 'logstash-*', fields: stubFieldSpecMap, @@ -29,7 +34,9 @@ export const stubIndexPatternWithoutTimeField = createStubIndexPattern({ }, }); -export const stubLogstashIndexPattern = createStubIndexPattern({ +export const stubIndexPatternWithoutTimeField = stubDataViewWithoutTimeField; + +export const stubLogstashDataView = createStubDataView({ spec: { id: 'logstash-*', title: 'logstash-*', @@ -38,9 +45,11 @@ export const stubLogstashIndexPattern = createStubIndexPattern({ }, }); -export function stubbedSavedObjectIndexPattern( +export const stubLogstashIndexPattern = stubLogstashDataView; + +export function stubbedSavedObjectDataView( id: string | null = null -): SavedObject { +): SavedObject { return { id: id ?? '', type: 'index-pattern', @@ -53,3 +62,5 @@ export function stubbedSavedObjectIndexPattern( references: [], }; } + +export const stubbedSavedObjectIndexPattern = stubbedSavedObjectDataView; diff --git a/src/plugins/data/common/data_views/data_views/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/data_views/data_views/__snapshots__/data_view.test.ts.snap similarity index 100% rename from src/plugins/data/common/data_views/data_views/__snapshots__/index_pattern.test.ts.snap rename to src/plugins/data/common/data_views/data_views/__snapshots__/data_view.test.ts.snap diff --git a/src/plugins/data/common/data_views/data_views/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/data_views/data_views/__snapshots__/data_views.test.ts.snap similarity index 100% rename from src/plugins/data/common/data_views/data_views/__snapshots__/index_patterns.test.ts.snap rename to src/plugins/data/common/data_views/data_views/__snapshots__/data_views.test.ts.snap diff --git a/src/plugins/data/common/data_views/data_views/index_pattern.stub.ts b/src/plugins/data/common/data_views/data_views/data_view.stub.ts similarity index 85% rename from src/plugins/data/common/data_views/data_views/index_pattern.stub.ts rename to src/plugins/data/common/data_views/data_views/data_view.stub.ts index 3b6660c6d93dc..5ff2d077812a8 100644 --- a/src/plugins/data/common/data_views/data_views/index_pattern.stub.ts +++ b/src/plugins/data/common/data_views/data_views/data_view.stub.ts @@ -6,18 +6,18 @@ * Side Public License, v 1. */ -import { IndexPattern } from './data_view'; +import { DataView } from './data_view'; import { DataViewSpec } from '../types'; import { FieldFormatsStartCommon } from '../../../../field_formats/common'; import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; /** - * Create a custom stub index pattern. Use it in your unit tests where an {@link IndexPattern} expected. + * Create a custom stub index pattern. Use it in your unit tests where an {@link DataView} expected. * @param spec - Serialized index pattern object * @param opts - Specify index pattern options * @param deps - Optionally provide dependencies, you can provide a custom field formats implementation, by default a dummy mock is used * - * @returns - an {@link IndexPattern} instance + * @returns - an {@link DataView} instance * * * @example @@ -32,7 +32,7 @@ import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; * * ``` */ -export const createStubIndexPattern = ({ +export const createStubDataView = ({ spec, opts, deps, @@ -45,12 +45,10 @@ export const createStubIndexPattern = ({ deps?: { fieldFormats?: FieldFormatsStartCommon; }; -}): IndexPattern => { - const indexPattern = new IndexPattern({ +}): DataView => + new DataView({ spec, metaFields: opts?.metaFields ?? ['_id', '_type', '_source'], shortDotsEnable: opts?.shortDotsEnable, fieldFormats: deps?.fieldFormats ?? fieldFormatsMock, }); - return indexPattern; -}; diff --git a/src/plugins/data/common/data_views/data_views/index_pattern.test.ts b/src/plugins/data/common/data_views/data_views/data_view.test.ts similarity index 99% rename from src/plugins/data/common/data_views/data_views/index_pattern.test.ts rename to src/plugins/data/common/data_views/data_views/data_view.test.ts index 5fd1d0d051acb..6aea86a7adae7 100644 --- a/src/plugins/data/common/data_views/data_views/index_pattern.test.ts +++ b/src/plugins/data/common/data_views/data_views/data_view.test.ts @@ -18,7 +18,7 @@ import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; import { FieldFormat } from '../../../../field_formats/common'; import { RuntimeField } from '../types'; import { stubLogstashFields } from '../field.stub'; -import { stubbedSavedObjectIndexPattern } from '../index_pattern.stub'; +import { stubbedSavedObjectIndexPattern } from '../data_view.stub'; class MockFieldFormatter {} diff --git a/src/plugins/data/common/data_views/data_views/data_view.ts b/src/plugins/data/common/data_views/data_views/data_view.ts index 596887e83ec05..c61f5f7f31e3a 100644 --- a/src/plugins/data/common/data_views/data_views/data_view.ts +++ b/src/plugins/data/common/data_views/data_views/data_view.ts @@ -170,7 +170,7 @@ export class DataView implements IIndexPattern { // Date value returned in "_source" could be in any number of formats // Use a docvalue for each date field to ensure standardized formats when working with date fields - // indexPattern.flattenHit will override "_source" values when the same field is also defined in "fields" + // dataView.flattenHit will override "_source" values when the same field is also defined in "fields" const docvalueFields = reject(this.fields.getByType('date'), 'scripted').map((dateField) => { return { field: dateField.name, diff --git a/src/plugins/data/common/data_views/data_views/index_patterns.test.ts b/src/plugins/data/common/data_views/data_views/data_views.test.ts similarity index 99% rename from src/plugins/data/common/data_views/data_views/index_patterns.test.ts rename to src/plugins/data/common/data_views/data_views/data_views.test.ts index 996700b3c9118..ef9381f16d934 100644 --- a/src/plugins/data/common/data_views/data_views/index_patterns.test.ts +++ b/src/plugins/data/common/data_views/data_views/data_views.test.ts @@ -11,7 +11,7 @@ import { DataViewsService, DataView } from '.'; import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; import { UiSettingsCommon, SavedObjectsClientCommon, SavedObject } from '../types'; -import { stubbedSavedObjectIndexPattern } from '../index_pattern.stub'; +import { stubbedSavedObjectIndexPattern } from '../data_view.stub'; const createFieldsFetcher = jest.fn().mockImplementation(() => ({ getFieldsForWildcard: jest.fn().mockImplementation(() => { diff --git a/src/plugins/data/common/data_views/data_views/data_views.ts b/src/plugins/data/common/data_views/data_views/data_views.ts index f903c65d95d2a..f9b193d154770 100644 --- a/src/plugins/data/common/data_views/data_views/data_views.ts +++ b/src/plugins/data/common/data_views/data_views/data_views.ts @@ -77,9 +77,9 @@ export class DataViewsService { private fieldFormats: FieldFormatsStartCommon; private onNotification: OnNotification; private onError: OnError; - private indexPatternCache: ReturnType; + private dataViewCache: ReturnType; - ensureDefaultIndexPattern: EnsureDefaultDataView; + ensureDefaultDataView: EnsureDefaultDataView; constructor({ uiSettings, @@ -96,12 +96,9 @@ export class DataViewsService { this.fieldFormats = fieldFormats; this.onNotification = onNotification; this.onError = onError; - this.ensureDefaultIndexPattern = createEnsureDefaultDataView( - uiSettings, - onRedirectNoIndexPattern - ); + this.ensureDefaultDataView = createEnsureDefaultDataView(uiSettings, onRedirectNoIndexPattern); - this.indexPatternCache = createDataViewCache(); + this.dataViewCache = createDataViewCache(); } /** @@ -190,9 +187,9 @@ export class DataViewsService { clearCache = (id?: string) => { this.savedObjectsCache = null; if (id) { - this.indexPatternCache.clear(id); + this.dataViewCache.clear(id); } else { - this.indexPatternCache.clearAll(); + this.dataViewCache.clearAll(); } }; @@ -505,12 +502,11 @@ export class DataViewsService { get = async (id: string): Promise => { const indexPatternPromise = - this.indexPatternCache.get(id) || - this.indexPatternCache.set(id, this.getSavedObjectAndInit(id)); + this.dataViewCache.get(id) || this.dataViewCache.set(id, this.getSavedObjectAndInit(id)); // don't cache failed requests indexPatternPromise.catch(() => { - this.indexPatternCache.clear(id); + this.dataViewCache.clear(id); }); return indexPatternPromise; @@ -580,7 +576,7 @@ export class DataViewsService { )) as SavedObject; const createdIndexPattern = await this.initFromSavedObject(response); - this.indexPatternCache.set(createdIndexPattern.id!, Promise.resolve(createdIndexPattern)); + this.dataViewCache.set(createdIndexPattern.id!, Promise.resolve(createdIndexPattern)); if (this.savedObjectsCache) { this.savedObjectsCache.push(response as SavedObject); } @@ -668,7 +664,7 @@ export class DataViewsService { indexPattern.version = samePattern.version; // Clear cache - this.indexPatternCache.clear(indexPattern.id!); + this.dataViewCache.clear(indexPattern.id!); // Try the save again return this.updateSavedObject(indexPattern, saveAttempts, ignoreErrors); @@ -682,7 +678,7 @@ export class DataViewsService { * @param indexPatternId: Id of kibana Index Pattern to delete */ async delete(indexPatternId: string) { - this.indexPatternCache.clear(indexPatternId); + this.dataViewCache.clear(indexPatternId); return this.savedObjectsClient.delete(DATA_VIEW_SAVED_OBJECT_TYPE, indexPatternId); } } diff --git a/src/plugins/data/common/data_views/data_views/flatten_hit.test.ts b/src/plugins/data/common/data_views/data_views/flatten_hit.test.ts index f8e1309a38ffe..73232a65b6b72 100644 --- a/src/plugins/data/common/data_views/data_views/flatten_hit.test.ts +++ b/src/plugins/data/common/data_views/data_views/flatten_hit.test.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -import { IndexPattern } from './data_view'; +import { DataView } from './data_view'; import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; import { flattenHitWrapper } from './flatten_hit'; -import { stubbedSavedObjectIndexPattern } from '../index_pattern.stub'; +import { stubbedSavedObjectIndexPattern } from '../data_view.stub'; class MockFieldFormatter {} @@ -24,7 +24,7 @@ function create(id: string) { attributes: { timeFieldName, fields, title }, } = stubbedSavedObjectIndexPattern(id); - return new IndexPattern({ + return new DataView({ spec: { id, type, @@ -41,7 +41,7 @@ function create(id: string) { } describe('flattenHit', () => { - let indexPattern: IndexPattern; + let indexPattern: DataView; // create an indexPattern instance for each test beforeEach(() => { diff --git a/src/plugins/data/common/data_views/data_views/flatten_hit.ts b/src/plugins/data/common/data_views/data_views/flatten_hit.ts index 58a5dff66acc8..ddf484affa298 100644 --- a/src/plugins/data/common/data_views/data_views/flatten_hit.ts +++ b/src/plugins/data/common/data_views/data_views/flatten_hit.ts @@ -103,11 +103,11 @@ function decorateFlattenedWrapper(hit: Record, metaFields: Record, deep = false) { const decorateFlattened = decorateFlattenedWrapper(hit, metaFields); const cached = cache.get(hit); - const flattened = cached || flattenHit(indexPattern, hit, deep); + const flattened = cached || flattenHit(dataView, hit, deep); if (!cached) { cache.set(hit, { ...flattened }); } diff --git a/src/plugins/data/common/data_views/data_views/format_hit.ts b/src/plugins/data/common/data_views/data_views/format_hit.ts index b226013752628..39f7fef564eb0 100644 --- a/src/plugins/data/common/data_views/data_views/format_hit.ts +++ b/src/plugins/data/common/data_views/data_views/format_hit.ts @@ -15,24 +15,24 @@ const partialFormattedCache = new WeakMap(); // Takes a hit, merges it with any stored/scripted fields, and with the metaFields // returns a formatted version -export function formatHitProvider(indexPattern: DataView, defaultFormat: any) { +export function formatHitProvider(dataView: DataView, defaultFormat: any) { function convert( hit: Record, val: any, fieldName: string, type: FieldFormatsContentType = 'html' ) { - const field = indexPattern.fields.getByName(fieldName); - const format = field ? indexPattern.getFormatterForField(field) : defaultFormat; + const field = dataView.fields.getByName(fieldName); + const format = field ? dataView.getFormatterForField(field) : defaultFormat; - return format.convert(val, type, { field, hit, indexPattern }); + return format.convert(val, type, { field, hit, indexPattern: dataView }); } function formatHit(hit: Record, type: string = 'html') { if (type === 'text') { // formatHit of type text is for react components to get rid of // since it's currently just used at the discover's doc view table, caching is not necessary - const flattened = indexPattern.flattenHit(hit); + const flattened = dataView.flattenHit(hit); const result: Record = {}; for (const [key, value] of Object.entries(flattened)) { result[key] = convert(hit, value, key, type); @@ -53,7 +53,7 @@ export function formatHitProvider(indexPattern: DataView, defaultFormat: any) { const cache: Record = {}; formattedCache.set(hit, cache); - _.forOwn(indexPattern.flattenHit(hit), function (val: any, fieldName?: string) { + _.forOwn(dataView.flattenHit(hit), function (val: any, fieldName?: string) { // sync the formatted and partial cache if (!fieldName) { return; @@ -77,7 +77,7 @@ export function formatHitProvider(indexPattern: DataView, defaultFormat: any) { partialFormattedCache.set(hit, partials); } - const val = fieldName === '_source' ? hit._source : indexPattern.flattenHit(hit)[fieldName]; + const val = fieldName === '_source' ? hit._source : dataView.flattenHit(hit)[fieldName]; return convert(hit, val, fieldName); }; diff --git a/src/plugins/data/common/data_views/errors/duplicate_index_pattern.ts b/src/plugins/data/common/data_views/errors/duplicate_index_pattern.ts index d35b09e39aa76..942c104eee4e5 100644 --- a/src/plugins/data/common/data_views/errors/duplicate_index_pattern.ts +++ b/src/plugins/data/common/data_views/errors/duplicate_index_pattern.ts @@ -9,6 +9,6 @@ export class DuplicateDataViewError extends Error { constructor(message: string) { super(message); - this.name = 'DuplicateIndexPatternError'; + this.name = 'DuplicateDataViewError'; } } diff --git a/src/plugins/data/common/data_views/field.stub.ts b/src/plugins/data/common/data_views/field.stub.ts index 03bb0dee33db3..7ff51007bcefa 100644 --- a/src/plugins/data/common/data_views/field.stub.ts +++ b/src/plugins/data/common/data_views/field.stub.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { FieldSpec, IndexPatternField } from '.'; +import { FieldSpec, DataViewField } from '.'; -export const createIndexPatternFieldStub = ({ spec }: { spec: FieldSpec }): IndexPatternField => { - return new IndexPatternField(spec); +export const createIndexPatternFieldStub = ({ spec }: { spec: FieldSpec }): DataViewField => { + return new DataViewField(spec); }; export const stubFieldSpecMap: Record = { @@ -71,7 +71,7 @@ export const stubFieldSpecMap: Record = { }, }; -export const stubFields: IndexPatternField[] = Object.values(stubFieldSpecMap).map((spec) => +export const stubFields: DataViewField[] = Object.values(stubFieldSpecMap).map((spec) => createIndexPatternFieldStub({ spec }) ); @@ -404,6 +404,6 @@ export const stubLogstashFieldSpecMap: Record = { }, }; -export const stubLogstashFields: IndexPatternField[] = Object.values(stubLogstashFieldSpecMap).map( +export const stubLogstashFields: DataViewField[] = Object.values(stubLogstashFieldSpecMap).map( (spec) => createIndexPatternFieldStub({ spec }) ); diff --git a/src/plugins/data/common/data_views/fields/__snapshots__/index_pattern_field.test.ts.snap b/src/plugins/data/common/data_views/fields/__snapshots__/data_view_field.test.ts.snap similarity index 100% rename from src/plugins/data/common/data_views/fields/__snapshots__/index_pattern_field.test.ts.snap rename to src/plugins/data/common/data_views/fields/__snapshots__/data_view_field.test.ts.snap diff --git a/src/plugins/data/common/data_views/fields/index_pattern_field.test.ts b/src/plugins/data/common/data_views/fields/data_view_field.test.ts similarity index 98% rename from src/plugins/data/common/data_views/fields/index_pattern_field.test.ts rename to src/plugins/data/common/data_views/fields/data_view_field.test.ts index 906cb0ad1badd..9107036c15c1a 100644 --- a/src/plugins/data/common/data_views/fields/index_pattern_field.test.ts +++ b/src/plugins/data/common/data_views/fields/data_view_field.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IndexPatternField } from './index_pattern_field'; +import { IndexPatternField } from './data_view_field'; import { IndexPattern } from '..'; import { KBN_FIELD_TYPES } from '../../../common'; import { FieldSpec, RuntimeField } from '../types'; diff --git a/src/plugins/data/common/data_views/fields/index_pattern_field.ts b/src/plugins/data/common/data_views/fields/data_view_field.ts similarity index 100% rename from src/plugins/data/common/data_views/fields/index_pattern_field.ts rename to src/plugins/data/common/data_views/fields/data_view_field.ts diff --git a/src/plugins/data/common/data_views/fields/field_list.ts b/src/plugins/data/common/data_views/fields/field_list.ts index 8dd407e16e4c0..e2c850c0c4dd0 100644 --- a/src/plugins/data/common/data_views/fields/field_list.ts +++ b/src/plugins/data/common/data_views/fields/field_list.ts @@ -8,7 +8,7 @@ import { findIndex } from 'lodash'; import { IFieldType } from './types'; -import { DataViewField } from './index_pattern_field'; +import { DataViewField } from './data_view_field'; import { FieldSpec, DataViewFieldMap } from '../types'; import { DataView } from '../data_views'; diff --git a/src/plugins/data/common/data_views/fields/index.ts b/src/plugins/data/common/data_views/fields/index.ts index 53c8ed213cda7..0ff7397c4f7b5 100644 --- a/src/plugins/data/common/data_views/fields/index.ts +++ b/src/plugins/data/common/data_views/fields/index.ts @@ -9,4 +9,4 @@ export * from './types'; export { isFilterable, isNestedField } from './utils'; export * from './field_list'; -export * from './index_pattern_field'; +export * from './data_view_field'; diff --git a/src/plugins/data/common/data_views/lib/index.ts b/src/plugins/data/common/data_views/lib/index.ts index ae59c7d417818..0554232e64cae 100644 --- a/src/plugins/data/common/data_views/lib/index.ts +++ b/src/plugins/data/common/data_views/lib/index.ts @@ -8,7 +8,6 @@ export { DataViewMissingIndices } from './errors'; export { getTitle } from './get_title'; -export { isDefault } from './is_default'; export * from './types'; -export { validateDataView } from './validate_index_pattern'; +export { validateDataView } from './validate_data_view'; diff --git a/src/plugins/data/common/data_views/lib/is_default.ts b/src/plugins/data/common/data_views/lib/is_default.ts deleted file mode 100644 index 5a50d2862c58b..0000000000000 --- a/src/plugins/data/common/data_views/lib/is_default.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { IIndexPattern } from '../..'; - -export const isDefault = (indexPattern: IIndexPattern) => { - // Default index patterns don't have `type` defined. - return !indexPattern.type; -}; diff --git a/src/plugins/data/common/data_views/lib/validate_index_pattern.test.ts b/src/plugins/data/common/data_views/lib/validate_data_view.test.ts similarity index 94% rename from src/plugins/data/common/data_views/lib/validate_index_pattern.test.ts rename to src/plugins/data/common/data_views/lib/validate_data_view.test.ts index ed90da122484e..edf20440931e3 100644 --- a/src/plugins/data/common/data_views/lib/validate_index_pattern.test.ts +++ b/src/plugins/data/common/data_views/lib/validate_data_view.test.ts @@ -8,7 +8,7 @@ import { CONTAINS_SPACES_KEY, ILLEGAL_CHARACTERS_KEY, ILLEGAL_CHARACTERS_VISIBLE } from './types'; -import { validateDataView } from './validate_index_pattern'; +import { validateDataView } from './validate_data_view'; describe('Index Pattern Utils', () => { describe('Validation', () => { diff --git a/src/plugins/data/common/data_views/lib/validate_index_pattern.ts b/src/plugins/data/common/data_views/lib/validate_data_view.ts similarity index 100% rename from src/plugins/data/common/data_views/lib/validate_index_pattern.ts rename to src/plugins/data/common/data_views/lib/validate_data_view.ts diff --git a/src/plugins/data/common/data_views/mocks.ts b/src/plugins/data/common/data_views/mocks.ts index 6e82118f7b8b8..9585b6e60f923 100644 --- a/src/plugins/data/common/data_views/mocks.ts +++ b/src/plugins/data/common/data_views/mocks.ts @@ -7,4 +7,4 @@ */ export * from './fields/fields.mocks'; -export * from './data_views/index_pattern.stub'; +export * from './data_views/data_view.stub'; diff --git a/src/plugins/data/common/stubs.ts b/src/plugins/data/common/stubs.ts index 36bd3357e7098..5cddcf397f442 100644 --- a/src/plugins/data/common/stubs.ts +++ b/src/plugins/data/common/stubs.ts @@ -7,5 +7,5 @@ */ export * from './data_views/field.stub'; -export * from './data_views/index_pattern.stub'; +export * from './data_views/data_view.stub'; export * from './es_query/stubs'; diff --git a/src/plugins/data/public/data_views/data_views/index_pattern.stub.ts b/src/plugins/data/public/data_views/data_views/data_view.stub.ts similarity index 87% rename from src/plugins/data/public/data_views/data_views/index_pattern.stub.ts rename to src/plugins/data/public/data_views/data_views/data_view.stub.ts index 49d31def92384..b3d8448064c65 100644 --- a/src/plugins/data/public/data_views/data_views/index_pattern.stub.ts +++ b/src/plugins/data/public/data_views/data_views/data_view.stub.ts @@ -10,15 +10,15 @@ import { CoreSetup } from 'kibana/public'; import { FieldFormatsStartCommon } from '../../../../field_formats/common'; import { getFieldFormatsRegistry } from '../../../../field_formats/public/mocks'; import * as commonStubs from '../../../common/stubs'; -import { IndexPattern, IndexPatternSpec } from '../../../common'; +import { DataView, DataViewSpec } from '../../../common'; import { coreMock } from '../../../../../core/public/mocks'; /** - * Create a custom stub index pattern. Use it in your unit tests where an {@link IndexPattern} expected. + * Create a custom stub index pattern. Use it in your unit tests where an {@link DataView} expected. * @param spec - Serialized index pattern object * @param opts - Specify index pattern options * @param deps - Optionally provide dependencies, you can provide a custom field formats implementation, by default client side registry with real formatters implementation is used * - * @returns - an {@link IndexPattern} instance + * @returns - an {@link DataView} instance * * @remark - This is a client side version, a browser-agnostic version is available in {@link commonStubs | common}. * The main difference is that client side version by default uses client side field formats service, where common version uses a dummy field formats mock. @@ -35,12 +35,12 @@ import { coreMock } from '../../../../../core/public/mocks'; * * ``` */ -export const createStubIndexPattern = ({ +export const createStubDataView = ({ spec, opts, deps, }: { - spec: IndexPatternSpec; + spec: DataViewSpec; opts?: { shortDotsEnable?: boolean; metaFields?: string[]; @@ -49,8 +49,8 @@ export const createStubIndexPattern = ({ fieldFormats?: FieldFormatsStartCommon; core?: CoreSetup; }; -}): IndexPattern => { - return commonStubs.createStubIndexPattern({ +}): DataView => { + return commonStubs.createStubDataView({ spec, opts, deps: { diff --git a/src/plugins/data/public/data_views/data_views/index_patterns_api_client.test.mock.ts b/src/plugins/data/public/data_views/data_views/data_views_api_client.test.mock.ts similarity index 100% rename from src/plugins/data/public/data_views/data_views/index_patterns_api_client.test.mock.ts rename to src/plugins/data/public/data_views/data_views/data_views_api_client.test.mock.ts diff --git a/src/plugins/data/public/data_views/data_views/index_patterns_api_client.test.ts b/src/plugins/data/public/data_views/data_views/data_views_api_client.test.ts similarity index 83% rename from src/plugins/data/public/data_views/data_views/index_patterns_api_client.test.ts rename to src/plugins/data/public/data_views/data_views/data_views_api_client.test.ts index a6742852533a0..09ee001c218b5 100644 --- a/src/plugins/data/public/data_views/data_views/index_patterns_api_client.test.ts +++ b/src/plugins/data/public/data_views/data_views/data_views_api_client.test.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { http } from './index_patterns_api_client.test.mock'; -import { IndexPatternsApiClient } from './index_patterns_api_client'; +import { http } from './data_views_api_client.test.mock'; +import { DataViewsApiClient } from './data_views_api_client'; describe('IndexPatternsApiClient', () => { let fetchSpy: jest.SpyInstance; - let indexPatternsApiClient: IndexPatternsApiClient; + let indexPatternsApiClient: DataViewsApiClient; beforeEach(() => { fetchSpy = jest.spyOn(http, 'fetch').mockImplementation(() => Promise.resolve({})); - indexPatternsApiClient = new IndexPatternsApiClient(http); + indexPatternsApiClient = new DataViewsApiClient(http); }); test('uses the right URI to fetch fields for time patterns', async function () { diff --git a/src/plugins/data/public/data_views/data_views/index_patterns_api_client.ts b/src/plugins/data/public/data_views/data_views/data_views_api_client.ts similarity index 95% rename from src/plugins/data/public/data_views/data_views/index_patterns_api_client.ts rename to src/plugins/data/public/data_views/data_views/data_views_api_client.ts index 295cd99e7e017..d11ec7cfa003d 100644 --- a/src/plugins/data/public/data_views/data_views/index_patterns_api_client.ts +++ b/src/plugins/data/public/data_views/data_views/data_views_api_client.ts @@ -10,13 +10,13 @@ import { HttpSetup } from 'src/core/public'; import { DataViewMissingIndices } from '../../../common/data_views/lib'; import { GetFieldsOptions, - IIndexPatternsApiClient, + IDataViewsApiClient, GetFieldsOptionsTimePattern, } from '../../../common/data_views/types'; const API_BASE_URL: string = `/api/index_patterns/`; -export class IndexPatternsApiClient implements IIndexPatternsApiClient { +export class DataViewsApiClient implements IDataViewsApiClient { private http: HttpSetup; constructor(http: HttpSetup) { diff --git a/src/plugins/data/public/data_views/data_views/index.ts b/src/plugins/data/public/data_views/data_views/index.ts index 4b31933442893..e0d18d47f39db 100644 --- a/src/plugins/data/public/data_views/data_views/index.ts +++ b/src/plugins/data/public/data_views/data_views/index.ts @@ -8,4 +8,4 @@ export * from '../../../common/data_views/data_views'; export * from './redirect_no_index_pattern'; -export * from './index_patterns_api_client'; +export * from './data_views_api_client'; diff --git a/src/plugins/data/public/data_views/index.ts b/src/plugins/data/public/data_views/index.ts index 02e36d893fa6f..0125b173989fb 100644 --- a/src/plugins/data/public/data_views/index.ts +++ b/src/plugins/data/public/data_views/index.ts @@ -12,7 +12,6 @@ export { ILLEGAL_CHARACTERS_VISIBLE, ILLEGAL_CHARACTERS, validateDataView, - isDefault, } from '../../common/data_views/lib'; export { flattenHitWrapper, formatHitProvider, onRedirectNoIndexPattern } from './data_views'; @@ -22,7 +21,7 @@ export { IndexPatternsService, IndexPatternsContract, IndexPattern, - IndexPatternsApiClient, + DataViewsApiClient, DataViewsService, DataViewsContract, DataView, diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 6480a0a340340..e1f5b98baca9c 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -45,7 +45,6 @@ import { CONTAINS_SPACES_KEY, ILLEGAL_CHARACTERS_VISIBLE, ILLEGAL_CHARACTERS, - isDefault, validateDataView, flattenHitWrapper, } from './data_views'; @@ -58,7 +57,6 @@ export const indexPatterns = { CONTAINS_SPACES_KEY, ILLEGAL_CHARACTERS_VISIBLE, ILLEGAL_CHARACTERS, - isDefault, isFilterable, isNestedField, validate: validateDataView, diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 63f32e50f61ab..aa766f78a5ecb 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -22,9 +22,9 @@ import { SearchService } from './search/search_service'; import { QueryService } from './query'; import { createIndexPatternSelect } from './ui/index_pattern_select'; import { - IndexPatternsService, + DataViewsService, onRedirectNoIndexPattern, - IndexPatternsApiClient, + DataViewsApiClient, UiSettingsPublicToCommon, } from './data_views'; import { @@ -145,10 +145,10 @@ export class DataPublicPlugin setOverlays(overlays); setUiSettings(uiSettings); - const indexPatterns = new IndexPatternsService({ + const indexPatterns = new DataViewsService({ uiSettings: new UiSettingsPublicToCommon(uiSettings), savedObjectsClient: new SavedObjectsClientPublicToCommon(savedObjects.client), - apiClient: new IndexPatternsApiClient(http), + apiClient: new DataViewsApiClient(http), fieldFormats, onNotification: (toastInputFields) => { notifications.toasts.add(toastInputFields); diff --git a/src/plugins/data/public/stubs.ts b/src/plugins/data/public/stubs.ts index 8e790a2991b05..3d160a56bd8cf 100644 --- a/src/plugins/data/public/stubs.ts +++ b/src/plugins/data/public/stubs.ts @@ -7,4 +7,4 @@ */ export * from '../common/stubs'; -export { createStubIndexPattern } from './data_views/data_views/index_pattern.stub'; +export { createStubDataView } from './data_views/data_views/data_view.stub'; diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx index 7e3d7ff10b3a6..c2d09f31e3e0a 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.tsx @@ -23,11 +23,7 @@ import { METRIC_TYPE } from '@kbn/analytics'; import classNames from 'classnames'; import { DiscoverNoResults } from '../no_results'; import { LoadingSpinner } from '../loading_spinner/loading_spinner'; -import { - esFilters, - IndexPatternField, - indexPatterns as indexPatternsUtils, -} from '../../../../../../../data/public'; +import { esFilters, IndexPatternField } from '../../../../../../../data/public'; import { DiscoverSidebarResponsive } from '../sidebar'; import { DiscoverLayoutProps } from './types'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../../common'; @@ -79,7 +75,7 @@ export function DiscoverLayout({ }, [dataState.fetchStatus]); const timeField = useMemo(() => { - return indexPatternsUtils.isDefault(indexPattern) ? indexPattern.timeFieldName : undefined; + return indexPattern.type !== 'rollup' ? indexPattern.timeFieldName : undefined; }, [indexPattern]); const [isSidebarClosed, setIsSidebarClosed] = useState(false); diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap index 3ad902ed22fe8..ebb06e0b2ecd3 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap @@ -106,7 +106,7 @@ exports[`Discover IndexPattern Management renders correctly 1`] = ` } } selectedIndexPattern={ - IndexPattern { + DataView { "allowNoIndex": false, "deleteFieldFormat": [Function], "fieldAttrs": Object {}, diff --git a/src/plugins/discover/public/application/apps/main/discover_main_route.tsx b/src/plugins/discover/public/application/apps/main/discover_main_route.tsx index 53f95f38c96bd..5141908e44ade 100644 --- a/src/plugins/discover/public/application/apps/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/apps/main/discover_main_route.tsx @@ -59,7 +59,7 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { const savedSearchId = id; async function loadDefaultOrCurrentIndexPattern(usedSavedSearch: SavedSearch) { - await data.indexPatterns.ensureDefaultIndexPattern(); + await data.indexPatterns.ensureDefaultDataView(); const { appStateContainer } = getState({ history, uiSettings: config }); const { index } = appStateContainer.getState(); const ip = await loadIndexPattern(index || '', data.indexPatterns, config); diff --git a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts index 5a4a543a1d5c7..4dfcbc7b79712 100644 --- a/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts +++ b/src/plugins/discover/public/application/apps/main/utils/update_search_source.ts @@ -10,7 +10,6 @@ import { SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; import { IndexPattern, ISearchSource } from '../../../../../../data/common'; import { SortOrder } from '../../../../saved_searches/types'; import { DiscoverServices } from '../../../../build_services'; -import { indexPatterns as indexPatternsUtils } from '../../../../../../data/public'; import { getSortForSearchSource } from '../components/doc_table'; /** @@ -52,8 +51,7 @@ export function updateSearchSource( // document-like response. .setPreferredSearchStrategyId('default'); - // this is not the default index pattern, it determines that it's not of type rollup - if (indexPatternsUtils.isDefault(indexPattern)) { + if (indexPattern.type !== 'rollup') { // Set the date range filter fields from timeFilter using the absolute format. Search sessions requires that it be converted from a relative range searchSource.setField('filter', data.query.timefilter.timefilter.createFilter(indexPattern)); } diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts index 2f45ee211c8c9..bd8d69d6b693e 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts @@ -20,7 +20,7 @@ import { coreMock } from '../../../../core/public/mocks'; import { dataPluginMock, createSearchSourceMock } from '../../../../plugins/data/public/mocks'; import { createStubIndexPattern } from '../../../../plugins/data/common/stubs'; import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public'; -import { IndexPattern } from '../../../data/common'; +import { DataView } from '../../../data/common'; import { savedObjectsDecoratorRegistryMock } from './decorators/registry.mock'; describe('Saved Object', () => { @@ -725,7 +725,7 @@ describe('Saved Object', () => { type: 'dashboard', afterESResp: afterESRespCallback, searchSource: true, - indexPattern: { id: indexPatternId } as IndexPattern, + indexPattern: { id: indexPatternId } as DataView, }; stubESResponse( @@ -752,7 +752,7 @@ describe('Saved Object', () => { return savedObject.init!().then(() => { expect(afterESRespCallback).toHaveBeenCalled(); const index = savedObject.searchSource!.getField('index'); - expect(index instanceof IndexPattern).toBe(true); + expect(index instanceof DataView).toBe(true); expect(index!.id).toEqual(indexPatternId); }); }); @@ -765,7 +765,7 @@ describe('Saved Object', () => { type: 'dashboard', afterESResp: afterESRespCallback, searchSource: false, - indexPattern: { id: indexPatternId } as IndexPattern, + indexPattern: { id: indexPatternId } as DataView, }; stubESResponse(getMockedDocResponse(indexPatternId)); diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index aef131ce8d530..b128c09209743 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -162,7 +162,7 @@ export class VisualizePlugin pluginsStart.data.indexPatterns.clearCache(); // make sure a default index pattern exists // if not, the page will be redirected to management and visualize won't be rendered - await pluginsStart.data.indexPatterns.ensureDefaultIndexPattern(); + await pluginsStart.data.indexPatterns.ensureDefaultDataView(); appMounted(); diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 5326927d2c6c5..7891b5990989c 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -233,10 +233,10 @@ export class LensPlugin { const getPresentationUtilContext = () => startServices().plugins.presentationUtil.ContextProvider; - const ensureDefaultIndexPattern = async () => { + const ensureDefaultDataView = async () => { // make sure a default index pattern exists // if not, the page will be redirected to management and visualize won't be rendered - await startServices().plugins.data.indexPatterns.ensureDefaultIndexPattern(); + await startServices().plugins.data.indexPatterns.ensureDefaultDataView(); }; core.application.register({ @@ -261,7 +261,7 @@ export class LensPlugin { const frameStart = this.editorFrameService!.start(coreStart, deps); this.stopReportManager = stopReportManager; - await ensureDefaultIndexPattern(); + await ensureDefaultDataView(); return mountApp(core, params, { createEditorFrame: frameStart.createInstance, attributeService: getLensAttributeService(coreStart, deps), From 48b126a2ea9ea46808b3137fbd9bfe01231b4285 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Wed, 22 Sep 2021 11:50:07 -0400 Subject: [PATCH 68/69] Adding cases team to directories (#112835) --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6ae834b58fc89..73d22362345bd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -381,9 +381,9 @@ #CC# /x-pack/plugins/security_solution/ @elastic/security-solution # Security Solution sub teams -/x-pack/plugins/cases @elastic/security-threat-hunting +/x-pack/plugins/cases @elastic/security-threat-hunting-cases /x-pack/plugins/timelines @elastic/security-threat-hunting -/x-pack/test/case_api_integration @elastic/security-threat-hunting +/x-pack/test/case_api_integration @elastic/security-threat-hunting-cases /x-pack/plugins/lists @elastic/security-detections-response ## Security Solution sub teams - security-onboarding-and-lifecycle-mgt From cdc34cf32f7582235483e64385cd5d15b7f4b27c Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 22 Sep 2021 11:54:45 -0400 Subject: [PATCH 69/69] [Mappings editor] Add multi-field support for ip type (#112477) --- .../components/mappings_editor/lib/utils.test.ts | 15 ++++++++++++++- .../components/mappings_editor/lib/utils.ts | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts index 321500730c82f..162bb59a0528a 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts @@ -10,7 +10,7 @@ jest.mock('../constants', () => { return { MAIN_DATA_TYPE_DEFINITION: {}, TYPE_DEFINITION }; }); -import { stripUndefinedValues, getTypeLabelFromField } from './utils'; +import { stripUndefinedValues, getTypeLabelFromField, getFieldMeta } from './utils'; describe('utils', () => { describe('stripUndefinedValues()', () => { @@ -77,4 +77,17 @@ describe('utils', () => { ).toBe('Other: hyperdrive'); }); }); + + describe('getFieldMeta', () => { + test('returns "canHaveMultiFields:true" for IP data type', () => { + expect(getFieldMeta({ name: 'ip_field', type: 'ip' })).toEqual({ + canHaveChildFields: false, + canHaveMultiFields: true, + childFieldsName: 'fields', + hasChildFields: false, + hasMultiFields: false, + isExpanded: false, + }); + }); + }); }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts index bc02640ba7b78..44461f1b98aef 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts @@ -40,7 +40,7 @@ import { TreeItem } from '../components/tree'; export const getUniqueId = () => uuid.v4(); export const getChildFieldsName = (dataType: DataType): ChildFieldName | undefined => { - if (dataType === 'text' || dataType === 'keyword') { + if (dataType === 'text' || dataType === 'keyword' || dataType === 'ip') { return 'fields'; } else if (dataType === 'object' || dataType === 'nested') { return 'properties';