From 746e236869927812c0d66906fe1abecb63e9ca36 Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 16 Mar 2020 10:44:28 +0100 Subject: [PATCH 1/8] [SIEM] Adds 'Closes and opens signals' Cypress test (#59950) * adds signals data * adds 'closes and opens signals' * refactors test * adds extra check to see that the selected number of signals is correct Co-authored-by: Elastic Machine --- .../cypress/integration/detections.spec.ts | 114 + .../siem/cypress/screens/detections.ts | 16 + .../plugins/siem/cypress/tasks/detections.ts | 46 +- .../plugins/siem/cypress/tasks/es_archiver.ts | 8 + .../utility_bar/utility_bar_action.tsx | 16 +- .../utility_bar/utility_bar_text.tsx | 5 +- .../signals/signals_filter_group/index.tsx | 2 + .../signals/signals_utility_bar/index.tsx | 7 +- .../es_archives/signals/data.json.gz | Bin 0 -> 58602 bytes .../es_archives/signals/mappings.json | 7602 +++++++++++++++++ 10 files changed, 7809 insertions(+), 7 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts create mode 100644 x-pack/test/siem_cypress/es_archives/signals/data.json.gz create mode 100644 x-pack/test/siem_cypress/es_archives/signals/mappings.json diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts new file mode 100644 index 0000000000000..1624586d4ca14 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + NUMBER_OF_SIGNALS, + SELECTED_SIGNALS, + SHOWING_SIGNALS, + SIGNALS, +} from '../screens/detections'; + +import { + closeSignals, + goToClosedSignals, + goToOpenedSignals, + openSignals, + selectNumberOfSignals, + waitForSignalsPanelToBeLoaded, + waitForSignals, + waitForSignalsToBeLoaded, +} from '../tasks/detections'; +import { esArchiverLoad } from '../tasks/es_archiver'; +import { loginAndWaitForPage } from '../tasks/login'; + +import { DETECTIONS } from '../urls/navigation'; + +describe('Detections', () => { + before(() => { + esArchiverLoad('signals'); + loginAndWaitForPage(DETECTIONS); + }); + + it('Closes and opens signals', () => { + waitForSignalsPanelToBeLoaded(); + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignals} signals`); + + const numberOfSignalsToBeClosed = 3; + selectNumberOfSignals(numberOfSignalsToBeClosed); + + cy.get(SELECTED_SIGNALS) + .invoke('text') + .should('eql', `Selected ${numberOfSignalsToBeClosed} signals`); + + closeSignals(); + waitForSignals(); + cy.reload(); + waitForSignals(); + + const expectedNumberOfSignalsAfterClosing = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignalsAfterClosing.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignalsAfterClosing.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signals`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + + const numberOfSignalsToBeOpened = 1; + selectNumberOfSignals(numberOfSignalsToBeOpened); + + cy.get(SELECTED_SIGNALS) + .invoke('text') + .should('eql', `Selected ${numberOfSignalsToBeOpened} signal`); + + openSignals(); + waitForSignals(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + goToClosedSignals(); + waitForSignals(); + + const expectedNumberOfClosedSignalsAfterOpened = 2; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', expectedNumberOfClosedSignalsAfterOpened.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfClosedSignalsAfterOpened.toString()} signals`); + cy.get(SIGNALS).should('have.length', expectedNumberOfClosedSignalsAfterOpened); + + goToOpenedSignals(); + waitForSignals(); + + const expectedNumberOfOpenedSignals = + +numberOfSignals - expectedNumberOfClosedSignalsAfterOpened; + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfOpenedSignals.toString()} signals`); + + cy.get('[data-test-subj="server-side-event-count"]') + .invoke('text') + .should('eql', expectedNumberOfOpenedSignals.toString()); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8089b028a10d4..8b5ba23578807 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -4,6 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +export const CLOSED_SIGNALS_BTN = '[data-test-subj="closedSignals"]'; + export const LOADING_SIGNALS_PANEL = '[data-test-subj="loading-signals-panel"]'; export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal-detection-rules"]'; + +export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; + +export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; + +export const SELECTED_SIGNALS = '[data-test-subj="selectedSignals"]'; + +export const SHOWING_SIGNALS = '[data-test-subj="showingSignals"]'; + +export const SIGNALS = '[data-test-subj="event"]'; + +export const SIGNAL_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 4a0a565a74e27..21a0c136b90df 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -4,7 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN } from '../screens/detections'; +import { + CLOSED_SIGNALS_BTN, + LOADING_SIGNALS_PANEL, + MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNALS_BTN, + OPENED_SIGNALS_BTN, + SIGNALS, + SIGNAL_CHECKBOX, +} from '../screens/detections'; +import { REFRESH_BUTTON } from '../screens/siem_header'; + +export const closeSignals = () => { + cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); +}; + +export const goToClosedSignals = () => { + cy.get(CLOSED_SIGNALS_BTN).click({ force: true }); +}; export const goToManageSignalDetectionRules = () => { cy.get(MANAGE_SIGNAL_DETECTION_RULES_BTN) @@ -12,6 +29,28 @@ export const goToManageSignalDetectionRules = () => { .click({ force: true }); }; +export const goToOpenedSignals = () => { + cy.get(OPENED_SIGNALS_BTN).click({ force: true }); +}; + +export const openSignals = () => { + cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); +}; + +export const selectNumberOfSignals = (numberOfSignals: number) => { + for (let i = 0; i < numberOfSignals; i++) { + cy.get(SIGNAL_CHECKBOX) + .eq(i) + .click({ force: true }); + } +}; + +export const waitForSignals = () => { + cy.get(REFRESH_BUTTON) + .invoke('text') + .should('not.equal', 'Updating'); +}; + export const waitForSignalsIndexToBeCreated = () => { cy.request({ url: '/api/detection_engine/index', retryOnStatusCodeFailure: true }).then( response => { @@ -26,3 +65,8 @@ export const waitForSignalsPanelToBeLoaded = () => { cy.get(LOADING_SIGNALS_PANEL).should('exist'); cy.get(LOADING_SIGNALS_PANEL).should('not.exist'); }; + +export const waitForSignalsToBeLoaded = () => { + const expectedNumberOfDisplayedSignals = 25; + cy.get(SIGNALS).should('have.length', expectedNumberOfDisplayedSignals); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts index 72c95cba2361b..1743fcb561064 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/es_archiver.ts @@ -12,6 +12,14 @@ export const esArchiverLoadEmptyKibana = () => { ); }; +export const esArchiverLoad = (folder: string) => { + cy.exec( + `node ../../../../scripts/es_archiver load ${folder} --dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( + 'ELASTICSEARCH_URL' + )} --kibana-url ${Cypress.config().baseUrl}` + ); +}; + export const esArchiverUnloadEmptyKibana = () => { cy.exec( `node ../../../../scripts/es_archiver empty_kibana unload empty--dir ../../../test/siem_cypress/es_archives --config ../../../../test/functional/config.js --es-url ${Cypress.env( diff --git a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx index d3e2be0e8f816..19e884e326390 100644 --- a/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx +++ b/x-pack/legacy/plugins/siem/public/components/utility_bar/utility_bar_action.tsx @@ -42,11 +42,23 @@ Popover.displayName = 'Popover'; export interface UtilityBarActionProps extends LinkIconProps { popoverContent?: (closePopover: () => void) => React.ReactNode; + dataTestSubj?: string; } export const UtilityBarAction = React.memo( - ({ children, color, disabled, href, iconSide, iconSize, iconType, onClick, popoverContent }) => ( - + ({ + children, + color, + dataTestSubj, + disabled, + href, + iconSide, + iconSize, + iconType, + onClick, + popoverContent, + }) => ( + {popoverContent ? ( (({ children }) => ( - {children} +export const UtilityBarText = React.memo(({ children, dataTestSubj }) => ( + {children} )); UtilityBarText.displayName = 'UtilityBarText'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx index 3c1317d463f8e..a8dd22863e3c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_filter_group/index.tsx @@ -32,6 +32,7 @@ const SignalsTableFilterGroupComponent: React.FC = ({ onFilterGroupChange return ( = ({ onFilterGroupChange diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx index 25c0424cadf11..2000a699ab18d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/signals_utility_bar/index.tsx @@ -65,13 +65,15 @@ const SignalsUtilityBarComponent: React.FC = ({ - {i18n.SHOWING_SIGNALS(formattedTotalCount, totalCount)} + + {i18n.SHOWING_SIGNALS(formattedTotalCount, totalCount)} + {canUserCRUD && hasIndexWrite && ( <> - + {i18n.SELECTED_SIGNALS( showClearSelection ? formattedTotalCount : formattedSelectedEventsCount, showClearSelection ? totalCount : Object.keys(selectedEventIds).length @@ -79,6 +81,7 @@ const SignalsUtilityBarComponent: React.FC = ({ +#yKt5Ih8T7#Ii;oPprM-zGWd z)_wQ9`@j0Cdhe;4-rc>{>eaoz)lK;x3CW4p(G2E=wWXIOhoh&PE6fqZPlI&4gX;Iw zWVuwle8)Az7bo)%AKt+%<0?`ej_K;K62(J(!@kYm9BV^gfV=Wg4Qq#8hq?-7Ty z5k4ScR9W*%uQDdKS6LGeJwZg-Dxp~2fJ4mIzu~~yMU67pnzaUWxKT4xOUbWRYlzUp!*dTgsd!}wILFNq4t7k08cq>3p-DZ-D(Oa}ja6LDUJb$iGZqU@F^ z)0AN>eaqCG)Ni<#In+9x>}Or7*C^Ajh0j1W+AhTB&U^3g`F7u7kMEULW;;u@HloN{wBS%Jl)NS=-vsQMY3A%b;cD#j zbzafCKzDFF`7vPUK&{qYkiYI`yw*m4*GLb>>FM-Mh!uMX^~0`FcjtmzeJQ!YE2lLR z5M}*F>Zf(3r-POz|E6_RNDiozRAl!uwoGzSeQlvA-ssbaKL(%-cy6*Ko^9ZX^Ko?$7D3-G2G)^O48L?ekj3-TJjwv*qHBO0kD85_xX}tvX!?StG7GQXnN&F?!tj zU7e=8NAV=eWxiZX(_RJcjeTUF@!xw?EZO7DBRRxPd#!mSqmf>$Hii%`%;=Q|>uA_q zxLhG{oe*$cGkoYlzfk2=<>-J)d=PcJdHCUa66FKRK1il9KhMxx@j}5U`mM?lKoN4u z_INAY1x4FYv1>ky?f1%ScIyq{?GE4EB#qZ)#xZ9UaR$hrbaLq^ZcWfAU&evWa>{{OT-JOgyU&?cindE9ah}23K z&pVsy@Ez0<<}MX*UfnJm_GFOD{E%4M=yUuX!dNdbD^Y!~U%JRWe*D|$rhfnMvuUPGUhST&C}f4bA^gK2j=x5ZaWq@B?U zMmMA{d2@y!WRIeNj`UIWtz0BsrJcbNat}t~Bmd*w`qS-o&tqEJQ3_0Xv9vnZaq8&; zm`bf#S)r)Nr&q3AczaIRp(K%wT~`lBzM%*uI&~X72lJCc>{}G&rI)q~MUaz^QDtY! zNz3+DLwIR_e`~Uki}t!KY8Hlh@da+F7(Tp4H;$SK-;L-M3X@svrA>$d>Oy{tkwQYl zm!Mq#pO#QR`{q}6P|3CO3ffD5(Wmw+$~#U_W4iLsCzGZOrw3Ofv4(7aALv7LtDAXO z5Xf^rv3g-HHgCVUZt)8kDi6u3IRH7~-o56Bf?PV&;6B*a8wY@u20f<_a@9+XI?Y}u zY!x_CMn_H8R^oZ;J2;Y7Wu}0C7nbRBc~*QRRWG+XvCvE(@zkueM(?w=YxeTDtciZ7 zp=kl(V;L^nC$+>Kotsi-ukej0PK(oINFK^&QKx52;+B_r8&6D2;?di%bHuGKT}e0V zG#TDFK2;+Yz|&19XOh=(sAp1~pID=Hoij&D*_f_9%Z4;>MI ztQaIozGztOY54ge6RtV+kFykFtvdMj_oL?uM}C%n6Hf&{KIzP08c3w&({98Mp*)*F z9?w#RifrLVy=@cS+i|n<0*uM>Jf+$_!)|>^l7-*<7Z9sirIALYV31))($WXl%6@j& zw}x!P4u)|k3fFx@8MBbzHy2RNdgiae!jMTp+YJTxMcdEe`fInJ_bD`57b_!Ae=dxo zHT}rMigj1^r6f}T+nICBc&RZvdc`okdm~NSIxhK{7ut(F0v^4O?$_X!d&83t#gm(Q z6Oqi4<}1VA^k(81`gKj|`uhSFyW>mg-~BkhtCfTk((Ib1Pgs!u9H10*RFfn&i$>vX^l;_OPnJ&` z?phyN94d|(dt=IGbTV#)TJ0%PMf83V)byD?{Hacuvbj3sMCd8)>01Bwr10T~6dtT{ zp$TUVY&R;PA>A9I7mPP` zMywqoJYSJiECv(>rq54d9S2UoI4e;dn349)oA!|mWu6R=?<7gyw= zBN=mk;6@wA{DUeOKn^Z`77%m)jfNX0w6f-{YB~~B(u-GkWJ1@4hM-DKLB5|vhuGAJ zcYez<>{|voOjw|08W}u+9wH1Z@Xil|1%vgS^94}CU>WFVijKv%gp;Hd1;sN9WLi*& zZw`@+q`BuZC7=(gYU0M|P8?D-W@g9tOu_%FA&upm?XRfsDSz$$a4mn0_9f_WDcty~ z#$NFoyHYvg>-z;NGB%;DNSqX~@;I{H9{0r1HdEHYt16J<=v+%_D_Y;_<*`Mil4`AN z)lbOy;tr2@-kL-};DWaEujWvhke6b(@%d90bDOuPK5(~t|A z*c*~da&=vLY$6f>UP8^Ye|KnNkFupHLR!3{yKW_M-(=^-9n|Yif=Ipy zvf3orj_0OzO<&z3r?KNak8ukHRnJzdk$+FMQS~k#iY?btgOIHsk#z};>QRUk#HDDw zq^t8z_mHL48LqBRyxJC-ueE=hs}4IW z{4ngW>?52nfD)G7aF>qhFqV+UXp<{}hp9I=g>+5gQI5xI;M{i^w zx;+ar!FTk2;fFsJEl_Yd+ZFN;Lb-e21<=y=YQ0IxW^4LMT(#0FzJP4>n`)j%L|V|^ zahS{WWnL(<*4e&tJua6t%H9AInjM*bocSL7;#d3S%MeGgxbJw_uTmFEyo>YVYkkrzaOpq#utwHCqwy z+jm7#HXlu_3r5fH2k%P?u3LwZ9Z@mbvV(Jjg{-C1>6v7Q-re7yZ4u5l5_b9J76|zG zq1+&*kEM*IP@@QNL&83k56Q~dT*#N`36+I@b1!h!S7&LAF-%{}`rP@UXxKj8lTe4= zA3y)o4a)h{uh}$+DKORD3EeXQHTb~#0BQhASRX@1K5!^oI-lZ+%LnYiIk}i6#iIDM zvDZ5_{5UE}pQkHrg_XrE!|ZpR;s5>L)W>f{`TBy?R5QeFA*(k%Zl{Zg%>a&*B2O~N z2gfP;9f_%AjbwHNp=8sBjmx((i3mBAQ$_KQ6s2XHEtSc_s%;+coa##4oxh@wIiDH9 z%+;g7XzC{1Opcq2>gCl)sdi@t1s4-U1l2%G2)252V8=R>HT3;?L;VE5p~>HTL;Hpn zr;IiR1Hhu!0R5!7Tvm=P0$V14@`Wv3B>h)S_omHEF>>9)r^G&KZBiUFCw>SVvvey zx3WdfD8Px`){Lv*7Ln~-c!H}h-@clz@dtACXOtnHLwKOxU)(gE_tucH*T8~-z2|gv zH0WXN{Car!e2cCTm0hZK!+Oh2kj?5@neDuM4a=|7RD*6+|t= zxpYJcQn{9fzr|WCyiD(tJLa27kJ=NSCR34lgQd>m(nh7}{|Zhl578dczV%CoeQVJ2 zvGr3T?fj={WRAy*X?$@a+f|Rf1^Jcw(gQR4t##M!sF7~=zR(f8k5QKao!qByFO3$~ zRphc3(nVZ8X*krz(t9@Y^?>A_$uNdHMwmDbKDRlx_S|IKo|_&8}**bHO@| z?DHfW-`hqH+0jxurX`XjyVfi$4Mbw4+`f`YI$nJZTpFG%sMJ{T#M*<+?Pzy?)y)@! zFeLPeB1bAlOUu5d`vU^)7?w9Td*-A-t6M+`-+o-CE(wF;qm6MZGrDqX8;jMS-TXq6 zuMA>dU~aBcEeX?Q2Rln|7(hEC`Y!;H=iT%C6@1=hA+mSe+jt#EuXal@rubx~hZpho za}>soGlSH4=LY`lNXgVMzb}-%m&fvr!2u*yF5Aq@i=%>dgZVoDWwE5Ini!PBQ$dH{ zIIYgXf900Dn~tp&R4t0kkDrcN-5P=pkY6;OkpZbUG$EK^OlVN^Mc6g`Me;~O#4~&&Qw-3tDX3sV^)FB?C9CsJH#B-hTGGW1B8l> zG>&yYUCys?+=YalET@VbGVwJ{n-4OR8gVS`uh`JL4p^0NQe9~_H)|3A>qiHxAnEq9mW(-Q;GKkDDkFq>u0 z6$(5Ei*!Ufs9t0&S^j8`U$cr}?~hTejrfVW*3&xYYO+##Pd@!iS3cNVy-m`ST*CJ% zvV99Mw3AF_sBCdvlJ20LIT1;7EAu+z|Jd7Dlhmxs-R@(KMdn%`itVu(|*w(61a zd3vlDy+L(5wYq~_{NPLifW%r)e1+x&ou>6_!N#0Bl!i@%O!;F$v9Gp! zHl;0Mm}Q585p0Aa-Tq?~mS|~H`z5}?yr@L3rACwpD`o>NlC4)aw(?w>Mk?`g2V2UO znKCNwODniVW~R;c80+1u(31;B8f~H;eWBo_`-MN^db%;;pz&FbOfU9=x(o2jk9A?7 zgtIVE7zjU&$es|uVj%RbvK&jiyR6k@Y9djNV#DPeGhVk9hn({dr7d?a4x%zG(Ib4? z1%I3_SSTC-BU9+DZyRo-Qi%w#Dqy~Y-9Yy{l~guEjOcJDU)*v2X$6U1u&kbMsHK$C z7I&Ftyfh*M1OP*UqYXx~$sNdSop23&3-1catg=;x*|tQms_jcmUXOY;gB4lv z2sBA1lV4j9Kcm`-c$U^D9~$yqy!!hi?3T~FyRNYpGKL-d$utq4IhLDDqBIv?QA~Q? zDdui$m|TAnFn2M15(fWxxZ%0=@u~hfxkgnv`uJv%^0>J!WVJ`>mZ{(ba#@xi9|G=P z?#npbUNK%AHHWbEw3&VN{P+a9JY(dn%aH6IdD^$VnZ7);6TE?pzXf&Q52`)hUY+4oHKda(q?PGurSe__C75LU zmM1Ob!=az%Bed_nw&=e8{n<%Z&RD_SDP%5bvg`~qIPk4z>Qe>NQ+3h(O4QA?&Lii? ztal*@cf3K5`Ku-@vp40F>r_t{H>5iS5+M=y5$D&Q#VZX`GAL~L*e19`pC_)F+~ijE zciq+wKDI(R?c64l%%&islxtU`&_5C#po*S( zPWq~!^tNm@8V;KVebGz zWDa1RRA|+%UFexFF`Uv5G8J%4UIMW3HZ5H~YiC3pS(iz3Jio62Nd$l3Sh0tStyr4M z@f*8gNMwmMI<^Sha~fZ`7!-H3IHoV*E2%s_@(nX$zvB3-Es*wct*RH7Y(b?3MOL&nPUk zG9DNaCl*&4HWIM8M1n_p-dswB0vIDinkc3vLmhsD#PHDHt}ua3T#~lrm;*N5Y*Qq+ zh?K?qjuXv{oh|PagYUzHMFbBAU{m5HoFrLT#bsxnQSr8F@fUIu^w~<&Z1tvh)$sZr zR|0Kgs0e;dlXhmvf?9D2^p=~-JeQ5@qEg3X;{I!9;+fxZ2Z$Ide)PmUW2jbC*-8py zpQH2+%*pZ%2Xscm;BH67%FV-Cz**3kqr))3Fr1A(moN+q{5aoB9%~fSRs*VHdsV3h z?3XM z-(Db#vc#oYrBj|wk{h3lch~KKy?LEB1cf%}QAzUQ&qm+ub?5jZaJW($&CqFq7V60=9O7ZX}bwJkZ zsiF7PZT^MTOlNLjvzP)190+#k%+aM(q*TsEpGzr~h1jAcdl{M`LsX|yo_!P~8hPR( z^>P+0g_3hw$B*5FQgV?rpfOxpHkvKe7k{Ir5>+D_X_e#GD*kr|XGb^HgaL&0>K7Ah z6!s%x>?4xZ+FIA!f6((ZYH5_n!#&?vLlX#^RJ!OiTGNDVp0 zH)O=zDt23gRVZmaQ+`y0ZwQ#;%5x{zk!I37$7dz+Ml@2-E&e%QlZ!s4?7q^F2yv9i zYMYQ!8Jc&gq2JVRiL9!U8C3<2GQ1qe5N2CF7jr(QR^=?>}n=&gkrQb7VyN>ZkPF zE;@5C)kUq2N;fDX;V($Ap z=(Pvm%boE<4Ra2R0UNegS|MXS=Yg!3=r&#I>vP^>L&th5}|BZ*6hU8SL^WR#{NL}mzcVsEoxR{9o$EF1ntjHXO{ z>Z+vUv_ zsW)i|)n<>SMsle6{bQ?;COK}9h+A(>IIqrtgHMla-wT^2bNktzN+ONI`TmMH1GHt+ zbY9kTdE|ZG5YUWi#G|0{;n9YDLw9K8KWp`CI@V6|)sRqBTN1&>_m~nQ+}fg_B<;<3 zyy+>-n=ThLT2}5qr%Og(<#Yz(L_g~_G+hjfJ^8PoDYp(!Ak{<5hm-+!_lK6~&DQzp z-^~|rj7nL#-IPN8RDk8Ho}SAM*?xqH^4Yxs%h%s8j4k4v5HAv`9CjsglN{H2&tVPF zq*1@#Z_$1@85N%6Bid5R9M4ZEC? zdp{pP54DONqJkUoZNjf}X7T7e>pk<=NTRmfB*%V>Mahf^}dn`5EyRyd;MT9aP>b%``gO*h zm{Nqlpe_t-U(!FK!^pD!XRQkBY_)5-AR(EI<9uc+czRBs zI)6Dc%T*P<>EqhQMSHxCHXkKfS*uVPf|6}zm`8R9^anfmo7aMR^@zbXflJ%NFLB@K zP-33v9U~UkD+D26eOQ+@D* zg2=#EceetuTYptaq+;{t;F|1LG2!|ZkEyRJY-~I35k&{6g=D`r{5ZyXU-ja7;9p6e zhy5$#-|%aAS+YKm1(0YhroZN>OcXPUtJpL+lzK58-hScv+enxV-bQS214oW}#B?GI z83Pu8$1uie&AfMap2#`CMxD1^kZ#yp#ls&auZ|#_O6+D=@K(M?U zkWf`G`;gP)@m8ar5KnElLhGqVS$pO6VCvjEME>sZtXJ&WgO<|&GixS3)8U|#s5UFu z$B=HIS@{w_$-}V%f3$3A+Mv8FOIe-5D!%cHXuwm8ya`v0a6r5^qG8H@iyisEf9K6E zA4R+uYun_-$qh@71uF$5tl7)U45JmDOsBaYNa(QPn6cqzW}S!;u<>V4T3GTve46x% zvwfoC5X+REyik5xRP4ygu+d-|YXg;$-f6elMIq^f|4waVI;^ z`kkSTYK*K9eHUgTEwuf&!xNv@+#Z%D+WGh~BWNf4JZ|k&g>lV>H~QRN_iZ!W$4at1 z81Ob~^^tvRg~&7AwOtJh5M8Fh#DD27(*w7kb6MLIN=gQNVpIe1m&)$QrdN``^Mk_z&ro*_eMFNzxkqB#RvI~ zxVM=CneWeBg;72g%PboLZfV&nrTs{ykr*vS7V5P5A^Upk4y1k4iBL3vbwjkjem#9W z`8CLY+ujm=%2jtteU%xUY%_JN+~v~cc;sb1y-S!WktfK0{lnA{6Av1<=KGZm%xwy+ z&ok#%nPsd6b%0D7{y$^_5jlJ3M{bZxLCVq0$UsD*?Y@U?7dHCMS9SdJrJh9}{AYp7 z(9f@8jN|5oj^O_GpOqVYDk&PV_+z?{q!3b7NDib&dqif`Im19Q-S@%1-b(?HKWE3Y z*x;AVoo>xrFPk6_XkhLyD74T-OZCpcj+tWeF$Y9@!5_~f)7N@*pt=$HgFX1Ab&>$h*PbeR%g@d9E61j|#L_uMDe7vPO7 ztEW!;;S4F!i|zp3yW8WN>IlYNmpgHD*HF-P1n59;8iW13Y77w|t;C^q{k0KOA zGiNz~eXu>ECt#onXV08Dz?A(pP)N8iw1{WblJe!Wko8FtzoPPS{BW|YEZt)A$_4eJ z2{-nKlyqU$HwRJ{xJ-X*0Q7nJb2mz*I$7|Dx zj|^asb~tRy}1gh?jlH*!=)Qi7IM(m zX0z5&%*N~C-pukgvvQ8PLJClawDkY0kwygNdj1aclxvyu2_-afo!2;I4aep7miMGf z0#LqBbN>xy2m#bO&7lM3dB6Y-Ljko=2Q;TC@vJ{!Tm09ga*{wP0q;`ZG930Av5I43 zI~{44hp}#!%Jw?oB;)c$g)`CPCi}f&_FPkttmF7!a)i1RdpLC^$SZxHwzi z8a)lcM6gmmY zPYPDLK@UpTp=2SsZK$wd2r78n?>J-1m0tc?_BURo&(jcN$~6`9S&*1!NPTAbJ&GU(RF>+bkOOAH9G!1^RG0HDxiXSqTQa z=9)^KaI7LPxqW;-^{X};b-7;+`th&(5*UaQs!{)XAk(br#AjnngM->$-JTSYo54Xb zKppkqj^c^+Gs0hKD{#+_ptvU*Wb#v2>OXnEz~&|+R1WwdlF}FrQXqRMhB8|FLBf|@ z%feneNzf>QUptbEo=&Y>tIx}Niv#X|HUI3zfadogMG&5(VT$}npUZ_<30>p7lX8tS z8%%p2NqdE0OlYQA)UcJte{cfl)9SL5NY|;P4&;{x?v)J+W*+-&9xPGzmC{!;mLG$6 zv=BC8@ZfTo_&f905&!vsG5cs(z;cwsK)g10`qLPvHft1}cfp-&j^BOmyO%mUzWa~@ z*-J#II}kC;iDt)X)WiK}6C~eAewmn;riXHDD#c8ZDuD2d7`0C}K5cf%SUFc)3n^A7 zW=S|QDE^FNodWf*>i{+f7=OJ19Q5~CGDH--LAVR?sTb9_P=DX$_ef_B7!}Zi=a^ki zRLe%I76#03@w^Z>7DE6E2>3=uJ;L)WzOnh|S`XLc$b>MNFGhE2K7wg!>C0mp*aWQd z%c^Opu#0`b#B}Xe%&wozKKJtcv%h({PTSFFr>x=-ot;Kq4TwG>`NF?+C*D$wT3Yjr z05eM_Y}UE7hUILzO5wC)fz4vCPPX0wYQcQH%={HE&Pt_^fD>g0F^l!EmdagFO4Xw8 zVariUMZ)>qJ3rfa`pXrqW=|dQ{V&-$5fi&LeO@z46<>_=)))w9eR5aLOf&OE*C-i^ z-=}!@6%M)BieDLjOcc3_ik0`dbNyad?FicU5=;u{zLKvMFlydN)Gp`YuY4+>ep{AI zzdU7q1;enII-k%R%Onx~F?OYFrQ6>7e#cjPb|i^cHO$9y zFt-d5%*Y~@XjdhlQOnI{9iR6)02LRgLkb1PYY`0#zNCK%TyR`?^PsTs=;hQu1~K9_ z@v=f{k`cb`5|K%D?3wjlcf1_33Dho;^M1qN^W|go23se%-*&wDu*GeQFReTpG3|G8 z27W*>&yNqsJ$4VNZS(CC@#jxBZFi492iI0YFj)>&m(fMu>dq=YEZ5pr`}r*#`0S>= z?J>X3S;e7jn8K&g?ZD=8W@TW~?^PzMscbNqz#C6u!&7Fggw%qPj0xRlFgdd!SD{5; zx*W=BnO9vo%tiCUojE}w3@yG28VK&9jM|LW8Rnq8vV2cTe7ovtUv89g0f&PuwjVnk zyM`?yiu&^n>ez#JJT`Jm<0++$l6Tez-Tiare%TmR(@W~4WMmd1a}ndjm*lQ>q$ZN@ zS!TQsMKU73J?OTr-iI6#KG?a9zu#KOxne#!{*0*+Oy<3}GOE#bH`guUkNvdne~O}T zi2O)(7=64f-I8y3H&OZIT-TwGxMJRyG`U{Clrg*Z(QdMJy&xEGIK$0+ysn#&gu3`~ z)UnENT<0tb-F%_gsiaot?y{YI+Sfd&Gfif-UCFf76l!bEeGw-pyxif{nNWqg4?l!f z*{|2*v#PvptWoZ^c9{=V??5hFcj3J>pJaV7>z=N)aZ(P+sXDw!x46(>PwLQEs=hXM z>#P+tR=D#d5^v?4-?=e_2D1HJMsEb=Lt4G@=bgI^b2@A$E)PD4^r$LB?NJ`Yy<{4%NKyI3m#iE69fv9C&cXk;i8z1Ix`VICvY)|= zx_stl{G9`U6>lSH6EQ~*tq!X`AAK&vsu$^PO;}jC8^s1QIH>4rH@@!`;uVD>rgXH& z4d7j%Mu@$c_J}t{(QaAr3bcgLe*vJLO21RDX>p{X)H7j0_Gx;f#Pe4nNk_|6t#x-? zoRjs7KO!xbko^VVqymTTc8w-!hO~P zkdDZ1W^CmN63+Y@myLv)nYckCVUP`>jKk9ydJ_Xm)bx1u04sz#}f*T@2 z+$!Og7uA~??aWw?;C~e9+CuDa80yk>QbJx;)_Lbqapj+gZyD&HYL0s2QVoU&X zS`+x#b!uPjVad^Xm*>88(77)iUC?P7D9~N&>{h)~q%V%aK+0tx^v;p-P}%l{hmnM7 zV-6)ofnHg3IW(6~tz}s`^eb>12_{m2!uP_TvozjJ@{mDP@7ey%pd)|uIOR9stjxZUcIt0oq` z1a!}}17`wx$EHd|PmBxI=|qfZg*&%OV$O~klYp~`0`Y8l$U<4)fZlF}i16Cv7o+ax z)-qGbc!)#`Yp3=C&uX9h??3mrJZZe7Bcfyrrd%xkEG{hai^{Esqn5;=*{Btyv}M@e zvo{El$u3sS)N!xnnzBx-wA&F{7)ZN13{~`lU4l0&KbM!Si-PvmO`bp`9J}a4xjosj z@r(Pe4?~;7#pepyJI$QoqlyNH#F6vzgKhV2y*q^3$-^Y2I!91iXdjzr;YRV^YT?OUb{S z1)O0}UeqAl|7PS&GCQXBzg*o~Jn7Le3D=(beSXKCfrXU5G|0rpy(Gk{)z3_qB7k%l zkERJfK!@QkLA~0z@tJO_=sJL{7s;^NXi!BwP{JTAv}4 zUkyDXgtK#aE&8X0*L?-KRLt9TdQOW^3A=>x4?Bfl?4}hQ)og7qSxLk^yHk1t-Ipf< zJu{YNts&C}IgUmemQ;emoO4ly1y^gub-ix4c6T=(+|^v1bH6I0az3MZ2}e@tj&GkN z*bqrD89GJMZgJG_?vt8i@m~H38+oZpoCYMd+C!O(r8t137KaoEkktAG2jERFDMo4$ zHlmF4!STDFmF0A{Zl@{VeIp`c1dPV`+N}||koKD7?j_VrAHkUjRg zMYRbx?>-AByR8S=?@&4#qkDW`=~E<|8@QPB><#<4o)dFhfQF$xd&I}`TX~QtqQB?> z<|oY05A_Hz7%&(&HqRvt#^D%mNsPdn6^l0#L!@FR{r4^B50NE0B^;b`7=FKEd}MJ( zV#+0`ls?D*`+qem#-vegvAn^GgnD;a!<85xc-eXWL>rR{5+?fO=Yk#oFs+Ga3sK2^Q{JkoKM!cDm7ZwY=3Vxu;60<(;gYIiq~`` z;I@soA9&fH-83xZ{bBpUgfp!sqK!B^%}%2%!#+aRI4{&znt) z2;ek@OB-H#`X{s|VKYb(%Av|R}J z$RO=?3yQ4|l5Gayb#oxV^sq&=1p+b)kOdHsJ(m`sLwQ!$__)-zxjmAI&x5^6jx@;W zF?e)k1WTTI0@V?PsV@~iSizG*6OgtTqrM$IL)jT*5Vjn)oYr^r%QTz3*9*B>G{pbv z{$c*(JE5w(?1u}uspGHH7}e`iqVoE`_Lmq`9Wnrq(j37I3|ju?dFX&g`5WcB_NIc= z4IEo^`cQCc@Ozn~BGn#V?3U=v$R}zR=JE_Eb+CnNNxz|}xQNH~y?Z_m3?V5bC-^U7 zC^*Bubxx|&9Sre*=(=u<5{-=A7N zZWxdPzzzQ!KPmzk8OME&27*}mMV_g&CUr)LEq9=gYrm4)go;HB7frZ7Rzo_+YDt*I zQeKoxl&iC4o?moPWc1B}e8oa5Y3)}^g_;*tD%0(c#X3G*6&Njx-YeXBqu^l1rqmDT z5w+bB`j=+dgwFF4CvKEzuNGWueOs#~!LCM#aoQZP^!%4Uyd_2%L2ggY%l_0mQX~Ds zHT1Q#w(FCM8?j!IhGg3MCRD_1z&%r^3clr>l!QDt^7q%4F~o-aEL4euU!^Y*`gGZT zq5P3YWqA5g&f4h)oUHjt`jZqmS@RY4YqB{4P{MxwoPY2oZ2)U1b3PrOQx|1EPb1Hm z&f{Q(Cql`c??DZ_DaH6zBdQGVPG0+S%P4_=vX1 zLu{#Dle#$}oA@Tw=X*K@Q zor!6+NTKZ6l)Hp3-gwL9oA)i>^PQ;-by>OPUZa*F0sRiL#?*$FwwC;Xm@F=P1yGmI z53FTQUvP}$vFd=LeNk7*f)y^!V4R#X`Sh4(Y%X6SvsLa@I7umaiiCY}B#{sHF-;>O z*77TB%;X1%{M9mK_99?-N2EC;((TgIe7DC{cy>v7W#Y~4#tWSGlPk=B7dT8$ zEatD86ufME`Y74W`%n`pMuIr6FeaUMw{JSTV?{(mxcbajq4d}?q&2<+nMkAX>S}^< zW=Pgz*FuoD#VYUlkXu*j+z?3doIy2MjeFQ}^muS~)RfX*_;_$TV$Vl$WWMDx7GeQO zW2{0fV00+30JzPmb$*7ShIyp!oA*Zn=Yg)?m0`S?%*R}yakoz3K}n`Eh}+8Ypuu8c zW;`MxkfMHuo(6S#&UqwDujK@8=6MO4XMRj)P-QN%-DeMu4Ui6wN$`9|LI0F^n;Qru;Xx3C8@w;dw}K zWnDg~Q#&da>KMeP;>HjDq`mx(Qkz*aR~9OT-Ov4AMwYeS0}iMNR2{Qb=zVqymLMQO zcf4}+XTkO1d{}|DRv~2g$T|)6E1d1(JHJ36Q{eiXDaa(x1TqDo zfGgR~cy=gZ%eBMb-h7x)VJj6-WW^WWG{IJqI}qB$BNw6Yn(Hm8mW9vt@6Fe)gVPGz zI@PBBH-l{iMB$!iG)mJ)%!wmBNnyA7bKemtQ}cM08T_x;@On|Ns6XtQO}~Doz)oi!jktsB`BuF?)3ItO^Cg{;kB?wEVEVxK|6@ZU6)qw|t6d2ill@-~a0D zXm-ALes^i_*A|GY^Nzc-Xwibh1=JNcr7T8@`X76-YCj}1m+CjvyvY+~txlSxrIRVr zn#og#yaU!g=o*6GiYc*4QE{d32M)shQ~!YNF#oe8na5mh>z~S&fzv2bowNPw_Scw_ z5`HXS%zB18fizhH$m9tDjIBu@Spx$OibnoV*`kl%TXe$i<}{`RsP@^eGGDXMgQQU? znFZhw^Swh77F1_5Z#xHk-K-;{t)2~hDY{(0HpLKhM5I=d*lt-0T9~pgE=)Xk)NEkk+NdDgm!}IAx2Yl<)ME5E_{UsJg zx;L5fM=ftNSrl4ksW1Jybq(aP;|YG0Hij#7tH8s}xz1;5wn{Gl$1y8+Lqg^2&P6m}6g zm#70Cmpq&zE6Xqo|a1Xm1T@SC+XZx`8mva&1^K z^EBi}CK*C}YMbI2Nj`~~1$j4fE>^ngX0b*cB$++@`33Ge{Vo=iUZ0@N;`w}m?)CEp zx})P{BU_EE!XE~N^o%t86*0oE)RYT%U;M~TNa~IJQ1%g~ZsHcJ<^EHTGKGWMsF}UITmPn(L*aJ+X6nY_-@6njdo;y5C9r zhv#4ZJM;IlyrRw62_qn_=J7J0W)s0`T8zvaL z{w+-g5?Io!37c-Yy+Tb^xMe@n&Onf*_`U7UfMmmK$}+0wrL+Q58%w$ZSV})hMBW06 z&GQ56fHO1vyAuvQj`~}M3LyWOn1#Hmkke6?yD~?hatwE=JZz!Emm;U3?M%v~wPk=R z6NGe0J!jtgx+j=O#O{{}SmEGAyqRxFXZj<0e^4fg_v?|q+5OK*m8ATpaA`~V9e#`F zpGIp3+&R{LzUBwrgMFK>U$%G0-in^UCT}Obse1)K)WWCk=oX6^zBdOiI$EOw)pK;I z`J(0E{5>!_6SBiEQehaENv6rAPot%AUrx8#dk8m{_I$w7G_+kyiV%oJ38e^uoud~l z0L!l&7NzbnG$cbVzm^;}h}ECozN=Bwm--3jl21^F*7fdFCMA^osF>UJ{v{%TXbJ_3XKAWs9Ug;i;t}UWS~#&;cwhc^0!NC+Kg~Xmw=ELB1(Fz6{3|&)bTPqf7Hst zLv`%`%tJ8DM25LGfJc}8E$fT`^~Zlj0eXZ`*ZfCnlcoUQ+i255T)Mpgzq8}HnY9hF zBwrzoCu?d2g_?FA+D5U>LzyR7n|!rFAgh?b1l$Iwae zmYLi)q0-pdu4_8 z^fMkv4d=n##5NgF*8ze8D?&TR2x>cAyNZTsA;x1n_Ymih$b;r%d}yy%BOxh17xHte zuN&f`^N3G1manakECD&-b2_uUMHBh}6vzMCcw`Tgwx}Sq*^?6-p6%!B2(_=9cD%X!AQ0z5k%VnGz~hn#rSsOOe&g_#7_* z&6T|8u~7J@Xny}g=c8s}1dmvUrg(>7UR{(i2x%oi^hWK*b9vJ%_u;7W(1({*ukdY9^`M0UI-kdEgJ zf58)aMf^(sV=lq-5=Jhhsu;BI&VR~g0D~pr@7ke15R_3It#=8F1bMVtu#0w!_!@a> z11EB#C;!9dWHAL{CC9iY4m}uf9D*yXU$FmF>ump&?u`E85422#Nm9lT$Ha5W$%Pdz zktt>IE^z(IEL(YP9#cxLN|Wrat6s!%Aw~07$&N=%u=MZAp_?Cp>s0fj{c$oPtr;ee zU!$yI9XHZ~r9r4iZ_^n6Pv*wRDzT=*19iS{)AvZRMo&Z2cnS&66Bk~;hNM1I`cz4B zy@=d_5E^*yZN$G0VJ2kYJOcPKIZUhFah^b8>Ia$y*#@;q6s%rh1p?4@INT#~;WJpO zO$EK{uj$19vq=3*h&uA^6nu)tYM!QCrgc}b1m4d&l_ieEstr}*a;BC< zTiF-=OgipcaNBHCN`J9c0J2E&-ldNF{<l zb~MO;^U^w~{La6KClqWd>0#^sXcV()ZcR#X+sz^VRRB!M|H_;P2+$69&1YJ@o5K$F>7OnV9}Lt z<)~ox7J(pIrGgZL3jqFEpT_cAS2mgGFO*C2_^Q5L?tNESj2vO!T=LDY)l~qWUt1zmI2VX^=YMUKBIyrc4blC zZlhx_s@}Jcl$6gwct^x~$cEy1;{T4{*ILSrqB-&aY;tmcy<6YjtbyI z#d%h8%p4>9Aw&I-P965J?1#5K4>ZRvfq-P_^8Jk6~NF|o*>418aIwcOk>meUjCBq zIFr11#V=ALRAA%;`8KlvHAj~2VCP&>%-J!tXuNsCc|=KFw&vdUo$QFZjDq+m^5Suv zj>e~m#lT-Z)-6lcG72ebi)Wwa3gEe3O>rfmFLH*{M3gHW%Z3YS+d0muHy2~}{~#=8 zOGY56CJi8_vTfwnW@IHDKq_9#iT9LLy7ek#`D9b-8#9jm#i{r^l~cg%2z|^ua7ELZ zje{A5f|F`_VDsLQYLYyEdxS=S08;U-xH!QMQ zmHd3d8GABTuXP0*Bc9JWM@Xst>1u33mN@IxTx*locGvl}URv~PJ#Xu&epjGU!_3^N zz#H&9TqM>6|Hp9L*?Rs>%)2njoJ@d{QwSC?e}eVNJrRD)!5wvhLOJN*`sld&c$5K4 zEK2V|LlVns?x&*Yr+xMf9pvFX&o93tUkS5BBpw>nuu*KyCL>prq{P)T(>DJO@X4na^!a+pDra9)dZC+z_lg8@(KwVP?j7&N_vr%! z!m93XMZdgW8&9Lpx|vUp9CAnErB@YinVpc3d7%3bx&Gj`r1!m?7cm>JT*dP6@za4c zC5URb48OxN7Tp}ZDHAdCetr5z#B2zi_RY%^oZ4ER1vfjBxRg1r^y>?l&QiLFW^HXq zgu^ut+~c;Fo%i&i5c1a6-pNo8Iz*v~Q~c18csXpUOV5?#I;-{CAgm&;|NLqAujM6= z2opUuHA#Wy{`hIQ(1;_a`r44mB@SU3!sb#DGjji-9;?HS)xPwrc+P4DKl0;ryiA>F zl`zZNvb~)LOFP}~R%6s83YL60lUSj@>FuVD)|VlB)5ryN8gYcmb=DRaB*mZJCKPON zFMOgXP*@egzUe3zsIv}GF5r}lB^?FZPU~K3^gb$eZxN0~*LXU60x^#8QO_4th*@kr z?-^LMUFDq@)p^sy@fJ^NBUnm9G<3>nONtpR9aYB2Uwmd`bjQ9=?T7CvO8bQHxLtHs zA$;!|iT{j|q1PBK-ddO*Loz@4)I`pQK2Gb0Y~Tdj*DB+VOJePrYn(?R0b>Y4<4!Zl=`*F= zS(1tzR@`P>eXg@bm5#DK!xdedYQ`av{47^?k0bJh8!_o;1mo%J92VVL4A;zj+PDR> z=R5suUq37LvVF}x3=RMK8@vZ>atAeyYoK+U&qP(kk>7EsKNZI6qX7cj_-KH@1HB3u zAn=r<5H>#$msOERMAHa(l``q8Gn(q?$W9K?H>7TI#?kiahh@#3V?HM?nmZ9vf8r?l z)Blfcyix)$_cf1M#p_X-ze6nGJR!MxQNY;?ZB!!JqJO4qsFM6DrL;Z%F-fDfC<<+cHwYd)PDiOy%d^g7hp3c{#=)@?! zi=_Cv@!6d`eBlXCsar-;g1(r=)b;HjX2UU++k14}!~}BN`FQ%V zJU6>n%|{SojCgc#xgH4?HsuegC;fDNG-E^gD7J`CEbx+Ynpr=;kHytniNnGkUB!Jz z*yqJKxL*5xNvw(>JZ=eBw0G$}I9gtGc?s@mM$Zz*7t|pX4&IUkVq&tMCAgu;!J){} zoaF|7bCYv$I6O@yQov_|UY(DYPqUD|pgGk)$Hc#OpA{Ffv*()vS6!2hqa-Nlw+s?a zXU7I6#qtvx4yx1ZzDpj_WFCWd5-8I&hPw2Haxg|_C1^#)@-HG_5gcc*Apwj9sVRCT%n~=l0O=ACx-5Gw(>; z?JcH0r+L?u)S1o@(78Wox9Zm==t?w~$-_nE^xZt;m^l>)pIoK_xA~E)u^Fv!_CoK-)w~MAm!KOvr9S3(+h>3zMkH#Zmk#D5xrC` z$#+EI@4qUdJ>h?QBN*TVeic3dc7M;CIojRUc56$qOAa*u_xZH1sk+-wqUYIGAfHqp9!)oq_DS~nSV#O@=QC)R9Yz^he8mH_034nrn8fM6} zkW8F9Ky9JHul2H-;D>Qn`z$(pb!c4Uj|Rg~b-!0K8SL1r^|^u77sXY2WW8G+!*z>? zSuy6P*h|V3BN0gE%Y$-8I^@Y#X!J;HR3ri9+qqYJAFG3F9IwmNs)A*ZVG+ zOmX!E#s*^)gO9$X{rDV+9A@5aq8n03nedrUOLx$@rRhTshMX-Sgp`64CooR0_b>z3 zT=ur|bR{*UtJLWHiiWO+R%>64syS?S?3y%PI>cGD8YVWc$r=T4;Kq^Nxu(K7QKEvh zA-WBkd{VaWhf+1)aP-Nv$oz2OaFzr`BpR8_7j{VXr3obP_&x@V^|*y9$vU+{$Z$L; zj3@I5!3e>zGj2#KQGv=H44LMExmF7sN;ZKsVey#wXE%Z8KZls-U!-XjFI?r#hgf_| zXh8STdumUM=aST}(E3%AQnSwtn_mC*2fgq3k^O`izXw={i@y6;F)~2@qYLaxEf~;UDQ{~ba2Sl zC}8Cj)N!&KQm{`_^WM*LaZ^RKqFiFwi8r@g(}0V-CN#43B!E(i&+Y}SZ+HZHey3(cU~wUp+sD%Hku2_h zAs?!|m+1(#W;wz?QxRe)N}`BI-?>_J5?v%HK z)lcJ<%(nt2wMr%Yinqca3J~_2*(eai1RGm;?~E_H^_8EdxhGszB>^x%`UsZv{6qtDU@i+2zcNR*lA0D zF|1@p?aOvATHEgaesbN5Mi2!@bx~90yJ>B)b1U6N@$7SPs9`@w7ovtZatM{Xwx75t z{DWEEsAiZS`)r4y=f&WOyfbm6zOg;PHRQ;O;c^COY@9I4*ON^tYYF$hL6PM>MftX> z3{Lf*8j51-!$lD6k?r$tZ(iCX51aRB^(-(7%PjdC>S=#0w%~YD!)>B{FB_6NK~{Zc z2LjJ2GzncP%caaB+?U{w2J_!;dXe1)R*N{@Uh;m~eqJ8pG*u8^+yD7KK8r@*($81I zO<#h8md(P@n>>%hK?3XNN4naE5_q%bP1zsco~?;(VYBIGIinW7pi(gn>!ufbA|?x1 zKV!`5OmpI7*>|`lBV*OLm0XJ>R+bD~1B?vW6r;f!h=WOv6Ion)u~0x(b9Gz0Hs!cj8q?jn`>&*MYisca7Hv3=J3M zfxbh>iBoDT0-R56+)P-o_37=nD{_lKdQ^2^>s;4VXBpp(OU`ggZQf6++>TL z-^OpO6-sMARbT9;uDVbdsGJTY%Q61-`fx<)7P84(zWh`BI4ss+#m5=^tA5nPe)O}! z1th7R2n60t05eb_o1G8&)2Nxmc@OX~raWsyV?JC~o8BGpu+$@0wZVu_mtuU|X7iF= zkulc1n31)(7Fll#?)^H_+YMdxV5DID(jBz-D7^lvXg#;`$xFYo=?)8h_%D@h>o3O5!)XaZMfx(W*b94o#e@d+-E@J&8rGSGQq6B%EeV(=o~hxh zAgGv%$PIkIt4`vz4Sx36!cub!2W}#-gYe{1uh$CapcKn2ni>sEVKXV1D|GrVTBG}g z$3AXQ&0)>rk!3}-)_q9k$VcY(y6Jl9dOQs|nwX@GPc8fvpFOK*O@tc>By<>F6mTX3 zHQ8Q$&I=(N!9YXV6L=n?N}e zn6L#&t6!o*a4XxL(A&q;sMzaz40W%VQ-C4l&%>L%3)TU?Qz`>%0K%h%wsNb zUuDG!cbT1kZt7}rgVuVPRZ&sEU$XS&r@pNZ77iA!>7{fohChTqjG2M=!@LDe8^Zgc zh@OXeIKU_oeD%Rzi)`#GcH9Ld#*@1qWG2dTFJ|nQB*)`T-opExg$T1jt=45a?YtaMGYD)!3*ci?)LK2g?0ilBd5F@)V!07(DFyEk9!%Lo*^|#m zQ9IlNA&bHPYk5kPFl_8g_G(I7yTyw72GpenG(WY_dy64TR>H@qO!^s>Kkx;FXYM_e zh}};9B0c%Nllq4?_96@W%3euiL<4L`qg8t?Piw89?FqFEbaF=0hfm+k_B^hXHUi++c=-3J94${R{se zOAG{4hXf0U6|^l$<|=xEXjbj=AN13cxX%gyrJl_Gu_apXbh9crKi5#fSO3k&qI>oz!8NP_nxG{&hnOc3t;%0F-fe@J46^A8b2og)fjgm>2O z4Pb4{D?viGr=m^Ij3=KT`gpWT$i?A5YH%py{2!CxhBMzP9R3a<5z1h`=%6_E(e4vP zJ$x4~T!W+i$%0_gRpPa=vQjPTKXfnN+BZ3nUp2nkuhs_IBKQ8}?;{3?WioltPY}6; z7^fdBZ1(R>1`ag0L(xx#u2rHFPfwlCCHY(wN4IBw9@B8_c6uszgLQ@Efy)VLITjeL zUcOG%9e3(h52?C&JBJ%5?SvNQ@P|67K9Q2I;8k*HRvThANl`N7c9%!a(LC!0Git@I z1EPovsg~?G;ZC0u(F4@^9yG+BMPv>*4r;l{rznj9UI=hM0>Tz^_YU0UHzVW*F9Ryw zMb#SnbJCCdQS{zu)RP^FkJwL^*&XPu@A4JvwL2`;v!a$hBpB8^e)ghZ+#}2exofJp zUNESTu24Ff@+R%cdax-)`}urjcO2YX7(adkQDW(dS%f*!2rvqN=A>bX;u@x9YY?t} z#ew_lb)QGA5)ow+qpkw>sbFHJcnMO7Nv@I;VRjhSnC@n$^>bQ8-mxHWIQ-vmvA$z* zCDY()1Qj@-VXCb)+uf}dLyoXQ73U)__1!*4^4 zC51x<+90)XjhIO%WMZrP8Dk>Wj`I<$z|Pn5t=(|Yt&NyT$>B!8Ql3~G#b#U2R|>b2>ZPK;#gHHD`bZ%iYc1dRUXdjpV*P|D zkXce-m*o;z>AjwKdv5UR;)_O!uPvjgoSbU;52Cm3Jma$_vT4g!s*K_1y|x@<30A&X z^X?d3g)})4cFUeQwgedVY9=IeSE$?-Z+I*a-Ypf;mxrbVaY#pQ5OYhj3}8jFPTcq6 z(~AGlb{g>v&}_ocvpdaApMklzBbP5v2{XU?uDNe}iO!%DCnpoMldf4SbXWdtfaARl zW5ky$O%1`HpF$5|w-SXCzHu4WFue$|{Yha)srW?$3%BaCF9_WmS9#?Ezb}-G_&Q4W zN{nGV3`p(Hm0Mn9q&mEq>xgUX$|)({ARK({^*kMEr&#uA1{Womuwg+VW5Ic>MYWtJM^^RgTxxot!%E zoV8pMUn>)c+{JID-fV%sM4Yf@nm03*o<(nFMjpJ3q8j#&3IOv_(uHFmZ%a70m z?kQNotbDs(k&Iqx`NA=f5&`LvO)|Ej82I&*U9w$2?>4)W3B+rZ>%^H5Pzv-8)Qb~H zP74?CKzz+Bk_M6`ybk2W&hZ<+IHp_+>~VLSrc9-RSp=wGD`B)Ir-8CG8>zQZreNWq z-ZTPWJV7}?kSV?(BlbnAVKgzYXe3Cr$coDLrkQK1#pHN>KHskEZhQXQ8wv-wfFPcL z^G5DGK$iHBBS01f@CNL0H58?Y0StSqNPtumhyhztV9|7t56ZQ)*V@iZLCR6r{z||w zD2I}-5K{Be={y0NP>mr_^9vj0ZD9&aNSP-l`!Hzu7n7hXMM3a3p=Meus*7Ft?O56 z(f!r$0rV|*x|Y7)MtD@8)PEECCIH9y_U{c4WW?` z6gZ$m@8+T7>-gtV_)j9D!^FVFqiezcR9UnS!lTNRF7FQZaz0*$9e9p!Ik4>ejc0uW zWS8H)QRGyHo@71FW_FG8Pr;pMgr>Yic5hOYv#m&WZ=!jzb}V5(@*R#vAh}~>Yhqv{ z39yrZO+LvR+rDb~LI~%7-z$C_%3VP!=-oGlajTC`1+%jbH=ky84z#{q2uP9N`d$Z) z%ANy8y2>{N%LMg8$o^$%Gkm$PaG-fO*Z1F3wmC<{!22`8f*XzvE_$NS<5PEx5Kwm{ z2<$#1#I8$h4@eCxIP{+j=668-Yu@ss_n6SNCYFn-tAGF}%DIWq{U=kfj9FNY((2}$ z6xT=Fc={hQ|5x7)OsIhYGgpcEndW|C*UA5?Ed?D51##5 zZ=02YWsy^-8(4T5XW@%S0l9sRVz=I(iGki9@Uu8V0Mn9zQk>s_G;@lx5CMgg?cPMq zcJooh8TVC+YE1aH2xN0^TZB_^&*Xu5Rtllwv+XoRR&Q(-)2bY0uA_~O6 z#Dk~Z6t>RiPfc|HAn5-r~V^+{<p`Z-%)l$cN@cB3FF+(PQJ`|kicO(;p z$6p<_%_2Hpw`+D@d7wo&7L#3WN$~KPRaW3J&MO_wI)BnTJJk`1K<`;mSN71Z%dcX| zkDghxxx6SQ>r})5BL>76%7WW|wa6Q8apnfKmws|>nbX2CQ2ty}!KuKGZS!dfC%YU3 zCQCRqe>=M#BXv}p%rlSP?b)RJAy0Y_teVwlS*5Fz)aAUC6JZh|(K5Y%&=<&YJaqTq z1#;0{6L@_q8kWJs>FJwz>=jqP45b=z=)RjfyIFfv{2gMa8NNE8xm1fTm8px(VNX=T zm50EK^*K$df9N^qt0aoO@B2}6dxRZ}_x&OF7UgS4n3?s5BZfLXtOY^y>fm&{F-pBy0WQ6%ueIF!y~j@iLnVbH@p9E zKPee>pmMB#gPNzeE^KOLmvj+KDPpj3T-IO&KD~zp&9L)#Cc0MA7*Q^VExvZSxX^>C ziFg%MrZHc5`@qJk9sTU|R?}uz-L|gxoY{RY@@*?rtb$(d^Ho;KhaPw`sitC|iYi}Gvj?}x!Duyk z@!yx;s5t%=GpZ?=vuOyWSsMMxcfAKJ{v+Rg6Gt$4MIp07r#Q_zXAS>pF^8ox~`ZD>LE&=%tfQC;dbY{?QOg}qd4%r#%$|De44V1_Ff@?x+R+iy0 z&)TfXo|-!2fw)#!M4M-)d3cdsDg9l8x5ZsA0viLS%0ph`>C<-B@!kKu^4gZz{4EI2 z2&MJV(}g*(rnc$L?ifO>Jt2r+ii+O`M z88gW9s$585{41ehGAaKnb;`;@-V@(THE0+k<&q8yGje_o83ZingR*5jqFIT+4(8R6~Jd&=V>G!$$>T*r7?3vFS zX02vKa2gy}d{Cw(Xv?>CEUEecb%yCuczwH8U*T(0y=(Dw?gY7I&m=m~2_D=5SGWV_ zAQV2Rh>(&5>XWTN!}gO;hc2yGPe+Q(lf}ms?=-*c%Pg`Qcs*!}pVUO|JyaG0$X>9= zjNIC9NYMU-lW^&t72u?U`n3Iw=M7GWh$;k4F46)gpFI6Z70?^xTfa_`>UnwFo z!cWhWKVQ-N1Ki-a65=1fKK?1`qh?^5dOxP{><>q)O%1&2eEj~>l8i)Hr=5)y%<%TG zPcGrAH6mjUEX8iUbaz6)?5pSlL9MJlGw&ub>QhLiY(2Npq%YNPG~ToE%sChS3V)Ojr*DA#FpCk$@IOnLg7nyf$aSmbeO zY2g%a*}2@|CzxmlwJK`9Ws@B~*jsPx*c6WBD?%e+N!JmNohR+&*>s(;l@0n`Iv8PA zDP9lm@N9hHc# zs58~txXCk)HtW-*9=s)Dl4ea*hh1X&GqHiaY_k&)a@P0C~ZCU`NWW@MC-kz##Xkw#PupI9}+$_-@$m*mJ>)Vm)3u704>;3*j)i?3QZ6kY^7QN_5>w^S? zgh7W6OTq)+SugDRsCTrwJXi%o(Hm5r)i6Xva-|TJ@O#k*J

<@<3bF$Vx!=P?#9{ zV;A3pPGq+wQ4r(p4<~3|ZWFD-7mV5DBBCiICLnRwXh&X%_=@`RuR2D+GO7_h^;;3# z7u#CPEMgp3Vz^@OP2`kte;Q2M%9_6U48|(TQ!0(M<+V`p{UF{E9*0Ea(i)17ejZ|v zJsZczIYeC4FE`~xWZtszimOl(Qeq)O?6r6%O`f0IWG9MEp_Z7!VRdQB@&Z+9z4-v) zEBx0@65s>=s|nykUB41X#`T?!;zlwPnY7lXp)s8#o3!?wZ>OKLr;+te&#r}exGpa- z3M;@6`P<$~lsPr`@f;aHCLqE^5K4$L?Kw?zp zl>4loo;!dWJ&}Q1dt5jm3<#bd3tE42qsMp-NN^}{5NqNLS7sn6`2QD3EN+EZPjftY8J^5K%13BmwQ^K68klFva;jfQ}xxsgb6^o&eV~(BI{5xis;>#XcWHJxYN`{#ZdAX>tP%X@6=TCQR`EXyeFrypIq-c zj-*)nMVI6zd6lq&&FXw0gOK;fSB@JeyY(e%!ef)vR|nmLb#~UU>G~jBn9};+D z5FHo;Nc;U6HpvhHL3R*82~v|FQqXE*N8P&>e(zdd$b#n>?Vr9jkYl3$7S%?i@M8EY z%6`5ppu@jpk2HG^_h(`<3@!}+%D6n;1bYJ^?E>~LOAhWu1plBX{@vHaCAc7;r!>xj zKzD;@^d9QtI{uHQS`ZV5b1w zp*_o|9Osyw28vOA;f(q05B{ozFmOLY;bWHWU zCx_w$gSxXpdzD?FnEQhCW28_lgn*NV&BO$ z-g&Et-m9-8UEc%QaMNl_nUg)i)2I~5V9+oW%MnN?{{jF$fO-KW1xTgyh*y(JAuRa} z1k$&(F?lO*ZY0*enLUCR5bc|QDCl^sKJjCx{!!$Afi64IAU#KZNaY3?jbx}%QRDy$ zirp@@eWBK_eim3U5VHU6hCVqb~5vlX3fCbq@$ zVIiE@Ra%|kFGdD=O}^sXRTs>A)7|^L<@@*SKr-$E#;a4wwkvaSy{$wIHhN!EPbTh9 zOb)}dw&5Rl)Cb6AOWBNB=|3X#Q6=N}to;t9xtw~=Y0^PvJW1@=vq|^|4xfB_Ddbfn z^Qr%&gOm{CdWHn){#c~DpQ!03L2h!rBT&SM`s7Hu1K;pfF>zU{sgmMot;2KS9KmSP zxl;Ocji6BpfqW*kVmJD$X}6d)XGY%(9lAH{7S_IaWBz+_-pMH}sPTCok~vCOEOOj# zPD~Mv)D3_e_;x6*h|J1Gxcc?B^`fV@YK9Q2i1$T?M(9ne zN8#S$7Ol$3>xL&`QEj;d$AdN}I%xQ@F7j_>bgC8gEpX!m zN9wYUc5UR9d7CXTX8S&{pv3+Z9)=pzXPIgZ8%5*KjXY}$2*nS6BG8L;!hoOvrw~2k z7XF0fF6LA4tbA_)BhkIop}JYCStK{dQTB;y$ECN6dv~{!rgHfW{JoUXrgF(n|BFZ~ zCaJA_}O4$F_tNzu!>v<({U)8*Ha z-K$hDw>Tr;i#tW%c@x<%kes8dGrJX7?ABho8p*m^57pUyyVLyV@K1X^9@sUF685fI ziubB7_$x{>1wiUBpZrp3lXV`#xeZ!u*Ij$vws90UabM6p@9PV?Q9ZET3{!airp%)$ znvpTmqp7iSn-Z_*4uu1rg9h+&^yYfnv}2(jJUZH=gYBdQ1#Q*RWpsD?Wx|3L=`Ma- z$6qv$FRHPfj17+L7U%nZbHr>eFU-HMldzc{s5Qa*124`$p2>f~i)2dC0A>gPWfj_F zy+Viq;HGLh;MEG{M9}-(tAp7s6zClf^ll&T`SWXfl-x-asBpVJPUlMSzaP>5Cr`z| zzYCr2?{;r~o?HjkXf+qG!4Ar?b^riubnj-KK}rgmfVHHK02MBCa#lh98)U_UI_Um| z|3D^%3s~_23Vu&^G(~~4S-s|`j~#)|m3ynX*rQjb*Ys~p`}V&6KVTabandOR2f`FA z>Eq)NSXKfB4t`zd-`jkpGbsFnaUf;oxR2NA8qkDR{o>VOm#haGXj;Cn-fQxaxCUh!xQs_BH+64{=k$*y0+mE9A6gqtX*fcXO z$swS{W2!F~>yTjw_u7!3@lKdiutfm>#R%9cve9|`Ex^V3Cr}9So#>hT8{xDpvI5{L zJ~GtB3E80Gr5dh`@5)KSj{|s(ynD`G>=ez9_Ncp)n@0@oFMt~B6xe`A{EG}Ch!;S% zz4CM`qht5hrc1TL9zc`<)QxG~>n)fV2yp%boF)4K#Bw`fAW?yJFT!h(E1z!DnPa!T zlhMgaKlBCspY1O524{oX;2se>!(+-vc=cZjlk5Z(=064Fi-!fYK@QW_&M9`HFBb(@ zxf?hDpl}M7^8@gzSWi(y9v$+3A+jGpPanfBwq3#L{hD&8x_4cE70!Xi-yKu1+)y9w zgsdy=&re{{{{%LtT7n<(J7A&UlSYaIjr-+&p|$)LNPc2Rckd;D0JE;UuOyZ^q5KaM zZZ-k|;G?KQ4m&5y9KV%rI$eCsM$fbV>}=qyjbJv2Ns1f^pgA4G|8tG~HiC~94RP{b zQF30cUNdbRo$LRt>9<|+H!|qJP~ubzXz@RciR>?Pc1i`{%vrDB0Huw*Ks$f3rUhnh zW9M6saDD&mI)D36`!5jmKRWln%lLzZ-hKlx$&XGuoJ(s>6B94!ulC&SEdS~oxb-gM z8t?zXQJ-A@F-AaOGL5E*XCfg)kVZc zz$SIU_1-{0g-3m}C-9@tUhAzrO>`%w)>k1NngnGJ_#WwW+HU!SNs7-CkO_8nubAaX zGiA^c8i`U=4UYsDvwII6t~bes9|CwI7~@Z9wu1(0`r;ru8Ra;8-BbF|+qJ{ho10!5 za|Yc1NK2^eC1FU#HPt0#$o&}P96ee#1qlm0dQX>Wxh-IS$lH#{1%FTcvs-Q@)Ev9$67S{|y8h**I6FGQ%%3==h9RzelbeX;2K0JK-;J)9Io0B~8 zNhqar=LX$+q=x%kPOl4pRS~8Irz@U{t=lWbELKPmti_Rc z4r?8Kp=%gUWscv?lq&V5z zd6X%#Cip&aGB^k060G2JA#21!EUCRhtoSB)lh8*!xle$Lv0f;L{2A6*4TEMDpB!U? zH5PfK1j|4C+`shKx8r8@^$N#|iTgm*#t^!dU=DB@Rwn)9Nbzhia<_aZ3WxY`q{qL5 zdEaY0Y#>fLp*p}dhTzPjJQEZbC(|j!(uqbBR|wP9^>md>PL5{IFT;HCy>4Bcgnzy5 zEDx<-$a)lX20YfW^${c zriQ$-*iiI~k+gtQx`wwf{(|}aaf*KoO^5QYp-o)GNOXQO3`E#)g$UFU&jg`m6L;fV z1k>j}O5F-&uRw=r9QZX|wi76b+Nq$ZrG@Z0((q#8rNXPANRRYzGa?pZ26J(|MYS+4 z!t5b5RY9?Iy?<^I9LB|=Zc)e8qhQZzxywiX#8I_(oF(L~O&`$#MQX@uRPnaQO=#=^ z<<_CAE*Q>&T9d?k3)^%yIPG!V49Qw6xW^fa09_1?1OvPwF_O%0zvEIvkj6r}YOO7T z>z!wV1Ba0TFNUf2ju-32BF?%v8TEKx!r7^r^R2saDjR8Sn0FShai_;DTvRFi6w1&F zoINSr<9;zxG={|R!sVcdkv$ID7&2f6m`w<8j{qG$r!-o>e)m{p7IF703+lT2GwhKb zlK@EFb(DRsd{g&MVU{e}9|c$Cpzw#|Av~(LrwkBPj3jzSoP?MeaSp+PI%*_1ABnWx zTlj6n2u&A}Z~Z;Cuhg@PQ|YT9QL1jvW?W7vS$LgImha3wL>1-!J~{# ztY<4=77PZH_ms z2YH;ZrCySGQiqk>Fa@kTrx~5!m6bIY@yW60$>bfRkCe(DxbK;##3fNMuuZDRTJ*fH z8Y*t`@Nci)ojrGSI`@T-^bfl?H^)=Duo}bMxIZ0Z^B zyP06lkdikfljwAj$g0Tja?%<78*G>W)$SQU_>;42+Nj!UU~((GqTKGORUNHDGx@AR zYi#`>3p(q*rxY}vga5VH=aARkuKsUkCv!?k-kI!GLGPrlPi{Bo^~H;^B-aDG6uB+` zpxkbe&q1#yjF^+o`RFeu>3?f)-B!s42z(17-f>diUmX6OmgjPRToP#a!H8-HqPXwH z@Y_B&h~x7E(5V3W;H*}lc*bxinm_j-EF8!rN>?zP8#yapSd2-F1pl`MylI;ov?_;F{87KS=pJ zSh=!0@Pk&t;J(qgR%{^37D>>3SU28e5Rh8mqF!uGidIko|G7Th5&b0UegA8Io~S*J zIxI)mqcxDm&g>2Q>hv>uE!%QwNeqWG(Q&ayd%MNi&mkS-%tm)?~w zCERz;G&8Gjf_g>0)MtgQ>Pt~>CYqE$^jR_^YK;Q!FY1v;sOpRrT*EhCGvcw)GKT1_3p|tA%k$|&JkLuQB-e^^ zAEA_D!Lz?!elvG$Q0Zuf0Nf0~-~<=A#8OHd z#;Fr?Xx6^Aw)R+>+LK5T7M1s1^x%H444bphdzF}!(}9W#o3wO-|Lvb+Xkkty`8JGS zj#Q4A-bKNDDN>xrS3QQYIF&?KSVf=3GJ|eh)*_>kr{~K?Umg4g!Uk76&6NW}D_pD8 zo&Z9|m{vay<_Q!po~*ma_Db?gWX#7q-^$H&50HTu6(y_xjQ4t#`DNq{uz0>E(lTT< z-;nwDDofu0m<>eZ4gN2r?!G5%F%aPqsl&Yr9E3M@3U$hb2Nlrm)s4=06bKh8-m(0F z>SnR|M=`;jUln!gQLiY$xVILMFY$Xsy!id?B#;1YO(6OQ)pvhMh6jz4oz2znla(vE zb(7~B!1)Jp`X4x5vI2NCd9Ni;Fys`g*N6~+?F508+@EUhl9|;06(dp|Zdu>+f}hX* z3VrL_gchvoKihm1Nh#%;N-z>RRbun1?n*yZ>0oOini^m9prw6J9VU+|Q06W$laf*P zbo-e^oxFY7?*Vm6nEq@XA>y|mH%TPnB^+<*;hrFZoppRs;YbV;L&3~&XmD>+vEjJh zv+?u6sktWE^7Rh&Vz2NC?r34ay`T+Nm>rUb52xW*rGPWXq~5qeg+qXQ^mAhLtHS*m zOYB<3cqh`lS2S^%1s0f0Q3-GJgSc3Gh?@Bo)7ihgA3T5A%HP>`u?LCD-ZkWl*Pi~i zUj?qEz|KsZ|fZ~dpbzwRRJkZO`o$&3<|rszNWBp3%!*fLW1 zFbh@8(;yhqv8Msz$WYeifIK5K7$#D{H^4L)UO=pQwJwqMvIAwbknvysORzb+`H=Qi z_QnYNQ>Lz?%arayq)dGq^G|`3IXHIPRv%BlFi`BWq!ePZ=u=5Rd|79|NZiTR1r)BY zbFN+U+XCKKsw_f>#Y_0J<}~x4$xxk^N-=z}RG9-;(cX=#D?gpXZn)e~B9J}*f1I1b zrxCDoQ_u!1NNzIdQz2!*3Magirw_iD{A?Yj29vN<{EZNPw!wzZz$21;9a!bbXDQJ zQ)qIpN8_aybW&Zfb*9r(;P`HyEeE85O+qy`d(si|DEKr^9iokAmyh)WS-`jS*Z#0i zo$HcClvZ1Y0KR9^oxm9C7XhIr%NI7-dI(QOW2L~N8mh_0oXGM#VS5Ayv;!b>H#Q0! z1{|9#Y!3m(!3%G4;(d7745jgnwP0XTx znEARrp*3+NHp6eWB(!Lzl$mw0{FWm_(#@?**{?nq_NUpq16y*-7O1{Y#Vc|Cc)#3c zQU5#}@v3(_>=?Z_7zei&zYs$;P5tUQ<}^uKoNmJ3uSA)4#3UlqUGX^?vRyoOInYr= zwc|SN=bv|~tivK?NDXC*ML1a3?G*VBYQLkqG2J$6p5j!*@-?PW$nLA&A-~W(NY@dk z&+25hZP0x^A23i2fxwO+ii&%c~;BnUD1r+lHTR zT->35=LTtTeW;A2zrGUIr?`TLMfu#JzT~R~-L@S#c_$)8&ydE|c=g-(!4?Do7w-wP z*OMuqH&bAI@gU{D=U>IQZVIdGMeBtgvc%lgS!aXkShjWctyN8O4Jvszn#@TA0)h4# zC-7qLV%!G5-CEOUo0ym<6!v+>B!lopua$@C?lm*t74>wvZSPN`+{8OeNr)QrJ#~(d zWZ&457Edi_E|A%FQesrV`3Act5lcE7!}%tEMBqD%YSB2^8ledSv7jZu)#Sw5;LH})RY;yK*e4WRP`2iK0 zCLGYW=XEOyX$*CYs1WKVCiNW(^#X<|HZ^7rnEo0VTZ?#_otc< z{KvD%1n;xb(cE&)`0)3og38gu$pMi#5jSY59=~siCqSW1o*qC_XKoKA3@2>v%wzxZ z2@MkM_Y6eP0I~6#Fd8i;-kB}c?f&_rh}{=&UdpHn&DCwQ>WuhktNDcV-I)!$8DH)z z7b5@oTGg3)gQG8r;w;UvkQ7l&u#5%5GQu+8Lr>~q_4!~oy+!gFT8zzr7|yik zB)C@>OZ`#!yuP+LZ4fQ=GX(=XeuboA;dirpzU}GJ;AbbYnnjvdM{Ceod57EG?O)#5 zgQRMk;V6-O^Fqyyj)%e*5vT8{CHb(bFm#0~%_Wb%qakf0sWx<=cbH(HhQ4%dz&`wU z;gLRVV$hx3q3><)nP_8ZoI)m~gH9=;VKFhX5~lSYGb*B(2E}`RT!X#_wi;)DW4Ap- zo5zT0NCZOs&vM~7`x-fUM0W(I1Wh2$*c>R*mDZ~^t-<%g{qt4;JHzDX%fAX&jn(2M;<0+-{r)-oJac7|7FY<&SzN+2kh zO2|N7sX$Orr9e%JgCEu~Sf^08ENw~R_WktZMyz{6Guw`Gz#>fa-c&{n&XWHtM}!lw zeb}$Y48J2!rVsb24fn&Uy$^h zkmwzXjyDi$58}XEeEWsNNYJ_%h9m`i)PH9agCQPF#R#;rjCG;-Qi!%G0oY*c2HE- z;=*_8=fL6cYYbZN&5E}h)D|JTX4?JOm#dn*{nVRt)wB$k5pUTV`Zmi;P#}8o!@pOz zhm&$moi6;%93foyAgi67F*GnrKp>Zgkp!n;?GZxeKQpQDYAv!CdEOW zdbE1pYA+G7*pnwkHGPaWR$`u9l6XvVD5j`wgH{7;S@tRiaF{Ax*CL-|rUj zCUlSD(0=K;Kd;z$x6HR+KNjm|J-6V!wS7~3DXmf%iIMx4s1(v!k+diou9fim%-yL% zbVwwBz0B=vq|(Vdp~r`NXs39hr*I8&c)Ks?z&q3!`42L0RpxCT4B=CGxBWozZD!Ab zy}T-=XJ778CCaCSHzI|T^aL+X4j&64s+vx^ZQq?qvx+QrupjsTh3jR0!1XHXpRG_W z{)a2~12kNvdY7+f1d8!#mo4keRPSUhW&i%+$Yrbhg2>f71+_}~EtFoL?4m}gz<|B? z958YZ6@20Qf!u=yBlq4-I4!L0BXB-p1R3--7a=D1UwBT|a^R@ZJ|5>ate&rV*Q54Z zk&hWdHNlilcfPXfmCBU<>(4wtzz*Jv#QdZ(maR_Nq0p>4S zAAfn}I(mg?v%;`m%V7s3t8@Ivey6^~ z`PS9;C(P)qFKuoda%zYQwL8`=HGdy}rpM7T9k`Jsn?W-bo9>RCv!LSD*8zCg-NK1p z2j`9jkIpi^Fw?ZM(7E`6W&92MFBozD>2}Tg=Y-jtd87FUnc{`zYp)m`e}nlM|8H;v z_g63+Vb*;GVhIy>`6vS$K7@?SZ$c^sV`~P<8O52f5Gs@E_!Y|4{0&+Xz&nYYNj2~$ zcAT+;(rbzlN!)+Je?rO`g>ggpQ1}p>%7SMttu)=FYl?b*e>e)iaWR2<2G^eno?D>L~?09?rd# zsTTM~r<@y>%)m{aDV{0!L=@+&9H(plmv~>WpOtwSJb@v0Q)6#y>UYz&_0RI+oAdg3 z*|pKBy+;gn*HcOtkIW`!+Sdsf-~3VX7zzEz^MiNO!l@u zj2Smb_U9aXSc7J+QLCZj&)efXUd>|&kp4tt0hVK^3 zrI7z;z{CyY;jj_25avW(Mu%!=$HS>9=s{BH7P>)S?r5>M#f>b(3Bn%5UNFGPo)?xI zmOEw!Vj{ub764`mmC;r% zu%96^Vi?W*CwaPfy1>uRJ}b!S6POK({W<08YfbVEJ-#R(!?I9mqpR!aVbu7@Jmtr@ zc|7qm$S~*+0f$E1u-Q(2PJTDwZDp@(`zOcs=mBp-*;ph0*jsep*AA_Jba$khYklUg z4;B%hVg}Md()f;k&O$V2)BC^5uk$D_(U808`ZH}Y;*#8=IEX<6{_?{u#hr=)g=U6( z?#X7jSQsG;1vJNo+~7hfEME%yt4Bk`%ZF$P#^I#dB}p%DVaOrL!PO6f#19*OeZl(? z5I#pRi{`VL^J~7lO?ky`$sZ}JNCM>E``u^!Asu`;LAg1~#wt)aY_Y>%_~TO%&)P|b z(8EYEmDI@}7fK>*=5ekgB}vw8HQ#~%7V(vm7?RrmQ^Y3>gGGD}baDUr2oeR{KXw3x z5j6y=6c_><4}J{F@g2g!CM}9TsX41ovml)%c$T9Q?qtB7g8Qb1R*!lyT2%m^sc$mf76g^3) z>+x88Zinibx~@b`pE;75ySYU0-VkFl`9RWL@-(9AZ+R+Mex5Wabay>BIe)ns(wobMPtTpV&;_6Nzr?t-7zW6<>u9MMDQzZwOVqJr7%hO7)ZsJ8eHAggTzmWqM$p zlJWr}C;IR3;90m(XTxwYdKHnc&qq)Ydp<^}Qv}p~qg8Uuu((V3JQ(Vk&Sf<@an6wJ z`lgpN*zQ27S2Eku!$u%lIH*&}!HZ93sv@qCe)>~esbMS`eHHk3V3}JKyCE^f(H88+fMEGTG2L-+BF$ZNDDB;U5#J%E8@MX`CK7v*c1eoplpt zRa$z$EZe@ci?C_%LEYJ?wJ{gFJbEubBWf{Z{_0H+G2*y)1)khh{ikfoMzvO9iB{pr z<@HS>7ek_+>;5AQ*xC^qQrE#!oIv2ybCj-* zGP#$XMnct%b!wNZM!x!+Co@}JpWI24*RO-+fzc3DiMpi{Z!L8|^qF zyQRvftRE?;N2V1Qc3Eq-{I+uwwby-!`0?N0jP^B?_koFM5i9b`Ba%TzTFpJiW6aRg z@RWM=@3ZH;oNVnIPT!U0uzq9-Z^gg}x>W{r!TNuc9NC(r9X2X`=dgvQK@`C)=xD9V zDkswA_!`0#ZRtW+Hs@;~Rx9g$f?HH8AIu=zQ$Z_>Ba7i@EGpfnU?8*o!*aN2rKL$x z$H2q?sF+S0Q^7jm2zFZN?^>sWYfsNTO_&cFi1-@Jv;|U~Zkg*Oi0B6X{p2nn`&4t?`b||NB2t|5sf!pbkgpK8 zI-jzy{#;}%p!w8zwrUVIXX}?KMp5trq+;`?4dKpRy4mT&vqPbTg}Rg1y)Jzae19`V z0U7-wWhHA^|JVJcF_+u&!b7~(+F%QV7Gn(Q>nt*7>E&U&c=}ZV(yXqZ6KkoL(88jM z#3UWBD6s%AG0P4kl6!5{zOY`kYFAfff6bb+`%=Vl=hoo82{H%(%_E)wv3MX3-O zN0TrRPhN<}M1X@?k`oRzdo zG6^K!93Q#W&sYbt5qAyzI;@^q^=>^~J3qfz7>9wo^gkPeLt5fg=w;^@qwed)@iiC| zLi{#(rA=?W9Tew6EgIo(0In9aC9I2#(rga=Xeni6FHL_2#SH)xlBy3qxB0pov1w8J z)mV(c7%5Fm!-Os}ehuUIQ9GsZy>H@ysp_VFFz-99iVCX;mA3cZ|8Z$-ng#(0wqmk`IvM#9c>npx=+O$++VZhW8D#-U6x4fvsDis zJogWP)OVj3HabiH5rNzhJ-rO1*c>Az=yx1PKKG*2Na!5Et5R1MBiM#G724B>Zc!9x znAr@&S?RXui>fz&7mw^1t8%<`>zPi7x+mtJ4Y*HUxRXavOT?g+I}c7O zPAVx4r@v}$EGs zC2F0tOft196KTvhRS1FWAi}LLP)86);4)Eg0+dd^g~t!;{@Q1j%Uc{LAYsK<<4dZS z#c2GxCFVFylRu!=rG1Be3dtz9TIXY2_e?AUiYlVY`>G;mhboahdAxv z{5P(}?Y3&6Z$I5)6&{9d7+3MtV7J?8PvcmPwZg~Te=SxXaPHRR7!Uu^!~=yTkim&U zSXTL>T)QS$(|)wH9%ExEiOG_59X71cTU&yO)3UY|(jzWw>Co$By>AFhR)Zt4<4SNf zKu4C2;?^sU7Dk|K5Q(rg2vq_oK?9HfONO!%!O{cO(B%YGY~P-JKk8hc3iGd*dS;5* zC)}g0&qTG)*$vNZI<84q&D8QEZ4IpzRDdH zVj16(Gb1#~|3EdU4Vz}8#!nq3IGzo&U)(kO|4wNxPG3Pm&MAhJD(6C?>RbCMi!<$6grb{|4W7i=E7nFhaKf$C%`J3~ax@is+ zB0F6f96L)B$%9L666m6Mts+dq{+WU`tjHmee^>{!zqy^(D5&a9EF}8yo@qK!gr^W# zBbI^^<{`)jh#^qW0O$a8aP&(!Z?!5Ef<}@KPt@z$XU{)2dIhajig6>5)zbQt^_JFm z+R;B+y@#u2=$$m>tEVf#&v;0bu?Cvahs~UINMcLSd{|A$tB$ecKm^NW1fGaolCw%N zX|$@<6uF8olD`SGi2&%7v4Bcc06YL5{CmXP>H^ChNNeCu37JzpFKM_vb<^p{e2J5V zeFAK7ApG66wq;V^td-75-|X~B#GtnwEtACcO7cX5LHp7yMbUSO^Vj#Y%%7@cE!Rz* zBibGBTY!}mjA`tDeB&#tyUHHWDzGHWsD^wCq5;TD-e`A)R3VH-0wSUw!p*;`fju8J^$m`z{TZ(?d6<|lQozZjwC5pg(BBQ z(x{JtRXcRf$t;c>9yRV~x7pZC$|^(9^RM)ZU%{fy$^scnA9;B_bssgPOBC zp02Q)yTH5C^r)Ta`R);qZh4AIhOx=ioRWK!NtCi_Yr_Bd znuh(f{;=qoB=ZUDn?fF@I1OfnqcmSkq)DkBQ9H?kR17dRse-jP91=sf$tNcU zp5F?YRKF)cA98FAiV{G1JBkg5Avv=YWv%8j&EB96F0s=CbJfv&`CDgJZ^-|L3u@q) z7#k~zVnsRxrRU}x&)~Si)v{V-8uo9_c zGl)dN6C3#zb7hCrI(@9jK?6t)P$QziUY zr|<9dx2v%WCsVz(UFRrY)dEYgN>Wl@D;#Rp+z z3eAWcX*q$!Yp`R%5y|5}u9R(?zy(1HJ-vG1$v2tV#2QY`l%R-NdP%>*LH_^*1A@UL z4inXsAr|H0)~_y5fxC0k z39SUVsD?()zDq)V5gw*ZT!eYOaWoV}%7*nl8Q;ecDfd7hY0N<8Q&$fFi+Bi1A3{G@ zufx1r8ChdM07YNqt=%B^!sZjb_52P@TjjgCWNn8h=xI=@rzasHwc3vc8v)<8np}2J zn4){k;dit8g2AzS0eKF=4>#LpKbR-WAF*_4!X>c(;Xe0Y+@-6PW6nHhkuoJcM0txvFW0-Gy~ z@`?Hz-^P?Lli|Rf{LKG4|CYc`#H8k>z?}Wok>eQ{%0C$g$)MXpTOW#rj+GA3L}F}0 z&c*P<^27R=y@+oOf7kFMa?yyISR}%+O(0+?iuVgbc>lirT@K?KO!w4?I^epOO$7E1 z$_H&>@)5PDY^!h`7)g}5xqk&Tp0_}{G2i3<1 z;05r0EYGhKbb{Qo{D|M#fW7(`DPJ7tv_no#8@#9a9~_Gl-Q_z{3JsSd}xAbKCYiulo#NC+OE{6#2^sD5%335HArg1?!8ALBMgs zxpaE@L$}RK>lq$avIsf}Z@R38eq;H{1teLqGLL#+6BPWpUX)%BB4(BF>`^STM$8}t z9g)|38zyFvoTgfCWO?oqs}_{uRk4*^8A!1ag6Hzyr$mxzzWPl%HT!9s5RPl?JIS1!nSkeR;3^r3d;UgZlwS-X(K~T+N*mK| zahasq)6>m5pKcjbll=JVbMIc9Pa&EZ+nma{60BAKq~yDAu~lz&WPtwVL>aNG)%%jsx_NK@2A=lpxW6pO_0uZut7q+79r#1%!>dALj8b3c8o14I_;&JoLn(!XmhGaQ*xBqs(GMzV z+X{?vPOc^k4<#<1l|BLro`WEh+i^~p4qGAup^dYPt3O3Y9fzg{C{RbLXLp9 zg21J9;9{wHjl_f$E_%4iHoQ;ntg<;sE;+mPS2KBeI$7LmjaZQAAPTRGQp##-Scheh z0Q^_Jqa~5SN{#Eu^Q9=%LDDO|v&Fki_iy2KQjRgUOCgUfFLdW4-mA}7k0JH3hlZ4N z;<+5($2=nnBZl&ZV&f4=WU6nIDa~WYYH-7S^_X9S5RB9=i%a~}3;bYq{SctkfMJc# z%gaJAY5*`DCA}Jb+x|PPg~*NrBZzoxVi$7oF5nN9a=}gfYWymp<1^jDn&fk}H`Bdy zFgraQIR8BFSgzJvTmnYZT0yjhWC)yv%4D_I308 zP4H(Bzb3bh-&bvTUYd#7atm<&{>-3C7u1j^T4v^|#=kaNsVQSOm-AbaIDwp$n$)WeNERjs6i}cgfy4V6d zRxtJ_ny7NNkW%Pp90NWwy`|AcLy?C&F!yimM zi;^>vA@Ii!?DRIjmMZtU@(k;MFTRyC8`KDIZzAHb+&}giUf$= zI1{EwR`RYn!buvGgs1rk1!AyjxOpQmIS62qt@0B2X2Nbn)PpA>pkxeE&;{A8W8eHx5(1U7UZS&5l3m$0}bgj$69WhXzZq>@O z*i*7`e&z#u%qZ{0hYFVK4F$TanR@;TC6p_=vDoc&5K0y9vG9mUjm=sHuCN_qgfj2Y zM8MS$CsC;7gpD~9r*3y)#KQ@o#!CyM3&%sm3j<+@;fZCyiZRxB*Zk$R=3roeq;ECk z+0-U}rS!{m+-i~~AG=h6*IYl+A8x(Y%0(2q7zR3*+x8zr4?t+rn`U0uaPmoTgJ3WC0}wk;|oEdU9C^t_J<{b~OB zdttKRLz`*yA7^aN7YM??Hh=?j%G*OpHt6@;03DlhfrayqiUqFoo_@^kYy&#gp2Ea2 zxbhm)5OB2@iF{zq#IHql{fK~QK}`X&;|aBw;MhqFTpD;-5?B(ZATQFj9MDr&VC)Ay z$iCHx3btgp9_~5DIv++oNN3VvOD$GquoJA`3<%{dyDz_oM0mY7VQqC}boj)+dI7iG z&w_97vOSAi&zU%>*UC}f_;IsZ*){EAg*||8-d5%YKp!6?px?8;n-}=X>BqY>@XZH& z<@w|N^zxAz^g2=9flo+Ol)Z_+P=z$Ri8_06fqJ?aIv9HVfEs$oC-zP?+w|Z=DYSVv ze0!{@EkSJwGW)0Y+Gd11!t{p>NUr_|7)G?p$tG9>6^HZFV64e_) z5gk#>(X;XM&BjGp?K7;&U3Q_;1&C20Td;iY{rHLP2~>fqmo)sGu&F2H?P&oTzW7j! zpms4Qb}RpOQZR@!s4ILmR*eVoACqlKOIvC7HZtyt7T2=Os*tp25$dg_RWN!885Kvv zgq&=N1g|t@2Bsp72`P(j{nA?ttb|YvIlX}AaJ%Mb%;YsOC5I{1zy4XIx0t!bOLTfU zS%&2rWp^)%M%*(mMG(h4vlZjj>rOkh-v~#4ZCJ3MjNhMh%_D_9-nV$HJkKxlwt{|Z zq|41Zwsb~>rvBA$c4nEm-M~#-A!HJ@S3_X(wzMx0^QZ2oPpio`D_w>}KM2=9%m9!A zNHN2hKQk`ChaURDeNcP6FFk4EVaVRe1#9H%VGGH{NrSHGnR>~9iE7zAIBPK*ubstY zQU3Qj^P9z7Gk@J)dBXWvuVeAI3fH=To7%t)#wOa%-eMU8Tmps7o}09F*SVsmdgn3I zJ+{{};dsN#CeLTR^wRsiFMQ)C#iuT9)6WklmKQ~>e1TQoiAKQ&cA9GjzaHm zU%gw|D&MTfJa7Bnns1AXt(17`a9VsE`dVLdsX|(jN2h&NaQV$+e3mcxKNAzp+tJ~NAm}&GFZV8KO3SlyQ z|AozLOuMwJ-Uz@D;a1`5pg-2mn$SV%3GxNj3^Z62nbEK-(K5FDt<@F8?Sh z;nX!h3v`J5r1(t~_pqX7#Snv;R4@!Tkeo*s)12&31Rq6)9bof&^sBHjVh(bS0$4In znVweqs4kz+d7w`J1o>PX3ANLsu9u-)9_VmUu9tD}o^?ixzObBdLriI&2&rOz`~q-WD9+|V%r9G-}2ZD|60{@!lYE^ z;>CTi0FaaF)}yLOYJ&Fzkw1@=` z1VjT-3Ajb1-%8-hhkteET@vB~b|MXkGVzM|W;&N|42(Htj2q;z=TcRrOGbeefQo5f>@{+!X zj2DXa3OfW0!wz4hFvad=m!An{@8?|L!SM>h+t0{j|15i2_n1#XeUEARA-EyrBKwIZ zwrmh~`i;`9q3&mHr=CFI70QHJUhv&mob6xgfeo~;!Leh!6VNJipThrAb{ldU`Nm@_ z2tj82AtEq4lu%$@|HJb)z_CXKT7o6K~^wT9Tw4vkx<~$R;QD z`cbq2g*uxXk$M;nE?eL!itap0iC(So1xCW|P81=QXA?9r_X=ou~ z1#5V{rel<2_BlCzi)P4jn8X}NY<@s&Llg&S;sfFXu!E0&6E)WpJXeTM{kUqnf|Fj$ zWSum=j6KN84iXXMyq72YXSb6%0sj_{&k}1QsJb7hie!|WMr0Xa%m~7dQ|l6U6%+y5 zI!-wg%WFKbmIc>?0V_d43Vnl>t(P+>B2dI<#Al#uiTHcN`g`hng$I&l4|7)xym?r6 z3hJjV?f}d7U9Qv028*8ch*UPc`!;Kly-_o>oFIO*V%MYr$^Ai7ftiFd8tN<8wibEi zs~>pL!C%&3ztXUYOP1&DmntMI$%-K%1cc${#P3V_Ga3^*lf;fePMP>IqP+sZIpZ_n zRq1T8wBYw`!^NaGDAKhl5#QXYHmw^8rzMShLPb~n2pO-u;r!|Q9+>ku{+K!TAw z>phb_azTbY0aem{8G!y(XXRyW-pMag}IBn9Z|k$m&|HY8;JduHF_L*nRiUZO?*S|fQHmK58%e> zf)E!55C8~1;xj0%T1?Qp+U=@VsM}ll59no^h1jbU40b8S0fLAitilG~13MJz>T!;` z_Q{>&@yYK8hofbenp|wT{s=K-F_eFB;1Q5*7mwEoa-xq%Ho<2WL;6{31=WRX#j@`*eHdDngc@mx}qa(VA4> zo;i?$@m{Fv%=ci`6PlyLqVmuvys0x#er#$3L88GhL(8H*N9D&CZq+^#g~I(H)$q*h zXxu_m#6BPu2D@4HDG#xvLyJ)u5YPwc`w;Pl&UZCU4l1o9nh%-yIRkBsLpX>WZUBS;q&6@< zbI-8#e5PU$JQm0@d$Al4xe&R4^+1Jy$$7|m0B`V_mu|!6GG!v#&c~&afJ|kkfd#Y4 z;g~jl?7FXN8Md z7>A{I@o~6N$V{t@0P!&>VscnvM^}#{4>&%4dbAp^kVj}@EaGIJC&SNjb_`v9-yb(! zhQ+rnC&K!9c-B20pY}I4Qdh>G-}s@g@le@XdI~s$`D}6FyAj3`!yz1G;qrBVmbG`Q zj7RFz*J+}`WJgdFpF#q90BFRd5C8}OIHW7>H`T$h7zK7oPJfS#9S7`9{yv-Y;^05s zct9})^xXaWyZ88Ve*oeBNJ~5JCG18=#P*p)xkx$yDlgM`0P#?z&9*@mD_6Qjy2LLi zmaVbM$F>d-@MI7zKUs=o^5{?7z7kD=Xt!RvWXYj86viKZ>tMfZ-!-ek9LJpMm6ic$( z8P>tg@4m+O?&DI6*^#;TFmDE&P;-?vS5w3wD9s6D3B*M9|D!Kqj-idg0&hSR4QqfC>_mE97T@AEqDXM`-%@$^5TD6u%vtMb^lDm?K=;*|`n> zt2RyD#({Mg{2|Q=wXT@-gHN07%`3crOMHZu*ML3nRZbSq6JuHZ(t!rk(6V6zKQJmd zzO1QyDNOz?Vf-5T8kPW>g_)_P&36+~?Qf%fv1w$z*+M={EmyU4*!lg3-To8(|n1NY#QsI3P{D zE~;@)WVA=iy6Dhh_(C!IHEcjEY7oJ}qsWlR(8nRsUQ@%fH^*aHHNPP8W#bE8q~xf= zkFi*Lr&f;^SF?MjCLiq!U%t<(Km*gb)9BjOxfx&X)0mQ2;vnl|Z9oohAzM{c7VIO{98(3NGw5wZWsg3XMZ!Aed9N8oQlup0I@F{ct z6Yv?>i}jj!wmj!8#_!ZPUCXW-_RzLF_Z6`5bvNPiK?yAug~G_NG-1A9f;%lg6g2ON zL`^o5bZ`10#aJZP3yv9?0;zhLlk8G2&DJ!eL3Ps25I4_$_FSC4ebaC63-0-i z!Kz~SPW#$mfVXbvcK3JId@lWLz7m9_wzT%Ty;70g$!U4=_;xd_DM%~Nl0FQPfyiha z8M!N5V!Fc8T{4YHQ%NxJ8=a;U$l@SmQNwh5%$6+&qKL%t3&KW6xw$9aCf@$AI4;{( zeX>2B>$8ttbtbvcSvobWoL8iBy>%XT;q61>u6GGdhXl~gQksV!ru!B6o$!5$ADnoS z;h|^X<jfpMR(14P$An&ND*Z*M9IN@$hIU_VerNp(9os;Q~F__(BJ{)0P`vE z;$XjY?VSLvGC~q$CMv13vbVpr&A&R1ET*jY5HQ5yf#{p<)Do{OM&Sp zt>RQO$tAQeMw<2t%3L#tm{w01;XNjUr^l^Bd`zbLmhYev_%;{u+G74$J6UMvWj?JBL&M2% z*8ryqrdjYv&G%HK1|vD65l*QO{6DcerMIi}DqZFrq}5=9+q46i0RUhPEC2xb(6|6K z?e_b*yrzMjC(Nhn&mm@l#?pc;i+c9n4=7)4wwQJvf{&Mrz zb)SPDyXTF$a?@@u45xaGN;P|n*Zp7*JB}5`cBC{D?AwDA#4-gN0Ox&&;cBp0KhC)=EsiQg_ zp4z)E#kihl=t;q|KHGk?>z%v+<#CjHykVjy9In7GerO7qYeIwI@NjkBf0&t-g9%uO zB9b?7tgs@VL^amrsI zU-kl~gXK~H#?mTUjtPJPEfg>XIrZVMn9H2ZBMGYi$xS27>h=q8TNECuxeLZ1H1-p} zL$utunN7EIbxm)e_%;d~UK+yoD~nV-JipI~pyXf!$MQ!`X@71S?uB@cBAkHH?hbel zI%HD~p&5PJLD|s_;~QHHYz}Px_cAW}s9n?dHzQIBjM(Y$xT1FJp zoNg4GHG*=RO^j0+^2j53xLR z?JawhEuGKT|g#33j z&AL|R5E)s>C)0qL5Ra(*Z?nf+60=}sQ^(G71)l?vBjW!F&Gmn)8}s==SQJ6-)a!4X z`3oY20(pOXmtUwJMkH^uQB10Pmalj`(O^+spnf$fPHG(D%L7-p<%7b~3X_rWFL=I; zd=mLw3s3CUw?Z401kdTnXM}7LJ`G8DATt(eKfyY(){d8*5hQ?z4aeOf+y@DO1%MsS zIKSN$_E|DEf)KY(FSy`RB=JWtKNKI3+HLDXYrFSaW@DgJX|TM)Afqe={BK|wa8DbJ z3dp4>-Q7H&MiWV4V0<4CBvTe#GMRu6K>|YtNjw!SEEGfY2oH%2rggffL4@0XYj9q< zPGn&(zs}O0cdU22!vfPSmXY4oU6aG9--FlSUhi5y_x%nLmi9{~sppTe2be>E={V)p zD0g3uVy%jc?v1wm7a{GuZ2T%jh1VtNNG@HSt2(VioBX_-;%Mr3pRgU9meO~6$#)_B{iii08>=CNB z{!i_GXfl6k#d=SyFWWu7^tH#*E<;MvLLm{|Uot!&Nf9rq8Ux&TS|E*u_iD|QlH%Fk+L|FOu(D1hk(qH25L`9aH+!Ql zH-~4Fdh?^vZWPe3HD;;GXboNBq)KG{B52#zNa|XaC7hgSGV+OP+qlbVx{W$r5b200 z`e~3``T$sL@GOI={Te19aJ;qcD{*&QWhVi~M&&YV%UQD7kkF>*k3V01`8G6H{^qJF0XTP9dbW5)@gB6}X)Fwq_ z3l`EB3KA7eA4BoCOFvoikbSE>r5W67V9q2tYHMzB+POPFyl?<5&X>8G;QScFys*7C zK2;`;#IU2iJaBeYRJc-hn(>B-yxML zvncX5+1+XKAkm>s;2jb9kLN9|;h>nD_C-kpqgYiH^bv`zZa5-CI7Z2ZuY%Gxd(MwQ z+s>}oMW!Bk98nSm41H;b7CUf)84e`QhZskqAwQ^kqm=Ufy11J3!=XJm04BEDRVRdJ zVK)0faXI#_{)TO7ck8H5D8fx)vw3iQ_VFoc7sKx47o#b+L3LM;kqAqSq=iLZy+5lQ z+#-;Am^{ga*L2V08k)em5+)IIKmfq~sS6?w{v%8Smave~70Ff$7ZGpInjGw~PB{N| zIf0BVvjR=g=gouS``ES-r5H1|fFc91i$w+hFQxP+4Dnc2 z%}TU|A>{#6<~}4?1ZOwU!n_~Ku7-}AvmX#v7*;qGY!Bp$J^Mb0p?2U92>F(`+#(@A zZUykqg_gJYIGv>bKm=Vjm=LQj4t~-Xdg9C#w)aG3IXSldw zeKpr?IeFZ?37N>K7+Hkv|JBoV#|CHJ>YHw=SOl?K2)=Vi%QB^x=ZJJWVUX8EO z(olP=-P&8N)~rUw2&vI1sZ}Gy$S?j6{;&Js^X%T&z4v_1d7tw>=N=Xqh$qZ3VA%b= zoUJSOcXt#1*fKuulma~ zTKFcfb}ElN;<6px?_)^4@4MRc~Dbjs9 z=CoQ*MGk`Ue==qC^CJYu$(`cc$eG-kup}9w2?U3?6v_7D2no|SFJ_d&w&>XNw$qnS z{v>g12va5ZU*;pZxM4pk!?o&oX}So(Yh&ER46W$B*&g?Q(YK?h_M> z?}sRWypz8udKSA*mIykqzlf!JgK6x+mnVemF5E6V+513K9h63nVb>y^06?Y*(&hDCnR`c%rOum@UE3Y;(@v*#*|}-auTFmw z5(Ka>K4_&qRlfhL;Hjo%R`^W6$!=}ysD&B*(Kz+w!#-vzj%Fd9Qg+u4X)`mm6^ z6dBr#{MSPSzk*m8qk+*^uAWXyW_rj8bg8gigtopjBjWRp-3KvrH*4hM!^F=2+zknM z@5_s=vq?Gsqthnqs{AP=gcwMaFA}`RYvfg(%U~*~LoYg_odB|>`#%=3Hb z4in7jrsYR9Q}26z!xY8BKfxi!4Wc5!f0Q7+YNZYACEosOMn~U*_n#>v2%}I^|I5r@alXCzzZm z1a(nfV0Mt&DPMe2(hf5|mod5aD8P=gxlMp)WPN2-qN$EyM;(RCU_3|dim~{2HQcZ? z;7gun+V&{4Gm!>5KN6vTIcK|m0B=KRW0xI~$@Omzy+ ztx@m!FKVUEPdy|kb@7Tu0Hkm9L@RBod#$f+#JR8qG%{heei z{Gk7OZ01^Fq@3;&-3tag8vB(CF&KX|jPuLN*_kvRrrOqI$PL+gh-k$9 zds9xr=i;K~{9@;ula!i|Z^k>N1IV2@Nr3>+Rmurx^Y@)3k6v@L)Zu|OfWop|t9jB- zpnF5B*1#rVKeM~H6YX69Uexr=Y6JuNWE()hSG()w6ntT!h4D zdqBU2Q5xx;ChGY2flMp_ZGiTb3KArF9L=HXKxXc=)`{?$ZTHp}Wi59=?}~7*6EUNr z`xhpG+aqg7N0)G)^T8tpwmUg9&Vz?vZ6<2*Vm2|n;>ov1L~-SU$z+w9)dcgf^xh#M%Pc z6*p@-iUM(g^k2T&7KMAvSTu*xvC|}X0kIF ztu?@OYAGddPlR1#3;caIR+IUfWPB)XIsm{1U?btO=w!QUWKD}jV8LjD_I`^k?MU>F z!M2-rUuqc!;=|&ryt?r^=w+S!2J7&ID{X5Z7_aQB?fU&iu|u8@xiI<6n?EyNpmUd< zk@GzS{RLD-JCYa|2hP+_rt*=YA(Mh5@Yh1*+JxjizPUY0cF7qG9X=!}Sn;UggLU*8 zH`$4HA=Sclg;T-rg5ODdVD>m@!i_yF!YE;u|6JZR%QDdK>^WtM6up0iqZ?sTGtCKYBt;QDhkMqyqOAzrOvTOEO=4IE^iMsQ?w0+-%4 ziIL+(h(8v13>strfxhb?TB$ezoL80!ct{nCe6}g6C-`baeKEo2^P_`afzD)y#a!jr*+NcvUI?Nt1HA>3nfFh7Hp6 z#ZT>J`?^#Ul@qU$!S5|*dmY&p@)uu{jWp$Oh(MP2BfI2N|o1~nYh#FX@`0?F{ z_VZv=)tlF$=I!nF-Q&9>Q@?G*IQ>R4@27jkCH4&l;pc?Wo(YvlqialwL+S0CjwBkD z42U(#BX^v@A0J3=6&MvkR8t>ou90oBN@8-m)1~Ue@?w z%Z!pe$S0@fLio~NOT0k9Ozqo9w3eSR3mI=R;$c!OSnj`~n-(z=x51$)o`qj-Z_(ow zj*(+8%Oaygn2-G^_09nlcbQarJ1p61oaM?>7m^sePu!6XV>S?=e z!n-2kdy^Cl&cI-2Jl=bMEZjSwP#<~OXprL#p>l`tQc7MeyNtz*<(J*yreaV*ev7*X>4qVUJE;!2$Z&y7W) z*t%VZ$r|GB8kW!z_BU*4ezroq`ruDQ-Q#R(!FR}h3$}(88cHVR9Lf@kK!!l!l9E{? z-7oO=NQg2WH3ITe!?NK*R$JS@oqGC;n7elMCq-IwZtnJCoJp_?c#GH`+fsTY-id{K z*P?bR3{O8-LyQBomuZEX_B3c3Q_T&T%Rme?a>3Nrj$bl!Z)=k10;cs4>p@HU&;LjT z96Ir~Axy4J>!e-K1a!`O(xV21Pkl1bDvCQ7{{nVg&n`DJUNjpWpr(U^H-mf^T+ber zAUmgS8f94D7W|8_v@f-e^%!|E!)m)yEI88+a)~+*{9xvlf}PLGXMXtnAuh^pAV{rJ zsq%`2o9Va%tTOQBvC#UM^RuSmByaDaqdCO-EMl7n+RB!H>)TH)+tQtm`SrFe2P(5l zvHs^3DY|ec-kQMN4}$AKnsr%}O(h{@3%A&>;+Aoo1F_19w-fzIGY`HhiGa4I=U0jJ z#fkOeh80QI8FPtoQ*anz7I; z;ke5rz*R9@Iimzot(34-7qeVs6U{ayC)G?zARzTz$&S%Hg@tWsHn1N#siEx^prgIh zY>ekF_p--h26hOG!=3ba|AGdU2z4uDRXgsRIo_J#x;<5EuoVnFU);N3kax^V{dM2% z<0pl33bw?PLO19k-?u*3tt}JbkiFX%qTs!t;kGkHTwKo-OP{{ty~dax#zQ{rm<~n< zLqd0@yLUC^n?ql>{iX{&lW56DFaT39+B>nQv!|%C;co-|R=9tzn~<{f5{#Qz1#RmM zJKL?Yd#pz8^r-RS8JX1lVncsIZ4%Z?-|q>@)HQA-`3dJlJI6S4B-00C3+D>wNTOx& zcWega8%*jopMFo_QLned=``1lf6N;6fH)iRa-8V)2daAFqk@0=9pziQE%1JX#8FH& zXfJ3r;nhWl8pJFia2&+F)>I zaK2*bZ$lfWf&QQx)1gHyoGU1fPQ4x%mJqJzvr}zS{evH)Sa%$F2P4W}f$!#WLyXMm zvFDnvN>~}%_>vWKyRbG0_fx32O#PIvHuI8kDEJpw>rtjtt3xucQ2Mtkg|mgTBo531 z7pj0TJqwjU39t!5ZJ^~HSzu`JeD;o>eb&>mJvlw(ZtwEt0?2)Uw*Z5hmAw?CJLmOr z`vkz-JgmPh4mWv`lAP07shG@uf5nF2_9F2GCEdGh8OL8o>Haj7*T-|ofj~!q`{XrB z0Ob|JBLBanURfLtZ>Cykkkz1sJ33Yudo6Ig$C*1NsA1N03i@0roU>rcfH4)*T#d3j z^7{IVyr<*h2>Sc+#;=1^s|0g}0!Az}NRoHXFv$|FI+pHkOr@Ldbc5n~Sy2+Rw5T0{ z%Wp4wd7&o&SDB}k4^$g6!UWtA6BZ8IEd9(jg4JNMlrC#&&QywV$kIqHl1W`&C zyZ+fJ^59ML=Qp(zsLD&b67%pa<5uetUQX0hy2DA^hZu6VCpFbGEj8Z{qw5`wtPY;38OU`Z-hU+=e}vFb(pSc8L47f5*oVPBY~Bs-lBiC^HxLlSQd zT^_JiWW0qr!DH7+Et(Zqqn#$^dqG2n8Q-cOKiJ-~td!ZUG2?Hi`&Ry3rqfGwX+B^G zR=j-j-TP8eN!Gt>cI-|$jM6Ap+c-AzaNX*Q9>Kn&VCUa~bs1^cdyQOY=-PhV+T#?{j{446uCbQ44-2zRd!Elc z$-Ku;>Xs*T$svi!Hui4E&qyz(E!V6EafpU8uDNEej`5xzQimU}%xsPI&IDA_~2pS`ldqG_-Ua6!O@mer}kyWN^+s^wzRHA9SM}zI^El3-mohpPW6z__=_+ z#6ZUac{PBn5H)U*f*EpEw9@}a5yxFueGlSxgMIklNg%Ne)9bb`U zN&DqGl_?~TA9IfQZ;>;3J?%(b|rts31Ln#>^$IAY2(XiewoFn=>fl*0ArZPu9)| zlit5iUir#!=8U#1CmGV413M4ZX*4!8Vg=K1|GYzefk$wVLY}umE+@#__C8u@@C$)Y-X@Bi(n+DE9M2)13W$^t@;duj7_@pbku5f*rA9OtQZBxQU0D3%i zH?8rO?Mlh1QWPl>mXq^rbHjT$MI6r(>F>g+a=AFY2lsawwtSM>8Hi;TZxed-y~ckH z0-4bCkv61M#Gr|FOyjM#aXT$?@Ab8wa;nI>Equ~mL;Jhr5GcroQ=&Ynz^}={bfj@M zECRdvOO)w6Q44cmW6xFNIFAOO+)~79H5j>P$m{n_Ji!l z<*tyoGraDj)qt^EiQVpru3qxecGbik)H1`j4R72d6q^Pn89mTL{~(E@`+X z^hRbv{0G*Je+mi>&Q^SFSe14ktpfDy)@x^4>+^6QDHb)MRT0CLq@Y+^-jcUu-yDtB xNA2{=Xku+#Aa3CWzXu%gE%K{~6rM*!o)tD}kzd1FTq>7Z>Z;UcGO8?q{{j8C(24*6 literal 0 HcmV?d00001 diff --git a/x-pack/test/siem_cypress/es_archives/signals/mappings.json b/x-pack/test/siem_cypress/es_archives/signals/mappings.json new file mode 100644 index 0000000000000..114faa0dae336 --- /dev/null +++ b/x-pack/test/siem_cypress/es_archives/signals/mappings.json @@ -0,0 +1,7602 @@ +{ + "type": "index", + "value": { + "aliases": { + ".siem-signals-default": { + "is_write_index": true + } + }, + "index": ".siem-signals-default-000001", + "mappings": { + "dynamic": "false", + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "doc_values": false, + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signal": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_event": { + "properties": { + "action": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "code": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "original": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "outcome": { + "type": "keyword" + }, + "provider": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_time": { + "type": "date" + }, + "parent": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "false_positives": { + "type": "keyword" + }, + "filters": { + "type": "object" + }, + "from": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "language": { + "type": "keyword" + }, + "max_signals": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "output_index": { + "type": "keyword" + }, + "query": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "risk_score": { + "type": "keyword" + }, + "rule_id": { + "type": "keyword" + }, + "saved_id": { + "type": "keyword" + }, + "severity": { + "type": "keyword" + }, + "size": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": ".siem-signals-default", + "rollover_alias": ".siem-signals-default" + }, + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + "auditbeat-7.6.0": { + "is_write_index": true + } + }, + "index": "auditbeat-7.6.0-2020.03.11-000001", + "mappings": { + "_meta": { + "beat": "auditbeat", + "version": "7.6.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "dns.answers": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "dns.answers.*" + } + }, + { + "log.syslog": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "log.syslog.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.annotations.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "auditd": { + "properties": { + "data": { + "properties": { + "a0": { + "ignore_above": 1024, + "type": "keyword" + }, + "a1": { + "ignore_above": 1024, + "type": "keyword" + }, + "a2": { + "ignore_above": 1024, + "type": "keyword" + }, + "a3": { + "ignore_above": 1024, + "type": "keyword" + }, + "a[0-3]": { + "ignore_above": 1024, + "type": "keyword" + }, + "acct": { + "ignore_above": 1024, + "type": "keyword" + }, + "acl": { + "ignore_above": 1024, + "type": "keyword" + }, + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "added": { + "ignore_above": 1024, + "type": "keyword" + }, + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "apparmor": { + "ignore_above": 1024, + "type": "keyword" + }, + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "argc": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_limit": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_wait_time": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_failure": { + "ignore_above": 1024, + "type": "keyword" + }, + "banners": { + "ignore_above": 1024, + "type": "keyword" + }, + "bool": { + "ignore_above": 1024, + "type": "keyword" + }, + "bus": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "capability": { + "ignore_above": 1024, + "type": "keyword" + }, + "cgroup": { + "ignore_above": 1024, + "type": "keyword" + }, + "changed": { + "ignore_above": 1024, + "type": "keyword" + }, + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "cmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "compat": { + "ignore_above": 1024, + "type": "keyword" + }, + "daddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "default-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "dmac": { + "ignore_above": 1024, + "type": "keyword" + }, + "dport": { + "ignore_above": 1024, + "type": "keyword" + }, + "enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "entries": { + "ignore_above": 1024, + "type": "keyword" + }, + "exit": { + "ignore_above": 1024, + "type": "keyword" + }, + "fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "fd": { + "ignore_above": 1024, + "type": "keyword" + }, + "fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "feature": { + "ignore_above": 1024, + "type": "keyword" + }, + "fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "file": { + "ignore_above": 1024, + "type": "keyword" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "format": { + "ignore_above": 1024, + "type": "keyword" + }, + "fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "grantors": { + "ignore_above": 1024, + "type": "keyword" + }, + "grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "hook": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "icmp_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "igid": { + "ignore_above": 1024, + "type": "keyword" + }, + "img-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "inif": { + "ignore_above": 1024, + "type": "keyword" + }, + "ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "invalid_context": { + "ignore_above": 1024, + "type": "keyword" + }, + "ioctlcmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipx-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "items": { + "ignore_above": 1024, + "type": "keyword" + }, + "iuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "ksize": { + "ignore_above": 1024, + "type": "keyword" + }, + "laddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "len": { + "ignore_above": 1024, + "type": "keyword" + }, + "list": { + "ignore_above": 1024, + "type": "keyword" + }, + "lport": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "macproto": { + "ignore_above": 1024, + "type": "keyword" + }, + "maj": { + "ignore_above": 1024, + "type": "keyword" + }, + "major": { + "ignore_above": 1024, + "type": "keyword" + }, + "minor": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "ignore_above": 1024, + "type": "keyword" + }, + "msg": { + "ignore_above": 1024, + "type": "keyword" + }, + "nargs": { + "ignore_above": 1024, + "type": "keyword" + }, + "net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ocomm": { + "ignore_above": 1024, + "type": "keyword" + }, + "oflag": { + "ignore_above": 1024, + "type": "keyword" + }, + "old": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_val": { + "ignore_above": 1024, + "type": "keyword" + }, + "op": { + "ignore_above": 1024, + "type": "keyword" + }, + "opid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oses": { + "ignore_above": 1024, + "type": "keyword" + }, + "outif": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "ignore_above": 1024, + "type": "keyword" + }, + "per": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm_mask": { + "ignore_above": 1024, + "type": "keyword" + }, + "permissive": { + "ignore_above": 1024, + "type": "keyword" + }, + "pfs": { + "ignore_above": 1024, + "type": "keyword" + }, + "printer": { + "ignore_above": 1024, + "type": "keyword" + }, + "prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "proto": { + "ignore_above": 1024, + "type": "keyword" + }, + "qbytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "range": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "removed": { + "ignore_above": 1024, + "type": "keyword" + }, + "res": { + "ignore_above": 1024, + "type": "keyword" + }, + "resrc": { + "ignore_above": 1024, + "type": "keyword" + }, + "rport": { + "ignore_above": 1024, + "type": "keyword" + }, + "sauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "scontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "selected-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperm": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperms": { + "ignore_above": 1024, + "type": "keyword" + }, + "seqno": { + "ignore_above": 1024, + "type": "keyword" + }, + "seresult": { + "ignore_above": 1024, + "type": "keyword" + }, + "ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "sig": { + "ignore_above": 1024, + "type": "keyword" + }, + "sigev_signo": { + "ignore_above": 1024, + "type": "keyword" + }, + "smac": { + "ignore_above": 1024, + "type": "keyword" + }, + "socket": { + "properties": { + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "ignore_above": 1024, + "type": "keyword" + }, + "saddr": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "spid": { + "ignore_above": 1024, + "type": "keyword" + }, + "sport": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "subj": { + "ignore_above": 1024, + "type": "keyword" + }, + "success": { + "ignore_above": 1024, + "type": "keyword" + }, + "syscall": { + "ignore_above": 1024, + "type": "keyword" + }, + "table": { + "ignore_above": 1024, + "type": "keyword" + }, + "tclass": { + "ignore_above": 1024, + "type": "keyword" + }, + "tcontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "uri": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "val": { + "ignore_above": 1024, + "type": "keyword" + }, + "ver": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "watch": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "paths": { + "properties": { + "dev": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "item": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "nametype": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_role": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_user": { + "ignore_above": 1024, + "type": "keyword" + }, + "objtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "ogid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ouid": { + "ignore_above": 1024, + "type": "keyword" + }, + "rdev": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "actor": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "how": { + "ignore_above": 1024, + "type": "keyword" + }, + "object": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "fields": { + "raw": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "selinux": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "setgid": { + "type": "boolean" + }, + "setuid": { + "type": "boolean" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "jolokia": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secured": { + "type": "boolean" + }, + "server": { + "properties": { + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "socket": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "system": { + "properties": { + "audit": { + "properties": { + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "boottime": { + "type": "date" + }, + "containerized": { + "type": "boolean" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timezone": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "package": { + "properties": { + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "installtime": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "release": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "summary": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "properties": { + "last_changed": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shell": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_information": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tracing": { + "properties": { + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "audit": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "filesystem": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name_map": { + "type": "object" + }, + "saved": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "selinux": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": "auditbeat", + "rollover_alias": "auditbeat-7.6.0" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1", + "query": { + "default_field": [ + "message", + "tags", + "agent.ephemeral_id", + "agent.id", + "agent.name", + "agent.type", + "agent.version", + "as.organization.name", + "client.address", + "client.as.organization.name", + "client.domain", + "client.geo.city_name", + "client.geo.continent_name", + "client.geo.country_iso_code", + "client.geo.country_name", + "client.geo.name", + "client.geo.region_iso_code", + "client.geo.region_name", + "client.mac", + "client.registered_domain", + "client.top_level_domain", + "client.user.domain", + "client.user.email", + "client.user.full_name", + "client.user.group.domain", + "client.user.group.id", + "client.user.group.name", + "client.user.hash", + "client.user.id", + "client.user.name", + "cloud.account.id", + "cloud.availability_zone", + "cloud.instance.id", + "cloud.instance.name", + "cloud.machine.type", + "cloud.provider", + "cloud.region", + "container.id", + "container.image.name", + "container.image.tag", + "container.name", + "container.runtime", + "destination.address", + "destination.as.organization.name", + "destination.domain", + "destination.geo.city_name", + "destination.geo.continent_name", + "destination.geo.country_iso_code", + "destination.geo.country_name", + "destination.geo.name", + "destination.geo.region_iso_code", + "destination.geo.region_name", + "destination.mac", + "destination.registered_domain", + "destination.top_level_domain", + "destination.user.domain", + "destination.user.email", + "destination.user.full_name", + "destination.user.group.domain", + "destination.user.group.id", + "destination.user.group.name", + "destination.user.hash", + "destination.user.id", + "destination.user.name", + "dns.answers.class", + "dns.answers.data", + "dns.answers.name", + "dns.answers.type", + "dns.header_flags", + "dns.id", + "dns.op_code", + "dns.question.class", + "dns.question.name", + "dns.question.registered_domain", + "dns.question.subdomain", + "dns.question.top_level_domain", + "dns.question.type", + "dns.response_code", + "dns.type", + "ecs.version", + "error.code", + "error.id", + "error.message", + "error.stack_trace", + "error.type", + "event.action", + "event.category", + "event.code", + "event.dataset", + "event.hash", + "event.id", + "event.kind", + "event.module", + "event.original", + "event.outcome", + "event.provider", + "event.timezone", + "event.type", + "file.device", + "file.directory", + "file.extension", + "file.gid", + "file.group", + "file.hash.md5", + "file.hash.sha1", + "file.hash.sha256", + "file.hash.sha512", + "file.inode", + "file.mode", + "file.name", + "file.owner", + "file.path", + "file.target_path", + "file.type", + "file.uid", + "geo.city_name", + "geo.continent_name", + "geo.country_iso_code", + "geo.country_name", + "geo.name", + "geo.region_iso_code", + "geo.region_name", + "group.domain", + "group.id", + "group.name", + "hash.md5", + "hash.sha1", + "hash.sha256", + "hash.sha512", + "host.architecture", + "host.geo.city_name", + "host.geo.continent_name", + "host.geo.country_iso_code", + "host.geo.country_name", + "host.geo.name", + "host.geo.region_iso_code", + "host.geo.region_name", + "host.hostname", + "host.id", + "host.mac", + "host.name", + "host.os.family", + "host.os.full", + "host.os.kernel", + "host.os.name", + "host.os.platform", + "host.os.version", + "host.type", + "host.user.domain", + "host.user.email", + "host.user.full_name", + "host.user.group.domain", + "host.user.group.id", + "host.user.group.name", + "host.user.hash", + "host.user.id", + "host.user.name", + "http.request.body.content", + "http.request.method", + "http.request.referrer", + "http.response.body.content", + "http.version", + "log.level", + "log.logger", + "log.origin.file.name", + "log.origin.function", + "log.original", + "log.syslog.facility.name", + "log.syslog.severity.name", + "network.application", + "network.community_id", + "network.direction", + "network.iana_number", + "network.name", + "network.protocol", + "network.transport", + "network.type", + "observer.geo.city_name", + "observer.geo.continent_name", + "observer.geo.country_iso_code", + "observer.geo.country_name", + "observer.geo.name", + "observer.geo.region_iso_code", + "observer.geo.region_name", + "observer.hostname", + "observer.mac", + "observer.name", + "observer.os.family", + "observer.os.full", + "observer.os.kernel", + "observer.os.name", + "observer.os.platform", + "observer.os.version", + "observer.product", + "observer.serial_number", + "observer.type", + "observer.vendor", + "observer.version", + "organization.id", + "organization.name", + "os.family", + "os.full", + "os.kernel", + "os.name", + "os.platform", + "os.version", + "package.architecture", + "package.checksum", + "package.description", + "package.install_scope", + "package.license", + "package.name", + "package.path", + "package.version", + "process.args", + "text", + "process.executable", + "process.hash.md5", + "process.hash.sha1", + "process.hash.sha256", + "process.hash.sha512", + "process.name", + "text", + "text", + "text", + "text", + "text", + "process.thread.name", + "process.title", + "process.working_directory", + "server.address", + "server.as.organization.name", + "server.domain", + "server.geo.city_name", + "server.geo.continent_name", + "server.geo.country_iso_code", + "server.geo.country_name", + "server.geo.name", + "server.geo.region_iso_code", + "server.geo.region_name", + "server.mac", + "server.registered_domain", + "server.top_level_domain", + "server.user.domain", + "server.user.email", + "server.user.full_name", + "server.user.group.domain", + "server.user.group.id", + "server.user.group.name", + "server.user.hash", + "server.user.id", + "server.user.name", + "service.ephemeral_id", + "service.id", + "service.name", + "service.node.name", + "service.state", + "service.type", + "service.version", + "source.address", + "source.as.organization.name", + "source.domain", + "source.geo.city_name", + "source.geo.continent_name", + "source.geo.country_iso_code", + "source.geo.country_name", + "source.geo.name", + "source.geo.region_iso_code", + "source.geo.region_name", + "source.mac", + "source.registered_domain", + "source.top_level_domain", + "source.user.domain", + "source.user.email", + "source.user.full_name", + "source.user.group.domain", + "source.user.group.id", + "source.user.group.name", + "source.user.hash", + "source.user.id", + "source.user.name", + "threat.framework", + "threat.tactic.id", + "threat.tactic.name", + "threat.tactic.reference", + "threat.technique.id", + "threat.technique.name", + "threat.technique.reference", + "tracing.trace.id", + "tracing.transaction.id", + "url.domain", + "url.extension", + "url.fragment", + "url.full", + "url.original", + "url.password", + "url.path", + "url.query", + "url.registered_domain", + "url.scheme", + "url.top_level_domain", + "url.username", + "user.domain", + "user.email", + "user.full_name", + "user.group.domain", + "user.group.id", + "user.group.name", + "user.hash", + "user.id", + "user.name", + "user_agent.device.name", + "user_agent.name", + "text", + "user_agent.original", + "user_agent.os.family", + "user_agent.os.full", + "user_agent.os.kernel", + "user_agent.os.name", + "user_agent.os.platform", + "user_agent.os.version", + "user_agent.version", + "text", + "agent.hostname", + "timeseries.instance", + "cloud.project.id", + "cloud.image.id", + "host.os.build", + "host.os.codename", + "kubernetes.pod.name", + "kubernetes.pod.uid", + "kubernetes.namespace", + "kubernetes.node.name", + "kubernetes.replicaset.name", + "kubernetes.deployment.name", + "kubernetes.statefulset.name", + "kubernetes.container.name", + "kubernetes.container.image", + "jolokia.agent.version", + "jolokia.agent.id", + "jolokia.server.product", + "jolokia.server.version", + "jolokia.server.vendor", + "jolokia.url", + "raw", + "file.origin", + "file.selinux.user", + "file.selinux.role", + "file.selinux.domain", + "file.selinux.level", + "user.audit.id", + "user.audit.name", + "user.effective.id", + "user.effective.name", + "user.effective.group.id", + "user.effective.group.name", + "user.filesystem.id", + "user.filesystem.name", + "user.filesystem.group.id", + "user.filesystem.group.name", + "user.saved.id", + "user.saved.name", + "user.saved.group.id", + "user.saved.group.name", + "user.selinux.user", + "user.selinux.role", + "user.selinux.domain", + "user.selinux.level", + "user.selinux.category", + "source.path", + "destination.path", + "auditd.message_type", + "auditd.session", + "auditd.result", + "auditd.summary.actor.primary", + "auditd.summary.actor.secondary", + "auditd.summary.object.type", + "auditd.summary.object.primary", + "auditd.summary.object.secondary", + "auditd.summary.how", + "auditd.paths.inode", + "auditd.paths.dev", + "auditd.paths.obj_user", + "auditd.paths.obj_role", + "auditd.paths.obj_domain", + "auditd.paths.obj_level", + "auditd.paths.objtype", + "auditd.paths.ouid", + "auditd.paths.rdev", + "auditd.paths.nametype", + "auditd.paths.ogid", + "auditd.paths.item", + "auditd.paths.mode", + "auditd.paths.name", + "auditd.data.action", + "auditd.data.minor", + "auditd.data.acct", + "auditd.data.addr", + "auditd.data.cipher", + "auditd.data.id", + "auditd.data.entries", + "auditd.data.kind", + "auditd.data.ksize", + "auditd.data.spid", + "auditd.data.arch", + "auditd.data.argc", + "auditd.data.major", + "auditd.data.unit", + "auditd.data.table", + "auditd.data.terminal", + "auditd.data.grantors", + "auditd.data.direction", + "auditd.data.op", + "auditd.data.tty", + "auditd.data.syscall", + "auditd.data.data", + "auditd.data.family", + "auditd.data.mac", + "auditd.data.pfs", + "auditd.data.items", + "auditd.data.a0", + "auditd.data.a1", + "auditd.data.a2", + "auditd.data.a3", + "auditd.data.hostname", + "auditd.data.lport", + "auditd.data.rport", + "auditd.data.exit", + "auditd.data.fp", + "auditd.data.laddr", + "auditd.data.sport", + "auditd.data.capability", + "auditd.data.nargs", + "auditd.data.new-enabled", + "auditd.data.audit_backlog_limit", + "auditd.data.dir", + "auditd.data.cap_pe", + "auditd.data.model", + "auditd.data.new_pp", + "auditd.data.old-enabled", + "auditd.data.oauid", + "auditd.data.old", + "auditd.data.banners", + "auditd.data.feature", + "auditd.data.vm-ctx", + "auditd.data.opid", + "auditd.data.seperms", + "auditd.data.seresult", + "auditd.data.new-rng", + "auditd.data.old-net", + "auditd.data.sigev_signo", + "auditd.data.ino", + "auditd.data.old_enforcing", + "auditd.data.old-vcpu", + "auditd.data.range", + "auditd.data.res", + "auditd.data.added", + "auditd.data.fam", + "auditd.data.nlnk-pid", + "auditd.data.subj", + "auditd.data.a[0-3]", + "auditd.data.cgroup", + "auditd.data.kernel", + "auditd.data.ocomm", + "auditd.data.new-net", + "auditd.data.permissive", + "auditd.data.class", + "auditd.data.compat", + "auditd.data.fi", + "auditd.data.changed", + "auditd.data.msg", + "auditd.data.dport", + "auditd.data.new-seuser", + "auditd.data.invalid_context", + "auditd.data.dmac", + "auditd.data.ipx-net", + "auditd.data.iuid", + "auditd.data.macproto", + "auditd.data.obj", + "auditd.data.ipid", + "auditd.data.new-fs", + "auditd.data.vm-pid", + "auditd.data.cap_pi", + "auditd.data.old-auid", + "auditd.data.oses", + "auditd.data.fd", + "auditd.data.igid", + "auditd.data.new-disk", + "auditd.data.parent", + "auditd.data.len", + "auditd.data.oflag", + "auditd.data.uuid", + "auditd.data.code", + "auditd.data.nlnk-grp", + "auditd.data.cap_fp", + "auditd.data.new-mem", + "auditd.data.seperm", + "auditd.data.enforcing", + "auditd.data.new-chardev", + "auditd.data.old-rng", + "auditd.data.outif", + "auditd.data.cmd", + "auditd.data.hook", + "auditd.data.new-level", + "auditd.data.sauid", + "auditd.data.sig", + "auditd.data.audit_backlog_wait_time", + "auditd.data.printer", + "auditd.data.old-mem", + "auditd.data.perm", + "auditd.data.old_pi", + "auditd.data.state", + "auditd.data.format", + "auditd.data.new_gid", + "auditd.data.tcontext", + "auditd.data.maj", + "auditd.data.watch", + "auditd.data.device", + "auditd.data.grp", + "auditd.data.bool", + "auditd.data.icmp_type", + "auditd.data.new_lock", + "auditd.data.old_prom", + "auditd.data.acl", + "auditd.data.ip", + "auditd.data.new_pi", + "auditd.data.default-context", + "auditd.data.inode_gid", + "auditd.data.new-log_passwd", + "auditd.data.new_pe", + "auditd.data.selected-context", + "auditd.data.cap_fver", + "auditd.data.file", + "auditd.data.net", + "auditd.data.virt", + "auditd.data.cap_pp", + "auditd.data.old-range", + "auditd.data.resrc", + "auditd.data.new-range", + "auditd.data.obj_gid", + "auditd.data.proto", + "auditd.data.old-disk", + "auditd.data.audit_failure", + "auditd.data.inif", + "auditd.data.vm", + "auditd.data.flags", + "auditd.data.nlnk-fam", + "auditd.data.old-fs", + "auditd.data.old-ses", + "auditd.data.seqno", + "auditd.data.fver", + "auditd.data.qbytes", + "auditd.data.seuser", + "auditd.data.cap_fe", + "auditd.data.new-vcpu", + "auditd.data.old-level", + "auditd.data.old_pp", + "auditd.data.daddr", + "auditd.data.old-role", + "auditd.data.ioctlcmd", + "auditd.data.smac", + "auditd.data.apparmor", + "auditd.data.fe", + "auditd.data.perm_mask", + "auditd.data.ses", + "auditd.data.cap_fi", + "auditd.data.obj_uid", + "auditd.data.reason", + "auditd.data.list", + "auditd.data.old_lock", + "auditd.data.bus", + "auditd.data.old_pe", + "auditd.data.new-role", + "auditd.data.prom", + "auditd.data.uri", + "auditd.data.audit_enabled", + "auditd.data.old-log_passwd", + "auditd.data.old-seuser", + "auditd.data.per", + "auditd.data.scontext", + "auditd.data.tclass", + "auditd.data.ver", + "auditd.data.new", + "auditd.data.val", + "auditd.data.img-ctx", + "auditd.data.old-chardev", + "auditd.data.old_val", + "auditd.data.success", + "auditd.data.inode_uid", + "auditd.data.removed", + "auditd.data.socket.port", + "auditd.data.socket.saddr", + "auditd.data.socket.addr", + "auditd.data.socket.family", + "auditd.data.socket.path", + "geoip.continent_name", + "geoip.city_name", + "geoip.region_name", + "geoip.country_iso_code", + "hash.blake2b_256", + "hash.blake2b_384", + "hash.blake2b_512", + "hash.md5", + "hash.sha1", + "hash.sha224", + "hash.sha256", + "hash.sha384", + "hash.sha3_224", + "hash.sha3_256", + "hash.sha3_384", + "hash.sha3_512", + "hash.sha512", + "hash.sha512_224", + "hash.sha512_256", + "hash.xxh64", + "event.origin", + "user.entity_id", + "user.terminal", + "process.entity_id", + "process.hash.blake2b_256", + "process.hash.blake2b_384", + "process.hash.blake2b_512", + "process.hash.sha224", + "process.hash.sha384", + "process.hash.sha3_224", + "process.hash.sha3_256", + "process.hash.sha3_384", + "process.hash.sha3_512", + "process.hash.sha512_224", + "process.hash.sha512_256", + "process.hash.xxh64", + "socket.entity_id", + "system.audit.host.timezone.name", + "system.audit.host.hostname", + "system.audit.host.id", + "system.audit.host.architecture", + "system.audit.host.mac", + "system.audit.host.os.codename", + "system.audit.host.os.platform", + "system.audit.host.os.name", + "system.audit.host.os.family", + "system.audit.host.os.version", + "system.audit.host.os.kernel", + "system.audit.package.entity_id", + "system.audit.package.name", + "system.audit.package.version", + "system.audit.package.release", + "system.audit.package.arch", + "system.audit.package.license", + "system.audit.package.summary", + "system.audit.package.url", + "system.audit.user.name", + "system.audit.user.uid", + "system.audit.user.gid", + "system.audit.user.dir", + "system.audit.user.shell", + "system.audit.user.user_information", + "system.audit.user.password.type", + "fields.*" + ] + }, + "refresh_interval": "5s" + } + } + } +} \ No newline at end of file From 168239ca07d4a0d90e8fea04a7c4d63fa455cd45 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 16 Mar 2020 12:39:27 +0100 Subject: [PATCH 2/8] [Uptime] Index Status API to Rest (#59657) * gql to rest * update snap * fix api Co-authored-by: Elastic Machine --- .../plugins/uptime/common/constants/index.ts | 1 + .../uptime/common/constants/rest_api.ts | 9 ++++ .../plugins/uptime/common/graphql/types.ts | 12 +---- .../uptime/common/runtime_types/common.ts | 6 +++ .../connected/empty_state/empty_state.tsx | 30 ++++++++++++ .../public/components/connected/index.ts | 1 + .../kuerybar/kuery_bar_container.tsx | 2 +- .../connected/pages/overview_container.tsx | 2 +- .../__snapshots__/empty_state.test.tsx.snap | 37 +++++++-------- .../__tests__/empty_state.test.tsx | 24 ++++------ .../functional/empty_state/empty_state.tsx | 33 ++++++------- .../empty_state/empty_state_error.tsx | 7 ++- .../public/components/functional/index.ts | 1 - .../functional/kuery_bar/kuery_bar.tsx | 16 +++---- .../__tests__/monitor_list.test.tsx | 4 +- .../monitor_list_pagination.test.tsx | 4 +- .../monitor_list_drawer/__tests__/data.json | 2 +- .../public/hooks/update_kuery_string.ts | 2 +- .../plugins/uptime/public/pages/overview.tsx | 7 ++- .../uptime/public/queries/doc_count_query.ts | 22 --------- .../plugins/uptime/public/queries/index.ts | 1 - .../public/queries/monitor_states_query.ts | 4 +- .../uptime/public/state/actions/index.ts | 1 + .../public/state/actions/index_status.ts | 10 ++++ .../uptime/public/state/actions/types.ts | 8 ++++ .../uptime/public/state/actions/utils.ts | 16 +++++++ .../plugins/uptime/public/state/api/index.ts | 1 + .../uptime/public/state/api/index_status.ts | 31 +++++++++++++ .../public/state/effects/fetch_effect.ts | 4 -- .../uptime/public/state/effects/index.ts | 2 + .../public/state/effects/index_status.ts | 17 +++++++ .../uptime/public/state/reducers/index.ts | 2 + .../public/state/reducers/index_pattern.ts | 3 +- .../public/state/reducers/index_status.ts | 30 ++++++++++++ .../index.ts => state/reducers/types.ts} | 5 +- .../uptime/public/state/reducers/utils.ts | 33 +++++++++++++ .../state/selectors/__tests__/index.test.ts | 5 ++ .../uptime/public/state/selectors/index.ts | 6 ++- .../graphql/monitor_states/resolvers.ts | 16 +------ .../graphql/monitor_states/schema.gql.ts | 13 +----- .../server/lib/requests/get_index_status.ts | 6 +-- .../server/lib/requests/uptime_requests.ts | 7 +-- .../plugins/uptime/server/rest_api/index.ts | 3 +- .../get_index_pattern.ts | 0 .../rest_api/index_state/get_index_status.ts | 29 ++++++++++++ .../{index_pattern => index_state}/index.ts | 1 + .../apis/uptime/feature_controls.ts | 25 ++++------ .../apis/uptime/graphql/doc_count.js | 36 --------------- .../uptime/graphql/fixtures/doc_count.json | 8 ---- .../graphql/fixtures/monitor_states.json | 46 +++++-------------- .../fixtures/monitor_states_id_filtered.json | 10 ++-- .../fixtures/monitor_states_page_1.json | 46 +++++-------------- .../fixtures/monitor_states_page_10.json | 46 +++++-------------- .../monitor_states_page_10_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_2.json | 46 +++++-------------- .../monitor_states_page_2_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_3.json | 46 +++++-------------- .../monitor_states_page_3_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_4.json | 46 +++++-------------- .../monitor_states_page_4_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_5.json | 46 +++++-------------- .../monitor_states_page_5_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_6.json | 46 +++++-------------- .../monitor_states_page_6_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_7.json | 46 +++++-------------- .../monitor_states_page_7_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_8.json | 46 +++++-------------- .../monitor_states_page_8_previous.json | 46 +++++-------------- .../fixtures/monitor_states_page_9.json | 46 +++++-------------- .../monitor_states_page_9_previous.json | 46 +++++-------------- .../apis/uptime/graphql/index.js | 1 - .../apis/uptime/rest/doc_count.ts | 20 ++++++++ .../apis/uptime/rest/fixtures/doc_count.json | 4 ++ .../api_integration/apis/uptime/rest/index.ts | 1 + 74 files changed, 590 insertions(+), 911 deletions(-) create mode 100644 x-pack/legacy/plugins/uptime/common/constants/rest_api.ts create mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/actions/utils.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/api/index_status.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts rename x-pack/legacy/plugins/uptime/public/{components/functional/empty_state/index.ts => state/reducers/types.ts} (76%) create mode 100644 x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts rename x-pack/plugins/uptime/server/rest_api/{index_pattern => index_state}/get_index_pattern.ts (100%) create mode 100644 x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts rename x-pack/plugins/uptime/server/rest_api/{index_pattern => index_state}/index.ts (82%) delete mode 100644 x-pack/test/api_integration/apis/uptime/graphql/doc_count.js delete mode 100644 x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json create mode 100644 x-pack/test/api_integration/apis/uptime/rest/doc_count.ts create mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json diff --git a/x-pack/legacy/plugins/uptime/common/constants/index.ts b/x-pack/legacy/plugins/uptime/common/constants/index.ts index 9d5ad4607491c..0425fc19a7b45 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/index.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/index.ts @@ -12,3 +12,4 @@ export * from './capabilities'; export { PLUGIN } from './plugin'; export { QUERY, STATES } from './query'; export * from './ui'; +export * from './rest_api'; diff --git a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts new file mode 100644 index 0000000000000..f09c795977831 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export enum REST_API_URLS { + INDEX_STATUS = '/api/uptime/index_status', +} diff --git a/x-pack/legacy/plugins/uptime/common/graphql/types.ts b/x-pack/legacy/plugins/uptime/common/graphql/types.ts index a33a69c229873..1a37ce0b18c73 100644 --- a/x-pack/legacy/plugins/uptime/common/graphql/types.ts +++ b/x-pack/legacy/plugins/uptime/common/graphql/types.ts @@ -21,8 +21,6 @@ export interface Query { /** Fetches the current state of Uptime monitors for the given parameters. */ getMonitorStates?: MonitorSummaryResult | null; - /** Fetches details about the uptime index. */ - getStatesIndexStatus: StatesIndexStatus; } export interface PingResults { @@ -392,7 +390,7 @@ export interface MonitorSummaryResult { /** The objects representing the state of a series of heartbeat monitors. */ summaries?: MonitorSummary[] | null; /** The number of summaries. */ - totalSummaryCount: DocCount; + totalSummaryCount: number; } /** Represents the current state and associated data for an Uptime monitor. */ export interface MonitorSummary { @@ -525,13 +523,7 @@ export interface SummaryHistogramPoint { /** The number of _down_ documents. */ down: number; } -/** Represents the current status of the uptime index. */ -export interface StatesIndexStatus { - /** Flag denoting whether the index exists. */ - indexExists: boolean; - /** The number of documents in the index. */ - docCount?: DocCount | null; -} + export interface AllPingsQueryArgs { /** Optional: the direction to sort by. Accepts 'asc' and 'desc'. Defaults to 'desc'. */ diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts index 84e3ae33294f0..37101b5b46fd2 100644 --- a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts +++ b/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts @@ -22,6 +22,12 @@ export const SummaryType = t.partial({ geo: CheckGeoType, }); +export const StatesIndexStatusType = t.type({ + indexExists: t.boolean, + docCount: t.number, +}); + export type Summary = t.TypeOf; export type CheckGeo = t.TypeOf; export type Location = t.TypeOf; +export type StatesIndexStatus = t.TypeOf; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx new file mode 100644 index 0000000000000..cac7042ca5b5c --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { indexStatusAction } from '../../../state/actions'; +import { indexStatusSelector } from '../../../state/selectors'; +import { EmptyStateComponent } from '../../functional/empty_state/empty_state'; + +export const EmptyState: React.FC = ({ children }) => { + const { data, loading, errors } = useSelector(indexStatusSelector); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(indexStatusAction.get()); + }, [dispatch]); + + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts index 2e30e5c3cb24f..baa961ddc87d2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts @@ -13,3 +13,4 @@ export { MonitorStatusBar } from './monitor/status_bar_container'; export { MonitorListDrawer } from './monitor/list_drawer_container'; export { MonitorListActionsPopover } from './monitor/drawer_popover_container'; export { DurationChart } from './charts/monitor_duration'; +export { EmptyState } from './empty_state/empty_state'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx index d0f160b2c5540..a42f96962b95e 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx @@ -10,7 +10,7 @@ import { selectIndexPattern } from '../../../state/selectors'; import { getIndexPattern } from '../../../state/actions'; import { KueryBarComponent } from '../../functional'; -const mapStateToProps = (state: AppState) => ({ indexPattern: selectIndexPattern(state) }); +const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); const mapDispatchToProps = (dispatch: any) => ({ loadIndexPattern: () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx index cbd1fae77c518..79aaa071507e1 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx @@ -18,6 +18,6 @@ const mapDispatchToProps = (dispatch: any): DispatchProps => ({ setEsKueryFilters: (esFilters: string) => dispatch(setEsKueryString(esFilters)), }); -const mapStateToProps = (state: AppState) => ({ indexPattern: selectIndexPattern(state) }); +const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); export const OverviewPage = connect(mapStateToProps, mapDispatchToProps)(OverviewPageComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap index 472d9c2be59e4..a885cfe22ccd2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap @@ -2,16 +2,6 @@ exports[`EmptyState component does not render empty state with appropriate base path and no docs 1`] = ` `; -exports[`EmptyState component doesn't render child components when count is falsey 1`] = ` +exports[`EmptyState component doesn't render child components when count is falsy 1`] = ` { let statesIndexStatus: StatesIndexStatus; @@ -16,15 +16,13 @@ describe('EmptyState component', () => { beforeEach(() => { statesIndexStatus = { indexExists: true, - docCount: { - count: 1, - }, + docCount: 1, }; }); it('renders child components when count is truthy', () => { const component = shallowWithIntl( - +

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

{error.message}

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

{error.message}

)}
} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts index e86ba548fb5d9..daba13d8df641 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts @@ -5,7 +5,6 @@ */ export { DonutChart } from './charts/donut_chart'; -export { EmptyState } from './empty_state'; export { KueryBarComponent } from './kuery_bar/kuery_bar'; export { MonitorCharts } from './monitor_charts'; export { MonitorList } from './monitor_list'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx index 496e8d898df3c..2f5ccc2adf313 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx @@ -34,14 +34,16 @@ function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) { interface Props { autocomplete: DataPublicPluginSetup['autocomplete']; - loadIndexPattern: any; - indexPattern: any; + loadIndexPattern: () => void; + indexPattern: IIndexPattern | null; + loading: boolean; } export function KueryBarComponent({ autocomplete: autocompleteService, loadIndexPattern, indexPattern, + loading, }: Props) { useEffect(() => { if (!indexPattern) { @@ -53,19 +55,13 @@ export function KueryBarComponent({ suggestions: [], isLoadingIndexPattern: true, }); - const [isLoadingIndexPattern, setIsLoadingIndexPattern] = useState(true); const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); let currentRequestCheck: string; - useEffect(() => { - if (indexPattern !== undefined) { - setIsLoadingIndexPattern(false); - } - }, [indexPattern]); const [getUrlParams, updateUrlParams] = useUrlParams(); const { search: kuery } = getUrlParams(); - const indexPatternMissing = !isLoadingIndexPattern && !indexPattern; + const indexPatternMissing = loading && !indexPattern; async function onChange(inputValue: string, selectionStart: number) { if (!indexPattern) { @@ -124,7 +120,7 @@ export function KueryBarComponent({ { }, }, ], - totalSummaryCount: { - count: 2, - }, + totalSummaryCount: 2, }; }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx index ff54e61006156..1aef9281a3066 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_pagination.test.tsx @@ -89,9 +89,7 @@ describe('MonitorListPagination component', () => { }, }, ], - totalSummaryCount: { - count: 2, - }, + totalSummaryCount: 2, }; }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json index a45e974685b9c..e8142f0480c4a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json @@ -3,7 +3,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": null, - "totalSummaryCount": { "count": 147428, "__typename": "DocCount" }, + "totalSummaryCount": 147428, "summaries": [ { "monitor_id": "andrewvc-com", diff --git a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts b/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts index 5fcacf8424660..ab4d6f75849e8 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts @@ -24,7 +24,7 @@ const getKueryString = (urlFilters: string): string => { }; export const useUpdateKueryString = ( - indexPattern: IIndexPattern, + indexPattern: IIndexPattern | null, filterQueryString = '', urlFilters: string ): [string?, Error?] => { diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index 15e31d5e44629..af9b8bf046416 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -9,7 +9,6 @@ import React, { useContext, useEffect } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { - EmptyState, MonitorList, OverviewPageParsingErrorCallout, StatusPanel, @@ -19,13 +18,13 @@ import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../../../plugins/observability/public'; import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public'; import { UptimeThemeContext } from '../contexts'; -import { FilterGroup, KueryBar } from '../components/connected'; +import { EmptyState, FilterGroup, KueryBar } from '../components/connected'; import { useUpdateKueryString } from '../hooks'; import { PageHeader } from './page_header'; interface OverviewPageProps { autocomplete: DataPublicPluginSetup['autocomplete']; - indexPattern: IIndexPattern; + indexPattern: IIndexPattern | null; setEsKueryFilters: (esFilters: string) => void; } @@ -81,7 +80,7 @@ export const OverviewPageComponent = ({ autocomplete, indexPattern, setEsKueryFi return ( <> - + diff --git a/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts b/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts deleted file mode 100644 index 3067a9d16f050..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/queries/doc_count_query.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const docCountQueryString = ` -query GetStateIndexStatus { - statesIndexStatus: getStatesIndexStatus { - docCount { - count - } - indexExists - } -} -`; - -export const docCountQuery = gql` - ${docCountQueryString} -`; diff --git a/x-pack/legacy/plugins/uptime/public/queries/index.ts b/x-pack/legacy/plugins/uptime/public/queries/index.ts index f2fff9bc506d0..283382ec1b7ba 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/index.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { docCountQuery, docCountQueryString } from './doc_count_query'; export { pingsQuery, pingsQueryString } from './pings_query'; diff --git a/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts b/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts index 76f62ad453bd9..9e609786094d5 100644 --- a/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts +++ b/x-pack/legacy/plugins/uptime/public/queries/monitor_states_query.ts @@ -17,9 +17,7 @@ query MonitorStates($dateRangeStart: String!, $dateRangeEnd: String!, $paginatio ) { prevPagePagination nextPagePagination - totalSummaryCount { - count - } + totalSummaryCount summaries { monitor_id histogram { diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts b/x-pack/legacy/plugins/uptime/public/state/actions/index.ts index dfcea64bf9c08..b2ab73879a4a7 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/index.ts @@ -11,3 +11,4 @@ export * from './monitor_status'; export * from './index_patternts'; export * from './ping'; export * from './monitor_duration'; +export * from './index_status'; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts new file mode 100644 index 0000000000000..336758a71ce60 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createAsyncAction } from './utils'; +import { StatesIndexStatus } from '../../../common/runtime_types'; + +export const indexStatusAction = createAsyncAction('GET INDEX STATUS'); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts b/x-pack/legacy/plugins/uptime/public/state/actions/types.ts index dba70ed839ac5..e9bf11256b0b8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/types.ts @@ -4,6 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Action } from 'redux-actions'; + +export interface AsyncAction { + get: (payload?: any) => Action; + success: (payload?: any) => Action; + fail: (payload?: any) => Action; +} + export interface QueryParams { monitorId: string; dateStart: string; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts b/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts new file mode 100644 index 0000000000000..337c4bfb2fa47 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createAction } from 'redux-actions'; +import { AsyncAction } from './types'; + +export function createAsyncAction(actionStr: string): AsyncAction { + return { + get: createAction(actionStr), + success: createAction(`${actionStr}_SUCCESS`), + fail: createAction(`${actionStr}_FAIL`), + }; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index.ts b/x-pack/legacy/plugins/uptime/public/state/api/index.ts index 7d42c6ee46bdc..518091cb36dde 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/index.ts @@ -9,5 +9,6 @@ export * from './overview_filters'; export * from './snapshot'; export * from './monitor_status'; export * from './index_pattern'; +export * from './index_status'; export * from './ping'; export * from './monitor_duration'; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts new file mode 100644 index 0000000000000..9c531b3406a7c --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PathReporter } from 'io-ts/lib/PathReporter'; +import { isRight } from 'fp-ts/lib/Either'; +import { getApiPath } from '../../lib/helper'; +import { REST_API_URLS } from '../../../common/constants/rest_api'; +import { StatesIndexStatus, StatesIndexStatusType } from '../../../common/runtime_types'; + +interface ApiRequest { + basePath: string; +} + +export const fetchIndexStatus = async ({ basePath }: ApiRequest): Promise => { + const url = getApiPath(REST_API_URLS.INDEX_STATUS, basePath); + + const response = await fetch(url); + if (!response.ok) { + throw new Error(response.statusText); + } + const responseData = await response.json(); + const decoded = StatesIndexStatusType.decode(responseData); + PathReporter.report(decoded); + if (isRight(decoded)) { + return decoded.right; + } + throw PathReporter.report(decoded); +}; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts index d293cdbe451b5..ea389ff0a6745 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts @@ -26,10 +26,6 @@ export function fetchEffectFactory( ) { return function*(action: Action) { try { - if (!action.payload) { - yield put(fail(new Error('Cannot fetch snapshot for undefined parameters.'))); - return; - } const { payload: { ...params }, } = action; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts index 43af88f4cc291..7c45aa142ecfd 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index.ts @@ -12,6 +12,7 @@ import { fetchMonitorStatusEffect } from './monitor_status'; import { fetchIndexPatternEffect } from './index_pattern'; import { fetchPingHistogramEffect } from './ping'; import { fetchMonitorDurationEffect } from './monitor_duration'; +import { fetchIndexStatusEffect } from './index_status'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -21,4 +22,5 @@ export function* rootEffect() { yield fork(fetchIndexPatternEffect); yield fork(fetchPingHistogramEffect); yield fork(fetchMonitorDurationEffect); + yield fork(fetchIndexStatusEffect); } diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts new file mode 100644 index 0000000000000..793a671f5fed8 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { takeLatest } from 'redux-saga/effects'; +import { indexStatusAction } from '../actions'; +import { fetchIndexStatus } from '../api'; +import { fetchEffectFactory } from './fetch_effect'; + +export function* fetchIndexStatusEffect() { + yield takeLatest( + indexStatusAction.get, + fetchEffectFactory(fetchIndexStatus, indexStatusAction.success, indexStatusAction.fail) + ); +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts index 32362afae42bc..4a83b54504ca8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts @@ -13,6 +13,7 @@ import { monitorStatusReducer } from './monitor_status'; import { indexPatternReducer } from './index_pattern'; import { pingReducer } from './ping'; import { monitorDurationReducer } from './monitor_duration'; +import { indexStatusReducer } from './index_status'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -23,4 +24,5 @@ export const rootReducer = combineReducers({ indexPattern: indexPatternReducer, ping: pingReducer, monitorDuration: monitorDurationReducer, + indexStatus: indexStatusReducer, }); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts index dff043f81b95c..bc482e2f35c45 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts @@ -5,9 +5,10 @@ */ import { handleActions, Action } from 'redux-actions'; import { getIndexPattern, getIndexPatternSuccess, getIndexPatternFail } from '../actions'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; export interface IndexPatternState { - index_pattern: any; + index_pattern: IIndexPattern | null; errors: any[]; loading: boolean; } diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts new file mode 100644 index 0000000000000..50a02210fb5d3 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { handleActions } from 'redux-actions'; +import { indexStatusAction } from '../actions'; +import { handleAsyncAction } from './utils'; +import { IReducerState } from './types'; +import { StatesIndexStatus } from '../../../common/runtime_types'; + +export interface IndexStatusState extends IReducerState { + data: StatesIndexStatus | null; +} + +const initialState: IndexStatusState = { + data: null, + loading: false, + errors: [], +}; + +type PayLoad = StatesIndexStatus & Error; + +export const indexStatusReducer = handleActions( + { + ...handleAsyncAction('data', indexStatusAction), + }, + initialState +); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts similarity index 76% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts rename to x-pack/legacy/plugins/uptime/public/state/reducers/types.ts index 8ee70bf51f006..40fe4bddbf172 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts @@ -4,4 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EmptyState } from './empty_state'; +export interface IReducerState { + errors: Error[]; + loading: boolean; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts new file mode 100644 index 0000000000000..773ec10686943 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Action } from 'redux-actions'; +import { AsyncAction } from '../actions/types'; +import { IReducerState } from './types'; + +export function handleAsyncAction( + storeKey: string, + asyncAction: AsyncAction +) { + return { + [String(asyncAction.get)]: (state: ReducerState) => ({ + ...state, + loading: true, + }), + + [String(asyncAction.success)]: (state: ReducerState, action: Action) => ({ + ...state, + loading: false, + [storeKey]: action.payload === null ? action.payload : { ...action.payload }, + }), + + [String(asyncAction.fail)]: (state: ReducerState, action: Action) => ({ + ...state, + errors: [...state.errors, action.payload], + loading: false, + }), + }; +} diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts index 24d34b4d067cc..de446418632b8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -60,6 +60,11 @@ describe('state selectors', () => { loading: false, errors: [], }, + indexStatus: { + loading: false, + data: null, + errors: [], + }, }; it('selects base path from state', () => { diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index 0a914a14c372b..adba288b8b145 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -30,7 +30,7 @@ export const selectMonitorStatus = (state: AppState) => { }; export const selectIndexPattern = ({ indexPattern }: AppState) => { - return indexPattern.index_pattern; + return { indexPattern: indexPattern.index_pattern, loading: indexPattern.loading }; }; export const selectPingHistogram = ({ ping, ui }: AppState) => { @@ -45,3 +45,7 @@ export const selectPingHistogram = ({ ping, ui }: AppState) => { export const selectDurationLines = ({ monitorDuration }: AppState) => { return monitorDuration; }; + +export const indexStatusSelector = ({ indexStatus }: AppState) => { + return indexStatus; +}; diff --git a/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts b/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts index 6ac42f7717259..1560b78b3c050 100644 --- a/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts +++ b/x-pack/plugins/uptime/server/graphql/monitor_states/resolvers.ts @@ -10,9 +10,8 @@ import { UMResolver } from '../../../../../legacy/plugins/uptime/common/graphql/ import { GetMonitorStatesQueryArgs, MonitorSummaryResult, - StatesIndexStatus, } from '../../../../../legacy/plugins/uptime/common/graphql/types'; -import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants/context_defaults'; +import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; export type UMGetMonitorStatesResolver = UMResolver< MonitorSummaryResult | Promise, @@ -21,19 +20,11 @@ export type UMGetMonitorStatesResolver = UMResolver< UMContext >; -export type UMStatesIndexExistsResolver = UMResolver< - StatesIndexStatus | Promise, - any, - {}, - UMContext ->; - export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( libs: UMServerLibs ): { Query: { getMonitorStates: UMGetMonitorStatesResolver; - getStatesIndexStatus: UMStatesIndexExistsResolver; }; } => { return { @@ -64,7 +55,7 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( }), ]); - const totalSummaryCount = indexStatus?.docCount ?? { count: undefined }; + const totalSummaryCount = indexStatus?.docCount ?? 0; return { summaries, @@ -73,9 +64,6 @@ export const createMonitorStatesResolvers: CreateUMGraphQLResolvers = ( totalSummaryCount, }; }, - async getStatesIndexStatus(_resolver, {}, { APICaller }): Promise { - return await libs.requests.getIndexStatus({ callES: APICaller }); - }, }, }; }; diff --git a/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts b/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts index 198f97eab9652..d088aed951204 100644 --- a/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts +++ b/x-pack/plugins/uptime/server/graphql/monitor_states/schema.gql.ts @@ -156,15 +156,7 @@ export const monitorStatesSchema = gql` "The objects representing the state of a series of heartbeat monitors." summaries: [MonitorSummary!] "The number of summaries." - totalSummaryCount: DocCount! - } - - "Represents the current status of the uptime index." - type StatesIndexStatus { - "Flag denoting whether the index exists." - indexExists: Boolean! - "The number of documents in the index." - docCount: DocCount + totalSummaryCount: Int! } enum CursorDirection { @@ -186,8 +178,5 @@ export const monitorStatesSchema = gql` filters: String statusFilter: String ): MonitorSummaryResult - - "Fetches details about the uptime index." - getStatesIndexStatus: StatesIndexStatus! } `; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts index 95aa7eeef88e1..d8a05c08b1417 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts @@ -5,8 +5,8 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/graphql/types'; import { INDEX_NAMES } from '../../../../../legacy/plugins/uptime/common/constants'; +import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/runtime_types'; export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ callES }) => { const { @@ -15,8 +15,6 @@ export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = asy } = await callES('count', { index: INDEX_NAMES.HEARTBEAT }); return { indexExists: total > 0, - docCount: { - count, - }, + docCount: count, }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts index 6fd77afe711d4..7f192994bd075 100644 --- a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts +++ b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts @@ -5,11 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { - Ping, - PingResults, - StatesIndexStatus, -} from '../../../../../legacy/plugins/uptime/common/graphql/types'; +import { Ping, PingResults } from '../../../../../legacy/plugins/uptime/common/graphql/types'; import { GetFilterBarParams, GetLatestMonitorParams, @@ -26,6 +22,7 @@ import { MonitorDetails, MonitorLocations, Snapshot, + StatesIndexStatus, } from '../../../../../legacy/plugins/uptime/common/runtime_types'; import { GetMonitorStatesResult } from './get_monitor_states'; import { GetSnapshotCountParams } from './get_snapshot_counts'; diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 69981b7860d59..b0cc38ebfb4b6 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -6,7 +6,6 @@ import { createGetOverviewFilters } from './overview_filters'; import { createGetPingsRoute } from './pings'; -import { createGetIndexPatternRoute } from './index_pattern'; import { createLogMonitorPageRoute, createLogOverviewPageRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; import { UMRestApiRouteFactory } from './types'; @@ -18,6 +17,7 @@ import { } from './monitors'; import { createGetPingHistogramRoute } from './pings/get_ping_histogram'; import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; +import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -27,6 +27,7 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createGetOverviewFilters, createGetPingsRoute, createGetIndexPatternRoute, + createGetIndexStatusRoute, createGetMonitorRoute, createGetMonitorDetailsRoute, createGetMonitorLocationsRoute, diff --git a/x-pack/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts similarity index 100% rename from x-pack/plugins/uptime/server/rest_api/index_pattern/get_index_pattern.ts rename to x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts new file mode 100644 index 0000000000000..44799aa19c140 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; +import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; + +export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: REST_API_URLS.INDEX_STATUS, + validate: false, + options: { + tags: ['access:uptime'], + }, + handler: async ({ callES }, _context, _request, response): Promise => { + try { + return response.ok({ + body: { + ...(await libs.requests.getIndexStatus({ callES })), + }, + }); + } catch (e) { + return response.internalError({ body: { message: e.message } }); + } + }, +}); diff --git a/x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts b/x-pack/plugins/uptime/server/rest_api/index_state/index.ts similarity index 82% rename from x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts rename to x-pack/plugins/uptime/server/rest_api/index_state/index.ts index b35e2e7b65a29..ff44794bfe7d1 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_pattern/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/index.ts @@ -5,3 +5,4 @@ */ export { createGetIndexPatternRoute } from './get_index_pattern'; +export { createGetIndexStatusRoute } from './get_index_status'; diff --git a/x-pack/test/api_integration/apis/uptime/feature_controls.ts b/x-pack/test/api_integration/apis/uptime/feature_controls.ts index adbfacb014e9f..15666acab2335 100644 --- a/x-pack/test/api_integration/apis/uptime/feature_controls.ts +++ b/x-pack/test/api_integration/apis/uptime/feature_controls.ts @@ -5,9 +5,9 @@ */ import expect from '@kbn/expect'; -import { docCountQueryString } from '../../../../legacy/plugins/uptime/public/queries'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PINGS_DATE_RANGE_END, PINGS_DATE_RANGE_START } from './constants'; +import { REST_API_URLS } from '../../../../legacy/plugins/uptime/common/constants'; export default function featureControlsTests({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); @@ -26,22 +26,13 @@ export default function featureControlsTests({ getService }: FtrProviderContext) expect(result.response).to.have.property('statusCode', 200); }; - const executeGraphQLQuery = async (username: string, password: string, spaceId?: string) => { + const executeRESTAPIQuery = async (username: string, password: string, spaceId?: string) => { const basePath = spaceId ? `/s/${spaceId}` : ''; - const getDocCountQuery = { - operationName: null, - query: docCountQueryString, - variables: { - dateRangeStart: '2019-01-28T17:40:08.078Z', - dateRangeEnd: '2019-01-28T19:00:16.078Z', - }, - }; return await supertest - .post(`${basePath}/api/uptime/graphql`) + .get(basePath + REST_API_URLS.INDEX_STATUS) .auth(username, password) .set('kbn-xsrf', 'foo') - .send({ ...getDocCountQuery }) .then((response: any) => ({ error: undefined, response })) .catch((error: any) => ({ error, response: undefined })); }; @@ -82,7 +73,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -121,7 +112,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expectResponse(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -163,7 +154,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) full_name: 'a kibana user', }); - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); @@ -232,7 +223,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }); it('user_1 can access APIs in space_1', async () => { - const graphQLResult = await executeGraphQLQuery(username, password, space1Id); + const graphQLResult = await executeRESTAPIQuery(username, password, space1Id); expectResponse(graphQLResult); const pingsResult = await executePingsRequest(username, password, space1Id); @@ -240,7 +231,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }); it(`user_1 can't access APIs in space_2`, async () => { - const graphQLResult = await executeGraphQLQuery(username, password); + const graphQLResult = await executeRESTAPIQuery(username, password); expect404(graphQLResult); const pingsResult = await executePingsRequest(username, password); diff --git a/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js b/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js deleted file mode 100644 index 1aa69faaab736..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/graphql/doc_count.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { docCountQueryString } from '../../../../../legacy/plugins/uptime/public/queries'; -import { expectFixtureEql } from './helpers/expect_fixture_eql'; - -export default function({ getService }) { - describe('docCount query', () => { - before('load heartbeat data', () => getService('esArchiver').load('uptime/full_heartbeat')); - after('unload heartbeat index', () => getService('esArchiver').unload('uptime/full_heartbeat')); - - const supertest = getService('supertest'); - - it(`will fetch the index's count`, async () => { - const getDocCountQuery = { - operationName: null, - query: docCountQueryString, - variables: { - dateRangeStart: '2019-01-28T17:40:08.078Z', - dateRangeEnd: '2025-01-28T19:00:16.078Z', - }, - }; - const { - body: { data }, - } = await supertest - .post('/api/uptime/graphql') - .set('kbn-xsrf', 'foo') - .send({ ...getDocCountQuery }); - - expectFixtureEql(data, 'doc_count'); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json deleted file mode 100644 index 4daf223a79a69..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/doc_count.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "statesIndexStatus": { - "docCount": { - "count": 2000 - }, - "indexExists": true - } -} \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json index 59f5f95e7d840..05724f0716e8d 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0009-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json index 9a1363f00578a..6e62787069f40 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_id_filtered.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": null, - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0002-up", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -198,4 +194,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json index 59f5f95e7d840..05724f0716e8d 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_1.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0009-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json index 5c07b4daaf543..6cbe4ee3659a8 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0090-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": null, - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0090-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json index 71184093f4318..9a3f781735cb7 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_10_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0080-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0089-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0080-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json index 3b15ea3c24eeb..4f4af9c2c6012 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0010-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0019-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0010-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json index eb6512d2f75b3..fe48ad49d13ba 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_2_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": null, "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0009-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0000-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json index aee4fa6946fc0..70ca665704a79 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0020-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0029-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0020-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json index 03164f794a4d5..3f09c951ec2fa 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_3_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0010-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0019-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0010-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json index 488fdab14f1e2..cdc0f32c9765e 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0030-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0039-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0030-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json index 79ce05d86f533..9f6d004380c16 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_4_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0020-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0029-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0020-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json index ef62cdd86c2a9..dedddb2a78ade 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0040-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0049-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0040-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json index 34e8269cb95d9..fabcf70404952 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_5_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0030-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0039-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0030-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json index d733467c3f9b6..943cc68249dc1 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0050-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0059-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0050-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json index 12e27106bd533..564f58f59f373 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_6_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0040-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0049-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0040-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json index d0f2b820f8327..cb94273e91fd8 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0060-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0069-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0060-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json index cf0c8641cc87b..7aac62bba84f7 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_7_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0050-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0059-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0050-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json index 2801e94e034c7..08cbd0d878b44 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0070-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0079-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0070-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json index 7fcbd4dd59659..8de639b705ee9 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_8_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0060-intermittent\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0069-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0060-intermittent", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json index 0adb7ad0b0dba..c38f5c801a267 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0080-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorDirection\":\"AFTER\",\"sortOrder\":\"ASC\",\"cursorKey\":{\"monitor_id\":\"0089-up\"}}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0080-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json index a796be38bd0d9..5c2ec8512e320 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json +++ b/x-pack/test/api_integration/apis/uptime/graphql/fixtures/monitor_states_page_9_previous.json @@ -2,9 +2,7 @@ "monitorStates": { "prevPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0070-down\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"BEFORE\"}", "nextPagePagination": "{\"cursorKey\":{\"monitor_id\":\"0079-up\"},\"sortOrder\":\"ASC\",\"cursorDirection\":\"AFTER\"}", - "totalSummaryCount": { - "count": 2000 - }, + "totalSummaryCount": 2000, "summaries": [ { "monitor_id": "0070-down", @@ -172,9 +170,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -362,9 +358,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -552,9 +546,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -742,9 +734,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -932,9 +922,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1122,9 +1110,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1312,9 +1298,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1502,9 +1486,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1692,9 +1674,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1882,9 +1862,7 @@ "geo": null, "observer": { "geo": { - "name": [ - "mpls" - ], + "name": ["mpls"], "location": null } }, @@ -1908,4 +1886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/test/api_integration/apis/uptime/graphql/index.js b/x-pack/test/api_integration/apis/uptime/graphql/index.js index c2fdc57edede3..ee22974d47170 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/index.js +++ b/x-pack/test/api_integration/apis/uptime/graphql/index.js @@ -10,7 +10,6 @@ export default function({ loadTestFile }) { // the uptime app and runs it against the live HTTP server, // verifying the pre-loaded documents are returned in a way that // matches the snapshots contained in './fixtures' - loadTestFile(require.resolve('./doc_count')); loadTestFile(require.resolve('./monitor_states')); loadTestFile(require.resolve('./ping_list')); }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts new file mode 100644 index 0000000000000..1f5322f581b39 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { expectFixtureEql } from '../graphql/helpers/expect_fixture_eql'; +import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; + +export default function({ getService }: FtrProviderContext) { + describe('docCount query', () => { + const supertest = getService('supertest'); + + it(`will fetch the index's count`, async () => { + const apiResponse = await supertest.get(REST_API_URLS.INDEX_STATUS); + const data = apiResponse.body; + expectFixtureEql(data, 'doc_count'); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json new file mode 100644 index 0000000000000..41b9af392dded --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/doc_count.json @@ -0,0 +1,4 @@ +{ + "docCount": 2000, + "indexExists": true +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index 5e26cb9216f45..67b94f19c638f 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -21,6 +21,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./selected_monitor')); loadTestFile(require.resolve('./ping_histogram')); loadTestFile(require.resolve('./monitor_duration')); + loadTestFile(require.resolve('./doc_count')); }); }); } From a946adbf10b0148bf510b0588713dd7a2a04579f Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 16 Mar 2020 13:01:48 +0100 Subject: [PATCH 3/8] adds new test (#60064) --- .../cypress/integration/detections.spec.ts | 43 ++++++++++++++++++- .../siem/cypress/screens/detections.ts | 4 +- .../plugins/siem/cypress/tasks/detections.ts | 7 +++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts index 1624586d4ca14..c048510c50c36 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -5,12 +5,14 @@ */ import { NUMBER_OF_SIGNALS, + OPEN_CLOSE_SIGNALS_BTN, SELECTED_SIGNALS, SHOWING_SIGNALS, SIGNALS, } from '../screens/detections'; import { + closeFirstSignal, closeSignals, goToClosedSignals, goToOpenedSignals, @@ -26,7 +28,7 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Detections', () => { - before(() => { + beforeEach(() => { esArchiverLoad('signals'); loginAndWaitForPage(DETECTIONS); }); @@ -111,4 +113,43 @@ describe('Detections', () => { .should('eql', expectedNumberOfOpenedSignals.toString()); }); }); + + it('Closes one signal when more than one opened signals are selected', () => { + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + const numberOfSignalsToBeClosed = 1; + const numberOfSignalsToBeSelected = 3; + + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('have.attr', 'disabled'); + selectNumberOfSignals(numberOfSignalsToBeSelected); + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('not.have.attr', 'disabled'); + + closeFirstSignal(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + + const expectedNumberOfSignals = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignals.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignals.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signal`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8b5ba23578807..f388ac1215d01 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -12,7 +12,9 @@ export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; -export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; +export const OPEN_CLOSE_SIGNAL_BTN = '[data-test-subj="update-signal-status-button"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] button'; export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 21a0c136b90df..3416e3eb81de3 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -8,6 +8,7 @@ import { CLOSED_SIGNALS_BTN, LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNAL_BTN, OPEN_CLOSE_SIGNALS_BTN, OPENED_SIGNALS_BTN, SIGNALS, @@ -15,6 +16,12 @@ import { } from '../screens/detections'; import { REFRESH_BUTTON } from '../screens/siem_header'; +export const closeFirstSignal = () => { + cy.get(OPEN_CLOSE_SIGNAL_BTN) + .first() + .click({ force: true }); +}; + export const closeSignals = () => { cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); }; From 4be60e54261be05eda4c5b4530c93d9450398d8d Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 16 Mar 2020 15:05:41 +0300 Subject: [PATCH 4/8] [Step 1][App Arch] Saved object migrations from kibana plugin to new platform (#59044) * [App Arch] Saved object migrations from kibana plugin to new platform Part of #55946 * search type -> data plugin * remove migrations folder * visualization type -> visualizations plugin * src/legacy/core_plugins/data/mappings-> np * savedObjectsManagement -> management section * move code into save_objects folder Co-authored-by: Elastic Machine --- .../saved_objects/serialization/types.ts | 2 +- src/legacy/core_plugins/data/index.ts | 19 - src/legacy/core_plugins/kibana/index.js | 51 - src/legacy/core_plugins/kibana/mappings.json | 89 - .../kibana/migrations/migrations.js | 564 +----- .../kibana/migrations/migrations.test.js | 1574 ----------------- .../index_patterns/index_patterns_service.ts | 7 +- src/plugins/data/server/plugin.ts | 3 + .../data/server/query/index.ts} | 32 +- .../data/server/query/query_service.ts | 29 + .../data/server/saved_objects/index.ts | 22 + .../index_pattern_migrations.test.ts | 97 + .../saved_objects/index_pattern_migrations.ts | 60 + .../server/saved_objects/index_patterns.ts | 58 + .../data/server/saved_objects/query.ts | 52 + .../data/server/saved_objects/search.ts | 60 + .../saved_objects/search_migrations.test.ts | 299 ++++ .../server/saved_objects/search_migrations.ts | 92 + .../data/server/search/search_service.ts | 4 + src/plugins/data/server/server.api.md | 2 +- src/plugins/visualizations/kibana.json | 2 +- src/plugins/visualizations/server/index.ts | 30 + src/plugins/visualizations/server/plugin.ts | 54 + .../server/saved_objects/index.ts | 20 + .../server/saved_objects/visualization.ts | 56 + .../visualization_migrations.test.ts | 1356 ++++++++++++++ .../saved_objects/visualization_migrations.ts | 574 ++++++ src/plugins/visualizations/server/types.ts | 23 + 28 files changed, 2899 insertions(+), 2332 deletions(-) rename src/{legacy/core_plugins/data/mappings.ts => plugins/data/server/query/index.ts} (60%) create mode 100644 src/plugins/data/server/query/query_service.ts create mode 100644 src/plugins/data/server/saved_objects/index.ts create mode 100644 src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts create mode 100644 src/plugins/data/server/saved_objects/index_pattern_migrations.ts create mode 100644 src/plugins/data/server/saved_objects/index_patterns.ts create mode 100644 src/plugins/data/server/saved_objects/query.ts create mode 100644 src/plugins/data/server/saved_objects/search.ts create mode 100644 src/plugins/data/server/saved_objects/search_migrations.test.ts create mode 100644 src/plugins/data/server/saved_objects/search_migrations.ts create mode 100644 src/plugins/visualizations/server/index.ts create mode 100644 src/plugins/visualizations/server/plugin.ts create mode 100644 src/plugins/visualizations/server/saved_objects/index.ts create mode 100644 src/plugins/visualizations/server/saved_objects/visualization.ts create mode 100644 src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts create mode 100644 src/plugins/visualizations/server/saved_objects/visualization_migrations.ts create mode 100644 src/plugins/visualizations/server/types.ts diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts index 524c2c8ffae7a..dfaec127ba159 100644 --- a/src/core/server/saved_objects/serialization/types.ts +++ b/src/core/server/saved_objects/serialization/types.ts @@ -50,7 +50,7 @@ export interface SavedObjectsRawDocSource { * scenario out of the box. */ interface SavedObjectDoc { - attributes: unknown; + attributes: any; id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional type: string; namespace?: string; diff --git a/src/legacy/core_plugins/data/index.ts b/src/legacy/core_plugins/data/index.ts index 813eab00f7258..10c8cf464b82d 100644 --- a/src/legacy/core_plugins/data/index.ts +++ b/src/legacy/core_plugins/data/index.ts @@ -19,8 +19,6 @@ import { resolve } from 'path'; import { Legacy } from '../../../../kibana'; -import { mappings } from './mappings'; -import { SavedQuery } from '../../../plugins/data/public'; // eslint-disable-next-line import/no-default-export export default function DataPlugin(kibana: any) { @@ -36,23 +34,6 @@ export default function DataPlugin(kibana: any) { init: (server: Legacy.Server) => ({}), uiExports: { injectDefaultVars: () => ({}), - mappings, - savedObjectsManagement: { - query: { - icon: 'search', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj: SavedQuery) { - return obj.attributes.title; - }, - getInAppUrl(obj: SavedQuery) { - return { - path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, - }, }, }; diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 092eed924f330..1d772536fa1ea 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -126,57 +126,6 @@ export default function(kibana) { ], savedObjectsManagement: { - 'index-pattern': { - icon: 'indexPatternApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'management.kibana.index_patterns', - }; - }, - }, - visualization: { - icon: 'visualizeApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/visualize/edit/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'visualize.show', - }; - }, - }, - search: { - icon: 'discoverApp', - defaultSearchField: 'title', - isImportableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getEditUrl(obj) { - return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; - }, - getInAppUrl(obj) { - return { - path: `/app/kibana#/discover/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'discover.show', - }; - }, - }, dashboard: { icon: 'dashboardApp', defaultSearchField: 'title', diff --git a/src/legacy/core_plugins/kibana/mappings.json b/src/legacy/core_plugins/kibana/mappings.json index 4cf9ea1d301c0..af3f79588552b 100644 --- a/src/legacy/core_plugins/kibana/mappings.json +++ b/src/legacy/core_plugins/kibana/mappings.json @@ -1,93 +1,4 @@ { - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "dashboard": { "properties": { "description": { diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.js b/src/legacy/core_plugins/kibana/migrations/migrations.js index 29b6e632d19fd..d37887c640b90 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.js @@ -17,7 +17,7 @@ * under the License. */ -import { cloneDeep, get, omit, has, flow } from 'lodash'; +import { get } from 'lodash'; import { migrations730 as dashboardMigrations730 } from '../public/dashboard/migrations'; function migrateIndexPattern(doc) { @@ -58,559 +58,7 @@ function migrateIndexPattern(doc) { doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); } -// [TSVB] Migrate percentile-rank aggregation (value -> values) -const migratePercentileRankAggregation = doc => { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState && visState.type === 'metrics') { - const series = get(visState, 'params.series') || []; - - series.forEach(part => { - (part.metrics || []).forEach(metric => { - if (metric.type === 'percentile_rank' && has(metric, 'value')) { - metric.values = [metric.value]; - - delete metric.value; - } - }); - }); - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } - return doc; -}; - -// Migrate date histogram aggregation (remove customInterval) -const migrateDateHistogramAggregation = doc => { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type === 'date_histogram' && agg.params) { - if (agg.params.interval === 'custom') { - agg.params.interval = agg.params.customInterval; - } - delete agg.params.customInterval; - } - - if ( - get(agg, 'params.customBucket.type', null) === 'date_histogram' && - agg.params.customBucket.params - ) { - if (agg.params.customBucket.params.interval === 'custom') { - agg.params.customBucket.params.interval = agg.params.customBucket.params.customInterval; - } - delete agg.params.customBucket.params.customInterval; - } - }); - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } - return doc; -}; - -function removeDateHistogramTimeZones(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - // We're checking always for the existance of agg.params here. This should always exist, but better - // be safe then sorry during migrations. - if (agg.type === 'date_histogram' && agg.params) { - delete agg.params.time_zone; - } - - if ( - get(agg, 'params.customBucket.type', null) === 'date_histogram' && - agg.params.customBucket.params - ) { - delete agg.params.customBucket.params.time_zone; - } - }); - doc.attributes.visState = JSON.stringify(visState); - } - } - return doc; -} - -// migrate gauge verticalSplit to alignment -// https://github.com/elastic/kibana/issues/34636 -function migrateGaugeVerticalSplitToAlignment(doc, logger) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.type === 'gauge' && !visState.params.gauge.alignment) { - visState.params.gauge.alignment = visState.params.gauge.verticalSplit - ? 'vertical' - : 'horizontal'; - delete visState.params.gauge.verticalSplit; - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - logger.warning(`Exception @ migrateGaugeVerticalSplitToAlignment! ${e}`); - logger.warning(`Exception @ migrateGaugeVerticalSplitToAlignment! Payload: ${visStateJSON}`); - } - } - return doc; -} -// Migrate filters (string -> { query: string, language: lucene }) -/* - Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters. - In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself. - We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene. - For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object. - Path to the series array is thus: - attributes.visState. -*/ -function transformFilterStringToQueryObject(doc) { - // Migrate filters - // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly - const newDoc = cloneDeep(doc); - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const visType = get(visState, 'params.type'); - const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; - if (tsvbTypes.indexOf(visType) === -1) { - // skip - return doc; - } - // migrate the params fitler - const params = get(visState, 'params'); - if (params.filter && typeof params.filter === 'string') { - const paramsFilterObject = { - query: params.filter, - language: 'lucene', - }; - params.filter = paramsFilterObject; - } - - // migrate the annotations query string: - const annotations = get(visState, 'params.annotations') || []; - annotations.forEach(item => { - if (!item.query_string) { - // we don't need to transform anything if there isn't a filter at all - return; - } - if (typeof item.query_string === 'string') { - const itemQueryStringObject = { - query: item.query_string, - language: 'lucene', - }; - item.query_string = itemQueryStringObject; - } - }); - // migrate the series filters - const series = get(visState, 'params.series') || []; - series.forEach(item => { - if (!item.filter) { - // we don't need to transform anything if there isn't a filter at all - return; - } - // series item filter - if (typeof item.filter === 'string') { - const itemfilterObject = { - query: item.filter, - language: 'lucene', - }; - item.filter = itemfilterObject; - } - // series item split filters filter - if (item.split_filters) { - const splitFilters = get(item, 'split_filters') || []; - splitFilters.forEach(filter => { - if (!filter.filter) { - // we don't need to transform anything if there isn't a filter at all - return; - } - if (typeof filter.filter === 'string') { - const filterfilterObject = { - query: filter.filter, - language: 'lucene', - }; - filter.filter = filterfilterObject; - } - }); - } - }); - newDoc.attributes.visState = JSON.stringify(visState); - } - } - return newDoc; -} -function transformSplitFiltersStringToQueryObject(doc) { - // Migrate split_filters in TSVB objects that weren't migrated in 7.3 - // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly - const newDoc = cloneDeep(doc); - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const visType = get(visState, 'params.type'); - const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; - if (tsvbTypes.indexOf(visType) === -1) { - // skip - return doc; - } - // migrate the series split_filter filters - const series = get(visState, 'params.series') || []; - series.forEach(item => { - // series item split filters filter - if (item.split_filters) { - const splitFilters = get(item, 'split_filters') || []; - if (splitFilters.length > 0) { - // only transform split_filter filters if we have filters - splitFilters.forEach(filter => { - if (typeof filter.filter === 'string') { - const filterfilterObject = { - query: filter.filter, - language: 'lucene', - }; - filter.filter = filterfilterObject; - } - }); - } - } - }); - newDoc.attributes.visState = JSON.stringify(visState); - } - } - return newDoc; -} - -function migrateFiltersAggQuery(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type !== 'filters') return; - - agg.params.filters.forEach(filter => { - if (filter.input.language) return filter; - filter.input.language = 'lucene'; - }); - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - } - return doc; -} - -function replaceMovAvgToMovFn(doc, logger) { - const visStateJSON = get(doc, 'attributes.visState'); - let visState; - - if (visStateJSON) { - try { - visState = JSON.parse(visStateJSON); - - if (visState && visState.type === 'metrics') { - const series = get(visState, 'params.series', []); - - series.forEach(part => { - if (part.metrics && Array.isArray(part.metrics)) { - part.metrics.forEach(metric => { - if (metric.type === 'moving_average') { - metric.model_type = metric.model; - metric.alpha = get(metric, 'settings.alpha', 0.3); - metric.beta = get(metric, 'settings.beta', 0.1); - metric.gamma = get(metric, 'settings.gamma', 0.3); - metric.period = get(metric, 'settings.period', 1); - metric.multiplicative = get(metric, 'settings.type') === 'mult'; - - delete metric.minimize; - delete metric.model; - delete metric.settings; - delete metric.predict; - } - }); - } - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - logger.warning(`Exception @ replaceMovAvgToMovFn! ${e}`); - logger.warning(`Exception @ replaceMovAvgToMovFn! Payload: ${visStateJSON}`); - } - } - - return doc; -} - -function migrateSearchSortToNestedArray(doc) { - const sort = get(doc, 'attributes.sort'); - if (!sort) return doc; - - // Don't do anything if we already have a two dimensional array - if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) { - return doc; - } - - return { - ...doc, - attributes: { - ...doc.attributes, - sort: [doc.attributes.sort], - }, - }; -} - -function migrateFiltersAggQueryStringQueries(doc) { - const visStateJSON = get(doc, 'attributes.visState'); - - if (visStateJSON) { - try { - const visState = JSON.parse(visStateJSON); - if (visState && visState.aggs) { - visState.aggs.forEach(agg => { - if (agg.type !== 'filters') return doc; - - agg.params.filters.forEach(filter => { - if (filter.input.query.query_string) { - filter.input.query = filter.input.query.query_string.query; - } - }); - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - visState: JSON.stringify(visState), - }, - }; - } - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - } - return doc; -} - -function migrateSubTypeAndParentFieldProperties(doc) { - if (!doc.attributes.fields) return doc; - - const fieldsString = doc.attributes.fields; - const fields = JSON.parse(fieldsString); - const migratedFields = fields.map(field => { - if (field.subType === 'multi') { - return { - ...omit(field, 'parent'), - subType: { multi: { parent: field.parent } }, - }; - } - - return field; - }); - - return { - ...doc, - attributes: { - ...doc.attributes, - fields: JSON.stringify(migratedFields), - }, - }; -} - -const executeMigrations720 = flow( - migratePercentileRankAggregation, - migrateDateHistogramAggregation -); -const executeMigrations730 = flow( - migrateGaugeVerticalSplitToAlignment, - transformFilterStringToQueryObject, - migrateFiltersAggQuery, - replaceMovAvgToMovFn -); - -const executeVisualizationMigrations731 = flow(migrateFiltersAggQueryStringQueries); - -const executeSearchMigrations740 = flow(migrateSearchSortToNestedArray); - -const executeMigrations742 = flow(transformSplitFiltersStringToQueryObject); - export const migrations = { - 'index-pattern': { - '6.5.0': doc => { - doc.attributes.type = doc.attributes.type || undefined; - doc.attributes.typeMeta = doc.attributes.typeMeta || undefined; - return doc; - }, - '7.6.0': flow(migrateSubTypeAndParentFieldProperties), - }, - visualization: { - /** - * We need to have this migration twice, once with a version prior to 7.0.0 once with a version - * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already - * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, - * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we - * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects - * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced - * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 - * only contained the 6.7.2 migration and not the 7.0.1 migration. - */ - '6.7.2': removeDateHistogramTimeZones, - '7.0.0': doc => { - // Set new "references" attribute - doc.references = doc.references || []; - - // Migrate index pattern - migrateIndexPattern(doc); - - // Migrate saved search - const savedSearchId = get(doc, 'attributes.savedSearchId'); - if (savedSearchId) { - doc.references.push({ - type: 'search', - name: 'search_0', - id: savedSearchId, - }); - doc.attributes.savedSearchRefName = 'search_0'; - } - delete doc.attributes.savedSearchId; - - // Migrate controls - const visStateJSON = get(doc, 'attributes.visState'); - if (visStateJSON) { - let visState; - try { - visState = JSON.parse(visStateJSON); - } catch (e) { - // Let it go, the data is invalid and we'll leave it as is - } - if (visState) { - const controls = get(visState, 'params.controls') || []; - controls.forEach((control, i) => { - if (!control.indexPattern) { - return; - } - control.indexPatternRefName = `control_${i}_index_pattern`; - doc.references.push({ - name: control.indexPatternRefName, - type: 'index-pattern', - id: control.indexPattern, - }); - delete control.indexPattern; - }); - doc.attributes.visState = JSON.stringify(visState); - } - } - - // Migrate table splits - try { - const visState = JSON.parse(doc.attributes.visState); - if (get(visState, 'type') !== 'table') { - return doc; // do nothing; we only want to touch tables - } - - let splitCount = 0; - visState.aggs = visState.aggs.map(agg => { - if (agg.schema !== 'split') { - return agg; - } - - splitCount++; - if (splitCount === 1) { - return agg; // leave the first split agg unchanged - } - agg.schema = 'bucket'; - // the `row` param is exclusively used by split aggs, so we remove it - agg.params = omit(agg.params, ['row']); - return agg; - }); - - if (splitCount <= 1) { - return doc; // do nothing; we only want to touch tables with multiple split aggs - } - - const newDoc = cloneDeep(doc); - newDoc.attributes.visState = JSON.stringify(visState); - return newDoc; - } catch (e) { - throw new Error( - `Failure attempting to migrate saved object '${doc.attributes.title}' - ${e}` - ); - } - }, - '7.0.1': removeDateHistogramTimeZones, - '7.2.0': doc => executeMigrations720(doc), - '7.3.0': executeMigrations730, - '7.3.1': executeVisualizationMigrations731, - // migrate split_filters that were not migrated in 7.3.0 (transformFilterStringToQueryObject). - '7.4.2': executeMigrations742, - }, dashboard: { '7.0.0': doc => { // Set new "references" attribute @@ -651,14 +99,4 @@ export const migrations = { }, '7.3.0': dashboardMigrations730, }, - search: { - '7.0.0': doc => { - // Set new "references" attribute - doc.references = doc.references || []; - // Migrate index pattern - migrateIndexPattern(doc); - return doc; - }, - '7.4.0': executeSearchMigrations740, - }, }; diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.test.js b/src/legacy/core_plugins/kibana/migrations/migrations.test.js index e39bc59201e7f..b02081128c858 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.test.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.test.js @@ -19,1312 +19,6 @@ import { migrations } from './migrations'; -describe('index-pattern', () => { - describe('6.5.0', () => { - const migrate = doc => migrations['index-pattern']['6.5.0'](doc); - - it('adds "type" and "typeMeta" properties to object when not declared', () => { - expect( - migrate({ - attributes: {}, - }) - ).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "type": undefined, - "typeMeta": undefined, - }, -} -`); - }); - - it('keeps "type" and "typeMeta" properties as is when declared', () => { - expect( - migrate({ - attributes: { - type: '123', - typeMeta: '123', - }, - }) - ).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "type": "123", - "typeMeta": "123", - }, -} -`); - }); - }); - - describe('7.6.0', function() { - const migrate = doc => migrations['index-pattern']['7.6.0'](doc); - - it('should remove the parent property and update the subType prop on every field that has them', () => { - const input = { - attributes: { - title: 'test', - fields: - '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":"multi","parent":"customer_name"}]', - }, - }; - const expected = { - attributes: { - title: 'test', - fields: - '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"customer_name"}}}]', - }, - }; - - expect(migrate(input)).toEqual(expected); - }); - }); -}); - -describe('visualization', () => { - describe('date histogram time zone removal', () => { - const migrate = doc => migrations.visualization['6.7.2'](doc); - let doc; - beforeEach(() => { - doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - enabled: true, - id: '1', - params: { - // Doesn't make much sense but we want to test it's not removing it from anything else - time_zone: 'Europe/Berlin', - }, - schema: 'metric', - type: 'count', - }, - { - enabled: true, - id: '2', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - time_zone: 'Europe/Berlin', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '4', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '3', - params: { - customBucket: { - enabled: true, - id: '1-bucket', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - time_zone: 'Europe/Berlin', - useNormalizedEsInterval: true, - }, - type: 'date_histogram', - }, - customMetric: { - enabled: true, - id: '1-metric', - params: {}, - type: 'count', - }, - }, - schema: 'metric', - type: 'max_bucket', - }, - ], - }), - }, - }; - }); - - it('should remove time_zone from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.time_zone'); - }); - - it('should not remove time_zone from non date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[0]).toHaveProperty('params.time_zone'); - }); - - it('should remove time_zone from nested aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); - }); - - it('should not fail on date histograms without a time_zone', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[2]).not.toHaveProperty('params.time_zone'); - }); - - it('should be able to apply the migration twice, since we need it for 6.7.2 and 7.0.1', () => { - const migratedDoc = migrate(migrate(doc)); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.time_zone'); - expect(aggs[0]).toHaveProperty('params.time_zone'); - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); - expect(aggs[2]).not.toHaveProperty('params.time_zone'); - }); - }); - - describe('7.0.0', () => { - const migrate = doc => migrations.visualization['7.0.0'](doc); - const generateDoc = ({ type, aggs }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ type, aggs }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - - it('does not throw error on empty object', () => { - const migratedDoc = migrate({ - attributes: { - visState: '{}', - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{}", - }, - "references": Array [], -} -`); - }); - - it('skips errors when searchSourceJSON is null', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: null, - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips errors when searchSourceJSON is undefined', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: undefined, - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when searchSourceJSON is not a string', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: 123, - }, - savedSearchId: '123', - }, - }; - expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when searchSourceJSON is invalid json', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{abc123}', - }, - savedSearchId: '123', - }, - }; - expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts "index" attribute from doc', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts index patterns from the filter', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - bar: true, - filter: [ - { - meta: { index: 'my-index', foo: true }, - }, - ], - }), - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); - }); - - it('extracts index patterns from controls', () => { - const doc = { - id: '1', - type: 'visualization', - attributes: { - foo: true, - visState: JSON.stringify({ - bar: false, - params: { - controls: [ - { - bar: true, - indexPattern: 'pattern*', - }, - { - foo: true, - }, - ], - }, - }), - }, - }; - const migratedDoc = migrate(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "control_0_index_pattern", - "type": "index-pattern", - }, - ], - "type": "visualization", -} -`); - }); - - it('skips extracting savedSearchId when missing', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); - }); - - it('extract savedSearchId from doc', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - savedSearchId: '123', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], -} -`); - }); - - it('delete savedSearchId when empty string in doc', () => { - const doc = { - id: '1', - attributes: { - visState: '{}', - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - savedSearchId: '', - }, - }; - const migratedDoc = migrate(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); - }); - - it('should return a new object if vis is table and has multiple split aggs', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - ]; - const tableDoc = generateDoc({ type: 'table', aggs }); - const expected = tableDoc; - const actual = migrate(tableDoc); - expect(actual).not.toBe(expected); - }); - - it('should not touch any vis that is not table', () => { - const aggs = []; - const pieDoc = generateDoc({ type: 'pie', aggs }); - const expected = pieDoc; - const actual = migrate(pieDoc); - expect(actual).toBe(expected); - }); - - it('should not change values in any vis that is not table', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'segment', - params: { hey: 'ya' }, - }, - ]; - const pieDoc = generateDoc({ type: 'pie', aggs }); - const expected = pieDoc; - const actual = migrate(pieDoc); - expect(actual).toEqual(expected); - }); - - it('should not touch table vis if there are not multiple split aggs', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - ]; - const tableDoc = generateDoc({ type: 'table', aggs }); - const expected = tableDoc; - const actual = migrate(tableDoc); - expect(actual).toBe(expected); - }); - - it('should change all split aggs to `bucket` except the first', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - { - id: '4', - schema: 'bucket', - params: { heyyy: 'yaaa' }, - }, - ]; - const expected = ['metric', 'split', 'bucket', 'bucket']; - const migrated = migrate(generateDoc({ type: 'table', aggs })); - const actual = JSON.parse(migrated.attributes.visState); - expect(actual.aggs.map(agg => agg.schema)).toEqual(expected); - }); - - it('should remove `rows` param from any aggs that are not `split`', () => { - const aggs = [ - { - id: '1', - schema: 'metric', - params: {}, - }, - { - id: '2', - schema: 'split', - params: { foo: 'bar', row: true }, - }, - { - id: '3', - schema: 'split', - params: { hey: 'ya', row: false }, - }, - ]; - const expected = [{}, { foo: 'bar', row: true }, { hey: 'ya' }]; - const migrated = migrate(generateDoc({ type: 'table', aggs })); - const actual = JSON.parse(migrated.attributes.visState); - expect(actual.aggs.map(agg => agg.params)).toEqual(expected); - }); - - it('should throw with a reference to the doc name if something goes wrong', () => { - const doc = { - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: '!/// Intentionally malformed JSON ///!', - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }; - expect(() => migrate(doc)).toThrowError(/My Vis/); - }); - }); - - describe('date histogram custom interval removal', () => { - const migrate = doc => migrations.visualization['7.2.0'](doc); - let doc; - beforeEach(() => { - doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - enabled: true, - id: '1', - params: { - customInterval: '1h', - }, - schema: 'metric', - type: 'count', - }, - { - enabled: true, - id: '2', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'auto', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '4', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'custom', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - schema: 'segment', - type: 'date_histogram', - }, - { - enabled: true, - id: '3', - params: { - customBucket: { - enabled: true, - id: '1-bucket', - params: { - customInterval: '2h', - drop_partials: false, - extended_bounds: {}, - field: 'timestamp', - interval: 'custom', - min_doc_count: 1, - useNormalizedEsInterval: true, - }, - type: 'date_histogram', - }, - customMetric: { - enabled: true, - id: '1-metric', - params: {}, - type: 'count', - }, - }, - schema: 'metric', - type: 'max_bucket', - }, - ], - }), - }, - }; - }); - - it('should remove customInterval from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1]).not.toHaveProperty('params.customInterval'); - }); - - it('should not change interval from date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[1].params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[1].params.interval - ); - }); - - it('should not remove customInterval from non date_histogram aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[0]).toHaveProperty('params.customInterval'); - }); - - it('should set interval with customInterval value and remove customInterval when interval equals "custom"', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[2].params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[2].params.customInterval - ); - expect(aggs[2]).not.toHaveProperty('params.customInterval'); - }); - - it('should remove customInterval from nested aggregations', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); - }); - - it('should remove customInterval from nested aggregations and set interval with customInterval value', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3].params.customBucket.params.interval).toBe( - JSON.parse(doc.attributes.visState).aggs[3].params.customBucket.params.customInterval - ); - expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); - }); - - it('should not fail on date histograms without a customInterval', () => { - const migratedDoc = migrate(doc); - const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; - expect(aggs[3]).not.toHaveProperty('params.customInterval'); - }); - }); - describe('7.3.0', () => { - const logMsgArr = []; - const logger = { - warning: msg => logMsgArr.push(msg), - }; - const migrate = doc => migrations.visualization['7.3.0'](doc, logger); - - it('migrates type = gauge verticalSplit: false to alignment: vertical', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: false } } }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", - }, -} -`); - }); - - it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: true } } }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", - }, -} -`); - }); - - it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { - const migratedDoc = migrate({ - attributes: { - visState: JSON.stringify({ type: 'gauge' }), - }, - }); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\"}", - }, -} -`); - expect(logMsgArr).toMatchInlineSnapshot(` -Array [ - "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", - "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", -] -`); - }); - - describe('filters agg query migration', () => { - const doc = { - attributes: { - visState: JSON.stringify({ - aggs: [ - { - type: 'filters', - params: { - filters: [ - { - input: { - query: 'response:200', - }, - label: '', - }, - { - input: { - query: 'response:404', - }, - label: 'bad response', - }, - { - input: { - query: { - exists: { - field: 'phpmemory', - }, - }, - }, - label: '', - }, - ], - }, - }, - ], - }), - }, - }; - - it('should add language property to filters without one, assuming lucene', () => { - const migrationResult = migrate(doc); - expect(migrationResult).toEqual({ - attributes: { - visState: JSON.stringify({ - aggs: [ - { - type: 'filters', - params: { - filters: [ - { - input: { - query: 'response:200', - language: 'lucene', - }, - label: '', - }, - { - input: { - query: 'response:404', - language: 'lucene', - }, - label: 'bad response', - }, - { - input: { - query: { - exists: { - field: 'phpmemory', - }, - }, - language: 'lucene', - }, - label: '', - }, - ], - }, - }, - ], - }), - }, - }); - }); - }); - - describe('replaceMovAvgToMovFn()', () => { - let doc; - - beforeEach(() => { - doc = { - attributes: { - title: 'VIS', - visState: `{"title":"VIS","type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417", - "type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(0,156,224,1)", - "split_mode":"terms","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count", - "numerator":"FlightDelay:true"},{"settings":"","minimize":0,"window":5,"model": - "holt_winters","id":"23054fe0-8915-11e9-9b86-d3f94982620f","type":"moving_average","field": - "61ca57f2-469d-11e7-af02-69e470af7417","predict":1}],"separate_axis":0,"axis_position":"right", - "formatter":"number","chart_type":"line","line_width":"2","point_size":"0","fill":0.5,"stacked":"none", - "label":"Percent Delays","terms_size":"2","terms_field":"OriginCityName"}],"time_field":"timestamp", - "index_pattern":"kibana_sample_data_flights","interval":">=12h","axis_position":"left","axis_formatter": - "number","show_legend":1,"show_grid":1,"annotations":[{"fields":"FlightDelay,Cancelled,Carrier", - "template":"{{Carrier}}: Flight Delayed and Cancelled!","index_pattern":"kibana_sample_data_flights", - "query_string":"FlightDelay:true AND Cancelled:true","id":"53b7dff0-4c89-11e8-a66a-6989ad5a0a39", - "color":"rgba(0,98,177,1)","time_field":"timestamp","icon":"fa-exclamation-triangle", - "ignore_global_filters":1,"ignore_panel_filters":1,"hidden":true}],"legend_position":"bottom", - "axis_scale":"normal","default_index_pattern":"kibana_sample_data_flights","default_timefield":"timestamp"}, - "aggs":[]}`, - }, - migrationVersion: { - visualization: '7.2.0', - }, - type: 'visualization', - }; - }); - - test('should add some necessary moving_fn fields', () => { - const migratedDoc = migrate(doc); - const visState = JSON.parse(migratedDoc.attributes.visState); - const metric = visState.params.series[0].metrics[1]; - - expect(metric).toHaveProperty('model_type'); - expect(metric).toHaveProperty('alpha'); - expect(metric).toHaveProperty('beta'); - expect(metric).toHaveProperty('gamma'); - expect(metric).toHaveProperty('period'); - expect(metric).toHaveProperty('multiplicative'); - }); - }); - }); - describe('7.3.0 tsvb', () => { - const migrate = doc => migrations.visualization['7.3.0'](doc); - const generateDoc = ({ params }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ params }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - it('should change series item filters from a string into an object', () => { - const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] }; - const testDoc1 = generateDoc({ params }); - const migratedTestDoc1 = migrate(testDoc1); - const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; - expect(series[0].filter).toHaveProperty('query'); - expect(series[0].filter).toHaveProperty('language'); - }); - it('should not change a series item filter string in the object after migration', () => { - const markdownParams = { - type: 'markdown', - series: [ - { - filter: 'Filter Bytes Test:>1000', - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - }; - const markdownDoc = generateDoc({ params: markdownParams }); - const migratedMarkdownDoc = migrate(markdownDoc); - const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series; - expect(markdownSeries[0].filter.query).toBe( - JSON.parse(markdownDoc.attributes.visState).params.series[0].filter - ); - expect(markdownSeries[0].split_filters[0].filter.query).toBe( - JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter - ); - }); - it('should change series item filters from a string into an object for all filters', () => { - const params = { - type: 'timeseries', - filter: 'bytes:>1000', - series: [ - { - filter: 'Filter Bytes Test:>1000', - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - annotations: [{ query_string: 'bytes:>1000' }], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - }); - it('should not fail on a metric visualization without a filter in a series item', () => { - const params = { type: 'metric', series: [{}, {}, {}] }; - const testDoc1 = generateDoc({ params }); - const migratedTestDoc1 = migrate(testDoc1); - const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; - expect(series[2]).not.toHaveProperty('filter.query'); - }); - it('should not migrate a visualization of unknown type', () => { - const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] }; - const doc = generateDoc({ params }); - const migratedDoc = migrate(doc); - const series = JSON.parse(migratedDoc.attributes.visState).params.series; - expect(series[0].filter).toEqual(params.series[0].filter); - }); - }); - - describe('7.3.1', () => { - const migrate = migrations.visualization['7.3.1']; - - it('should migrate filters agg query string queries', () => { - const state = { - aggs: [ - { type: 'count', params: {} }, - { - type: 'filters', - params: { - filters: [ - { - input: { - query: { - query_string: { query: 'machine.os.keyword:"win 8"' }, - }, - }, - }, - ], - }, - }, - ], - }; - const expected = { - aggs: [ - { type: 'count', params: {} }, - { - type: 'filters', - params: { - filters: [{ input: { query: 'machine.os.keyword:"win 8"' } }], - }, - }, - ], - }; - const migratedDoc = migrate({ attributes: { visState: JSON.stringify(state) } }); - expect(migratedDoc).toEqual({ attributes: { visState: JSON.stringify(expected) } }); - }); - }); - describe('7.4.2 tsvb split_filters migration', () => { - const migrate = doc => migrations.visualization['7.4.2'](doc); - const generateDoc = ({ params }) => ({ - attributes: { - title: 'My Vis', - description: 'This is my super cool vis.', - visState: JSON.stringify({ params }), - uiStateJSON: '{}', - version: 1, - kibanaSavedObjectMeta: { - searchSourceJSON: '{}', - }, - }, - }); - it('should change series item filters from a string into an object for all filters', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(Object.keys(timeSeriesParams.filter)).toEqual( - expect.arrayContaining(['query', 'language']) - ); - expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ - query: 'bytes:>1000', - language: 'lucene', - }); - }); - it('should change series item split filters when there is no filter item', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [{ filter: 'bytes:>1000' }], - }, - ], - annotations: [ - { - query_string: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ - query: 'bytes:>1000', - language: 'lucene', - }); - }); - it('should not convert split_filters to objects if there are no split filter filters', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [], - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters).not.toHaveProperty('query'); - }); - it('should do nothing if a split_filter is already a query:language object', () => { - const params = { - type: 'timeseries', - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - series: [ - { - split_filters: [ - { - filter: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }, - ], - annotations: [ - { - query_string: { - query: 'bytes:>1000', - language: 'lucene', - }, - }, - ], - }; - const timeSeriesDoc = generateDoc({ params: params }); - const migratedtimeSeriesDoc = migrate(timeSeriesDoc); - const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; - expect(timeSeriesParams.series[0].split_filters[0].filter.query).toEqual('bytes:>1000'); - expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); - }); - }); -}); - describe('dashboard', () => { describe('7.0.0', () => { const migration = migrations.dashboard['7.0.0']; @@ -1751,271 +445,3 @@ Object { }); }); }); - -describe('search', () => { - describe('7.0.0', () => { - const migration = migrations.search['7.0.0']; - - test('skips errors when searchSourceJSON is null', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: null, - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips errors when searchSourceJSON is undefined', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: undefined, - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when searchSourceJSON is not a string', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: 123, - }, - }, - }; - expect(migration(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when searchSourceJSON is invalid json', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: '{abc123}', - }, - }, - }; - expect(migration(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('skips error when "index" and "filter" is missing from searchSourceJSON', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true }), - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - }, - "id": "123", - "references": Array [], - "type": "search", -} -`); - }); - - test('extracts "index" attribute from doc', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), - }, - }, - }; - const migratedDoc = migration(doc); - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - }, - "id": "123", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - ], - "type": "search", -} -`); - }); - - test('extracts index patterns from filter', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - foo: true, - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - bar: true, - filter: [ - { - meta: { - foo: true, - index: 'my-index', - }, - }, - ], - }), - }, - }, - }; - const migratedDoc = migration(doc); - - expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - }, - "id": "123", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - ], - "type": "search", -} -`); - }); - }); - - describe('7.4.0', function() { - const migration = migrations.search['7.4.0']; - - test('transforms one dimensional sort arrays into two dimensional arrays', () => { - const doc = { - id: '123', - type: 'search', - attributes: { - sort: ['bytes', 'desc'], - }, - }; - - const expected = { - id: '123', - type: 'search', - attributes: { - sort: [['bytes', 'desc']], - }, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(expected); - }); - - test("doesn't modify search docs that already have two dimensional sort arrays", () => { - const doc = { - id: '123', - type: 'search', - attributes: { - sort: [['bytes', 'desc']], - }, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(doc); - }); - - test("doesn't modify search docs that have no sort array", () => { - const doc = { - id: '123', - type: 'search', - attributes: {}, - }; - - const migratedDoc = migration(doc); - - expect(migratedDoc).toEqual(doc); - }); - }); -}); diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index 78f34e21b9e41..58e8fbae9f9e2 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -19,10 +19,13 @@ import { CoreSetup, Plugin } from 'kibana/server'; import { registerRoutes } from './routes'; +import { indexPatternSavedObjectType } from '../saved_objects'; export class IndexPatternsService implements Plugin { - public setup({ http }: CoreSetup) { - registerRoutes(http); + public setup(core: CoreSetup) { + core.savedObjects.registerType(indexPatternSavedObjectType); + + registerRoutes(core.http); } public start() {} diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 616e65ad872ab..efb8759e7bead 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -21,6 +21,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../.. import { IndexPatternsService } from './index_patterns'; import { ISearchSetup } from './search'; import { SearchService } from './search/search_service'; +import { QueryService } from './query/query_service'; import { ScriptsService } from './scripts'; import { KqlTelemetryService } from './kql_telemetry'; import { UsageCollectionSetup } from '../../usage_collection/server'; @@ -47,6 +48,7 @@ export class DataServerPlugin implements Plugin { + public setup(core: CoreSetup) { + core.savedObjects.registerType(querySavedObjectType); + } + + public start() {} +} diff --git a/src/plugins/data/server/saved_objects/index.ts b/src/plugins/data/server/saved_objects/index.ts new file mode 100644 index 0000000000000..5d980974474de --- /dev/null +++ b/src/plugins/data/server/saved_objects/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { searchSavedObjectType } from './search'; +export { querySavedObjectType } from './query'; +export { indexPatternSavedObjectType } from './index_patterns'; diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts new file mode 100644 index 0000000000000..b1410e2498667 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.test.ts @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectMigrationContext } from 'kibana/server'; +import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration index-pattern', () => { + describe('6.5.0', () => { + const migrationFn = indexPatternSavedObjectTypeMigrations['6.5.0']; + + test('adds "type" and "typeMeta" properties to object when not declared', () => { + expect( + migrationFn( + { + type: 'index-pattern', + attributes: {}, + }, + savedObjectMigrationContext + ) + ).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "type": undefined, + "typeMeta": undefined, + }, + "type": "index-pattern", +} +`); + }); + + test('keeps "type" and "typeMeta" properties as is when declared', () => { + expect( + migrationFn( + { + type: 'index-pattern', + attributes: { + type: '123', + typeMeta: '123', + }, + }, + savedObjectMigrationContext + ) + ).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "type": "123", + "typeMeta": "123", + }, + "type": "index-pattern", +} +`); + }); + }); + + describe('7.6.0', () => { + const migrationFn = indexPatternSavedObjectTypeMigrations['7.6.0']; + + test('should remove the parent property and update the subType prop on every field that has them', () => { + const input = { + type: 'index-pattern', + attributes: { + title: 'test', + fields: + '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":"multi","parent":"customer_name"}]', + }, + }; + const expected = { + type: 'index-pattern', + attributes: { + title: 'test', + fields: + '[{"name":"customer_name","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"customer_name.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"customer_name"}}}]', + }, + }; + + expect(migrationFn(input, savedObjectMigrationContext)).toEqual(expected); + }); + }); +}); diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts new file mode 100644 index 0000000000000..7a16386ea484c --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { flow, omit } from 'lodash'; +import { SavedObjectMigrationFn } from 'kibana/server'; + +const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ + ...doc, + attributes: { + ...doc.attributes, + type: doc.attributes.type || undefined, + typeMeta: doc.attributes.typeMeta || undefined, + }, +}); + +const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { + if (!doc.attributes.fields) return doc; + + const fieldsString = doc.attributes.fields; + const fields = JSON.parse(fieldsString) as any[]; + const migratedFields = fields.map(field => { + if (field.subType === 'multi') { + return { + ...omit(field, 'parent'), + subType: { multi: { parent: field.parent } }, + }; + } + + return field; + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + fields: JSON.stringify(migratedFields), + }, + }; +}; + +export const indexPatternSavedObjectTypeMigrations = { + '6.5.0': flow(migrateAttributeTypeAndAttributeTypeMeta), + '7.6.0': flow(migrateSubTypeAndParentFieldProperties), +}; diff --git a/src/plugins/data/server/saved_objects/index_patterns.ts b/src/plugins/data/server/saved_objects/index_patterns.ts new file mode 100644 index 0000000000000..9838071eee5a4 --- /dev/null +++ b/src/plugins/data/server/saved_objects/index_patterns.ts @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsType } from 'kibana/server'; +import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migrations'; + +export const indexPatternSavedObjectType: SavedObjectsType = { + name: 'index-pattern', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'indexPatternApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/management/kibana/index_patterns/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'management.kibana.index_patterns', + }; + }, + }, + mappings: { + properties: { + fieldFormatMap: { type: 'text' }, + fields: { type: 'text' }, + intervalName: { type: 'keyword' }, + notExpandable: { type: 'boolean' }, + sourceFilters: { type: 'text' }, + timeFieldName: { type: 'keyword' }, + title: { type: 'text' }, + type: { type: 'keyword' }, + typeMeta: { type: 'keyword' }, + }, + }, + migrations: indexPatternSavedObjectTypeMigrations, +}; diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts new file mode 100644 index 0000000000000..ff0a6cfde8113 --- /dev/null +++ b/src/plugins/data/server/saved_objects/query.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsType } from 'kibana/server'; + +export const querySavedObjectType: SavedObjectsType = { + name: 'query', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'search', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/discover?_a=(savedQuery:'${encodeURIComponent(obj.id)}')`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + mappings: { + properties: { + title: { type: 'text' }, + description: { type: 'text' }, + query: { + properties: { language: { type: 'keyword' }, query: { type: 'keyword', index: false } }, + }, + filters: { type: 'object', enabled: false }, + timefilter: { type: 'object', enabled: false }, + }, + }, + migrations: {}, +}; diff --git a/src/plugins/data/server/saved_objects/search.ts b/src/plugins/data/server/saved_objects/search.ts new file mode 100644 index 0000000000000..8b30ff7d08201 --- /dev/null +++ b/src/plugins/data/server/saved_objects/search.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsType } from 'kibana/server'; +import { searchSavedObjectTypeMigrations } from './search_migrations'; + +export const searchSavedObjectType: SavedObjectsType = { + name: 'search', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'discoverApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/objects/savedSearches/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/discover/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'discover.show', + }; + }, + }, + mappings: { + properties: { + columns: { type: 'keyword' }, + description: { type: 'text' }, + hits: { type: 'integer' }, + kibanaSavedObjectMeta: { + properties: { + searchSourceJSON: { type: 'text' }, + }, + }, + sort: { type: 'keyword' }, + title: { type: 'text' }, + version: { type: 'integer' }, + }, + }, + migrations: searchSavedObjectTypeMigrations, +}; diff --git a/src/plugins/data/server/saved_objects/search_migrations.test.ts b/src/plugins/data/server/saved_objects/search_migrations.test.ts new file mode 100644 index 0000000000000..7fdf2e14aefed --- /dev/null +++ b/src/plugins/data/server/saved_objects/search_migrations.test.ts @@ -0,0 +1,299 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectMigrationContext } from 'kibana/server'; +import { searchSavedObjectTypeMigrations } from './search_migrations'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration search', () => { + describe('7.0.0', () => { + const migrationFn = searchSavedObjectTypeMigrations['7.0.0']; + + test('skips errors when searchSourceJSON is null', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips errors when searchSourceJSON is undefined', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: undefined, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when searchSourceJSON is not a string', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: 123, + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when searchSourceJSON is invalid json', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: '{abc123}', + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('skips error when "index" and "filter" is missing from searchSourceJSON', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + }, + "id": "123", + "references": Array [], + "type": "search", +} +`); + }); + + test('extracts "index" attribute from doc', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + }, + "id": "123", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + ], + "type": "search", +} +`); + }); + + test('extracts index patterns from filter', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + foo: true, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + bar: true, + filter: [ + { + meta: { + foo: true, + index: 'my-index', + }, + }, + ], + }), + }, + }, + }; + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + }, + "id": "123", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + ], + "type": "search", +} +`); + }); + }); + + describe('7.4.0', function() { + const migrationFn = searchSavedObjectTypeMigrations['7.4.0']; + + test('transforms one dimensional sort arrays into two dimensional arrays', () => { + const doc = { + id: '123', + type: 'search', + attributes: { + sort: ['bytes', 'desc'], + }, + }; + + const expected = { + id: '123', + type: 'search', + attributes: { + sort: [['bytes', 'desc']], + }, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(expected); + }); + + test("doesn't modify search docs that already have two dimensional sort arrays", () => { + const doc = { + id: '123', + type: 'search', + attributes: { + sort: [['bytes', 'desc']], + }, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(doc); + }); + + test("doesn't modify search docs that have no sort array", () => { + const doc = { + id: '123', + type: 'search', + attributes: {}, + }; + + const migratedDoc = migrationFn(doc, savedObjectMigrationContext); + + expect(migratedDoc).toEqual(doc); + }); + }); +}); diff --git a/src/plugins/data/server/saved_objects/search_migrations.ts b/src/plugins/data/server/saved_objects/search_migrations.ts new file mode 100644 index 0000000000000..db545e52ce170 --- /dev/null +++ b/src/plugins/data/server/saved_objects/search_migrations.ts @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { flow, get } from 'lodash'; +import { SavedObjectMigrationFn } from 'kibana/server'; + +const migrateIndexPattern: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return doc; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc; + } + + if (searchSource.index && Array.isArray(doc.references)) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: 'index-pattern', + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index || !Array.isArray(doc.references)) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + + return doc; +}; + +const setNewReferences: SavedObjectMigrationFn = (doc, context) => { + doc.references = doc.references || []; + // Migrate index pattern + return migrateIndexPattern(doc, context); +}; + +const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { + const sort = get(doc, 'attributes.sort'); + if (!sort) return doc; + + // Don't do anything if we already have a two dimensional array + if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) { + return doc; + } + + return { + ...doc, + attributes: { + ...doc.attributes, + sort: [doc.attributes.sort], + }, + }; +}; + +export const searchSavedObjectTypeMigrations = { + '7.0.0': flow(setNewReferences), + '7.4.0': flow(migrateSearchSortToNestedArray), +}; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 46f90e3c6fc62..5ee19cd3df19f 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -34,6 +34,8 @@ import { import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; +import { searchSavedObjectType } from '../saved_objects'; + declare module 'kibana/server' { interface RequestHandlerContext { search?: IRouteHandlerSearchContext; @@ -53,6 +55,8 @@ export class SearchService implements Plugin { this.contextContainer = core.context.createContextContainer(); + core.savedObjects.registerType(searchSavedObjectType); + core.http.registerRouteHandlerContext<'search'>('search', context => { return createApi({ caller: context.core.elasticsearch.dataClient.callAsCurrentUser, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 666df2900c2c3..2a2d9bb414c14 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -733,7 +733,7 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:62:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:64:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index cf79ce17293d6..8e63ea7833327 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -1,7 +1,7 @@ { "id": "visualizations", "version": "kibana", - "server": false, + "server": true, "ui": true, "requiredPlugins": [ "expressions" diff --git a/src/plugins/visualizations/server/index.ts b/src/plugins/visualizations/server/index.ts new file mode 100644 index 0000000000000..80c10c3945d4c --- /dev/null +++ b/src/plugins/visualizations/server/index.ts @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { PluginInitializerContext } from '../../../core/server'; +import { VisualizationsPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new VisualizationsPlugin(initializerContext); +} + +export { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; diff --git a/src/plugins/visualizations/server/plugin.ts b/src/plugins/visualizations/server/plugin.ts new file mode 100644 index 0000000000000..79cce6b5867a1 --- /dev/null +++ b/src/plugins/visualizations/server/plugin.ts @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + Logger, +} from '../../../core/server'; + +import { visualizationSavedObjectType } from './saved_objects'; + +import { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; + +export class VisualizationsPlugin + implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('visualizations: Setup'); + + core.savedObjects.registerType(visualizationSavedObjectType); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('visualizations: Started'); + return {}; + } + + public stop() {} +} diff --git a/src/plugins/visualizations/server/saved_objects/index.ts b/src/plugins/visualizations/server/saved_objects/index.ts new file mode 100644 index 0000000000000..be75f63582450 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { visualizationSavedObjectType } from './visualization'; diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts new file mode 100644 index 0000000000000..9f4782f3ec730 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsType } from 'kibana/server'; +import { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; + +export const visualizationSavedObjectType: SavedObjectsType = { + name: 'visualization', + hidden: false, + namespaceAgnostic: false, + management: { + icon: 'visualizeApp', + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getEditUrl(obj) { + return `/management/kibana/objects/savedVisualizations/${encodeURIComponent(obj.id)}`; + }, + getInAppUrl(obj) { + return { + path: `/app/kibana#/visualize/edit/${encodeURIComponent(obj.id)}`, + uiCapabilitiesPath: 'visualize.show', + }; + }, + }, + mappings: { + properties: { + description: { type: 'text' }, + kibanaSavedObjectMeta: { properties: { searchSourceJSON: { type: 'text' } } }, + savedSearchRefName: { type: 'keyword' }, + title: { type: 'text' }, + uiStateJSON: { type: 'text' }, + version: { type: 'integer' }, + visState: { type: 'text' }, + }, + }, + migrations: visualizationSavedObjectTypeMigrations, +}; diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts new file mode 100644 index 0000000000000..02c114bad4e72 --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts @@ -0,0 +1,1356 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { visualizationSavedObjectTypeMigrations } from './visualization_migrations'; +import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'kibana/server'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migration visualization', () => { + describe('6.7.2', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['6.7.2']( + doc as Parameters[0], + savedObjectMigrationContext + ); + let doc: any; + + describe('date histogram time zone removal', () => { + beforeEach(() => { + doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + enabled: true, + id: '1', + params: { + // Doesn't make much sense but we want to test it's not removing it from anything else + time_zone: 'Europe/Berlin', + }, + schema: 'metric', + type: 'count', + }, + { + enabled: true, + id: '2', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + time_zone: 'Europe/Berlin', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '4', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '3', + params: { + customBucket: { + enabled: true, + id: '1-bucket', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + time_zone: 'Europe/Berlin', + useNormalizedEsInterval: true, + }, + type: 'date_histogram', + }, + customMetric: { + enabled: true, + id: '1-metric', + params: {}, + type: 'count', + }, + }, + schema: 'metric', + type: 'max_bucket', + }, + ], + }), + }, + } as Parameters[0]; + }); + + it('should remove time_zone from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[1]).not.toHaveProperty('params.time_zone'); + }); + + it('should not remove time_zone from non date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[0]).toHaveProperty('params.time_zone'); + }); + + it('should remove time_zone from nested aggregations', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); + }); + + it('should not fail on date histograms without a time_zone', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[2]).not.toHaveProperty('params.time_zone'); + }); + + it('should be able to apply the migration twice, since we need it for 6.7.2 and 7.0.1', () => { + const migratedDoc = migrate(doc); + const aggs = JSON.parse(migratedDoc.attributes.visState).aggs; + + expect(aggs[1]).not.toHaveProperty('params.time_zone'); + expect(aggs[0]).toHaveProperty('params.time_zone'); + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); + expect(aggs[2]).not.toHaveProperty('params.time_zone'); + }); + }); + }); + + describe('7.0.0', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.0.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (type: any, aggs: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ type, aggs }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + references: [], + }); + + it('does not throw error on empty object', () => { + const migratedDoc = migrate({ + attributes: { + visState: '{}', + }, + }); + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{}", + }, + "references": Array [], +} +`); + }); + + it('skips errors when searchSourceJSON is null', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: null, + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips errors when searchSourceJSON is undefined', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: undefined, + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when searchSourceJSON is not a string', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: 123, + }, + savedSearchId: '123', + }, + }; + + expect(migrate(doc)).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when searchSourceJSON is invalid json', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{abc123}', + }, + savedSearchId: '123', + }, + }; + + expect(migrate(doc)).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts "index" attribute from doc', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ bar: true, index: 'pattern*' }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts index patterns from the filter', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + bar: true, + filter: [ + { + meta: { index: 'my-index', foo: true }, + }, + ], + }), + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", +} +`); + }); + + it('extracts index patterns from controls', () => { + const doc = { + id: '1', + type: 'visualization', + attributes: { + foo: true, + visState: JSON.stringify({ + bar: false, + params: { + controls: [ + { + bar: true, + indexPattern: 'pattern*', + }, + { + foo: true, + }, + ], + }, + }), + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "foo": true, + "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "control_0_index_pattern", + "type": "index-pattern", + }, + ], + "type": "visualization", +} +`); + }); + + it('skips extracting savedSearchId when missing', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], +} +`); + }); + + it('extract savedSearchId from doc', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + savedSearchId: '123', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], +} +`); + }); + + it('delete savedSearchId when empty string in doc', () => { + const doc = { + id: '1', + attributes: { + visState: '{}', + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + savedSearchId: '', + }, + }; + const migratedDoc = migrate(doc); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], +} +`); + }); + + it('should return a new object if vis is table and has multiple split aggs', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + ]; + const tableDoc = generateDoc('table', aggs); + const expected = tableDoc; + const actual = migrate(tableDoc); + + expect(actual).not.toEqual(expected); + }); + + it('should not touch any vis that is not table', () => { + const pieDoc = generateDoc('pie', []); + const expected = pieDoc; + const actual = migrate(pieDoc); + + expect(actual).toEqual(expected); + }); + + it('should not change values in any vis that is not table', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'segment', + params: { hey: 'ya' }, + }, + ]; + const pieDoc = generateDoc('pie', aggs); + const expected = pieDoc; + const actual = migrate(pieDoc); + + expect(actual).toEqual(expected); + }); + + it('should not touch table vis if there are not multiple split aggs', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + ]; + const tableDoc = generateDoc('table', aggs); + const expected = tableDoc; + const actual = migrate(tableDoc); + + expect(actual).toEqual(expected); + }); + + it('should change all split aggs to `bucket` except the first', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + { + id: '4', + schema: 'bucket', + params: { heyyy: 'yaaa' }, + }, + ]; + const expected = ['metric', 'split', 'bucket', 'bucket']; + const migrated = migrate(generateDoc('table', aggs)); + const actual = JSON.parse(migrated.attributes.visState); + + expect(actual.aggs.map((agg: any) => agg.schema)).toEqual(expected); + }); + + it('should remove `rows` param from any aggs that are not `split`', () => { + const aggs = [ + { + id: '1', + schema: 'metric', + params: {}, + }, + { + id: '2', + schema: 'split', + params: { foo: 'bar', row: true }, + }, + { + id: '3', + schema: 'split', + params: { hey: 'ya', row: false }, + }, + ]; + const expected = [{}, { foo: 'bar', row: true }, { hey: 'ya' }]; + const migrated = migrate(generateDoc('table', aggs)); + const actual = JSON.parse(migrated.attributes.visState); + + expect(actual.aggs.map((agg: any) => agg.params)).toEqual(expected); + }); + + it('should throw with a reference to the doc name if something goes wrong', () => { + const doc = { + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: '!/// Intentionally malformed JSON ///!', + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }; + expect(() => migrate(doc)).toThrowError(/My Vis/); + }); + }); + + describe('7.2.0', () => { + describe('date histogram custom interval removal', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.2.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + let doc: any; + + beforeEach(() => { + doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + enabled: true, + id: '1', + params: { + customInterval: '1h', + }, + schema: 'metric', + type: 'count', + }, + { + enabled: true, + id: '2', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'auto', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '4', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'custom', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + schema: 'segment', + type: 'date_histogram', + }, + { + enabled: true, + id: '3', + params: { + customBucket: { + enabled: true, + id: '1-bucket', + params: { + customInterval: '2h', + drop_partials: false, + extended_bounds: {}, + field: 'timestamp', + interval: 'custom', + min_doc_count: 1, + useNormalizedEsInterval: true, + }, + type: 'date_histogram', + }, + customMetric: { + enabled: true, + id: '1-metric', + params: {}, + type: 'count', + }, + }, + schema: 'metric', + type: 'max_bucket', + }, + ], + }), + }, + }; + }); + + it('should remove customInterval from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[1]).not.toHaveProperty('params.customInterval'); + }); + + it('should not change interval from date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[1].params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[1].params.interval + ); + }); + + it('should not remove customInterval from non date_histogram aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[0]).toHaveProperty('params.customInterval'); + }); + + it('should set interval with customInterval value and remove customInterval when interval equals "custom"', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[2].params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[2].params.customInterval + ); + expect(aggs[2]).not.toHaveProperty('params.customInterval'); + }); + + it('should remove customInterval from nested aggregations', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); + }); + + it('should remove customInterval from nested aggregations and set interval with customInterval value', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3].params.customBucket.params.interval).toBe( + JSON.parse(doc.attributes.visState).aggs[3].params.customBucket.params.customInterval + ); + expect(aggs[3]).not.toHaveProperty('params.customBucket.params.customInterval'); + }); + + it('should not fail on date histograms without a customInterval', () => { + const migratedDoc = migrate(doc); + const { aggs } = JSON.parse(migratedDoc.attributes.visState); + + expect(aggs[3]).not.toHaveProperty('params.customInterval'); + }); + }); + }); + + describe('7.3.0', () => { + const logMsgArr: string[] = []; + const logger = ({ + log: { + warn: (msg: string) => logMsgArr.push(msg), + }, + } as unknown) as SavedObjectMigrationContext; + + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.0']( + doc as Parameters[0], + logger + ); + + it('migrates type = gauge verticalSplit: false to alignment: vertical', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: false } } }), + }, + }); + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", + }, +} +`); + }); + + it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge', params: { gauge: { verticalSplit: true } } }), + }, + }); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", + }, +} +`); + }); + + it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { + const migratedDoc = migrate({ + attributes: { + visState: JSON.stringify({ type: 'gauge' }), + }, + }); + + expect(migratedDoc).toMatchInlineSnapshot(` +Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\"}", + }, +} +`); + expect(logMsgArr).toMatchInlineSnapshot(` +Array [ + "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", + "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", +] +`); + }); + + describe('filters agg query migration', () => { + const doc = { + attributes: { + visState: JSON.stringify({ + aggs: [ + { + type: 'filters', + params: { + filters: [ + { + input: { + query: 'response:200', + }, + label: '', + }, + { + input: { + query: 'response:404', + }, + label: 'bad response', + }, + { + input: { + query: { + exists: { + field: 'phpmemory', + }, + }, + }, + label: '', + }, + ], + }, + }, + ], + }), + }, + }; + + it('should add language property to filters without one, assuming lucene', () => { + const migrationResult = migrate(doc); + + expect(migrationResult).toEqual({ + attributes: { + visState: JSON.stringify({ + aggs: [ + { + type: 'filters', + params: { + filters: [ + { + input: { + query: 'response:200', + language: 'lucene', + }, + label: '', + }, + { + input: { + query: 'response:404', + language: 'lucene', + }, + label: 'bad response', + }, + { + input: { + query: { + exists: { + field: 'phpmemory', + }, + }, + language: 'lucene', + }, + label: '', + }, + ], + }, + }, + ], + }), + }, + }); + }); + }); + + describe('replaceMovAvgToMovFn()', () => { + let doc: any; + + beforeEach(() => { + doc = { + attributes: { + title: 'VIS', + visState: `{"title":"VIS","type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417", + "type":"timeseries","series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","color":"rgba(0,156,224,1)", + "split_mode":"terms","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417","type":"count", + "numerator":"FlightDelay:true"},{"settings":"","minimize":0,"window":5,"model": + "holt_winters","id":"23054fe0-8915-11e9-9b86-d3f94982620f","type":"moving_average","field": + "61ca57f2-469d-11e7-af02-69e470af7417","predict":1}],"separate_axis":0,"axis_position":"right", + "formatter":"number","chart_type":"line","line_width":"2","point_size":"0","fill":0.5,"stacked":"none", + "label":"Percent Delays","terms_size":"2","terms_field":"OriginCityName"}],"time_field":"timestamp", + "index_pattern":"kibana_sample_data_flights","interval":">=12h","axis_position":"left","axis_formatter": + "number","show_legend":1,"show_grid":1,"annotations":[{"fields":"FlightDelay,Cancelled,Carrier", + "template":"{{Carrier}}: Flight Delayed and Cancelled!","index_pattern":"kibana_sample_data_flights", + "query_string":"FlightDelay:true AND Cancelled:true","id":"53b7dff0-4c89-11e8-a66a-6989ad5a0a39", + "color":"rgba(0,98,177,1)","time_field":"timestamp","icon":"fa-exclamation-triangle", + "ignore_global_filters":1,"ignore_panel_filters":1,"hidden":true}],"legend_position":"bottom", + "axis_scale":"normal","default_index_pattern":"kibana_sample_data_flights","default_timefield":"timestamp"}, + "aggs":[]}`, + }, + migrationVersion: { + visualization: '7.2.0', + }, + type: 'visualization', + }; + }); + + test('should add some necessary moving_fn fields', () => { + const migratedDoc = migrate(doc); + const visState = JSON.parse(migratedDoc.attributes.visState); + const metric = visState.params.series[0].metrics[1]; + + expect(metric).toHaveProperty('model_type'); + expect(metric).toHaveProperty('alpha'); + expect(metric).toHaveProperty('beta'); + expect(metric).toHaveProperty('gamma'); + expect(metric).toHaveProperty('period'); + expect(metric).toHaveProperty('multiplicative'); + }); + }); + }); + + describe('7.3.0 tsvb', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + it('should change series item filters from a string into an object', () => { + const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[0].filter).toHaveProperty('query'); + expect(series[0].filter).toHaveProperty('language'); + }); + it('should not change a series item filter string in the object after migration', () => { + const markdownParams = { + type: 'markdown', + series: [ + { + filter: 'Filter Bytes Test:>1000', + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + }; + const markdownDoc = generateDoc(markdownParams); + const migratedMarkdownDoc = migrate(markdownDoc); + const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series; + + expect(markdownSeries[0].filter.query).toBe( + JSON.parse(markdownDoc.attributes.visState).params.series[0].filter + ); + expect(markdownSeries[0].split_filters[0].filter.query).toBe( + JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter + ); + }); + + it('should change series item filters from a string into an object for all filters', () => { + const params = { + type: 'timeseries', + filter: 'bytes:>1000', + series: [ + { + filter: 'Filter Bytes Test:>1000', + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + annotations: [{ query_string: 'bytes:>1000' }], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + }); + + it('should not fail on a metric visualization without a filter in a series item', () => { + const params = { type: 'metric', series: [{}, {}, {}] }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[2]).not.toHaveProperty('filter.query'); + }); + + it('should not migrate a visualization of unknown type', () => { + const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] }; + const doc = generateDoc(params); + const migratedDoc = migrate(doc); + const series = JSON.parse(migratedDoc.attributes.visState).params.series; + + expect(series[0].filter).toEqual(params.series[0].filter); + }); + }); + + describe('7.3.1', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.3.1']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + it('should migrate filters agg query string queries', () => { + const state = { + aggs: [ + { type: 'count', params: {} }, + { + type: 'filters', + params: { + filters: [ + { + input: { + query: { + query_string: { query: 'machine.os.keyword:"win 8"' }, + }, + }, + }, + ], + }, + }, + ], + }; + const expected = { + aggs: [ + { type: 'count', params: {} }, + { + type: 'filters', + params: { + filters: [{ input: { query: 'machine.os.keyword:"win 8"' } }], + }, + }, + ], + }; + const migratedDoc = migrate({ attributes: { visState: JSON.stringify(state) } }); + + expect(migratedDoc).toEqual({ attributes: { visState: JSON.stringify(expected) } }); + }); + }); + + describe('7.4.2 tsvb split_filters migration', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.4.2']( + doc as Parameters[0], + savedObjectMigrationContext + ); + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + + it('should change series item filters from a string into an object for all filters', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(Object.keys(timeSeriesParams.filter)).toEqual( + expect.arrayContaining(['query', 'language']) + ); + expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ + query: 'bytes:>1000', + language: 'lucene', + }); + }); + + it('should change series item split filters when there is no filter item', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [{ filter: 'bytes:>1000' }], + }, + ], + annotations: [ + { + query_string: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters[0].filter).toEqual({ + query: 'bytes:>1000', + language: 'lucene', + }); + }); + + it('should not convert split_filters to objects if there are no split filter filters', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [], + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters).not.toHaveProperty('query'); + }); + + it('should do nothing if a split_filter is already a query:language object', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [ + { + split_filters: [ + { + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }, + ], + annotations: [ + { + query_string: { + query: 'bytes:>1000', + language: 'lucene', + }, + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(timeSeriesParams.series[0].split_filters[0].filter.query).toEqual('bytes:>1000'); + expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); + }); + }); +}); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts new file mode 100644 index 0000000000000..9ee355cbb23cf --- /dev/null +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -0,0 +1,574 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectMigrationFn } from 'kibana/server'; +import { cloneDeep, get, omit, has, flow } from 'lodash'; + +const migrateIndexPattern: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + if (typeof searchSourceJSON !== 'string') { + return doc; + } + let searchSource; + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + return doc; + } + + if (searchSource.index && Array.isArray(doc.references)) { + searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + doc.references.push({ + name: searchSource.indexRefName, + type: 'index-pattern', + id: searchSource.index, + }); + delete searchSource.index; + } + if (searchSource.filter) { + searchSource.filter.forEach((filterRow: any, i: number) => { + if (!filterRow.meta || !filterRow.meta.index || !Array.isArray(doc.references)) { + return; + } + filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + doc.references.push({ + name: filterRow.meta.indexRefName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + delete filterRow.meta.index; + }); + } + + doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + + return doc; +}; + +// [TSVB] Migrate percentile-rank aggregation (value -> values) +const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series') || []; + + series.forEach(part => { + (part.metrics || []).forEach((metric: any) => { + if (metric.type === 'percentile_rank' && has(metric, 'value')) { + metric.values = [metric.value]; + + delete metric.value; + } + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + +// Migrate date histogram aggregation (remove customInterval) +const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type === 'date_histogram' && agg.params) { + if (agg.params.interval === 'custom') { + agg.params.interval = agg.params.customInterval; + } + delete agg.params.customInterval; + } + + if ( + get(agg, 'params.customBucket.type', null) === 'date_histogram' && + agg.params.customBucket.params + ) { + if (agg.params.customBucket.params.interval === 'custom') { + agg.params.customBucket.params.interval = agg.params.customBucket.params.customInterval; + } + delete agg.params.customBucket.params.customInterval; + } + }); + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + +const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + // We're checking always for the existance of agg.params here. This should always exist, but better + // be safe then sorry during migrations. + if (agg.type === 'date_histogram' && agg.params) { + delete agg.params.time_zone; + } + + if ( + get(agg, 'params.customBucket.type', null) === 'date_histogram' && + agg.params.customBucket.params + ) { + delete agg.params.customBucket.params.time_zone; + } + }); + doc.attributes.visState = JSON.stringify(visState); + } + } + return doc; +}; + +// migrate gauge verticalSplit to alignment +// https://github.com/elastic/kibana/issues/34636 +const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + if (visState && visState.type === 'gauge' && !visState.params.gauge.alignment) { + visState.params.gauge.alignment = visState.params.gauge.verticalSplit + ? 'vertical' + : 'horizontal'; + delete visState.params.gauge.verticalSplit; + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + logger.log.warn(`Exception @ migrateGaugeVerticalSplitToAlignment! ${e}`); + logger.log.warn(`Exception @ migrateGaugeVerticalSplitToAlignment! Payload: ${visStateJSON}`); + } + } + return doc; +}; +// Migrate filters (string -> { query: string, language: lucene }) +/* + Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters. + In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself. + We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene. + For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object. + Path to the series array is thus: + attributes.visState. +*/ +const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { + // Migrate filters + // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly + const newDoc = cloneDeep(doc); + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const visType = get(visState, 'params.type'); + const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; + if (tsvbTypes.indexOf(visType) === -1) { + // skip + return doc; + } + // migrate the params fitler + const params: any = get(visState, 'params'); + if (params.filter && typeof params.filter === 'string') { + const paramsFilterObject = { + query: params.filter, + language: 'lucene', + }; + params.filter = paramsFilterObject; + } + + // migrate the annotations query string: + const annotations: any[] = get(visState, 'params.annotations') || []; + annotations.forEach(item => { + if (!item.query_string) { + // we don't need to transform anything if there isn't a filter at all + return; + } + if (typeof item.query_string === 'string') { + const itemQueryStringObject = { + query: item.query_string, + language: 'lucene', + }; + item.query_string = itemQueryStringObject; + } + }); + // migrate the series filters + const series: any[] = get(visState, 'params.series') || []; + + series.forEach(item => { + if (!item.filter) { + // we don't need to transform anything if there isn't a filter at all + return; + } + // series item filter + if (typeof item.filter === 'string') { + const itemfilterObject = { + query: item.filter, + language: 'lucene', + }; + item.filter = itemfilterObject; + } + // series item split filters filter + if (item.split_filters) { + const splitFilters: any[] = get(item, 'split_filters') || []; + splitFilters.forEach(filter => { + if (!filter.filter) { + // we don't need to transform anything if there isn't a filter at all + return; + } + if (typeof filter.filter === 'string') { + const filterfilterObject = { + query: filter.filter, + language: 'lucene', + }; + filter.filter = filterfilterObject; + } + }); + } + }); + newDoc.attributes.visState = JSON.stringify(visState); + } + } + return newDoc; +}; + +const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { + // Migrate split_filters in TSVB objects that weren't migrated in 7.3 + // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly + const newDoc = cloneDeep(doc); + const visStateJSON = get(doc, 'attributes.visState'); + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const visType = get(visState, 'params.type'); + const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries']; + if (tsvbTypes.indexOf(visType) === -1) { + // skip + return doc; + } + // migrate the series split_filter filters + const series: any[] = get(visState, 'params.series') || []; + series.forEach(item => { + // series item split filters filter + if (item.split_filters) { + const splitFilters: any[] = get(item, 'split_filters') || []; + if (splitFilters.length > 0) { + // only transform split_filter filters if we have filters + splitFilters.forEach(filter => { + if (typeof filter.filter === 'string') { + const filterfilterObject = { + query: filter.filter, + language: 'lucene', + }; + filter.filter = filterfilterObject; + } + }); + } + } + }); + newDoc.attributes.visState = JSON.stringify(visState); + } + } + return newDoc; +}; + +const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type !== 'filters') return; + + agg.params.filters.forEach((filter: any) => { + if (filter.input.language) return filter; + filter.input.language = 'lucene'; + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + } + return doc; +}; + +const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series', []); + + series.forEach(part => { + if (part.metrics && Array.isArray(part.metrics)) { + part.metrics.forEach((metric: any) => { + if (metric.type === 'moving_average') { + metric.model_type = metric.model; + metric.alpha = get(metric, 'settings.alpha', 0.3); + metric.beta = get(metric, 'settings.beta', 0.1); + metric.gamma = get(metric, 'settings.gamma', 0.3); + metric.period = get(metric, 'settings.period', 1); + metric.multiplicative = get(metric, 'settings.type') === 'mult'; + + delete metric.minimize; + delete metric.model; + delete metric.settings; + delete metric.predict; + } + }); + } + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + logger.log.warn(`Exception @ replaceMovAvgToMovFn! ${e}`); + logger.log.warn(`Exception @ replaceMovAvgToMovFn! Payload: ${visStateJSON}`); + } + } + + return doc; +}; + +const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + try { + const visState = JSON.parse(visStateJSON); + + if (visState && visState.aggs) { + visState.aggs.forEach((agg: any) => { + if (agg.type !== 'filters') return doc; + + agg.params.filters.forEach((filter: any) => { + if (filter.input.query.query_string) { + filter.input.query = filter.input.query.query_string.query; + } + }); + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + } + return doc; +}; + +const addDocReferences: SavedObjectMigrationFn = doc => ({ + ...doc, + references: doc.references || [], +}); + +const migrateSavedSearch: SavedObjectMigrationFn = doc => { + const savedSearchId = get(doc, 'attributes.savedSearchId'); + + if (savedSearchId && doc.references) { + doc.references.push({ + type: 'search', + name: 'search_0', + id: savedSearchId, + }); + doc.attributes.savedSearchRefName = 'search_0'; + } + + delete doc.attributes.savedSearchId; + + return doc; +}; + +const migrateControls: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + + if (visStateJSON) { + let visState; + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState) { + const controls: any[] = get(visState, 'params.controls') || []; + controls.forEach((control, i) => { + if (!control.indexPattern || !doc.references) { + return; + } + control.indexPatternRefName = `control_${i}_index_pattern`; + doc.references.push({ + name: control.indexPatternRefName, + type: 'index-pattern', + id: control.indexPattern, + }); + delete control.indexPattern; + }); + doc.attributes.visState = JSON.stringify(visState); + } + } + + return doc; +}; + +const migrateTableSplits: SavedObjectMigrationFn = doc => { + try { + const visState = JSON.parse(doc.attributes.visState); + if (get(visState, 'type') !== 'table') { + return doc; // do nothing; we only want to touch tables + } + + let splitCount = 0; + visState.aggs = visState.aggs.map((agg: any) => { + if (agg.schema !== 'split') { + return agg; + } + + splitCount++; + if (splitCount === 1) { + return agg; // leave the first split agg unchanged + } + agg.schema = 'bucket'; + // the `row` param is exclusively used by split aggs, so we remove it + agg.params = omit(agg.params, ['row']); + return agg; + }); + + if (splitCount <= 1) { + return doc; // do nothing; we only want to touch tables with multiple split aggs + } + + const newDoc = cloneDeep(doc); + newDoc.attributes.visState = JSON.stringify(visState); + + return newDoc; + } catch (e) { + throw new Error(`Failure attempting to migrate saved object '${doc.attributes.title}' - ${e}`); + } +}; + +export const visualizationSavedObjectTypeMigrations = { + /** + * We need to have this migration twice, once with a version prior to 7.0.0 once with a version + * after it. The reason for that is, that this migration has been introduced once 7.0.0 was already + * released. Thus a user who already had 7.0.0 installed already got the 7.0.0 migrations below running, + * so we need a version higher than that. But this fix was backported to the 6.7 release, meaning if we + * would only have the 7.0.1 migration in here a user on the 6.7 release will migrate their saved objects + * to the 7.0.1 state, and thus when updating their Kibana to 7.0, will never run the 7.0.0 migrations introduced + * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 + * only contained the 6.7.2 migration and not the 7.0.1 migration. + */ + '6.7.2': flow(removeDateHistogramTimeZones), + '7.0.0': flow( + addDocReferences, + migrateIndexPattern, + migrateSavedSearch, + migrateControls, + migrateTableSplits + ), + '7.0.1': flow(removeDateHistogramTimeZones), + '7.2.0': flow( + migratePercentileRankAggregation, + migrateDateHistogramAggregation + ), + '7.3.0': flow( + migrateGaugeVerticalSplitToAlignment, + transformFilterStringToQueryObject, + migrateFiltersAggQuery, + replaceMovAvgToMovFn + ), + '7.3.1': flow(migrateFiltersAggQueryStringQueries), + '7.4.2': flow(transformSplitFiltersStringToQueryObject), +}; diff --git a/src/plugins/visualizations/server/types.ts b/src/plugins/visualizations/server/types.ts new file mode 100644 index 0000000000000..6924edb29627d --- /dev/null +++ b/src/plugins/visualizations/server/types.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VisualizationsPluginStart {} From 271c9597bed0487a899821538d183d91342e7461 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 16 Mar 2020 14:33:03 +0200 Subject: [PATCH 5/8] [SIEM][CASE] Change configuration button (#60229) * Change button * Make URLs constants --- .../pages/case/components/all_cases/index.tsx | 18 +++++++++--------- .../siem/public/pages/case/translations.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx index 1349246494ec8..7b655999ace09 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx @@ -8,7 +8,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiBasicTable, EuiButton, - EuiButtonIcon, EuiContextMenuPanel, EuiEmptyPrompt, EuiFlexGroup, @@ -45,6 +44,9 @@ import { OpenClosedStats } from '../open_closed_stats'; import { getActions } from './actions'; import { CasesTableFilters } from './table_filters'; +const CONFIGURE_CASES_URL = getConfigureCasesUrl(); +const CREATE_CASE_URL = getCreateCaseUrl(); + const Div = styled.div` margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; `; @@ -259,16 +261,14 @@ export const AllCases = React.memo(() => { /> - - {i18n.CREATE_TITLE} + + {i18n.CONFIGURE_CASES_BUTTON} - + + {i18n.CREATE_TITLE} + @@ -325,7 +325,7 @@ export const AllCases = React.memo(() => { titleSize="xs" body={i18n.NO_CASES_BODY} actions={ - + {i18n.ADD_NEW_CASE} } diff --git a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts index 9c0287a56ccbc..6ef412d408ae5 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/translations.ts @@ -120,7 +120,7 @@ export const CONFIGURE_CASES_PAGE_TITLE = i18n.translate( ); export const CONFIGURE_CASES_BUTTON = i18n.translate('xpack.siem.case.configureCasesButton', { - defaultMessage: 'Configure cases', + defaultMessage: 'Edit third-party connection', }); export const ADD_COMMENT = i18n.translate('xpack.siem.case.caseView.comment.addComment', { From dd7531deb4c83a2711b0d4bc4871acea425eb8cf Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 16 Mar 2020 14:30:20 +0100 Subject: [PATCH 6/8] Add UiSettings validation & Kibana default route redirection (#59694) * add schema to ui settings params * add validation for defaults and overrides * validate in ui settings client * ui settings routes validation * clean up tests * use schema for defaultRoutes * move URL redirection to NP * fix spaces test * update docs * update kbn pm * fix karma test * fix tests * address comments * get rid of getDEfaultRoute * regen docs * fix tests * fix enter-spaces test * validate on relative url format * update i18n * fix enter-spoace test * move relative url validation to utils * add CoreApp containing application logic * extract public uiSettings params in a separate type * make schema required * update docs --- ...in-core-public.iuisettingsclient.getall.md | 2 +- ...na-plugin-core-public.iuisettingsclient.md | 2 +- .../core/public/kibana-plugin-core-public.md | 1 + ...ugin-core-public.publicuisettingsparams.md | 13 ++ ...ana-plugin-core-public.uisettingsparams.md | 5 +- ...gin-core-public.uisettingsparams.schema.md | 11 ++ ...ugin-core-public.uisettingsparams.value.md | 2 +- ...-server.iuisettingsclient.getregistered.md | 2 +- ...na-plugin-core-server.iuisettingsclient.md | 2 +- .../core/server/kibana-plugin-core-server.md | 1 + ...ugin-core-server.publicuisettingsparams.md | 13 ++ ...ana-plugin-core-server.uisettingsparams.md | 5 +- ...gin-core-server.uisettingsparams.schema.md | 11 ++ ...ugin-core-server.uisettingsparams.value.md | 2 +- .../src/errors/validation_error.ts | 3 + .../src/kbn_client/kbn_client_ui_settings.ts | 4 +- packages/kbn-pm/dist/index.js | 4 +- src/core/public/index.ts | 2 +- src/core/public/public.api.md | 16 +- src/core/public/types.ts | 1 + .../ui_settings_api.test.ts.snap | 8 +- src/core/public/ui_settings/types.ts | 6 +- .../ui_settings/ui_settings_api.test.ts | 2 +- .../public/ui_settings/ui_settings_api.ts | 10 +- .../public/ui_settings/ui_settings_client.ts | 8 +- src/core/server/core_app/core_app.ts | 52 ++++++ src/core/server/core_app/index.ts | 20 +++ .../default_route_provider_config.test.ts | 90 ++++++++++ src/core/server/index.ts | 1 + src/core/server/server.api.md | 11 +- src/core/server/server.ts | 15 +- src/core/server/ui_settings/index.ts | 1 + .../integration_tests/routes.test.ts | 66 ++++++++ src/core/server/ui_settings/routes/set.ts | 4 +- .../server/ui_settings/routes/set_many.ts | 4 +- src/core/server/ui_settings/types.ts | 5 +- .../ui_settings/ui_settings_client.test.ts | 159 ++++++++++++++++-- .../server/ui_settings/ui_settings_client.ts | 73 +++++--- .../ui_settings/ui_settings_service.test.ts | 42 +++++ .../server/ui_settings/ui_settings_service.ts | 20 +++ src/core/types/ui_settings.ts | 19 ++- src/core/utils/url.test.ts | 16 +- src/core/utils/url.ts | 16 ++ .../kibana/ui_setting_defaults.js | 24 ++- .../tests_bundle/tests_entry_template.js | 11 +- src/legacy/server/http/index.js | 11 -- .../default_route_provider.test.ts | 87 ---------- .../default_route_provider_config.test.ts | 54 ------ .../http/setup_default_route_provider.ts | 74 -------- src/legacy/server/kbn_server.d.ts | 1 - .../management_app/advanced_settings.test.tsx | 10 +- .../lib/to_editable_config.test.ts | 6 +- .../management_app/lib/to_editable_config.ts | 4 +- .../public/management_app/types.ts | 11 +- src/plugins/data/public/public.api.md | 2 +- src/test_utils/kbn_server.ts | 1 + test/api_integration/apis/index.js | 1 - .../ui_settings_plugin/server/plugin.ts | 3 +- .../spaces/server/routes/views/enter_space.ts | 8 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../functional/apps/spaces/enter_space.ts | 32 ++-- .../es_archives/spaces/enter_space/data.json | 4 +- 63 files changed, 724 insertions(+), 372 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md create mode 100644 docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md create mode 100644 src/core/server/core_app/core_app.ts create mode 100644 src/core/server/core_app/index.ts create mode 100644 src/core/server/core_app/integration_tests/default_route_provider_config.test.ts create mode 100644 src/core/server/ui_settings/integration_tests/routes.test.ts delete mode 100644 src/legacy/server/http/integration_tests/default_route_provider.test.ts delete mode 100644 src/legacy/server/http/integration_tests/default_route_provider_config.test.ts delete mode 100644 src/legacy/server/http/setup_default_route_provider.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md index 805ac57b2fb9a..004979977376e 100644 --- a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md +++ b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.getall.md @@ -9,5 +9,5 @@ Gets the metadata about all uiSettings, including the type, default value, and u Signature: ```typescript -getAll: () => Readonly>; +getAll: () => Readonly>; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md index da566ed25cff5..87ef5784a6c6d 100644 --- a/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md +++ b/docs/development/core/public/kibana-plugin-core-public.iuisettingsclient.md @@ -18,7 +18,7 @@ export interface IUiSettingsClient | --- | --- | --- | | [get](./kibana-plugin-core-public.iuisettingsclient.get.md) | <T = any>(key: string, defaultOverride?: T) => T | Gets the value for a specific uiSetting. If this setting has no user-defined value then the defaultOverride parameter is returned (and parsed if setting is of type "json" or "number). If the parameter is not defined and the key is not registered by any plugin then an error is thrown, otherwise reads the default value defined by a plugin. | | [get$](./kibana-plugin-core-public.iuisettingsclient.get_.md) | <T = any>(key: string, defaultOverride?: T) => Observable<T> | Gets an observable of the current value for a config key, and all updates to that config key in the future. Providing a defaultOverride argument behaves the same as it does in \#get() | -| [getAll](./kibana-plugin-core-public.iuisettingsclient.getall.md) | () => Readonly<Record<string, UiSettingsParams & UserProvidedValues>> | Gets the metadata about all uiSettings, including the type, default value, and user value for each key. | +| [getAll](./kibana-plugin-core-public.iuisettingsclient.getall.md) | () => Readonly<Record<string, PublicUiSettingsParams & UserProvidedValues>> | Gets the metadata about all uiSettings, including the type, default value, and user value for each key. | | [getSaved$](./kibana-plugin-core-public.iuisettingsclient.getsaved_.md) | <T = any>() => Observable<{
key: string;
newValue: T;
oldValue: T;
}> | Returns an Observable that notifies subscribers of each update to the uiSettings, including the key, newValue, and oldValue of the setting that changed. | | [getUpdate$](./kibana-plugin-core-public.iuisettingsclient.getupdate_.md) | <T = any>() => Observable<{
key: string;
newValue: T;
oldValue: T;
}> | Returns an Observable that notifies subscribers of each update to the uiSettings, including the key, newValue, and oldValue of the setting that changed. | | [getUpdateErrors$](./kibana-plugin-core-public.iuisettingsclient.getupdateerrors_.md) | () => Observable<Error> | Returns an Observable that notifies subscribers of each error while trying to update the settings, containing the actual Error class. | diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index bafc2eb3a4bc9..a9fbaa25ea150 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -147,6 +147,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [MountPoint](./kibana-plugin-core-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. | | [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The plugin export at the root of a plugin's public directory should conform to this interface. | | [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | | +| [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. | | [RecursiveReadonly](./kibana-plugin-core-public.recursivereadonly.md) | | | [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value | | [SavedObjectAttributeSingle](./kibana-plugin-core-public.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | diff --git a/docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md b/docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md new file mode 100644 index 0000000000000..678a69289ff23 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.publicuisettingsparams.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) + +## PublicUiSettingsParams type + +A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. + +Signature: + +```typescript +export declare type PublicUiSettingsParams = Omit; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md index 00f1c0f0deca5..e7facb4a109cd 100644 --- a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md @@ -9,7 +9,7 @@ UiSettings parameters defined by the plugins. Signature: ```typescript -export interface UiSettingsParams +export interface UiSettingsParams ``` ## Properties @@ -24,7 +24,8 @@ export interface UiSettingsParams | [options](./kibana-plugin-core-public.uisettingsparams.options.md) | string[] | array of permitted values for this setting | | [readonly](./kibana-plugin-core-public.uisettingsparams.readonly.md) | boolean | a flag indicating that value cannot be changed | | [requiresPageReload](./kibana-plugin-core-public.uisettingsparams.requirespagereload.md) | boolean | a flag indicating whether new value applying requires page reloading | +| [schema](./kibana-plugin-core-public.uisettingsparams.schema.md) | Type<T> | | | [type](./kibana-plugin-core-public.uisettingsparams.type.md) | UiSettingsType | defines a type of UI element [UiSettingsType](./kibana-plugin-core-public.uisettingstype.md) | | [validation](./kibana-plugin-core-public.uisettingsparams.validation.md) | ImageValidation | StringValidation | | -| [value](./kibana-plugin-core-public.uisettingsparams.value.md) | SavedObjectAttribute | default value to fall back to if a user doesn't provide any | +| [value](./kibana-plugin-core-public.uisettingsparams.value.md) | T | default value to fall back to if a user doesn't provide any | diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md new file mode 100644 index 0000000000000..f90d5161f96a9 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.schema.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) > [schema](./kibana-plugin-core-public.uisettingsparams.schema.md) + +## UiSettingsParams.schema property + +Signature: + +```typescript +schema: Type; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md index 8775588290d70..2740f169eeecb 100644 --- a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.value.md @@ -9,5 +9,5 @@ default value to fall back to if a user doesn't provide any Signature: ```typescript -value?: SavedObjectAttribute; +value?: T; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md index 2ca6b4cbe1589..71a2bbf88472e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md +++ b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.getregistered.md @@ -9,5 +9,5 @@ Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-ser Signature: ```typescript -getRegistered: () => Readonly>; +getRegistered: () => Readonly>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md index 42fcc81419cbe..af99b5e5bb215 100644 --- a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md @@ -18,7 +18,7 @@ export interface IUiSettingsClient | --- | --- | --- | | [get](./kibana-plugin-core-server.iuisettingsclient.get.md) | <T = any>(key: string) => Promise<T> | Retrieves uiSettings values set by the user with fallbacks to default values if not specified. | | [getAll](./kibana-plugin-core-server.iuisettingsclient.getall.md) | <T = any>() => Promise<Record<string, T>> | Retrieves a set of all uiSettings values set by the user with fallbacks to default values if not specified. | -| [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | () => Readonly<Record<string, UiSettingsParams>> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | +| [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | () => Readonly<Record<string, PublicUiSettingsParams>> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | | [getUserProvided](./kibana-plugin-core-server.iuisettingsclient.getuserprovided.md) | <T = any>() => Promise<Record<string, UserProvidedValues<T>>> | Retrieves a set of all uiSettings values set by the user. | | [isOverridden](./kibana-plugin-core-server.iuisettingsclient.isoverridden.md) | (key: string) => boolean | Shows whether the uiSettings value set by the user. | | [remove](./kibana-plugin-core-server.iuisettingsclient.remove.md) | (key: string) => Promise<void> | Removes uiSettings value by key. | diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 8a88329031e1f..54cf496b2d6af 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -232,6 +232,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PluginInitializer](./kibana-plugin-core-server.plugininitializer.md) | The plugin export at the root of a plugin's server directory should conform to this interface. | | [PluginName](./kibana-plugin-core-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. | | [PluginOpaqueId](./kibana-plugin-core-server.pluginopaqueid.md) | | +| [PublicUiSettingsParams](./kibana-plugin-core-server.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) exposed to the client-side. | | [RecursiveReadonly](./kibana-plugin-core-server.recursivereadonly.md) | | | [RedirectResponseOptions](./kibana-plugin-core-server.redirectresponseoptions.md) | HTTP response parameters for redirection response | | [RequestHandler](./kibana-plugin-core-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) functions. | diff --git a/docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md b/docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md new file mode 100644 index 0000000000000..4ccc91fbe1f74 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.publicuisettingsparams.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PublicUiSettingsParams](./kibana-plugin-core-server.publicuisettingsparams.md) + +## PublicUiSettingsParams type + +A sub-set of [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) exposed to the client-side. + +Signature: + +```typescript +export declare type PublicUiSettingsParams = Omit; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md index fe6e5d956f3e2..f134decb5102b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md @@ -9,7 +9,7 @@ UiSettings parameters defined by the plugins. Signature: ```typescript -export interface UiSettingsParams +export interface UiSettingsParams ``` ## Properties @@ -24,7 +24,8 @@ export interface UiSettingsParams | [options](./kibana-plugin-core-server.uisettingsparams.options.md) | string[] | array of permitted values for this setting | | [readonly](./kibana-plugin-core-server.uisettingsparams.readonly.md) | boolean | a flag indicating that value cannot be changed | | [requiresPageReload](./kibana-plugin-core-server.uisettingsparams.requirespagereload.md) | boolean | a flag indicating whether new value applying requires page reloading | +| [schema](./kibana-plugin-core-server.uisettingsparams.schema.md) | Type<T> | | | [type](./kibana-plugin-core-server.uisettingsparams.type.md) | UiSettingsType | defines a type of UI element [UiSettingsType](./kibana-plugin-core-server.uisettingstype.md) | | [validation](./kibana-plugin-core-server.uisettingsparams.validation.md) | ImageValidation | StringValidation | | -| [value](./kibana-plugin-core-server.uisettingsparams.value.md) | SavedObjectAttribute | default value to fall back to if a user doesn't provide any | +| [value](./kibana-plugin-core-server.uisettingsparams.value.md) | T | default value to fall back to if a user doesn't provide any | diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md new file mode 100644 index 0000000000000..f181fbd309b7f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.schema.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) > [schema](./kibana-plugin-core-server.uisettingsparams.schema.md) + +## UiSettingsParams.schema property + +Signature: + +```typescript +schema: Type; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md index ca00cd0cd6396..78c8f0c8fcf8d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.value.md @@ -9,5 +9,5 @@ default value to fall back to if a user doesn't provide any Signature: ```typescript -value?: SavedObjectAttribute; +value?: T; ``` diff --git a/packages/kbn-config-schema/src/errors/validation_error.ts b/packages/kbn-config-schema/src/errors/validation_error.ts index d688d022da85c..2a4f887bc4349 100644 --- a/packages/kbn-config-schema/src/errors/validation_error.ts +++ b/packages/kbn-config-schema/src/errors/validation_error.ts @@ -44,5 +44,8 @@ export class ValidationError extends SchemaError { constructor(error: SchemaTypeError, namespace?: string) { super(ValidationError.extractMessage(error, namespace), error); + + // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, ValidationError.prototype); } } diff --git a/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts b/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts index ad01dea624c3c..dbfa87e70032b 100644 --- a/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts +++ b/packages/kbn-dev-utils/src/kbn_client/kbn_client_ui_settings.ts @@ -67,7 +67,7 @@ export class KbnClientUiSettings { * Replace all uiSettings with the `doc` values, `doc` is merged * with some defaults */ - async replace(doc: UiSettingValues) { + async replace(doc: UiSettingValues, { retries = 5 }: { retries?: number } = {}) { this.log.debug('replacing kibana config doc: %j', doc); const changes: Record = { @@ -85,7 +85,7 @@ export class KbnClientUiSettings { method: 'POST', path: '/api/kibana/settings', body: { changes }, - retries: 5, + retries, }); } diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index fe0491870e4bd..338d489300526 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -43920,7 +43920,7 @@ class KbnClientUiSettings { * Replace all uiSettings with the `doc` values, `doc` is merged * with some defaults */ - async replace(doc) { + async replace(doc, { retries = 5 } = {}) { this.log.debug('replacing kibana config doc: %j', doc); const changes = { ...this.defaults, @@ -43935,7 +43935,7 @@ class KbnClientUiSettings { method: 'POST', path: '/api/kibana/settings', body: { changes }, - retries: 5, + retries, }); } /** diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 483d4dbfdf7c5..0ff044878afa9 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -171,7 +171,7 @@ export { ErrorToastOptions, } from './notifications'; -export { MountPoint, UnmountCallback } from './types'; +export { MountPoint, UnmountCallback, PublicUiSettingsParams } from './types'; /** * Core services exposed to the `Plugin` setup lifecycle diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 69668176a397e..46667230edc3b 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -16,10 +16,11 @@ import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; +import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types'; import React from 'react'; import * as Rx from 'rxjs'; import { ShallowPromise } from '@kbn/utility-types'; -import { UiSettingsParams as UiSettingsParams_2 } from 'src/core/server/types'; +import { Type } from '@kbn/config-schema'; import { UnregisterCallback } from 'history'; import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types'; @@ -784,7 +785,7 @@ export type IToasts = Pick(key: string, defaultOverride?: T) => Observable; get: (key: string, defaultOverride?: T) => T; - getAll: () => Readonly>; + getAll: () => Readonly>; getSaved$: () => Observable<{ key: string; newValue: T; @@ -933,6 +934,9 @@ export interface PluginInitializerContext // @public (undocumented) export type PluginOpaqueId = symbol; +// @public +export type PublicUiSettingsParams = Omit; + // Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -1291,7 +1295,7 @@ export type ToastsSetup = IToasts; export type ToastsStart = IToasts; // @public -export interface UiSettingsParams { +export interface UiSettingsParams { category?: string[]; // Warning: (ae-forgotten-export) The symbol "DeprecationSettings" needs to be exported by the entry point index.d.ts deprecation?: DeprecationSettings; @@ -1301,16 +1305,18 @@ export interface UiSettingsParams { options?: string[]; readonly?: boolean; requiresPageReload?: boolean; + // (undocumented) + schema: Type; type?: UiSettingsType; // (undocumented) validation?: ImageValidation | StringValidation; - value?: SavedObjectAttribute; + value?: T; } // @public (undocumented) export interface UiSettingsState { // (undocumented) - [key: string]: UiSettingsParams_2 & UserProvidedValues_2; + [key: string]: PublicUiSettingsParams_2 & UserProvidedValues_2; } // @public diff --git a/src/core/public/types.ts b/src/core/public/types.ts index 267a9e9f7e014..26f1e46836378 100644 --- a/src/core/public/types.ts +++ b/src/core/public/types.ts @@ -19,6 +19,7 @@ export { UiSettingsParams, + PublicUiSettingsParams, UserProvidedValues, UiSettingsType, ImageValidation, diff --git a/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap b/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap index cd55c77526d52..b737c04a5f269 100644 --- a/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap +++ b/src/core/public/ui_settings/__snapshots__/ui_settings_api.test.ts.snap @@ -84,21 +84,21 @@ Array [ exports[`#batchSet rejects all promises for batched requests that fail: promise rejections 1`] = ` Array [ Object { - "error": [Error: Request failed with status code: 400], + "error": [Error: invalid], "isRejected": true, }, Object { - "error": [Error: Request failed with status code: 400], + "error": [Error: invalid], "isRejected": true, }, Object { - "error": [Error: Request failed with status code: 400], + "error": [Error: invalid], "isRejected": true, }, ] `; -exports[`#batchSet rejects on 301 1`] = `"Request failed with status code: 301"`; +exports[`#batchSet rejects on 301 1`] = `"Moved Permanently"`; exports[`#batchSet rejects on 404 response 1`] = `"Request failed with status code: 404"`; diff --git a/src/core/public/ui_settings/types.ts b/src/core/public/ui_settings/types.ts index 19fd91924f247..d92c033ae8c8c 100644 --- a/src/core/public/ui_settings/types.ts +++ b/src/core/public/ui_settings/types.ts @@ -18,11 +18,11 @@ */ import { Observable } from 'rxjs'; -import { UiSettingsParams, UserProvidedValues } from 'src/core/server/types'; +import { PublicUiSettingsParams, UserProvidedValues } from 'src/core/server/types'; /** @public */ export interface UiSettingsState { - [key: string]: UiSettingsParams & UserProvidedValues; + [key: string]: PublicUiSettingsParams & UserProvidedValues; } /** @@ -53,7 +53,7 @@ export interface IUiSettingsClient { * Gets the metadata about all uiSettings, including the type, default value, and user value * for each key. */ - getAll: () => Readonly>; + getAll: () => Readonly>; /** * Sets the value for a uiSetting. If the setting is not registered by any plugin diff --git a/src/core/public/ui_settings/ui_settings_api.test.ts b/src/core/public/ui_settings/ui_settings_api.test.ts index 1170c42cea704..9a462e0541347 100644 --- a/src/core/public/ui_settings/ui_settings_api.test.ts +++ b/src/core/public/ui_settings/ui_settings_api.test.ts @@ -148,7 +148,7 @@ describe('#batchSet', () => { '*', { status: 400, - body: 'invalid', + body: { message: 'invalid' }, }, { overwriteRoutes: false, diff --git a/src/core/public/ui_settings/ui_settings_api.ts b/src/core/public/ui_settings/ui_settings_api.ts index 33b43107acf1b..c5efced0a41e3 100644 --- a/src/core/public/ui_settings/ui_settings_api.ts +++ b/src/core/public/ui_settings/ui_settings_api.ts @@ -152,10 +152,14 @@ export class UiSettingsApi { }, }); } catch (err) { - if (err.response && err.response.status >= 300) { - throw new Error(`Request failed with status code: ${err.response.status}`); + if (err.response) { + if (err.response.status === 400) { + throw new Error(err.body.message); + } + if (err.response.status > 400) { + throw new Error(`Request failed with status code: ${err.response.status}`); + } } - throw err; } finally { this.loadingCount$.next(this.loadingCount$.getValue() - 1); diff --git a/src/core/public/ui_settings/ui_settings_client.ts b/src/core/public/ui_settings/ui_settings_client.ts index f0071ed08435c..f5596b1bc34fc 100644 --- a/src/core/public/ui_settings/ui_settings_client.ts +++ b/src/core/public/ui_settings/ui_settings_client.ts @@ -21,14 +21,14 @@ import { cloneDeep, defaultsDeep } from 'lodash'; import { Observable, Subject, concat, defer, of } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { UiSettingsParams, UserProvidedValues } from 'src/core/server/types'; +import { UserProvidedValues, PublicUiSettingsParams } from 'src/core/server/types'; import { IUiSettingsClient, UiSettingsState } from './types'; import { UiSettingsApi } from './ui_settings_api'; interface UiSettingsClientParams { api: UiSettingsApi; - defaults: Record; + defaults: Record; initialSettings?: UiSettingsState; done$: Observable; } @@ -39,8 +39,8 @@ export class UiSettingsClient implements IUiSettingsClient { private readonly updateErrors$ = new Subject(); private readonly api: UiSettingsApi; - private readonly defaults: Record; - private cache: Record; + private readonly defaults: Record; + private cache: Record; constructor(params: UiSettingsClientParams) { this.api = params.api; diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts new file mode 100644 index 0000000000000..2f8c85f47a76e --- /dev/null +++ b/src/core/server/core_app/core_app.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { InternalCoreSetup } from '../internal_types'; +import { CoreContext } from '../core_context'; +import { Logger } from '../logging'; + +/** @internal */ +export class CoreApp { + private readonly logger: Logger; + constructor(core: CoreContext) { + this.logger = core.logger.get('core-app'); + } + setup(coreSetup: InternalCoreSetup) { + this.logger.debug('Setting up core app.'); + this.registerDefaultRoutes(coreSetup); + } + + private registerDefaultRoutes(coreSetup: InternalCoreSetup) { + const httpSetup = coreSetup.http; + const router = httpSetup.createRouter('/'); + router.get({ path: '/', validate: false }, async (context, req, res) => { + const defaultRoute = await context.core.uiSettings.client.get('defaultRoute'); + const basePath = httpSetup.basePath.get(req); + const url = `${basePath}${defaultRoute}`; + + return res.redirected({ + headers: { + location: url, + }, + }); + }); + router.get({ path: '/core', validate: false }, async (context, req, res) => + res.ok({ body: { version: '0.0.1' } }) + ); + } +} diff --git a/src/core/server/core_app/index.ts b/src/core/server/core_app/index.ts new file mode 100644 index 0000000000000..342ed43f1ff8a --- /dev/null +++ b/src/core/server/core_app/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { CoreApp } from './core_app'; diff --git a/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts b/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts new file mode 100644 index 0000000000000..221e6fa42471c --- /dev/null +++ b/src/core/server/core_app/integration_tests/default_route_provider_config.test.ts @@ -0,0 +1,90 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import * as kbnTestServer from '../../../../test_utils/kbn_server'; +import { Root } from '../../root'; + +const { startES } = kbnTestServer.createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), +}); +let esServer: kbnTestServer.TestElasticsearchUtils; + +describe('default route provider', () => { + let root: Root; + + beforeAll(async () => { + esServer = await startES(); + root = kbnTestServer.createRootWithCorePlugins({ + server: { + basePath: '/hello', + }, + }); + + await root.setup(); + await root.start(); + }); + + afterAll(async () => { + await esServer.stop(); + await root.shutdown(); + }); + + it('redirects to the configured default route respecting basePath', async function() { + const { status, header } = await kbnTestServer.request.get(root, '/'); + + expect(status).toEqual(302); + expect(header).toMatchObject({ + location: '/hello/app/kibana', + }); + }); + + it('ignores invalid values', async function() { + const invalidRoutes = [ + 'http://not-your-kibana.com', + '///example.com', + '//example.com', + ' //example.com', + ]; + + for (const url of invalidRoutes) { + await kbnTestServer.request + .post(root, '/api/kibana/settings/defaultRoute') + .send({ value: url }) + .expect(400); + } + + const { status, header } = await kbnTestServer.request.get(root, '/'); + expect(status).toEqual(302); + expect(header).toMatchObject({ + location: '/hello/app/kibana', + }); + }); + + it('consumes valid values', async function() { + await kbnTestServer.request + .post(root, '/api/kibana/settings/defaultRoute') + .send({ value: '/valid' }) + .expect(200); + + const { status, header } = await kbnTestServer.request.get(root, '/'); + expect(status).toEqual(302); + expect(header).toMatchObject({ + location: '/hello/valid', + }); + }); +}); diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 4a1ac8988e4e5..89fee92a7ef02 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -250,6 +250,7 @@ export { export { IUiSettingsClient, UiSettingsParams, + PublicUiSettingsParams, UiSettingsType, UiSettingsServiceSetup, UiSettingsServiceStart, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 9cd0c26ea2497..84fe081adaae6 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -998,7 +998,7 @@ export interface IScopedRenderingClient { export interface IUiSettingsClient { get: (key: string) => Promise; getAll: () => Promise>; - getRegistered: () => Readonly>; + getRegistered: () => Readonly>; getUserProvided: () => Promise>>; isOverridden: (key: string) => boolean; remove: (key: string) => Promise; @@ -1443,6 +1443,9 @@ export interface PluginsServiceStart { contracts: Map; } +// @public +export type PublicUiSettingsParams = Omit; + // Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts // // @public (undocumented) @@ -2284,7 +2287,7 @@ export interface StringValidationRegexString { } // @public -export interface UiSettingsParams { +export interface UiSettingsParams { category?: string[]; deprecation?: DeprecationSettings; description?: string; @@ -2293,10 +2296,12 @@ export interface UiSettingsParams { options?: string[]; readonly?: boolean; requiresPageReload?: boolean; + // (undocumented) + schema: Type; type?: UiSettingsType; // (undocumented) validation?: ImageValidation | StringValidation; - value?: SavedObjectAttribute; + value?: T; } // @public (undocumented) diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 2402504f717ca..09a1328f346d8 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -26,8 +26,9 @@ import { RawConfigurationProvider, coreDeprecationProvider, } from './config'; +import { CoreApp } from './core_app'; import { ElasticsearchService } from './elasticsearch'; -import { HttpService, InternalHttpServiceSetup } from './http'; +import { HttpService } from './http'; import { RenderingService, RenderingServiceSetup } from './rendering'; import { LegacyService, ensureValidConfiguration } from './legacy'; import { Logger, LoggerFactory } from './logging'; @@ -69,6 +70,7 @@ export class Server { private readonly uiSettings: UiSettingsService; private readonly uuid: UuidService; private readonly metrics: MetricsService; + private readonly coreApp: CoreApp; private coreStart?: InternalCoreStart; @@ -92,6 +94,7 @@ export class Server { this.capabilities = new CapabilitiesService(core); this.uuid = new UuidService(core); this.metrics = new MetricsService(core); + this.coreApp = new CoreApp(core); } public async setup() { @@ -122,8 +125,6 @@ export class Server { context: contextServiceSetup, }); - this.registerDefaultRoute(httpSetup); - const capabilitiesSetup = this.capabilities.setup({ http: httpSetup }); const elasticsearchServiceSetup = await this.elasticsearch.setup({ @@ -168,6 +169,7 @@ export class Server { }); this.registerCoreContext(coreSetup, renderingSetup); + this.coreApp.setup(coreSetup); return coreSetup; } @@ -216,13 +218,6 @@ export class Server { await this.metrics.stop(); } - private registerDefaultRoute(httpSetup: InternalHttpServiceSetup) { - const router = httpSetup.createRouter('/core'); - router.get({ path: '/', validate: false }, async (context, req, res) => - res.ok({ body: { version: '0.0.1' } }) - ); - } - private registerCoreContext(coreSetup: InternalCoreSetup, rendering: RenderingServiceSetup) { coreSetup.http.registerRouteHandlerContext( coreId, diff --git a/src/core/server/ui_settings/index.ts b/src/core/server/ui_settings/index.ts index ddb66df3ffcbe..b11f398d59fa8 100644 --- a/src/core/server/ui_settings/index.ts +++ b/src/core/server/ui_settings/index.ts @@ -27,6 +27,7 @@ export { UiSettingsServiceStart, IUiSettingsClient, UiSettingsParams, + PublicUiSettingsParams, InternalUiSettingsServiceSetup, InternalUiSettingsServiceStart, UiSettingsType, diff --git a/src/core/server/ui_settings/integration_tests/routes.test.ts b/src/core/server/ui_settings/integration_tests/routes.test.ts new file mode 100644 index 0000000000000..c1261bc7c1350 --- /dev/null +++ b/src/core/server/ui_settings/integration_tests/routes.test.ts @@ -0,0 +1,66 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import * as kbnTestServer from '../../../../test_utils/kbn_server'; + +describe('ui settings service', () => { + describe('routes', () => { + let root: ReturnType; + beforeAll(async () => { + root = kbnTestServer.createRoot(); + + const { uiSettings } = await root.setup(); + uiSettings.register({ + custom: { + value: '42', + schema: schema.string(), + }, + }); + + await root.start(); + }, 30000); + afterAll(async () => await root.shutdown()); + + describe('set', () => { + it('validates value', async () => { + const response = await kbnTestServer.request + .post(root, '/api/kibana/settings/custom') + .send({ value: 100 }) + .expect(400); + + expect(response.body.message).toBe( + '[validation [custom]]: expected value of type [string] but got [number]' + ); + }); + }); + describe('set many', () => { + it('validates value', async () => { + const response = await kbnTestServer.request + .post(root, '/api/kibana/settings') + .send({ changes: { custom: 100, foo: 'bar' } }) + .expect(400); + + expect(response.body.message).toBe( + '[validation [custom]]: expected value of type [string] but got [number]' + ); + }); + }); + }); +}); diff --git a/src/core/server/ui_settings/routes/set.ts b/src/core/server/ui_settings/routes/set.ts index 51ad256b51335..e5158e274245c 100644 --- a/src/core/server/ui_settings/routes/set.ts +++ b/src/core/server/ui_settings/routes/set.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { schema } from '@kbn/config-schema'; +import { schema, ValidationError } from '@kbn/config-schema'; import { IRouter } from '../../http'; import { SavedObjectsErrorHelpers } from '../../saved_objects'; @@ -56,7 +56,7 @@ export function registerSetRoute(router: IRouter) { }); } - if (error instanceof CannotOverrideError) { + if (error instanceof CannotOverrideError || error instanceof ValidationError) { return response.badRequest({ body: error }); } diff --git a/src/core/server/ui_settings/routes/set_many.ts b/src/core/server/ui_settings/routes/set_many.ts index 3794eba004bee..5623c3fe11b80 100644 --- a/src/core/server/ui_settings/routes/set_many.ts +++ b/src/core/server/ui_settings/routes/set_many.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { schema } from '@kbn/config-schema'; +import { schema, ValidationError } from '@kbn/config-schema'; import { IRouter } from '../../http'; import { SavedObjectsErrorHelpers } from '../../saved_objects'; @@ -50,7 +50,7 @@ export function registerSetManyRoute(router: IRouter) { }); } - if (error instanceof CannotOverrideError) { + if (error instanceof CannotOverrideError || error instanceof ValidationError) { return response.badRequest({ body: error }); } diff --git a/src/core/server/ui_settings/types.ts b/src/core/server/ui_settings/types.ts index f3eb1f5a6859c..076e1de4458d7 100644 --- a/src/core/server/ui_settings/types.ts +++ b/src/core/server/ui_settings/types.ts @@ -17,9 +17,10 @@ * under the License. */ import { SavedObjectsClientContract } from '../saved_objects/types'; -import { UiSettingsParams, UserProvidedValues } from '../../types'; +import { UiSettingsParams, UserProvidedValues, PublicUiSettingsParams } from '../../types'; export { UiSettingsParams, + PublicUiSettingsParams, StringValidationRegexString, StringValidationRegex, StringValidation, @@ -41,7 +42,7 @@ export interface IUiSettingsClient { /** * Returns registered uiSettings values {@link UiSettingsParams} */ - getRegistered: () => Readonly>; + getRegistered: () => Readonly>; /** * Retrieves uiSettings values set by the user with fallbacks to default values if not specified. */ diff --git a/src/core/server/ui_settings/ui_settings_client.test.ts b/src/core/server/ui_settings/ui_settings_client.test.ts index b8aa57291dccf..4ce33eed267a3 100644 --- a/src/core/server/ui_settings/ui_settings_client.test.ts +++ b/src/core/server/ui_settings/ui_settings_client.test.ts @@ -18,6 +18,7 @@ */ import Chance from 'chance'; +import { schema } from '@kbn/config-schema'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { createOrUpgradeSavedConfigMock } from './create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.mock'; @@ -145,6 +146,22 @@ describe('ui settings', () => { expect(error.message).toBe('Unable to update "foo" because it is overridden'); } }); + + it('validates value if a schema presents', async () => { + const defaults = { foo: { schema: schema.string() } }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await expect( + uiSettings.setMany({ + bar: 2, + foo: 1, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: [validation [foo]]: expected value of type [string] but got [number]]` + ); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(0); + }); }); describe('#set()', () => { @@ -163,6 +180,17 @@ describe('ui settings', () => { }); }); + it('validates value if a schema presents', async () => { + const defaults = { foo: { schema: schema.string() } }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await expect(uiSettings.set('foo', 1)).rejects.toMatchInlineSnapshot( + `[Error: [validation [foo]]: expected value of type [string] but got [number]]` + ); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(0); + }); + it('throws CannotOverrideError if the key is overridden', async () => { const { uiSettings } = setup({ overrides: { @@ -193,6 +221,20 @@ describe('ui settings', () => { expect(savedObjectsClient.update).toHaveBeenCalledWith(TYPE, ID, { one: null }); }); + it('does not fail validation', async () => { + const defaults = { + foo: { + schema: schema.string(), + value: '1', + }, + }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await uiSettings.remove('foo'); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(1); + }); + it('throws CannotOverrideError if the key is overridden', async () => { const { uiSettings } = setup({ overrides: { @@ -235,6 +277,20 @@ describe('ui settings', () => { }); }); + it('does not fail validation', async () => { + const defaults = { + foo: { + schema: schema.string(), + value: '1', + }, + }; + const { uiSettings, savedObjectsClient } = setup({ defaults }); + + await uiSettings.removeMany(['foo', 'bar']); + + expect(savedObjectsClient.update).toHaveBeenCalledTimes(1); + }); + it('throws CannotOverrideError if any key is overridden', async () => { const { uiSettings } = setup({ overrides: { @@ -256,7 +312,13 @@ describe('ui settings', () => { const value = chance.word(); const defaults = { key: { value } }; const { uiSettings } = setup({ defaults }); - expect(uiSettings.getRegistered()).toBe(defaults); + expect(uiSettings.getRegistered()).toEqual(defaults); + }); + it('does not leak validation schema outside', () => { + const value = chance.word(); + const defaults = { key: { value, schema: schema.string() } }; + const { uiSettings } = setup({ defaults }); + expect(uiSettings.getRegistered()).toStrictEqual({ key: { value } }); }); }); @@ -274,7 +336,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource }); const result = await uiSettings.getUserProvided(); - expect(result).toEqual({ + expect(result).toStrictEqual({ user: { userValue: 'customized', }, @@ -286,7 +348,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource }); const result = await uiSettings.getUserProvided(); - expect(result).toEqual({ + expect(result).toStrictEqual({ user: { userValue: 'customized', }, @@ -296,6 +358,32 @@ describe('ui settings', () => { }); }); + it('ignores user-configured value if it fails validation', async () => { + const esDocSource = { user: 'foo', id: 'bar' }; + const defaults = { + id: { + value: 42, + schema: schema.number(), + }, + }; + const { uiSettings } = setup({ esDocSource, defaults }); + const result = await uiSettings.getUserProvided(); + + expect(result).toStrictEqual({ + user: { + userValue: 'foo', + }, + }); + + expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + ], + ] + `); + }); + it('automatically creates the savedConfig if it is missing and returns empty object', async () => { const { uiSettings, savedObjectsClient, createOrUpgradeSavedConfig } = setup(); savedObjectsClient.get = jest @@ -303,7 +391,7 @@ describe('ui settings', () => { .mockRejectedValueOnce(SavedObjectsClient.errors.createGenericNotFoundError()) .mockResolvedValueOnce({ attributes: {} }); - expect(await uiSettings.getUserProvided()).toEqual({}); + expect(await uiSettings.getUserProvided()).toStrictEqual({}); expect(savedObjectsClient.get).toHaveBeenCalledTimes(2); @@ -320,7 +408,7 @@ describe('ui settings', () => { SavedObjectsClient.errors.createGenericNotFoundError() ); - expect(await uiSettings.getUserProvided()).toEqual({ foo: { userValue: 'bar ' } }); + expect(await uiSettings.getUserProvided()).toStrictEqual({ foo: { userValue: 'bar ' } }); }); it('returns an empty object on Forbidden responses', async () => { @@ -329,7 +417,7 @@ describe('ui settings', () => { const error = SavedObjectsClient.errors.decorateForbiddenError(new Error()); savedObjectsClient.get.mockRejectedValue(error); - expect(await uiSettings.getUserProvided()).toEqual({}); + expect(await uiSettings.getUserProvided()).toStrictEqual({}); expect(createOrUpgradeSavedConfig).toHaveBeenCalledTimes(0); }); @@ -339,7 +427,7 @@ describe('ui settings', () => { const error = SavedObjectsClient.errors.decorateEsUnavailableError(new Error()); savedObjectsClient.get.mockRejectedValue(error); - expect(await uiSettings.getUserProvided()).toEqual({}); + expect(await uiSettings.getUserProvided()).toStrictEqual({}); expect(createOrUpgradeSavedConfig).toHaveBeenCalledTimes(0); }); @@ -382,7 +470,7 @@ describe('ui settings', () => { }; const { uiSettings } = setup({ esDocSource, overrides }); - expect(await uiSettings.getUserProvided()).toEqual({ + expect(await uiSettings.getUserProvided()).toStrictEqual({ user: { userValue: 'customized', }, @@ -404,15 +492,40 @@ describe('ui settings', () => { expect(savedObjectsClient.get).toHaveBeenCalledWith(TYPE, ID); }); - it(`returns defaults when es doc is empty`, async () => { + it('returns defaults when es doc is empty', async () => { const esDocSource = {}; const defaults = { foo: { value: 'bar' } }; const { uiSettings } = setup({ esDocSource, defaults }); - expect(await uiSettings.getAll()).toEqual({ + expect(await uiSettings.getAll()).toStrictEqual({ foo: 'bar', }); }); + it('ignores user-configured value if it fails validation', async () => { + const esDocSource = { user: 'foo', id: 'bar' }; + const defaults = { + id: { + value: 42, + schema: schema.number(), + }, + }; + const { uiSettings } = setup({ esDocSource, defaults }); + const result = await uiSettings.getAll(); + + expect(result).toStrictEqual({ + id: 42, + user: 'foo', + }); + + expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + ], + ] + `); + }); + it(`merges user values, including ones without defaults, into key value pairs`, async () => { const esDocSource = { foo: 'user-override', @@ -427,7 +540,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource, defaults }); - expect(await uiSettings.getAll()).toEqual({ + expect(await uiSettings.getAll()).toStrictEqual({ foo: 'user-override', bar: 'user-provided', }); @@ -451,7 +564,7 @@ describe('ui settings', () => { const { uiSettings } = setup({ esDocSource, defaults, overrides }); - expect(await uiSettings.getAll()).toEqual({ + expect(await uiSettings.getAll()).toStrictEqual({ foo: 'bax', bar: 'user-provided', }); @@ -518,6 +631,28 @@ describe('ui settings', () => { expect(await uiSettings.get('dateFormat')).toBe('foo'); }); + + it('returns the default value if user-configured value fails validation', async () => { + const esDocSource = { id: 'bar' }; + const defaults = { + id: { + value: 42, + schema: schema.number(), + }, + }; + + const { uiSettings } = setup({ esDocSource, defaults }); + + expect(await uiSettings.get('id')).toBe(42); + + expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` + Array [ + Array [ + "Ignore invalid UiSettings value. Error: [validation [id]]: expected value of type [number] but got [string].", + ], + ] + `); + }); }); describe('#isOverridden()', () => { diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts index a7e55d2b2da65..76c8284175f11 100644 --- a/src/core/server/ui_settings/ui_settings_client.ts +++ b/src/core/server/ui_settings/ui_settings_client.ts @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { defaultsDeep } from 'lodash'; +import { defaultsDeep, omit } from 'lodash'; import { SavedObjectsErrorHelpers } from '../saved_objects'; import { SavedObjectsClientContract } from '../saved_objects/types'; import { Logger } from '../logging'; import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config'; -import { IUiSettingsClient, UiSettingsParams } from './types'; +import { IUiSettingsClient, UiSettingsParams, PublicUiSettingsParams } from './types'; import { CannotOverrideError } from './ui_settings_errors'; export interface UiSettingsServiceOptions { @@ -40,14 +40,14 @@ interface ReadOptions { autoCreateOrUpgradeIfMissing?: boolean; } -interface UserProvidedValue { +interface UserProvidedValue { userValue?: T; isOverridden?: boolean; } type UiSettingsRawValue = UiSettingsParams & UserProvidedValue; -type UserProvided = Record>; +type UserProvided = Record>; type UiSettingsRaw = Record; export class UiSettingsClient implements IUiSettingsClient { @@ -72,7 +72,11 @@ export class UiSettingsClient implements IUiSettingsClient { } getRegistered() { - return this.defaults; + const copiedDefaults: Record = {}; + for (const [key, value] of Object.entries(this.defaults)) { + copiedDefaults[key] = omit(value, 'schema'); + } + return copiedDefaults; } async get(key: string): Promise { @@ -90,29 +94,21 @@ export class UiSettingsClient implements IUiSettingsClient { }, {} as Record); } - async getUserProvided(): Promise> { - const userProvided: UserProvided = {}; - - // write the userValue for each key stored in the saved object that is not overridden - for (const [key, userValue] of Object.entries(await this.read())) { - if (userValue !== null && !this.isOverridden(key)) { - userProvided[key] = { - userValue, - }; - } - } + async getUserProvided(): Promise> { + const userProvided: UserProvided = this.onReadHook(await this.read()); // write all overridden keys, dropping the userValue is override is null and // adding keys for overrides that are not in saved object - for (const [key, userValue] of Object.entries(this.overrides)) { + for (const [key, value] of Object.entries(this.overrides)) { userProvided[key] = - userValue === null ? { isOverridden: true } : { isOverridden: true, userValue }; + value === null ? { isOverridden: true } : { isOverridden: true, userValue: value }; } return userProvided; } async setMany(changes: Record) { + this.onWriteHook(changes); await this.write({ changes }); } @@ -147,6 +143,43 @@ export class UiSettingsClient implements IUiSettingsClient { return defaultsDeep(userProvided, this.defaults); } + private validateKey(key: string, value: unknown) { + const definition = this.defaults[key]; + if (value === null || definition === undefined) return; + if (definition.schema) { + definition.schema.validate(value, {}, `validation [${key}]`); + } + } + + private onWriteHook(changes: Record) { + for (const key of Object.keys(changes)) { + this.assertUpdateAllowed(key); + } + + for (const [key, value] of Object.entries(changes)) { + this.validateKey(key, value); + } + } + + private onReadHook(values: Record) { + // write the userValue for each key stored in the saved object that is not overridden + // validate value read from saved objects as it can be changed via SO API + const filteredValues: UserProvided = {}; + for (const [key, userValue] of Object.entries(values)) { + if (userValue === null || this.isOverridden(key)) continue; + try { + this.validateKey(key, userValue); + filteredValues[key] = { + userValue: userValue as T, + }; + } catch (error) { + this.log.warn(`Ignore invalid UiSettings value. ${error}.`); + } + } + + return filteredValues; + } + private async write({ changes, autoCreateOrUpgradeIfMissing = true, @@ -154,10 +187,6 @@ export class UiSettingsClient implements IUiSettingsClient { changes: Record; autoCreateOrUpgradeIfMissing?: boolean; }) { - for (const key of Object.keys(changes)) { - this.assertUpdateAllowed(key); - } - try { await this.savedObjectsClient.update(this.type, this.id, changes); } catch (error) { diff --git a/src/core/server/ui_settings/ui_settings_service.test.ts b/src/core/server/ui_settings/ui_settings_service.test.ts index 11766713b3be0..08400f56ad281 100644 --- a/src/core/server/ui_settings/ui_settings_service.test.ts +++ b/src/core/server/ui_settings/ui_settings_service.test.ts @@ -17,6 +17,8 @@ * under the License. */ import { BehaviorSubject } from 'rxjs'; +import { schema } from '@kbn/config-schema'; + import { MockUiSettingsClientConstructor } from './ui_settings_service.test.mock'; import { UiSettingsService, SetupDeps } from './ui_settings_service'; import { httpServiceMock } from '../http/http_service.mock'; @@ -35,6 +37,7 @@ const defaults = { value: 'bar', category: [], description: '', + schema: schema.string(), }, }; @@ -104,6 +107,45 @@ describe('uiSettings', () => { }); describe('#start', () => { + describe('validation', () => { + it('validates registered definitions', async () => { + const { register } = await service.setup(setupDeps); + register({ + custom: { + value: 42, + schema: schema.string(), + }, + }); + + await expect(service.start()).rejects.toMatchInlineSnapshot( + `[Error: [ui settings defaults [custom]]: expected value of type [string] but got [number]]` + ); + }); + + it('validates overrides', async () => { + const coreContext = mockCoreContext.create(); + coreContext.configService.atPath.mockReturnValueOnce( + new BehaviorSubject({ + overrides: { + custom: 42, + }, + }) + ); + const customizedService = new UiSettingsService(coreContext); + const { register } = await customizedService.setup(setupDeps); + register({ + custom: { + value: '42', + schema: schema.string(), + }, + }); + + await expect(customizedService.start()).rejects.toMatchInlineSnapshot( + `[Error: [ui settings overrides [custom]]: expected value of type [string] but got [number]]` + ); + }); + }); + describe('#asScopedToClient', () => { it('passes saved object type "config" to UiSettingsClient', async () => { await service.setup(setupDeps); diff --git a/src/core/server/ui_settings/ui_settings_service.ts b/src/core/server/ui_settings/ui_settings_service.ts index de2cc9d510e0c..83e66cf6dd06d 100644 --- a/src/core/server/ui_settings/ui_settings_service.ts +++ b/src/core/server/ui_settings/ui_settings_service.ts @@ -70,6 +70,9 @@ export class UiSettingsService } public async start(): Promise { + this.validatesDefinitions(); + this.validatesOverrides(); + return { asScopedToClient: this.getScopedClientFactory(), }; @@ -101,4 +104,21 @@ export class UiSettingsService this.uiSettingsDefaults.set(key, value); }); } + + private validatesDefinitions() { + for (const [key, definition] of this.uiSettingsDefaults) { + if (definition.schema) { + definition.schema.validate(definition.value, {}, `ui settings defaults [${key}]`); + } + } + } + + private validatesOverrides() { + for (const [key, value] of Object.entries(this.overrides)) { + const definition = this.uiSettingsDefaults.get(key); + if (definition?.schema) { + definition.schema.validate(value, {}, `ui settings overrides [${key}]`); + } + } + } } diff --git a/src/core/types/ui_settings.ts b/src/core/types/ui_settings.ts index eccd3f9616af0..ed1076b571960 100644 --- a/src/core/types/ui_settings.ts +++ b/src/core/types/ui_settings.ts @@ -16,8 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - -import { SavedObjectAttribute } from './saved_objects'; +import { Type } from '@kbn/config-schema'; /** * UI element type to represent the settings. @@ -49,11 +48,11 @@ export interface DeprecationSettings { * UiSettings parameters defined by the plugins. * @public * */ -export interface UiSettingsParams { +export interface UiSettingsParams { /** title in the UI */ name?: string; /** default value to fall back to if a user doesn't provide any */ - value?: SavedObjectAttribute; + value?: T; /** description provided to a user in UI */ description?: string; /** used to group the configured setting in the UI */ @@ -73,10 +72,22 @@ export interface UiSettingsParams { /* * Allows defining a custom validation applicable to value change on the client. * @deprecated + * Use schema instead. */ validation?: ImageValidation | StringValidation; + /* + * Value validation schema + * Used to validate value on write and read. + */ + schema: Type; } +/** + * A sub-set of {@link UiSettingsParams} exposed to the client-side. + * @public + * */ +export type PublicUiSettingsParams = Omit; + /** * Allows regex objects or a regex string * @public diff --git a/src/core/utils/url.test.ts b/src/core/utils/url.test.ts index 3c35ba44455bc..419c0cda2b8cb 100644 --- a/src/core/utils/url.test.ts +++ b/src/core/utils/url.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { modifyUrl } from './url'; +import { modifyUrl, isRelativeUrl } from './url'; describe('modifyUrl()', () => { test('throws an error with invalid input', () => { @@ -69,3 +69,17 @@ describe('modifyUrl()', () => { ).toEqual('mail:localhost'); }); }); + +describe('isRelativeUrl()', () => { + test('returns "true" for a relative URL', () => { + expect(isRelativeUrl('good')).toBe(true); + expect(isRelativeUrl('/good')).toBe(true); + expect(isRelativeUrl('/good/even/better')).toBe(true); + }); + test('returns "false" for a non-relative URL', () => { + expect(isRelativeUrl('http://evil.com')).toBe(false); + expect(isRelativeUrl('//evil.com')).toBe(false); + expect(isRelativeUrl('///evil.com')).toBe(false); + expect(isRelativeUrl(' //evil.com')).toBe(false); + }); +}); diff --git a/src/core/utils/url.ts b/src/core/utils/url.ts index 31de7e1814038..c2bf80ce3f86f 100644 --- a/src/core/utils/url.ts +++ b/src/core/utils/url.ts @@ -99,3 +99,19 @@ export function modifyUrl( slashes: modifiedParts.slashes, } as UrlObject); } + +export function isRelativeUrl(candidatePath: string) { + // validate that `candidatePath` is not attempting a redirect to somewhere + // outside of this Kibana install + const all = parseUrl(candidatePath, false /* parseQueryString */, true /* slashesDenoteHost */); + const { protocol, hostname, port } = all; + // We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not + // detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but + // browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser + // hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`) + // and the first slash that belongs to path. + if (protocol !== null || hostname !== null || port !== null) { + return false; + } + return true; +} diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index c0628b72c2ce7..85b1956f45333 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -16,11 +16,13 @@ * specific language governing permissions and limitations * under the License. */ - import moment from 'moment-timezone'; import numeralLanguages from '@elastic/numeral/languages'; import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + import { DEFAULT_QUERY_LANGUAGE } from '../../../plugins/data/common'; +import { isRelativeUrl } from '../../../core/utils'; export function getUiSettingDefaults() { const weekdays = moment.weekdays().slice(); @@ -67,17 +69,23 @@ export function getUiSettingDefaults() { defaultMessage: 'Default route', }), value: '/app/kibana', - validation: { - regexString: '^/', - message: i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage', { - defaultMessage: 'The route must start with a slash ("/")', - }), - }, + schema: schema.string({ + validate(value) { + if (!value.startsWith('/') || !isRelativeUrl(value)) { + return i18n.translate( + 'kbn.advancedSettings.defaultRoute.defaultRouteIsRelativeValidationMessage', + { + defaultMessage: 'Must be a relative URL.', + } + ); + } + }, + }), description: i18n.translate('kbn.advancedSettings.defaultRoute.defaultRouteText', { defaultMessage: 'This setting specifies the default route when opening Kibana. ' + 'You can use this setting to modify the landing page when opening Kibana. ' + - 'The route must start with a slash ("/").', + 'The route must be a relative URL.', }), }, 'query:queryString:options': { diff --git a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js index 57adf730f3dd9..3e3dc284671da 100644 --- a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js +++ b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import { Type } from '@kbn/config-schema'; import pkg from '../../../../package.json'; export const createTestEntryTemplate = defaultUiSettings => bundle => ` @@ -87,7 +87,14 @@ const coreSystem = new CoreSystem({ buildNum: 1234, devMode: true, uiSettings: { - defaults: ${JSON.stringify(defaultUiSettings, null, 2) + defaults: ${JSON.stringify( + defaultUiSettings, + (key, value) => { + if (value instanceof Type) return null; + return value; + }, + 2 + ) .split('\n') .join('\n ')}, user: {} diff --git a/src/legacy/server/http/index.js b/src/legacy/server/http/index.js index 265d71e95b301..d616afb533d0a 100644 --- a/src/legacy/server/http/index.js +++ b/src/legacy/server/http/index.js @@ -24,15 +24,12 @@ import Boom from 'boom'; import { registerHapiPlugins } from './register_hapi_plugins'; import { setupBasePathProvider } from './setup_base_path_provider'; -import { setupDefaultRouteProvider } from './setup_default_route_provider'; export default async function(kbnServer, server, config) { server = kbnServer.server; setupBasePathProvider(kbnServer); - setupDefaultRouteProvider(server); - await registerHapiPlugins(server); // provide a simple way to expose static directories @@ -60,14 +57,6 @@ export default async function(kbnServer, server, config) { }); }); - server.route({ - path: '/', - method: 'GET', - async handler(req, h) { - return h.redirect(await req.getDefaultRoute()); - }, - }); - server.route({ method: 'GET', path: '/{p*}', diff --git a/src/legacy/server/http/integration_tests/default_route_provider.test.ts b/src/legacy/server/http/integration_tests/default_route_provider.test.ts deleted file mode 100644 index d91438d904558..0000000000000 --- a/src/legacy/server/http/integration_tests/default_route_provider.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -jest.mock('../../../ui/ui_settings/ui_settings_mixin', () => { - return jest.fn(); -}); - -import * as kbnTestServer from '../../../../test_utils/kbn_server'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Root } from '../../../../core/server/root'; - -let mockDefaultRouteSetting: any = ''; - -describe('default route provider', () => { - let root: Root; - beforeAll(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); - - await root.setup(); - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - - kbnServer.server.decorate('request', 'getUiSettingsService', function() { - return { - get: (key: string) => { - if (key === 'defaultRoute') { - return Promise.resolve(mockDefaultRouteSetting); - } - throw Error(`unsupported ui setting: ${key}`); - }, - getRegistered: () => { - return { - defaultRoute: { - value: '/app/kibana', - }, - }; - }, - }; - }); - }, 30000); - - afterAll(async () => await root.shutdown()); - - it('redirects to the configured default route', async function() { - mockDefaultRouteSetting = '/app/some/default/route'; - - const { status, header } = await kbnTestServer.request.get(root, '/'); - expect(status).toEqual(302); - expect(header).toMatchObject({ - location: '/app/some/default/route', - }); - }); - - const invalidRoutes = [ - 'http://not-your-kibana.com', - '///example.com', - '//example.com', - ' //example.com', - ]; - for (const route of invalidRoutes) { - it(`falls back to /app/kibana when the configured route (${route}) is not a valid relative path`, async function() { - mockDefaultRouteSetting = route; - - const { status, header } = await kbnTestServer.request.get(root, '/'); - expect(status).toEqual(302); - expect(header).toMatchObject({ - location: '/app/kibana', - }); - }); - } -}); diff --git a/src/legacy/server/http/integration_tests/default_route_provider_config.test.ts b/src/legacy/server/http/integration_tests/default_route_provider_config.test.ts deleted file mode 100644 index 8365941cbeb10..0000000000000 --- a/src/legacy/server/http/integration_tests/default_route_provider_config.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import * as kbnTestServer from '../../../../test_utils/kbn_server'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Root } from '../../../../core/server/root'; - -describe('default route provider', () => { - let root: Root; - - afterEach(async () => await root.shutdown()); - - it('redirects to the configured default route', async function() { - root = kbnTestServer.createRoot({ - server: { - defaultRoute: '/app/some/default/route', - }, - migrations: { skip: true }, - }); - - await root.setup(); - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - - kbnServer.server.decorate('request', 'getSavedObjectsClient', function() { - return { - get: (type: string, id: string) => ({ attributes: {} }), - }; - }); - - const { status, header } = await kbnTestServer.request.get(root, '/'); - - expect(status).toEqual(302); - expect(header).toMatchObject({ - location: '/app/some/default/route', - }); - }); -}); diff --git a/src/legacy/server/http/setup_default_route_provider.ts b/src/legacy/server/http/setup_default_route_provider.ts deleted file mode 100644 index 9a580dd1c59bd..0000000000000 --- a/src/legacy/server/http/setup_default_route_provider.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { Legacy } from 'kibana'; -import { parse } from 'url'; - -export function setupDefaultRouteProvider(server: Legacy.Server) { - server.decorate('request', 'getDefaultRoute', async function() { - // @ts-ignore - const request: Legacy.Request = this; - - const serverBasePath: string = server.config().get('server.basePath'); - - const uiSettings = request.getUiSettingsService(); - - const defaultRoute = await uiSettings.get('defaultRoute'); - const qualifiedDefaultRoute = `${request.getBasePath()}${defaultRoute}`; - - if (isRelativePath(qualifiedDefaultRoute, serverBasePath)) { - return qualifiedDefaultRoute; - } else { - server.log( - ['http', 'warn'], - `Ignoring configured default route of '${defaultRoute}', as it is malformed.` - ); - - const fallbackRoute = uiSettings.getRegistered().defaultRoute.value; - - const qualifiedFallbackRoute = `${request.getBasePath()}${fallbackRoute}`; - return qualifiedFallbackRoute; - } - }); - - function isRelativePath(candidatePath: string, basePath = '') { - // validate that `candidatePath` is not attempting a redirect to somewhere - // outside of this Kibana install - const { protocol, hostname, port, pathname } = parse( - candidatePath, - false /* parseQueryString */, - true /* slashesDenoteHost */ - ); - - // We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not - // detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but - // browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser - // hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`) - // and the first slash that belongs to path. - if (protocol !== null || hostname !== null || port !== null) { - return false; - } - - if (!String(pathname).startsWith(basePath)) { - return false; - } - - return true; - } -} diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 68b5a63871372..9952b345fa06f 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -92,7 +92,6 @@ declare module 'hapi' { interface Request { getSavedObjectsClient(options?: SavedObjectsClientProviderOptions): SavedObjectsClientContract; getBasePath(): string; - getDefaultRoute(): Promise; getUiSettingsService(): IUiSettingsClient; } diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx index 7a2ab648ec258..6103041cf0a4c 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx @@ -22,7 +22,11 @@ import { Observable } from 'rxjs'; import { ReactWrapper } from 'enzyme'; import { mountWithI18nProvider } from 'test_utils/enzyme_helpers'; import dedent from 'dedent'; -import { UiSettingsParams, UserProvidedValues, UiSettingsType } from '../../../../core/public'; +import { + PublicUiSettingsParams, + UserProvidedValues, + UiSettingsType, +} from '../../../../core/public'; import { FieldSetting } from './types'; import { AdvancedSettingsComponent } from './advanced_settings'; import { notificationServiceMock, docLinksServiceMock } from '../../../../core/public/mocks'; @@ -68,7 +72,7 @@ function mockConfig() { remove: (key: string) => Promise.resolve(true), isCustom: (key: string) => false, isOverridden: (key: string) => Boolean(config.getAll()[key].isOverridden), - getRegistered: () => ({} as Readonly>), + getRegistered: () => ({} as Readonly>), overrideLocalDefault: (key: string, value: any) => {}, getUpdate$: () => new Observable<{ @@ -89,7 +93,7 @@ function mockConfig() { getUpdateErrors$: () => new Observable(), get: (key: string, defaultOverride?: any): any => config.getAll()[key] || defaultOverride, get$: (key: string) => new Observable(config.get(key)), - getAll: (): Readonly> => { + getAll: (): Readonly> => { return { 'test:array:setting': { ...defaultConfig, diff --git a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts index 881a2eb003cc8..7ac9b281eb99a 100644 --- a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts +++ b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { UiSettingsParams, StringValidationRegex } from 'src/core/public'; +import { PublicUiSettingsParams, StringValidationRegex } from 'src/core/public'; import expect from '@kbn/expect'; import { toEditableConfig } from './to_editable_config'; @@ -30,7 +30,7 @@ function invoke({ name = 'woah', value = 'forreal', }: { - def?: UiSettingsParams & { isOverridden?: boolean }; + def?: PublicUiSettingsParams & { isOverridden?: boolean }; name?: string; value?: any; }) { @@ -55,7 +55,7 @@ describe('Settings', function() { }); describe('when given a setting definition object', function() { - let def: UiSettingsParams & { isOverridden?: boolean }; + let def: PublicUiSettingsParams & { isOverridden?: boolean }; beforeEach(function() { def = { value: 'the original', diff --git a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts index 2c27d72f7f645..406bc35f826e8 100644 --- a/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts +++ b/src/plugins/advanced_settings/public/management_app/lib/to_editable_config.ts @@ -18,7 +18,7 @@ */ import { - UiSettingsParams, + PublicUiSettingsParams, UserProvidedValues, StringValidationRegexString, SavedObjectAttribute, @@ -40,7 +40,7 @@ export function toEditableConfig({ isCustom, isOverridden, }: { - def: UiSettingsParams & UserProvidedValues; + def: PublicUiSettingsParams & UserProvidedValues; name: string; value: SavedObjectAttribute; isCustom: boolean; diff --git a/src/plugins/advanced_settings/public/management_app/types.ts b/src/plugins/advanced_settings/public/management_app/types.ts index d44a05ce36f5d..ee9b9b0535b79 100644 --- a/src/plugins/advanced_settings/public/management_app/types.ts +++ b/src/plugins/advanced_settings/public/management_app/types.ts @@ -17,17 +17,12 @@ * under the License. */ -import { - UiSettingsType, - StringValidation, - ImageValidation, - SavedObjectAttribute, -} from '../../../../core/public'; +import { UiSettingsType, StringValidation, ImageValidation } from '../../../../core/public'; export interface FieldSetting { displayName: string; name: string; - value: SavedObjectAttribute; + value: unknown; description?: string; options?: string[]; optionLabels?: Record; @@ -36,7 +31,7 @@ export interface FieldSetting { category: string[]; ariaName: string; isOverridden: boolean; - defVal: SavedObjectAttribute; + defVal: unknown; isCustom: boolean; validation?: StringValidation | ImageValidation; readOnly?: boolean; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 333b13eedc17a..783411bbf27e2 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -38,6 +38,7 @@ import { Observable } from 'rxjs'; import { Plugin as Plugin_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import { PopoverAnchorPosition } from '@elastic/eui'; +import { PublicUiSettingsParams } from 'src/core/server/types'; import React from 'react'; import * as React_2 from 'react'; import { Required } from '@kbn/utility-types'; @@ -49,7 +50,6 @@ import { SearchResponse as SearchResponse_2 } from 'elasticsearch'; import { SimpleSavedObject } from 'src/core/public'; import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; -import { UiSettingsParams } from 'src/core/server/types'; import { Unit } from '@elastic/datemath'; import { UnregisterCallback } from 'history'; import { UserProvidedValues } from 'src/core/server/types'; diff --git a/src/test_utils/kbn_server.ts b/src/test_utils/kbn_server.ts index f4c3ecd8243ce..12f7eb5a0a043 100644 --- a/src/test_utils/kbn_server.ts +++ b/src/test_utils/kbn_server.ts @@ -50,6 +50,7 @@ const DEFAULTS_SETTINGS = { logging: { silent: true }, plugins: {}, optimize: { enabled: false }, + migrations: { skip: true }, }; const DEFAULT_SETTINGS_WITH_CORE_PLUGINS = { diff --git a/test/api_integration/apis/index.js b/test/api_integration/apis/index.js index 8cdbbf8e74a3d..57e9120773f33 100644 --- a/test/api_integration/apis/index.js +++ b/test/api_integration/apis/index.js @@ -33,6 +33,5 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./status')); loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./ui_metric')); - loadTestFile(require.resolve('./core')); }); } diff --git a/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts b/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts index c32e8a75d95da..3801d3bbce055 100644 --- a/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import { schema } from '@kbn/config-schema'; import { Plugin, CoreSetup } from 'kibana/server'; export class UiSettingsPlugin implements Plugin { @@ -27,6 +27,7 @@ export class UiSettingsPlugin implements Plugin { description: 'just for testing', value: '2', category: ['any'], + schema: schema.string(), }, }); diff --git a/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts b/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts index 337faa2a18fb6..60ae3a1fa77bb 100644 --- a/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts +++ b/x-pack/legacy/plugins/spaces/server/routes/views/enter_space.ts @@ -14,7 +14,13 @@ export function initEnterSpaceView(server: Legacy.Server) { path: ENTER_SPACE_PATH, async handler(request, h) { try { - return h.redirect(await request.getDefaultRoute()); + const uiSettings = request.getUiSettingsService(); + const defaultRoute = await uiSettings.get('defaultRoute'); + + const basePath = server.newPlatform.setup.core.http.basePath.get(request); + const url = `${basePath}${defaultRoute}`; + + return h.redirect(url); } catch (e) { server.log(['spaces', 'error'], `Error navigating to space: ${e}`); return wrapError(e); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fb0eb6e4bf804..3763020a0b692 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -833,7 +833,6 @@ "kbn.advancedSettings.defaultIndexTitle": "デフォルトのインデックス", "kbn.advancedSettings.defaultRoute.defaultRouteText": "この設定は、Kibana 起動時のデフォルトのルートを設定します。この設定で、Kibana 起動時のランディングページを変更できます。ルートはスラッシュ (\"/\") で始まる必要があります。", "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "デフォルトのルート", - "kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage": "ルートはスラッシュ (\"/\") で始まる必要があります。", "kbn.advancedSettings.disableAnimationsText": "Kibana UI の不要なアニメーションをオフにします。変更を適用するにはページを更新してください。", "kbn.advancedSettings.disableAnimationsTitle": "アニメーションを無効にする", "kbn.advancedSettings.discover.aggsTermsSizeText": "「可視化」ボタンをクリックした際に、フィールドドロップダウンやディスカバリサイドバーに可視化される用語の数を設定します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0a9c82afaec1d..0477470a4b8a1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -833,7 +833,6 @@ "kbn.advancedSettings.defaultIndexTitle": "默认索引", "kbn.advancedSettings.defaultRoute.defaultRouteText": "此设置指定打开 Kibana 时的默认路由。您可以使用此设置修改打开 Kibana 时的登陆页面。路由必须以正斜杠(“/”)开头。", "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "默认路由", - "kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage": "路由必须以正斜杠(“/”)开头", "kbn.advancedSettings.disableAnimationsText": "在 Kibana UI 中关闭所有没有必要的动画。刷新页面以应用更改。", "kbn.advancedSettings.disableAnimationsTitle": "禁用动画", "kbn.advancedSettings.discover.aggsTermsSizeText": "确定在单击“可视化”按钮时将在发现侧边栏的字段下拉列表中可视化多少个词。", diff --git a/x-pack/test/functional/apps/spaces/enter_space.ts b/x-pack/test/functional/apps/spaces/enter_space.ts index e0b1ec544d460..38220c15cb266 100644 --- a/x-pack/test/functional/apps/spaces/enter_space.ts +++ b/x-pack/test/functional/apps/spaces/enter_space.ts @@ -10,7 +10,6 @@ export default function enterSpaceFunctonalTests({ getPageObjects, }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['security', 'spaceSelector']); describe('Enter Space', function() { @@ -25,8 +24,8 @@ export default function enterSpaceFunctonalTests({ await PageObjects.security.forceLogout(); }); - it('allows user to navigate to different spaces, respecting the configured default route', async () => { - const spaceId = 'another-space'; + it('falls back to the default home page when the configured default route is malformed', async () => { + const spaceId = 'default'; await PageObjects.security.login(null, null, { expectSpaceSelector: true, @@ -34,22 +33,11 @@ export default function enterSpaceFunctonalTests({ await PageObjects.spaceSelector.clickSpaceCard(spaceId); - await PageObjects.spaceSelector.expectRoute(spaceId, '/app/kibana/#/dashboard'); - - await PageObjects.spaceSelector.openSpacesNav(); - - // change spaces - - await PageObjects.spaceSelector.clickSpaceAvatar('default'); - - await PageObjects.spaceSelector.expectRoute('default', '/app/canvas'); + await PageObjects.spaceSelector.expectHomePage(spaceId); }); - it('falls back to the default home page when the configured default route is malformed', async () => { - await kibanaServer.uiSettings.replace({ defaultRoute: 'http://example.com/evil' }); - - // This test only works with the default space, as other spaces have an enforced relative url of `${serverBasePath}/s/space-id/${defaultRoute}` - const spaceId = 'default'; + it('allows user to navigate to different spaces, respecting the configured default route', async () => { + const spaceId = 'another-space'; await PageObjects.security.login(null, null, { expectSpaceSelector: true, @@ -57,7 +45,15 @@ export default function enterSpaceFunctonalTests({ await PageObjects.spaceSelector.clickSpaceCard(spaceId); - await PageObjects.spaceSelector.expectHomePage(spaceId); + await PageObjects.spaceSelector.expectRoute(spaceId, '/app/canvas'); + + await PageObjects.spaceSelector.openSpacesNav(); + + // change spaces + const newSpaceId = 'default'; + await PageObjects.spaceSelector.clickSpaceAvatar(newSpaceId); + + await PageObjects.spaceSelector.expectHomePage(newSpaceId); }); }); } diff --git a/x-pack/test/functional/es_archives/spaces/enter_space/data.json b/x-pack/test/functional/es_archives/spaces/enter_space/data.json index 462a2a1ee38fe..475fc14e96e6a 100644 --- a/x-pack/test/functional/es_archives/spaces/enter_space/data.json +++ b/x-pack/test/functional/es_archives/spaces/enter_space/data.json @@ -7,7 +7,7 @@ "config": { "buildNum": 8467, "dateFormat:tz": "UTC", - "defaultRoute": "/app/canvas" + "defaultRoute": "http://example.com/evil" }, "type": "config" } @@ -24,7 +24,7 @@ "config": { "buildNum": 8467, "dateFormat:tz": "UTC", - "defaultRoute": "/app/kibana/#dashboard" + "defaultRoute": "/app/canvas" }, "type": "config" } From 1f8e938b9c9b996e438df8cae11da62c59ec743b Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 16 Mar 2020 14:38:31 +0100 Subject: [PATCH 7/8] [Searchprofiler] Spacing between rendered shards (#60238) * Added unique key and some spacing to rendered shards * Give key to React.Fragment --- .../components/profile_tree/profile_tree.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx b/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx index 87a73cdefba31..1dec8f0161c52 100644 --- a/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx +++ b/x-pack/plugins/searchprofiler/public/application/components/profile_tree/profile_tree.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo } from 'react'; +import React, { memo, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { IndexDetails } from './index_details'; @@ -53,13 +53,11 @@ export const ProfileTree = memo(({ data, target, onHighlight }: Props) => { - {index.shards.map(shard => ( - + {index.shards.map((shard, idx) => ( + + + {idx < index.shards.length - 1 ? : undefined} + ))} From 6598298d016f6e7ca4feab4cd74f734fbe9c404f Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 16 Mar 2020 15:13:03 +0100 Subject: [PATCH 8/8] skips 'config_open.ts' files from linter check (#60248) --- .eslintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.js b/.eslintrc.js index 7af162f349b5c..3d6a5c262c453 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -322,6 +322,7 @@ module.exports = { 'x-pack/test/functional/apps/**/*.js', 'x-pack/legacy/plugins/apm/**/*.js', 'test/*/config.ts', + 'test/*/config_open.ts', 'test/*/{tests,test_suites,apis,apps}/**/*', 'test/visual_regression/tests/**/*', 'x-pack/test/*/{tests,test_suites,apis,apps}/**/*',