From 37066c9e1b70b4957aa8bb30bd62850d09ce3bb1 Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:35:45 +0100 Subject: [PATCH 001/104] refactored frontend following best practices --- frontend/index.html | 104 ++++++----------- frontend/public/hkn_logo_white_vector.svg | 74 ++++++++++++ frontend/public/notfound404.jpg | Bin 0 -> 37104 bytes frontend/src/App.css | 85 ++------------ frontend/src/App.css.old | 82 ++++++++++++++ frontend/src/App.jsx | 105 ++++++++---------- frontend/src/Auth0ProviderWithNavigate.jsx | 32 ++++++ frontend/src/LoginButton.tsx | 22 ---- frontend/src/MyNavbar.jsx | 47 -------- .../ApplyForm.jsx.old} | 0 frontend/src/components/AuthGuard.jsx | 17 +++ .../AvailabilitiesCell.jsx} | 0 .../AvailabilitiesTable.jsx} | 18 +-- frontend/src/components/HKNavbar.jsx | 74 ++++++++++++ frontend/src/components/HomeButton.jsx | 15 +++ frontend/src/components/LoadingSpinner.jsx | 11 ++ frontend/src/components/LoginButton.jsx | 23 ++++ frontend/src/components/LogoutButton.jsx | 19 ++++ frontend/src/components/PageHeader.jsx | 14 +++ frontend/src/components/PageLayout.jsx | 17 +++ frontend/src/index.css | 4 - frontend/src/index.jsx | 17 +++ frontend/src/logo.svg | 1 - frontend/src/main.jsx | 21 ---- frontend/src/pages/ApplyPage.jsx | 13 +++ frontend/src/pages/AvailabilitiesPage.jsx | 32 ++++++ frontend/src/pages/DebugPage.jsx | 41 +++++++ frontend/src/pages/HomePage.jsx | 36 ++++++ frontend/src/pages/NotFoundPage.jsx | 14 +++ .../src/{ApiRequests.tsx => services/API.ts} | 0 30 files changed, 629 insertions(+), 309 deletions(-) create mode 100644 frontend/public/hkn_logo_white_vector.svg create mode 100644 frontend/public/notfound404.jpg create mode 100644 frontend/src/App.css.old create mode 100644 frontend/src/Auth0ProviderWithNavigate.jsx delete mode 100644 frontend/src/LoginButton.tsx delete mode 100644 frontend/src/MyNavbar.jsx rename frontend/src/{SignupForm.jsx => components/ApplyForm.jsx.old} (100%) create mode 100644 frontend/src/components/AuthGuard.jsx rename frontend/src/{AvaiabilitiesCell.jsx => components/AvailabilitiesCell.jsx} (100%) rename frontend/src/{AvaiabilitiesTable.tsx => components/AvailabilitiesTable.jsx} (94%) create mode 100644 frontend/src/components/HKNavbar.jsx create mode 100644 frontend/src/components/HomeButton.jsx create mode 100644 frontend/src/components/LoadingSpinner.jsx create mode 100644 frontend/src/components/LoginButton.jsx create mode 100644 frontend/src/components/LogoutButton.jsx create mode 100644 frontend/src/components/PageHeader.jsx create mode 100644 frontend/src/components/PageLayout.jsx create mode 100644 frontend/src/index.jsx delete mode 100644 frontend/src/logo.svg delete mode 100644 frontend/src/main.jsx create mode 100644 frontend/src/pages/ApplyPage.jsx create mode 100644 frontend/src/pages/AvailabilitiesPage.jsx create mode 100644 frontend/src/pages/DebugPage.jsx create mode 100644 frontend/src/pages/HomePage.jsx create mode 100644 frontend/src/pages/NotFoundPage.jsx rename frontend/src/{ApiRequests.tsx => services/API.ts} (100%) diff --git a/frontend/index.html b/frontend/index.html index 191149a..e8f3372 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,71 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - HKRecruitment - - - -
- - - + + + + + + + + + + + + + + + + + + + + + + + + + + + HKRecruitment + + + + +
+ + + + \ No newline at end of file diff --git a/frontend/public/hkn_logo_white_vector.svg b/frontend/public/hkn_logo_white_vector.svg new file mode 100644 index 0000000..e894e19 --- /dev/null +++ b/frontend/public/hkn_logo_white_vector.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/notfound404.jpg b/frontend/public/notfound404.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0c0862874aebdddeb2b2d48f7c649308fdac1054 GIT binary patch literal 37104 zcmb6A2T)T_)G!W*Dj?FkNC`n&C<#SCia_WcX#wd0q=|H-O7A5BLKP%{bdk^m1f=&a zT|g-c2ukne%kO#LXTJGnzWLAm_wLN*?zuVV?%h3o_ip~~2Y^aLMO_7ehldAH!(D*8 zC4drun2?B=h>)0=i1^+;ViGbcGBQ$9GCB%MawMdyfPMW&98F{{{W`2p&Gc-6DXT01tpqML>nS z-&fX^>mhZaOP^G*sp{?}07ha>_}0`u&31}s`J?hVKEyV7f24E>tXdh^jrv5@^{fkS z+YI9HKW3GKwJNI&S3>tj>4qOX*Qub8koAudSDZGiEOB7g*R+TER~o~JlC;EzOzn*{ ziC#Avs*+VYRWN8Myq_2h;(GXY*v1t4iBwPWITfUsya9wH%X&$Q7v9AykN^^4G;(vp zHhu5fHx#)-_*WFO(`(N!a)}leVUx5BE1YBRlN|8* zTf~0rs6GuLGTl+LCY$F&BEn-iP--GnS@<9YPI?P`7)XSw7fA3rW!#%sgWED)S`k~H z2#e*~>M*|${{Gj$ow1?m`PablCnwt>x;>}uU2?#V*B8$F{S()_`EM-DsK_$)w*6?GcPb2b0;m4+yTu0aQ4P>fHK zYRAb|%8IX!uRus|qfiVAQ%g0NSQf0z&j8zxSvYeQ6C0}#liCtEu5`#AHx$_YYO!z; z@r8O)ik|5v--w53p1Q}~^vQ+(Yo|Z`Q;>0E(s#KYcQ`gz<~&~PpQ7${$gcl8q{)%7 zPkxf+{BrS++NEP^TJ)6I&lUBp!^^zqE@56fUp+5cF1}R^+oD<{WWSYi6-@sYw70SS z90{&acngUieP8XW{{rs^2MV7_9mqu1QwuKg)k9p#hVqPi9p9I^1IWm~JSWb$Z2W69$L2C=`~GNEf4@EU zVto1@@#_1a+#M?x)Muf7)vc}L2?;EUj(=lk(>Mst%^`p=5Myqz*D;YV9OqAsEMy_h z(0Yq{z(8ZXu68dqjwBAi!dFB>LWlx%)BZVS`1xh#mEE|PtZ%>p>#Eb&y7(r@cgWDKlN$C zPHpivffFHU;Egg9xT3!SZAix!0QokO`K#2I2yKSXR3c;4Yz;Ae)-@9pQpIUVw%Eqo@4(|hhPh?mG z*NYs5z!ktcOvGWqdNtR|&%}!VRLThaerVnxPeR|`$=OKkWswHD)7}GPV${Hz@2C1s zAtMUG89&==|8}dJl=Hp`CBMf{Kq?`V8i#dcSTwhCxXI#B$D^mj_@DVTCq@hGeTI%K z*WZ;N<;V(?)eR@=3lV8rYm`J+qG@U|71bF@#?M{__!y`fm7KVZj&3_2zcKefRbXj~ z@=6Oz@eSYV6FJ0u8zpDtZJwHGwE2=ddvP z7x!5e=c}F#YYgdPcYYm9?#vmNlNxFkzVjZ2d>j%;`7K)`E8`pL^7df8IdN^YiHTy- zbwsQy=gE5UCwD=M$jU-pbQ@3eC`!#9z=5`&et#Mw>8xd@;^g(}4Ms_v-{zwbCK=OX{`=^G2r$GtJ7pt$0x_ z>&0*JVZPKpfEP_OXFfx>5hU)Q>1ix;YTtYx%rNBxLY15ARjAwhEE{=BEz7;EP#}S( z0?S^%C6P&uq)`H@;ls)~=?1y*KOs`_SU!o#WAPxaez@+FT`kzkT~ck1Zg|Dw zUd~SmZoF>&6aP*|{BWh8y?(s@=jp8*(`~{=dz2s&^R@qplotau!?sX}F1C)#y8ah7 zM@t&IfNG-4WsprL}QHqv~(5o?!3WkNpk#Tsl;j4p~LFydj>7;0U zEh77H5C}ETDXXfKG2M6h^pi`{oc%OQbtxPsmLuF<+{Vz8;63ZLf@wHvoVH^B8}LB? zx6HWGmv?`oL%{(rEV(cp3SmdZf*gwWrWJ{mdD%dtH?E`VdDV^$!O%h?JU zA+^nOZ9GopN(N@aw;*UG-^YHMZOyg1boW=SV+(8M6+Batp|+?{ zq_aDwF8m3nwTVi6vWal{2<$W;%vM?QOK!&a`tNE%fnxXiZ8s0*JagjKJ9ut=)#CbZ zhyTTR{0ZM_!T4$*P9F*Bos9B5*9Cslr_OI=A!03No$WUsS$gwN*NR8cVyXFtB(Tn$ z#eMKirAjbI#f~^3E!?)TOGRo5)tj+o_GZQWsOgE2v9x8%aDY=O87c!{mec&ep_0YK z!d!F_LjF9hgo0gk!b5ABr7`0n@Kxh>-f8-2aG%MHs zp@;VlHLc9XCeR~nJq^b!FOL&yZOqOK4}8H%<4+ybaH;xSrVt>PH+6MmoDWiLB~)P z`olH~cXjD}rG-P5%>YWSEp71@ZQ~TU00IOc!Dj-$Wu?*vv{f^A8!?NmC&I<-DulZY zUUhy<(pu}qR_tl~J`7}y(@&kyq)L6(34ocP5M)^*UHE)P=x)L?7{^O|?j%ACd5T{q z-R5+mAKzHy#*%Po+wWk=bYl}6e_!;6LDupL`w}#LlTz8_0~E80G7Fz9(E?+Ns-f-Z^`5|vEakgn{fSyrvF(rC z-&P4xZ(|C{V_BFl+H`F4hjtsXv-vA}8fb=)F10jeW?d)sjda{dGfd9#W9K9(m4JYU z(i(8v_d8)^P8PaG4x8RM{ek7hO`19<2|tX=n#!1psn^BU5Cw{yQqil*VX0=|^z`mx zdt`weHS;{7C@uXFAZj<`EiE(Dx?SHw{0vdVVNil7Wvl81jk0{Nhhy4(9CbeWi1r9DWu}S_bkey7@DrZ^c z-V8H^^3l{&1oD*(%ZweBX2(5w7a*An-k!rORJeCe{Vw*rA71E_{W0O~$0+9r6FP2z z4`sY3;+`D#d=AbfV$%+my5`yNKhEj|=AbwN1tlWtNE33>$XX35bs8o0 zmy9Bhs(o#OT6~Yh-W8~C21`vHHtcSfOkntc$@7dQ@f}< zlRq7bIc)2m{cTzJTPnXF{3dC657M>OmfccbZ={VW(PQ{H&?R;0w=K|el}7aLUL8?R zSyj;Ji=9f&F+oqN0W~V;DZ+66&;004QlKeo5=r5p*%Fy%SG;%{(IX8VLD>b%fWIp) zQhfDrD_Q6^#vfSY$kWXyq5L#88QtlAx^g?yu>I5~=1u`Z@ZiJ_~(rUuLxSEP(YeBz|Hg z2N%mg7hfRhxC4HYZ{UNR=E_Gm`Hrwait(r|)oJ%5%P^Pi3+k)_&otG|mz}bYAt24>Qn`Ld;3(8XW`qgYnzYD2U*0? z_@iX@3y-6mg74Mu=YQMUY{}Rh3bR-F^xTr5Y59OH{a*he(cPCfl^e10q}G6icQPW)jqS*e$y}FTcxH`rMXQB)0rXT}TKV!Ve_+s z+097}uBwqerkl1`)b}C6OEp%s;}*Pe+-a~kCIYEpfKCYaAc zdn*qAlt3q2Wo<^Mh8-Vmx5_?3f41SkGjwsX^KCb;v0Vw(7MjyZdv*OZ%{NDa$c@;{ zj}z%z7+rnLw6g?9y?pH3&^xtA^ckf>7#U7QLlrPd{CfL00=k51D0URKb}-&xb%8${;SwM)(0_@RQzBx81!$M z(R4*L)edi!x)hFWvozT(n%)OkaPdy-zjN2<7bl>aB|kI$;b_f(1R+U4pc$VfxVGPO zBYjMGqz~W6#i2sNNE(=6fE(Lll`w9q(E?E_dL=Kn_@#Im3$^`>O)Scc(X$}H%VPei zvQlfuu9no*!?(9jz15W zNFLg!|E4UioP=iSb22V@?SrbU*wNr`3~%|t@setzP*6|0)^5(mzW#;f$vpo&`Q!wt zHGgt=kuf2Hrm4(qu@=T*Z=FRQ8?aCKu$&_TR2Hdxx-s!WPd%iHg`#goSZmuy>Je>e zWkK=sE0-jdEclD}^pRk(T91m(`1+^3(dsYAl0dY^D3;;MoBcMiv zZDL#8MHWZ2?ufdvXt%u*ezs>7qmSK1vn+|Cr>X44!}&AK(Njk`Dz#xn+Hk67C;x^@ zw?`Q}N(8_#jf`m@Zts^>W1}Pr0>lq&CC#M3AE*BC+5W1It$}O1MpjQ0OXHD|RfQY* zD9#SO9S{}J7^!g_?<9*Rdh;N*k=O0Re0`y&xPF6=Qz<;;JHsQ{Aj`1kx@J!dL%Z!u zx~?MyFez~YnL$tdZj`G%pIu3WbOSAz4o-$X5q*v=B70e^+@SI9o2j)0m^2kV?c^em z2n)y78lf{-YUxuosuYaEEKDYhnE>IUH23M#es?LG8krW>BEtkL)B_#rmtNhIP8q5H zV=*-4?YDni<9n;p{l0nr(;U2P^7U>pXCYp=YJAv_sK|$W-fi318f@#cE?kf zD!CY2WcIh`x%IdkGgJJ=xMkHZ`)0B~ZDb7J1mbIu#U2Z9Dhj=KxohN-E#mZ$v7H=;%Q==YVQe z9}j~qM5!Aj(Md7R*5DohNK_G3XbTfBR;$1-JFu-M6G^RthBM(aIU19sh75@gzuBx+mF;(E81v8 zhh5kfUsVgknUv^FFtwzF{9h5y3$~JQQ_o0#GwQuhL*gab0v%9C%r0hzTF6=ByMR%> z5yRKF^+n`LRLuft6w;reb%!qzQ^cpNYcU>?x=cV=tVU%p7du76M7Hxb3Y^jTgWm!U zo>WLP-UeBT5^b*_^$Mogy%geuT;iHeCU;?h7Q zxa1HX5&p)o<73bNt>%Jx*@OQNHPzMPB_^Im8Hns<)$ijW$peN+79{hmhn@eBg#DzA z)S>34b97_f@IsEMO0h2BPz~YQ|9ks?8ytb+{{#R3?ceaqJSTB&8!$iM4a7Qxc1&9K zUy5JqErQz06k#3(DQk!pwehp7nOidU_pwEETK>1tAXP55-OO2!J3#8h_DZFk6<&E< znnGDbI;ic_8P_e>Su%rd*d2hmW@*k1>qZHgy%u78E@ZXH&K zL_E$txX@Y=C4M1Y^Psf5=nio5`-CzcHCX;#+>{JrbP zRxI!Q?f{Tg;rl83=evB)L3e<6Gn$%3x3?esuNmX&tTt-&H=wtIlqvle4btBeG>fMH zc!fF+C^$^Wf6;rxUA#ZvL76e#tzEXlze3Jn*WdSa6PcY-$(&`!xF`4V9)o1MzJ*Dp zW`2Nc&K-a&*E}hRjeW>&wA4~mS*G1&(01zQt>8VGj-kV#^Rh0p1@N0sPC;U?U1WZN zv+G0qAy2Aq)zgyM*_wB*(>vIrso(Id*T0^$=}tSeJG_v8-1@R6NrF$xrnlE!0)a%H zVpR06+)uc}pk}saw*D_)v#JYLuEhQxrdx81KTJ_z=c&5=g^p z=OEMTR)6^L4)CyhT>mSKTeNhgd`qeU$j)IXV033B9~31B-o6)ImDu3RWAl6YvA zNT(y-KxY!bHhEPO!K~~?a-X@h$1cgoHTUsgii@!0%wAq*dz+z2kgomAo-&0zb1l3; zEA&M^KMM=)RS~jf;TJW@AbK*1&s4qcZ11S2i`e)Dd?h5*QHWLJzRR|{lxvE7zm@pK zXm#3t$2I5=p11k@l0j97LRAJy>Nws+jgi-MIv`s+rLcHn$#{^7I;1wuK?ORd%2}H+ zlQ@2RUl$0rjF(L2it9-yU4#B)W6$#k5B*H)qt2F*r|#3?S#w+ass#AN{`UGYFBwA- z88B+rK5<|$cyw~unNEW@m|3{$4)Eg>l$MJV0=*Iw6Vq3{54B^H&+1C@8!PP+(lJ$y z_<73>4tUw{NE#;>6R++1R0wVLQk(3c(7utxH0~qp-!o+HHG@>vh9b-~f7Z%mnsViZ z6RKZ1$mI@j`sLoO4i9py)-H4>Gevjb+g5RVU_(Mf2@yRK@jsIR!JgTP(%bY7o~PxOV=6U;f5IcZ^qRW77Isav@Wn+~JWckGwW2bx;kf zg$(R5hgtjn zCSBz3g8)n6$1m5+-UYt$LaV;-79OQvs6!53V{*vONbY~Fni8=4S;irgkS#$ICFj-MPb~j1UaFy4h7BcILIJ<_JNT=A2YBy9 z$^C~~Ld zShyeU{_+C1;W*2|W^et=_jaUo$Grt4=airIW z2;2dx%kBWP`=JR}mmORY6yza(`ZrnfgE0dokF4(iiUVqWc2u#ovYEb+SGNrxk>3ia8ABIOuj?3-BKc34C!J;F`^Y4xzmRly2JhKfAs5rQCBaVhs=!&N}p()lymx zNF7Lpko1LiEs#Wyta|M77U-EK2rrbf$jw5xmanQs45s z2NqJqOha(_HZm3?tIwy0HUEk;g;DEX3%k{6Dvwn&shng zMi!{7J&%~)L6p?7tH_~NL?l@?mQk=d2Ya5gTLKnHssFl$xQ_B3d%aMS>8I<1>IP=Q zW)14~N4i(X#2xg=Ao~a&$$E!r6@-{G)PDEU%8WN2sDIv{=)t`YM@&kl6hUdPZd`xB_;6L;WJDwr4piuC)(2WUZyFq8p^WPw=W(I(?hdfD za7C4x8dV?1%=}n;KG}*!`PE2dd+*iJs9iQ|%!WMY1-Z}&{-wnx1q@Q=dfQ;dEJhd? ztV#3vS}^MpvOA6>r4y_&kfK2h(y;p?VC{cSX@xt0J^iBD_Z+f%INfYR5_5c>kh`#C z&0oq^`Zdsb&@lQ!VaOiTKP0UZ`^Wg>_ER`QWm}4Yi$&}Jav#H3n08rEe!06L@uZGf zDABbJ95;MsrK5x%P?35%t=1=8owE?Y%=LIcnbNQB1cL1wX6m0wbT9}FEmFFngXxQv z^>KQql%y@Y388SxIl*PCziv6jua!A9>kgoPRpYJnmt>l$N3ha<%Z|I%Tc%!9>SBB( z#4ZDErO7ZW#Tq@Pyt@zxsm1oUE85)wh-^7-ZAV2RYKpeYa;FlI#C&C1iDI;jMRGMqQZqvzO;9p*% zStc#<#}O$h0dVrJ9kUekVUU%SqyfrnSe~V@G_NAyMWjW=9Y9~1(tDWQq%E_9rU}U< zymW&GO75a5V_1TV&JugMSgV~GFFo%7EnDS?e6VQWCx;@|3lbbckYfgT+9%>4&-6fH zrpd;It$W%O6;MUOgPXc=w+(C?*Ygp}eUkgHfqpCG^Ud02=B)kE3^h!RN$U=H5!uDJh)dM_BTjH55`okG@J@~@Mw4Xr*Jk^<+(!+I>L<3R_6gq1ovD@lX z8>m_{{-)ov-ax;QF?phBCE1>HKGY(Znjj*oedug=n>T!;^*o)_K3_HYj{>?Rg*(Bw z?^de=K6o?3wCORNJoxh>nMLGT{CONT3N#2E24l~Hb9V=Un$El!h$ID<(nGK<)s@^} z79#oOxcF?(;{+8Jv9fQfH`1T4v5BuQiqSbnX<9vSyTFah;WO!WiO&;R2lJm&`xQAK zWdmK&!*wfT%Ey>^Ud&4m$vovmJ#(DRbXu6;xM%S4Ix+O=f_=OqLQ6j2InRg>)9zpt zCX)NHb?wVc`n~bwk#KUbUWstHR$d%sYw*M4w!9ESWr8rl zY}t}SeI>=%LEn%e$#@=oUaej(T_`(bICe03(5XH2quq4TL9sQ{Vc=Yz0x zQoFi+N0q)@(%r)DODfxz{4-Hrb$i|Z_!qV1s!i5 z1yGeiQRo$A*%|*ANh7xiBk=Uo5S;7hAEd<8%R zgNdnU=Yi&`wuDqrKhGG-*poXjm~}8aF;}prfed7; z!h?sy5(v4CdUclCrvbu9qGD&i!@p*hKggWD|66;1U-PFZ&xqsx@WaQ0L0|lLr`+9vQR#-f5QHlyA>BO>6b|anz6fGv>xGp&f$(Y=;Khx);2B`#T zvlT+r>SVy71jvzGj;d~IX>RDS)B_Y^{;C+HLTJsKt^&k4854c71%{Drx_+(Zl6m*= zFlrbWQaO+}T)+i9Jyen++%KAuO%2lgp)dF?5RP7OA zSbo<5!t_Z}!I+m?ej3Ul|fa#m-TfjLJgQ;4DpD&-L4nAPLmxx-lU^ooycXdsfi9-To zd)6eA2{B^wX|Wi+IWx(0W$HMlIBHlhc~*bjR@7eMNibZMy61-x4J>t%yG3!~FH-sb zk7R|XT&J;6a@=58)nRMK$NI`-JhX8=xb3Gs`ZUM2g@u^t#z4WECBc>P%H>4#v{LL@ zEzMRWFou9Wm735FMuq(Pnw#K1hI7R0S;I*e9o)yFp`qDrfh5|2-5n>rEi+r$9a|R) zd(wT=WhEX1fr>D-tgV#qYSlU1I7(|`l6fjP$rM`%Q?vX4Nkk<4ZAvLQ4#b+v?a^tz zOqs|YON5hbXc-Rl^fSO>TlAGFxRf8z=_|r)K_(?=NbT-m0K>s?`}hLqZmaPCCW<|> zEOL8&z$OC;6iRYEeTj*V=Z)vJjOTT6fcz6yTn=E@5U4wbVA#@Om{?@g=VxErfiQEy zEtdcs>(HFmCfY5ev@E+u{RwPa#k8qYnHMqBKIC|2E|eJT@*lh51U@9CnzCXcknNv? z{DgU*Di8`3uD3;yK%m1rHz_5jc1-;XnD~YJ2%(hLv)@A`ECibN@1;1%u8ahm-EcQh$I-zO6QrQv2-@Y#Rr}dSOyf&LlYM~Q zc3^vz`!UiswV030Hk&zbU?j4a6WYIvX}e#?EbJQ4p5iyFbhR83^(rfNz@QB?pk_e3 zGSD%B9XQ6G+9|QyFL;N{O$MKag2HuEpnu@2&@kpy zsTCxbg6Q3H50vr^eA3`<@rH3~Sc*awNH#{e1|6n~r&EJPQelvcF#F4izkPBI*qeKG ze=H+$WTz5eC5O^vB(YKeV>8@o#s?3G6;f{uhnKw(ZL>IdTP9BO^Zw7mmqQOrkty1z zIzS&;{`sC2DfFLP@8UQ+EbQ2N++l>)w>+Eb1%mtRXn~iqYr@{}N9bL22O9p@vAkQL9dk=lz%tNtwL^D5Yj6F^z=2 zra~&kiJ3G4w^|{ISl2Yr4T<9N{i3Zty*t1wl9gN>&B-$*yiqDzhr%*>WY(H5O8H*cPhwA2;RfPJ$rV;ROg(-v17;sOlRH`Fm;y zl90^>CoJM~)dvhaUK)aUStFn{CwoINVP(te-1Hqd$^D~Cp7?fR^or7>HnRe0N_#UQQ>n~bBaJ-zfJw0 zC_3&0hCJS(#1K#RH(+|nV9-fG%KH50k;uRB*O4^W3K|CK&8aWIy%{pI{x?E)V?k9- zsjWZdYlWq5yl^Dgsc}(zd;$Q$|6u<=6aacuA{@`G|Ca#%e+2eF)SvYknvDlp?*^aL z&rlFf+%GcRUyrW6CKS0OuvroDu7E_*o2HD*Kf}cHzX!%s8$+EtyCbLaUrBE3ZeCq6 z;)sdjQUrJec*I2b#CSyiham1q2sMP0iiQ@dZ%qhP(6iz25P4?c8J3C^RkVHk>Hp>l zh~@DI(;jPOLlOV~8pr)l)}0Z=)RCS=8LVZSs{B&=L&=6VOk&WHI`A;=KWO`G2l&XC zgMHq}F9$dd(SO}I81CkOK$$q#hhFTBxcG;0s7s^$Q|d2%AK%p9-s@Uk3C3>D#2apw zeDYk~Y1 zLWAAi{excc9g9EgNjWzUmy&z)1Nn0MPqUWKFSF2>DuxmfRC}CbW);_^fOo+aDDt{rFRt zT^$*T65Am462AK$%dqGyP|KG7P0v}V`gN-A`|qzUS;rOi(*G8Fd{I2S1hPHs3Y7q@ zD)OB?OpZkSI`MxPEL^K1U@Y_m(yF^$dm(O+c0?wwyw7j6oKGR1dBMupI=E$63C6sG zvg#Q2lMM2SAw-S7xM>XSrBt#})tX_PhP)Kjw3=WY-~Q+Eb^| zb;}h>WZHwLEwt`9>bv{aJyvteq)WiH>R%ql>NAYO?RP^=CEgSYA2h*S826f3l1I0T zjNfRR7SwvU2=p=~Qh(STlO=MGxV_K)+2yZ!41-6RF#n)V-hSWN>lh|s3nF={J3vIb zkId7SEwP&dLpVzv%+euY{T_8}PlwP^THtqQXvP3dK#w^N5$JnRne zawOisxHw^S5d!;F8|t*IB2gl2`%dKq;bai-C1*DS9`G`SN0jx$-yv?*1FYDs4l@3U zar#_?t{E}ey{eXfbyUR*J7GvRedn)?QZzwcWu2%fxXs(bz6 z3=!Lx+<3A$?WD0mXXZJ7-gwT_vwS{(-X6J}u~vWjPNk@hJG->OQfQ<>BdI#~+h)t? zVurcpXnFaEVNJ(O+qjU)T6fGHKqI&`IK{qkdp;*b`-bmyv(-*_%kOcZyF60=$(10; z(kjKvhjF)f!%SkYwP)tcvaKMrw=;RD|D>?p#0_QI{;2X@KOEGp)s61!krPr z$p``f9)OyPgA<~lZzJLncJK^YSo^bca{qtxhw_}al~*-89Tm5gm;W{LwztDq!dnIN z|MmP2K;HTPf^bdgY0aOGmj8a7ZOY9Y*j%PvAnxt-|Ic-A=Z5=w?nrf8_0M5k${hfD z2k5do*TjW5*l+3N?*O<#_F0_|-T@Aa@*`7PLVLIOV-qg_{ppjF=;X-={xTh9T7ABnuPd=+vpzmP~Osd4(ie8(|L?mFg@)q&^wNzIea0>NyT(d#bok49R#?xR10f8l*sDfv7| z#+kea3HT$~+PHwns^E!)zgx%diF_|sCvCbWB0o z(z3e$F4vk$w@CO#wRB1T@0rYk{{+;33W~2coW#7>R0)R*Z=CLx9FoA~+8UlM?nPkQZoyyV|3PIsM9z+O zv&kLYgxFnyTdOGK`)gG;HlobmktTJX)FaixAHTTgQ<7$+&|Idp--{l;jcR^y@kF$S zLdIC)_9?~qs!yZzU){@RX|thz>+Y>oXMaKn=D0&v018}78mgE9B@0^h_QDiB`CsaN zr*{CytanaWu-Fp+Nr{O%U?3792N;h$rTwA&|#;Rv~r`P@Lb)pKzGTG#9O|2>N zjJ-#cULqr@K5<*`*jWz;RimHlnk<0VyLd2}l!G8A$|b10i@4Cb@4UlxXV^h;^Kqbt zs+NR?Fd3+H-nz~`b8q69zJ(rXwY{_>LB9lzZoi`vq;*a>fh6~?U8fbPn9cBS{`rRg6GVc@#lDlUh9A-v?3AQy00xm+5?}?(DRtHh|sSSp~6;N5+aX6n2KaWeq<4xBXw>vixDFK`te=dL3k_59((+q3K{JS_p? zjT+Re=cn^aln2S}RrPJOrn<2n2f_Zj_qwI!XOLYBc9GXu>)XO>VV%uCMCS>s=tgW~ zwc=vK{Z~xUtBn7Cy^ejz^HGJwdv$UK+qkfo8iOCi&N#=}Na$sg9gE9_VPwJc#?^z} z=Fb;sx7x;^msUQ7Z{TsGmC>)I$2y}>+9$M+4o@5cYu^R15x9;!=ybT}{y|y%TKW{1 z6}J<8aZ7*(ay1bg}WT7X)cYx-RI{+B#_?280I@T;= z&M=Ph!qGaLe;lrxpxe{b*e5wsV zRo(9}>$-@`KC{mHr*-yrys>84pTh;wK&YcE>6E zSh=F8Yo)y~miG5@Q22)~HPvt^!3oa^;|{^WK>P4|%6-yo^RJ;I|H`!@&1pratXS(d zQLj@@fpD5&o1M1jBwjX+I}X4il5$hwYdr(0|ojD#*V8#YH4eY ze=VKhs=LDHby;9Hb3%K>cmBim zP2HMc;~ij)DSVRZCI~D4{SNR=-d}$0O{v86nR|5ia${gy$o_d|>&#CEr?F51)o)IZ7IthmGZ(o{J%>?q!1$=1`BcZCC61#>wNl`^}dfL^bc$st*=CO$hfV zu#VrJ6tA7ub^n%=x~fwn30v-{dL820bUZ%OajUu7CCho!CtSJZ)_W1Wccpr7t>Z%Y zws7zDXg@qZ_G=PK=m*a(>a49;S| zdggJ$cV2MaP;k?P+aS`+V0c=a*5woGOSP8EJ|!8f1rc!jDd6@aaW&@C7CYy=T$Xgk zLYcOUtIn@yj^(0moM&S4hHJw|%4|J#Y+WNQz$-_Vm2=NGJHoV+fsciK8^`BjMXVMF z*}p0Nt+pDuKG|VXF1=Prd+TR&2N)k_*L-rl{onT;_qlo3k`jeFYx;7u=DN;CsXYF( zf4wWFEMY#w`MUrcbl~duXB5=#>_SeirpMd{32q#JfIR~@%>qYAScz4IdxzTxIvLL3 z_GwQ>k4W=wGCr(avq$#hv~ji#=yC~Y_vah2Qz%h6eki~DJUV4IDC<}MS?4<13%ovo z9aavP=L*(+yGt&;AF}qFlqmJ}(n*uzNnV|)*05mhWIkhth$6+%0-?7MS^^=SeE)N9 z&bpdc$y(1d=A2{9_cz}8xH8Ty4PlL4H&qRiW{k9J-d81s9BLeA&~vH#=mz5;`doO$ z8tm3%T8&n}B&DOcq=WM1!9m%7$4B(oM9>>3mTsI#aOSGy)vg6ySh?+)(2yP9)u?*` zP&4>M!XqJ-q1fJY%&9J2w~{#a2asgq*_*FAuSJ4>%)P%A^KHk}(;0dQQ;3c~oTeF; z#gIZ;LkK$Rf!CBT?J9VwG5p@?lM}B#({zZ(co&0pITEFW!96A1EJ^^))lU}^f*tbK zZR{Y8u04)-jnNkf*=REuO0fKSUc$wD+ii8cw9aMaU@JF(;jN*(W6AaCD6 z-kV(--(mw?Cu*RY1RpuW9b$LchR$*1iLsvSfsf0i60tSLBRB_(la4EpBALnQkDd@n zBa_eoH{Tfh_Ao?UsnK?|i?C!V89i5#3J>j*Uf3H}kX5<{zuOM(-KcE$c|3ZOQ_5|* z*j|HhIs5U8z)yWJwqIPYXol=@t-xO4Ze@B8F%nuZtX@C2LuK^t9=k(jM)(5nPAZgL z3Ybk|tajtk8+LF z;9>U_tWKg&s9*<^j4q;wV>_J~w0EaRCb)~SAmv8P4o2sikk;8JZyj^76=0^3iHh*u zf$yq`@ctcguomlX$0iEiBD~bUXlyePbK}5REtD2SK)muVN*(8%MlZCI0@8?uBUx$~ z^~`1youl^K`-0DoP1O$L6|u8Coj#&ZHzL}vfJRY%GQS?g7q#qk_K<|s9#9l(ANT9N zOr1SWgJ`8iWL<}65c+1QR^Dd}Fgxg+TSp0r_tB|o4Y=R?g@$a&%wZ@cF5*zwZZS;3z&fvu#!p&oiAS`u*WmZyjtG}H zVef_gwyeFG;W*n(7}oR5aaN1=P*&0V;BGBS^kPwo4pF*GNJ4U!=i6@R7j2mvbWs(o zhIrvTPqsn#9&rAS0g>c6f>kxR`Uj)VzSGiqc(#!O+tbyKI!tl(cS^knY+J792n z&7lC_q&R{h&hjo%z1knOL!fS|U%qM+a;WnkZXwiSvUf%Z)Ba^a;I~saQH2(oQo_!C zRH3Pz^APg4mf@MSlbFi~1_@Y>e6fAKz}ae_VwMu@Gmx-=ntUh`0V!%1FIi~V?)kEx z8fQWmX_&%TzYr3Bcbv*c5~neDkYeq z;-?7T(&paeeq+5(kpf?5+C9pR;y$>307agr>Sy%ZQI&PO^>~Amn&Xu#7Rz@!qjrLu zG}%CsTTFLnTjRSWW*U!_sffMJA{HN}IuHBI^r=p%$Ta0Yo#J6R)kcUJ z#$FhMSP2C;vGew>C1+>p)XOri@`UlPj>N@xQ5CgKyR$q?;OnF{rpKg`J%aB-7-4*4 z$)x=&qV+krq@*FLLiAl2TD??rTCQyCz?u|76$;CX-(kz>10>l8Z5y3n4;L(9?A-n{ zy>qKw+g;ch?LOvgW=Q)-l;xYQq85^FAxn3?UxVkvj@F^^QG1LwG5zUUNL3q()|9aAGr&?Yr<0GOnBE;I9Sa`L6aKAP&q?p`_n@ARQkrYvW0OcXJVN+v z$VUITD2j@&=9xZOF0Chb$L+?q$zq1t`Vp5%R7i3Bp<#jdoIvQXLnI5p(jXZc;U@H<+emCjh@a;VbND%Qh>ix#6REezGeM=dq-0Xp2)P2JD*Q>t2kae=a zeVDnU-M+bFLQFJki|AP_gS5#Rh*(iWRw0GG$)bdL`R!sKV?&-WG=~})n2G0?L#`tw z)Y<}4C7(|Q8~Z0lA{(+UOFxjn>?a(yLid!NR;ZG1>$#`~%+J^ff8O;#`##rj%P?s<#Qw{FKn7q+u@XNW0+OI zPVZj&q)t#I%Kfq5Gp!!m)R1=m-X`yNEwsm#y5DnYD7S8EZ$^w(m?FghP>VgKWuI&1Ju8{Mr9;-Es+w-ZdzJAwn zLzqB!oFU>?K}{e^`!Y_o?`qE}=+Z$O$E=aUueHWW%CQ$`BRJ98q)`i>q?WocWyk+qe{3jsbSrOG99`G=HygAMIjf>q3Co{v&bjgE@T-yM zTmS?7CN8;zKJ#E_xA~85Duu>iwfwcXNArXweVFwOK(uh}crLxbMK!Rnt2wn3NS)cl z6cBnU28J+kQp<6=dIa71;J4|Gy{hu9!U8czzIgmGMn~^Nj3Hzp2TNN3rVK0?J7mw_ zA#(ZQN-%sYR^gEmDrlEm!zRW$MbWBCM5^~(_Q>$lt?szU$5kbEE(+9q`O~E_lhYZb z7*9iwZqU#vG*9Ajfg>Ip8w3-|XUS~s<6234*sHvYyMf^eGQo2)VRmc7BnVcE@Vahy zKCD8r#a}>=j+(v64ugVW6D}9Ep}kc(5=(02s^XzGEvZ(Iw*npoQm>JPd zsuV`Kv~yQUR!G*)k;osMRA#LG6X7kkUlwNx#;X{Ew%gi)Z>_VT?VDwmUAG}d>N9!< zF=71QBk@Qj^ATK!&Jevq$_>}8jBs6Gv)Lo34^#h^?*2>`*;B+!IyD@UWwAOxcVQkQ zqH{zZff}-@T0>H5S34Q;$(uVEuW3h~>%aXP%D?xGvxlq*b}uXYj(E_${i(G3@UO!z z(Qbn%6^!;eh!UliV(e3#9r8@ALOy3_u3R}SMqaS(Mg8(RSK`x^WAIFI2o@$2f+dA4 z&r7(;8Yls?%EglvgSp~}pYr=(31N=cP&3l#D{hc{mVirAdVL$r0|_$>84|-vMH|@5 zLRZU_r2H96)&-9xT=Ylxjui}MbGb=ziMugzL6D+%T2u-1U?1wSeEpit(e*wlL;V>n zie$V11sg8b^(g&piTf7ErBQDC19G=nD!8awAh4O638603xI_PU56QE4#Mx zl)I!cgR)X+ETjxL)4|&!r$xrdB}A$r()4heclK$*w-171^AE#HC^2O+zfUDCbqnim zr+Gj(u@mr~xo^!V>!P=>P|VxNhLLDp&Gud6Xs z)ro-WSnVO+Dea#i1qpWP85pb9r%lH0O_yK@z)T?3&vLt#*9@Z|w{$b|jzPa$a8d#_ zJa;>T6vne&2|-l=hA{(Fk4quXI@Xj+L$Z<<)asU;%H%gppjM6_{a};skJ5G=5W`qp zNNYexj}GFAr=ELMA#A69l(1AGF@YJ@8}taf^Ed<>i=}TngtU<~v66eAi{2p|`tvrb z7E$3OXhbHm8S<91yQn1C+-TnA-1=g^ybI^gJ_vToB+-)f+f{4U%jeVNN<>)%Y+rGR zhV(Req+Ht79_!r%4kS?=En(@3eNSzUAiJd$IW%m<> zj+ViI72;zC^pN(zM;l7e`zgk}F3!+a|F9_`g_h+4 zQ5~Z;%=1#P5>|?uCb|{TpPo?vIfUFsf~635l~euVM#%9(+;OIc6=Z%ssuOkwthlC~ ziF&+{y@``vO(TZ1Bs@gl>|g#jRw6><5_`5GC5dX~Uzickx9gtHGc+sQ+G`e+(ouLS1UPcZ#{)8J5t;-#w>>Ab1v?*+wz4}tc@C!b_FHn)z=?}X=YD~(mWEL)g}bBd4I^uPFw%f1SfRjH z!2Zuj@&A;2kxt;1IPPyJ=LcGEqai#lp0Lok$q6@3pvUs3JamNLNk$X(Z#Wibf9Agn zmnyxk|9ep-ecQ48OOr>d?OEVoGGCAn+=I2Vk9QfqhOX0ua+ev$@kdXc@c#GiIN||) z6yvOq**CLV+$;>oWHo|l8ri`9;?3VO59fr_G-^BVc39jqc=_LPL{}NGRS%hA;x>#5 zzwVZo6g-EzHT4{yj1a`hllia21+VVwfBN=0=BzGvH1~^{C>Pl!y1JiOXKxB6k)V=lt}q z%((C|Dpce18{v^w$@UX0=C5qg^(~djj>?zLjGvLG_1@8l+=n~=u}#pzR<1|P;T5(UZNJ1cuj-;T+W2$zpem!A~JaSWJBkd;8JBYB{fq@@!6+) z4?9N#e;ix*Nqb#|{eJaxFuuxQg?<_a~`x zzp*-KgqQrwhc1mTIYg8csa2g4txPq;S{bE((_{esVqwkUKjG%qF?aH$=ou2>|7qMn zyXUPP@*FQLNc*i)AHWRDc5qv9Z=kownb;=By@y>`E*QhfWUkY4I zrpqeZiwT7DuW$n|+od{TWHq-1CH_%=`r?wdAb^t_Xx1w<*U*!V=>5V{<6Yunyi4ae z!%Ym0^PAJ}eF5MY6o}+8xDooKS?Q(tv$DU(*r4~#Ptyu&zLfAU%L2Z8R^b3}wg5n+ zN1mZl&=4wPKz+A#xB1Nd4{aAqGhTL4bD`=DR`-wwyZy!~!Uo+r>irujg(Az?!K*7zp3oX@8Da76EPo>4G@qrw^L~``%5UCbuMd0 z^HPQGDBtJvT1wx~-gY$B>IHNs8RNJO{G2X|n-x3WJm0=hnfB!L-`U2aB7x+LE4tmr z032}6!MNT*!0gmTttIg?>2^y`*VpD(y7R5glP=cx%l=Ff2p7Hg$VR}DPfdr5YgItq zZn+p_9if{p)Z}RL0jDxWMC}byI(cs+jMqSL z_ewfPsy3N}>!wAE)k~4iL1Rp&fOv+=D;L=t0|NTDs)T}KV?7O&Y_H@c1Lxw;Wt(Yr zngjAqYiSL=$}5qwBnl))EjU?|PNO{wzFkS;R;gCqStbgI_MHU^U3Z{NWyycm`u1A@z<5LesM93lXSpm_Ej=_98bqqyy*u~2 z{9Yl^xIW#uju+aW#XG`Tk#D{L|7VJCFQQ)Q7Jh$p?goCTha=8RmfbS&yq6b~q2l{; z1Cty7--Uv!g1krBLb11FJnl*XYu;%~0uOr3%>WlMnOx83d!Q?`n&1lo7T`;EXO37W z^_+VDNt|62b>b2P;LL#w1QjovrPhcw5oxP}VZexmXs?shPc9R)3cgf4aKTO>l}8cB zxg`=^)xHOLGz{29@FhDyRR%7-5dtzFp7RF)76i{mar}E--^WMg!`zcoe&)Qu62a!;|9LW(618EOyUB`h69WvW?_*aW>kL}A3d4LYs|sh{&@TU z#mxT4bo}4*|B+^=*j$H}nYQ8$^PB%ibNr8E6UqGlKlg>T(iC|uqSAU4dClok{mZW} zg&$fy7ssTWPgTL;=KHhRdR(6{i{HoE|3r}7rl=P{o4!+d6LgDn=dAd(f3N_@Ln2a(pW9V3VhaLtfEpDWX zt!nkBc~<%%F;p+$_wgkW7J4rK3QXZ{rjsUjrlT0>Zr8s|kq7(&sS6HIJcmUB6|WgL zrl?P8KZ3a#c;4FRy;z*lm|gfJa;Lad4Y|$^Enq5^h_Qk zK2dts4qAptWOp-(>C0DR5^Y;nFjVG78qNx2{Q>kzT(eE#3o`W^OJ*8LuYoD-l*HA} zu_2hNe*lK74%y$te`+xu+?HXh4*%_VJDqK5o1vyH*f7}!TifBpYD#alDxs%u(DTB# ztdh?(;}LBzyvDJ9g^9G%aP+t8y*L>;o4=Oci&P79H^B=ucbgv}BmP0+V=mx4g2JC$^aMyKE zt9bH}zl5g5t*rIlni9QCyR`H2#_2shY8Jheod=d;y}g&ND<%p)YvyiFaZl^%Eow5) z&dAF4_H+=;xv4FfliGuc2ye1duu>99TU?t9UP-ShcKEERpA8ZRey6Kw^@xd#nY`A# z=<+SodTCwWlG;lbKd-3Wj<)j_czyB9Sx*e4N7dlgv&2jnkYl`c_rTAdzMe}C*(3Z| zup~cLKQop6$q@0K;&c+;(M4RMU1|p&F)wz=r3uOw$sRqws>oqsrO2q%$<`FVe_d7S z`9r#eg$2Kp@PFPCKlTRRxqqhzj9Sc9WSo4eNpYn;?4NT!3w3nWPPqSw?^zSmdxj`} zv0dhwJJZMMVSSXzPtCBmNU3Jm_P>2>wC5ErBWO1tHV4d$q>*8GuTN5V3z1?v`r2wZ zg7p{q!9_NA^)*eK`FDp-Pfwo(O#S5SE2&oW-yQB!Z7VyxXbVkz`}6F%G*H{Fr35kM z_s0 z7BQwT#7DGajnPW|OX+JYn?B|UI~G;{k45`mFtSlJE;f+%KNd|ouWaENA>Jzx(XvWG}!*&hI9OM!LBa!_Ef*|A|)b`!H2TNFUC zrLMz%R^lJPB#{;R2QWK$$cp^~7??d~LzThsjRluKfOz&OQIo-@V~pUNY}msTzO`Y> zYD_RCojNb_HWQb!PpRsGpJlNbGY*fDfNiEYTk&Q0EdH1O2e4bX&D3TKf^1-hR=$zd zcVKZ{&wWhM(60UVGx!QdVbe|F55QW1!G2SS0)zJtV6dD0W??5Anx>SFgK^excI%@H z>-Ib!`dB!Lr(>6$;vCJU{_hosh%9y|HVH=l1E}aY_{shhG_#l8Hiu;H!TtcYve3sL z*uNQJa+FVv7b{_iY>zMZuFa5ygBSde>P1CmYh#OP9_V~iNhsge8u^({k>ZA=*7t8i zLyZM(Z|irLm;)rj7wj}l>bQdtJ4x8U4q4A9Fc_MObcHq&hBZa#ecF%BDP%4C@S))W zdHszW%tWwt4ZRGha4zQyB_PdXPcJ%FpdGx=7Ds7UD08n{6TOTXVTbX!7R5`Ziwfqk z8YhB{wHa#7M+rzURF9UJ=6(XsVyTlfY|vAw%Kmu@;j) zI7Oe>7`#bUl`XD@4(~LROkB_il~V3YlX;I{1es4@Ns97+0N?eq`!@^)F&lA?mV5D3 z{Wm-pnu&G;4ks~ZCvJmJ&&%VUugWEg@hg)z6!`SFH#`J8tkA;BTQFX*_^u;5o!uYD zn<(Mc0n~e8Dsx}Wt50{v>HSdo0&7F>wm8=xz-PgL*Kgt>`6ND7Ee|N}=>m=4AsdK4 zoRdC{-ZR9T7TK8Y=oOa8WbyF(PM*`&T8J&z$3*(!A)^bbR=6D9?UPfdu|x5Eu|<#_ zLkNltZ>{SOz0mCeQfbBIG?E({`i4g;qUYXhA4Br|$>$RXK+4{gj;pmU8$Yb)f$gRWJ;llH8Wu%|CTwJSd7<4w(d+29NIwRSdW8$H*TFNs>XUVRm$)CrZDMs&&9(tX1c+e-}PVTx#JcL&X)FUE#}6Hb{huVvPR2q>OlFFRPLIQ`6pPv>yTn@(!JoMlRP zZ!WX+#Mw*2jm6gqz~N53X6x#wj;*tfO>qLTNw;4_Uh$nXec>#`&lUaXaadoPtu1Tr zDO_ZhS;!NLu)M!Bea9|S-V<96QmnbA7 zd?cQaVm_Y3fh{J}%&7`q6Cw@AMKo7=bB$_+Jr4V{liaTIlk{nYBbw7W_?_+*u7{Jv zr+DK(fZNd=pQW08J|%ZM5(moR@8b`dMjBl!QS`EX3ApI9V&6~k4`3n-EcFS%8E;kt zU4kqiG?h<=%c7-v{s5AbtJtf(gKbjBfY7qy2?DCbYx?2_ivoNIs3%hu&JR8z?sM2U z4a26e@ehBKo)tKjO5Qv*z~)EBgz%B1k5ke+Jy~^cOBE2xb1<&skPiNc7R|sE+OIDD z57+g8y^t!yCMS9>$QtR(vYArw##LChcN{d*A4EBB@yIDHA1tIa!ZwVi< za?+O>P}@8qH-A&1^Zx<(oi^iLt{4oyjv#lS z-A%c&UCSDU9nO8WIGK=qdO}f8n10c^agj)BXYX>tw_GD57Zko z{IT%N*H&B~CpF@q?!a1Ox#CEJY`(v5>sN2@Gc!NPz61vMG+HCgFIU%M+poDOobk+f zw;x|XOr-|*!R$2^L<<+oTM4FO1Rz@7eZ!opB>I%4bEZ?3Ct5BHRRknUH%|sRlCXVi zTEU@Ql?0cYC(1|8GblKw)!~TGbV7k-FD-Y>vu`pgQ=oab*;knrWOfzq#vyrpl6tBl z*H+6j!L;Yt5S_jdca=CGB6qeahS(xA}!6$-!-7dN_Vx;*mPz2M1H`4exf))=b2p7-?44*IsOBX7_vEoQ+g{L z64EWdQ95>Ir(*#Ag7y9+b4f0!C5w&i{JaqUtPLH$Pr6c(Xz0(P$T7v7CO?*Xk} z#?99Bm^Nln>LwBu9Yn-Y6_c%44*Tn-JKF3VC^Pmc18Jo)u%oaHZ(|)8f9{uMo$hr)L$M&N$^rw=bJLR zP236j%Y^iWJj@CGLCJkh5^X>#q$@Kdd)K2RFYTREf&DukmTHu=C#9O^Lz&mz&%@60 z(~~9e_`n8QtU0%}4wtZ-x)(2gG`qg@uXpmw%9cW2F8*PP$iBX_mQmrMe= z8XJlr40+nP#~Xec-W<)pz~$Xqf%1YagbtNzWn1tEgA`vkgP%W%* zpf}cE92h&o{v3T7O#2Zavxj2fJrvhsEhgmIl`nY2)vlv0Ky zN^w{$o=1i2l=v=SI;6>OrqjVHK2NaYyQ5bV!Xw_cTgi=qWfGJ5GD}Qd(k45&Wa$n- z@k{w%m`^K|76a{vka`0hV4cYUV5n4C$Tz9Wg*G(NBM-pJhK5XU*%xJ=$g`JJ&1j-D z>nrI3nQ|I9<11-=55_FVP8WzDOkljeE}q?QF|AICV$v&F9~FiD(B=~hL7UTP8{>{X zqwQ~nk2ez3U~DD=&9CHbwYc4%dG=$V7f@JnTpjw`>knWL&+ahD91Y-pqyZv?q)>h*Qr3$O zrta*L>Hstu=Z;@lZ1V)R7G|3%BU2)4=*Qrj$8&Rn(wEL|zRx+WyrFom{v6!5)~?Tu z{D76h)fAFV`i^Y8yr*BiS*N2;*<9P9Qlg0^)X<4ZQQog&+qG@aDcifs2aSP2Fm(4Rv(Mu;#4+dyl|*;?ahI(Iu!mzNr3*hydpVTv21F&+sqK zt5D6uE1$7O_YOQBeaTx;=gGyhko#Z?d0qbgWEo|jHWsZ9mcg)T)qr{Pj#EK>ptQF? zx1hGFr{uwBnq^)mtLA4n_E0<{^;)=Zc0U|;D~-{fOVsJ?8Fqm z*B#Z^yYNhxn26}qU7*zEL79xA#(*+FGkLiWK^JdQ8e;s3P|#!Vit+ibm9 z7S0`8X#3NIUCZZ`(Rm(cDYN0=zbItk6|I!mWTw%(>{IO25BhL{kPQ)Ae80;dmW)y; z5xe3P7Iur;RI1VcEd7J*mAdj4l_IqnM{Jv0^_A@qq$2)Q;o`celxd{;Ck0f~%pZUX z*%Gx3VsDHk@S&rF&4Pqhw|b4d_DGf85K{>s5EmccOQhG(WgH& z8oOvk5xxl7K$KKe?Vf?S&gS3!(Az_1yZ$bKE6Wm?DWgf_j&Pumq}507bTIbxM{w<8 zCC&EeVZip6uw~F0l}}47yKo%*$hQ84FheCa;x2^7&gmYJlBXwv6)>y`)v)D&kD8p_ z@}CSJ1`fCDeD}yd$@MNz6KujzY-Q^bRo|YxzH+9Mapho4ayv?1qE}UT@m`V58iUH@ z!Sg1TD4I3C$DQ~r{sHu;p8f-nEW2iIqJo?29T-et7E4NE_9o;p+pfWsw=@ur;+1;y z6xrlnUx+T>q-@yTe@*OKvGK+oyucbCM#g3KK~1Q_0!=q=;Uwvz!LmMU=XgxkjUWl;W+yHV*Ovvh?@WV5pbw;*RD<9e9h!S}EDHyXe3mXtf_nm` zCYvEpc2Hk-RVXe!nh!x@vq$nRvzPgOhcyN5bWHZ^GFYl>{Aw{F&8l|dWT}R- zW}+XTh<|gpkwts_V&6>{i!sFzT}_AX(fBq3uH};-_JFFdn*fu= zqIsO1>JBdE3KpNj;N&KSp~icj?#{BvzMeEa6-~P6p6PVG89e)CY(cvlk#Z25zBZ~? zZxtQ?*UhX7`#6kp%oqLbII+oj-GRYP+a`19j>{g!^dFyHZ4&!}izoJ`G@%J^QHLM+ zUlb5cj)lu&rn@BOy2du_VeweExU^DZHQr;+Fc3MpU)3g`lh^adNT6z<9lK^DgGF%o z7Fwzs+*$3yfOL9oSK~77_^M^mV)7Lr7ooiuL?C#A=rwRA=TucIuW)Y1j z+THA5FWgXEmo?_5n=10wHDJ=0wO=2Jz|Jtw#Vu|!pvPJ@#cRYlN8*B z6=g=Q=yoeXus1?V0Z2_gn8+QPS3_AHfZ`{S#O^w{(Cqy!bESCH^5Qv>e`y!HZ@Y0G zIf@_FFCMCXq!ST~&6ApbFCT>(-OrG`DByl&RB>LsZUK+fbpT)fSc|`8d(?0$l`}Go ztG1VUM;N6VP#Peqrikp2(+^aC82md@uCajaP>MK~d^?v5lXvOE5lR?We|6{(AVsPe zPcwK62WIFXY?Eb|is=&odKTzekUae2wN|bZM06YZm>SntJ_hQ&LkgB7k6%|Un5vsC zIV6y5a|i|IMVd7G79Pm!EWe1RaI9e4Dqe&q?`pD1CrW9c!%m|IR6WE5@IlegWnXVD)caeR3umbvU5cG|bqs_ZmzLMw>tbCpLO6T3X2sNQRBooNw2@3dKmJ(X61;D;pl>HF z)x5iq9$0gW1a%lF(mkgV89|e4@!&wZMXziGzyD$k^qIFcQZd>&YQc1XYhGtM2-Cz> zK^H*^(GE363~gu}C+p4_U>{*Dwq2Ezn53EYkgGXM4S&^HI$Linsm&!OK!-kQz& z?moD#uFuVO_@%V_!3)AT)lsWF0Z;L35ywFja&2LdE|=(oA#sA`{|qwpFLf0D0f27U zLA^$fERDmM)@^ zp;6MPj~#@%ZuGg*Vki?+@zg$+nCjsBhW8J*X9G8QhMFLHUK!R6ryC>9Lt{-;7K=dm zgWEtisy^zWD*<7}Wr|gIsdm(FeVP^#r#$M|ErcsD=XOo>Dl3P`7;4DUse`}+Bd0Xp zZC0kRBDzNgRh#HvA5md4LUjzDgoT%;UVPn`h&sWGn!NfUHPexrFiJ>Mi34? zn`>EbDN0C;N4b?FWw>Vt<{gwO-oPGar$d!#GGHi{B*JnecBTkoh5o1W z_l#od>%Uu9?XsD*SAPJ!WaY?|i?B9~JPF!Z-S)KsXnCZ*An7CJlb zW&7_cY<#I<5KsOnMo_pxy7-1(V~;(emXfr zH>C^X;41BC$}^kI|GCJwsBmLnUFTW~8=AZ;XSzjdVhwAyQ|5e6Zw5oM^7>_ z}LMZ zCeh3L!EG!T*^*^>*}4FrK1wDWb4ZTWaB0}?OYoKv0TpPt$|c#PwMWjNEoW18i0RL6 zXB#ns-(5fh(iMetpz$<@fLj-bnCM>xI<}o8>*`q7pA9ZI__D9K(7Mo@#c|Jj6^l^= z?93X5|`QuH-e`h>27o1`i}- zL)x{Gehw}w#o7&#v-aZyvx}39^Vcu_f=#vAzJH~1a^OHS>v-|Qwr&>8iSAP*Nwx%9 zCE{nyYO+qZ!Jjz%x5ohr1Bnx6@d2-Plxh@&qCEV|?fI zjX!|M4~^%1dwz39WG9g<5nb;*%tah?jzizt-EeFgTfF8bvOQ}{sy8l z*>5Cl2G@x$%b^Ca1Nj1jEBDpMFxCFijGUbNaokgOX`{L!v5Y{d?FMM$@};O^jqOQ>i`4!A%(1YC$P;YW;D*`D<`k*@US=B7-Fa9V+o?rnC ztNP;)B1JR3Y^RuCU2kF)FKa(Tfj=F>u2})c=M3elBQnhO%=H#qpVA#b)QsT+KOyoMpu~5NRbiT{H%|D1+n8+NGMoRjAE`;0cr5HRD5UF z&2bQ2Y4Q~z{0G3*&+mM@y+oB3Om~?xb3JnTn6|>ldntgSpJGi>b{)}+kI8@1ZJg)# z7%G#(zmav~;k-RYbX>$+lJ9fs@EZorE@=3_ULGD3AZs}ytuT)@dB2L}G4AaaxHv=) z?}Y65Ka-v+NiK*U2<#J~Nv_h$ykR`nCu)J5{v4S%kx!LhWh=&r_vlJL^h{-{sWYP~ z*Yp&)Wb&gmO+Nb-3_kROL9u>&G}gMa%v}kYUu90kda-KOv*1)o#l6#Itr;>ryOh!y z5ndOEqLRqinACYrdgGP^lq-w!Rqpy#C+llP$PGXI<01zYgkuD(~qIR(ko8QVZfon8TS`nX`>XRt5#Y-$d;R&=Y^zlm5BGTq(Ur@Jkzq|=!+Nf zxF8s1%309nQaFp4D8=>|@dx?kE$Bu1xuohx%6=A-JaT@u!FBEoZZg+SFR?(O2;(zU z6wBRN-ul(U&_J!K<&{BZ>t_cerC-2}hr^O`hS)XJy;mW>LVL@o9 zQsR$^8`Jpu00~D_H6(uLcF1pgzqCJq4gejCws~h12xV|hRO&J$OC*<+2Xb=M0_5G5 z=EhFvXAotQg>Z=mlYaL@&id;JLn|CQD|P}uhV)Q~`z{y#DGE8ORw9bAN2yU+g-C6 zRvi~m6Y9Nib+{@7$~QTS&*+L2qR2)=n&m2UVn6;)6^F9Vb${T!mBYixw3v9c2RnD? zQQnlqW?wqSnx(*(%8)5!XRMij{}`Md2}DbeW1;*V$c-u<2%O!)?I-tCWFS^2bJD26 zFCXoyFd?{-5yGaQGEWI)FM%91JBv7kaFM{iAH5|k z372fRKSkk=F7UU6u=1T?)Mb>n&(9(^q~;C3#BNVaSCal^84C}-$RRT!kA5#oxkLGC zp+<$BuTddeiZW?BL5TZT;JQ^8lM>B8-r=pg-If2=H7A}b)V(VeApYv-6(f$xeC51L zZxoeuSobD@bYbP4iI6+o6XEoePen#9Z;@}jK>}F{YFyapzgs1cypA9l8Sl5g?M2=e zft!fHZa+&byiup-V|RSwBs_~p#k;2$C?Crm+`#)lw9ZDiSu63kJ?)h65VW|KA}dnN>u_m&U!Cr<5-O(@>ArA52za7~C^~Y&%_7z`sM%+t z@&5c2ito3TLWZz$9xP11$#CbVXv#K9=Qnd)pXRdT7x{48@63?OS&DD0kS_%nQCObi zndKpCP8po7OPLp~{xIOgXSJ^S)0B`-HFj?y|6(L3Us7=@)y5Xw$Qhn449 zx|zM1+?Nn2#efYLq^9+3QEe44P%Uc;7effvsgaxqS!?p&6MDNu}*7-4wEZwM4- z^Uj0itFmwFsudhRj#LnMsr-1M3m9-yY+{G@tK8Mmn4&ODtfGOC2pza2-OB7M?H?Nc zmMvb$*TWaGwz64Efp;ly`G))D_Vm$}p7ZAtCx)E7YrQ za(b3?Wy1oY3@xcS0dn~ZVO%mnZ^7qLZ;OQ0^9Wjjs@9o&DQ+Ip1m5X4z|SOJv4oID z`#abC3j7j#w}inG1h8Ac<@x>lJNE{ncjWa{|NR5t1E~pLa8lI}`>$K~AU-4&Jh){> zETWVX@g&yo>n4bt5+i^Mq!yMeeKwoZP|A>?i0>~?qq@?~Vl}1V{-!_9(Q5*;1Xq=J z@CtZDbtv0bnopu6BGNZRM*CPSf45txqnalr< zpusR{Yu(b32>-%yG7byc`8L;LC` z3?SN|Xp6vjU=&AeSXL4Cy@XXWisT7}k_81v3NDF+6$y1VD8YFV1h`nyfDgfxfW%u8 zO4HDUDtso;q(xe~3?XD8%QkH zv3eb2#%&n;sD|)*gRhP7OB(vYT^m?uy`e0I*6OvK(&TghPXM?LNAm1vtb)hn{F!D} z=^h=kdww3pOqnK;X*U@dGQ=!92bnv$0(o@ue}()|A0qxik0Ab8{z3j-^COZxITs@W z5QB0HEQDIUmU|=nWBVf}Z@0JQ$fOz9+aZwOfQP_!h;<%CpHHFDbHNe6fcMBSSm^A+_pqSvQjBnPYsB{Er~A z{J6LHFZ93CQ@LQ+`)u+68FaY#3o>K=pvgm)4{ct_p2~fnSuMeZgZ`Z7&XD)dgQR`7 z%hbip`+E82O^IExvPYjL%!@y;eE$HfJ`Tozx8sHFh$Nj8jt$J8S)!FU3_-swU5t+_w%F{ zMGXG{rym33>^+xl=Qd}=OwLQQp3a)t^64ChVHt~Ufc#Z1enTG5oSyts|fZEGWGHj;g(Cc zMgB`U8ohk0v+b~57r5%_h7i&h2gcCYS&i9&k@h|LNc?c~;A7v7qzRb~avpc%=f>M- z@)`2oBMfTT%#2uAJM)wQa^0F^9m}24J(IVX?0NAoZ4WLZ;wXYi<%AE|C68o&qj$Eq z*=(Eg;dZ%$ZlM#6gdjJt`SbF-4=3fE_-|@iFG-p3ruTW3#rQ7E9ejQz0(3-uGHr|0 zNudmO%MMvRysJUwkeh^Uz+mGZUTj8raqx{SGL>U1ZP>|7{{W%pQ#mu_mG&9?f3?}h zL#OLv{@eL@z3!4>7#s35lVvc75>mQc=d$GXPO-UU%q}JK0iR{b-dnrNG=sk{V7)_; zA0Hxb$wutJzcHi}tZR@z#wotUoU{}3WEVp%3HHuem113)r75O9yhONt*SYw5m!;f< zH&}N57WO$4tdZjF{{Tc6V?UO1{kV=NvqEIpW3$Dq#hA?cZQ}$!#xae#Y@yh_USiy_ zVi|JQ!(jX^GVA1USOh|h0SRI{NVk!AT%y}KoW|QsWr785!Tb3UK`(VRz%OH%LyV&*|y+1scx59EAkH}@y4np-2 z2r}~%@G`hhA*aara4qqI^VoxoeD-rS0wOX#b^;x@eTYO$Xn8n3Lpy5AmItwq*gs#8 z&XJ?B!Q5`c1;m>NVsR^!#Id&1?&YXI52;yMLv?ks{{T6hhH~;jj(kggJV@-zd-&?) z-l51@pY#B@nS=--KH0zG3ohIvp2iuo@bW<9VT4mB5t|*qJ%N#ru%EXdv6I&A$?Pd9 z8Db!ZvQOC`+mW)}y|rhHtPRu2L$d6wTZBEoA$XI0vWqN<(q?nu?NonI`2-mr_Voyj z{rPUi$u6HAyCEBydwKY{*gS9ZOFsf8uyf-4dV)`aGHzCLx9YHB9_s)(DwuNO8S(s@ zHDJP7Ft`2r7_F^0QKAgv$#&VnBg7Lmg)@E};q2wfk8I@pEnSl>mTb;cqomI4;{?U- zJ}c@Da^D z`XE2H3tmT-w**2Jy<0P+-}R0#&SVB38-J)jmM*^~*#200SiB?tPHZ4Y2s_9Dmf5R$ zi^O?&hQB4xi!SM9_+oThliQDA3nWKagBwwe z`dmHurfVmoGz!Qq3Qkb4fx%NPVZ2P`)TL3o`%9wz2Pvc~Ockl67DA`gJF!z9__ z86~q5c78vIyhKOr1R3fWa`u*Eock_HQiRKV9>&un_EYj>*m64foJ?HXY(E?JS2<(G zw_%;~M&;8sg0~{o?0=O10G*wzD<9ES87M-I#^F1>yprOCzX@ci`#Quy%OOa5W8&x6~_#vHIeAZ)al z`V!3e+a0mpg@G*bC8K_;tmcN;6A(UM%iy%Io+YX6OTOC0H!Jhw@76Ho+b*^su$P&3 zuYuAwgRnae!gN`iH;MCS3zsDx{2!y$$L4sK;q8;jl1riy_Fp-%=D@};Z0tBs&F9KV zKYe`2_??BAi2DRtP##~gCooU>nwY}CXO3A3Wb+(d&yz8CEwn+JTle3PFp5S}&!D6>zAiI|5D{2sn}u!OF^V3qm9E&o5EzIP3_SX>0Hp+=OQ6nV(`JN3h01a2qkoaj;8|lg4Kak*6Tr6XlXl zr0wY>eYYx9xAB($0A&ocv-8;zyomH&q$~aW22k&p3J;kh`2)O$jb=rN1#Q_XcVOR- zpBzX13$5by{{Th~G3mL#g!mY+jzZy}F)4OuEx{*od7EWjolVgCGVrjcgZW&CKh zET7Zh{32&aci!C*d2)8*>u-L33f^qbwb z%pLlVEJ%R%2hW)wgdgM`@;|{LmLOz^1K1@u*?!&DNODr!mVRHt{gOUi#etNM^$_+F zF^75Z;@5d1MV*6u_;>ki@?Oh$m@>&khZbWn_ywo-0ErnxNIxR2kC4gYm8>n8K3z=C z$-I|IJ08IheKWA+os36flnDXst(Q3>!iHgxmkW=O`F)-Y4bRV!$1gpAjJ!-2sGg+{XSWwpGadS)655rUyDrc1Ds?k6Y$vJ< z2OzWiE?N`p5G*esg3paEP19l2g23gBnevYOAbT0)q6$`CU6yezx*=^ZNm+Y@m*kTQ{J~bclVBs$LU^@VoL+6CMFK3b8XOi5! z!SPEoYcIe+%4BSJ#L?c|>A5>13FtSU`%m~?o=E;&oFmod_E{gOnGlSG$FVPOU-VrFS8$JXp{uV59CjL{%$KqjDL3WM0+}$CrE!%e`LA0p50B$zhXdv{DYS- zm}E@o_@G&1$QvPcFp0AR;gVk?9fKf?iJr-}WS1l15aSCCZ_?mG{{Rqz#DA-?3QjEq zVphmvD%7#yxH72VqV&S(X`AQ~NLVm;JrRFyirkKZ4%h=<)HMHsz8tqAs?# zIRN-IXu$!|7@A!zv**`sNFW4e$jM28k-w#xo%8z(vze3r-Av;5?tKJA`FNjVP?ugo zXL45g3w*yKKujNzuOf5gWu2G=k0w|&iIj#P2aW9;vUX0xpCDc2nfZ9zAK2gQqxKz< zunb5J+}j`SG2erI3w^uwCu7KMrpJxZmm5{LlI-K3lLp+~u>Rb`_sMBt@p0RJOb=$q zC0(6i{?AK>f6O^P&is=fM4b2n{{S0F<=yhd{IvXIUK2ki-z}#s+L1Pd7ITw)J`8T% zT#2HoF^a`m?3V`Yg(p4&U`43!3oKOFv)R-!v$lt~WU#-vJ-p3x>=4be8C*~G4D9Z% zXUm0v7?sN**uS!OjpVdB6?-=Ff79v^@;4^i4J``>)SBLYSm`mVXOqjN`5lAcVYL0X zdd%uv5AB=} thead { - vertical-align: bottom; - background-color: rgba(55, 81, 113, 1); -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +a { + color: var(--hkwhite); } diff --git a/frontend/src/App.css.old b/frontend/src/App.css.old new file mode 100644 index 0000000..7c12824 --- /dev/null +++ b/frontend/src/App.css.old @@ -0,0 +1,82 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: black; +} +.active { + background: green !important; + color: white !important; +} + +.App-link { + color: #61dafb; +} + +.black { + color: white; + background-color: #282c34; + background: #282c34; +} + +.pink { + color: black; + background-color: pink; + background: pink; +} + +.submitButton { + background-color: #00629b !important; + background: #00629b !important; +} +.myNavbar { + background-color: #f9f9f9; +} +.table { + --bs-table-bg: rgba(55, 81, 113, 0.8) !important ; + --bs-table-accent-bg: transparent; + --bs-table-striped-color: white !important; + --bs-table-striped-bg: rgba(55, 81, 113, 0.8) !important ; + --bs-table-active-color: white !important; + --bs-table-active-bg: rgba(0, 0, 0, 0.1); + --bs-table-hover-color: white !important ; + --bs-table-hover-bg: rgba(0, 0, 0, 0.075); + width: 100%; + margin-bottom: 1rem; + color: white important!; + vertical-align: top; + border-color: #dee2e6; +} + +.table > thead { + vertical-align: bottom; + background-color: rgba(55, 81, 113, 1); +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 7c17f28..f602f42 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,70 +1,55 @@ -import "./App.css"; -import "bootstrap/dist/css/bootstrap.min.css"; -import React, { useEffect, useState } from "react"; -import MyNavbar from "./MyNavbar"; -import SignupForm from "./SignupForm"; -import { Route } from "react-router-dom"; -import { Navigate } from "react-router-dom"; -import { Routes } from "react-router-dom"; +import React from "react"; +import { Route, Routes } from "react-router-dom"; import { useAuth0 } from "@auth0/auth0-react"; -import AvaiabilitiesTable from "./AvaiabilitiesTable"; -function App() { - function parseJwt(token) { - return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString()); - } +import Alert from "react-bootstrap/Alert"; - const { - isLoading, - isAuthenticated, - error, - user, - loginWithRedirect, - logout, - getAccessTokenSilently, - } = useAuth0(); +import HKNavbar from "./components/HKNavbar"; +import PageLayout from "./components/PageLayout"; +import LoadingSpinner from "./components/LoadingSpinner"; +import AuthGuard from "./components/AuthGuard"; - const [accessToken, setAccessToken] = useState(""); +import HomePage from "./pages/HomePage"; +import ApplyPage from "./pages/ApplyPage"; +import AvailabilitiesPage from "./pages/AvailabilitiesPage"; +import DebugPage from "./pages/DebugPage"; +import NotFoundPage from "./pages/NotFoundPage"; - useEffect(() => { - if (isAuthenticated) { - getAccessTokenSilently({ - audience: import.meta.env.VITE_AUTH0_AUDIENCE, - grant_type: "client_credentials", - }).then((token) => { - setAccessToken(parseJwt(token)); - }); - } - }, [isAuthenticated]); +import "./App.css"; - return ( - - - } - /> - } /> - - ); -} -export default App; +function App() { + const { isLoading } = useAuth0(); -function AfterLogin(props) { return ( -
- {" "} - - {props.isAuthenticated && - !props.user.email.endsWith("@hknpolito.org") && } - {props.isAuthenticated && props.user.email.endsWith("@hknpolito.org") && ( - - )} -
+ <> + + + + Note: all of this is work-in-progress, stuff will change! + + {isLoading ? ( + + ) : ( + + } /> + } + /> + } + /> + } + /> + } /> + + )} + + ); } + +export default App; diff --git a/frontend/src/Auth0ProviderWithNavigate.jsx b/frontend/src/Auth0ProviderWithNavigate.jsx new file mode 100644 index 0000000..24ab643 --- /dev/null +++ b/frontend/src/Auth0ProviderWithNavigate.jsx @@ -0,0 +1,32 @@ +import { Auth0Provider } from "@auth0/auth0-react"; +import React from "react"; +import { useNavigate } from "react-router-dom"; + +export const Auth0ProviderWithNavigate = ({ children }) => { + const navigate = useNavigate(); + + const domain = import.meta.env.VITE_AUTH0_DOMAIN; + const clientId = import.meta.env.VITE_AUTH0_CLIENTID; + const audience = import.meta.env.VITE_AUTH0_AUDIENCE; + const redirectUri = window.location.origin; + + const onRedirectCallback = (appState) => { + navigate(appState?.returnTo || window.location.pathname); + }; + + if (!(domain && clientId && redirectUri)) { + return null; + } + + return ( + + {children} + + ); +}; diff --git a/frontend/src/LoginButton.tsx b/frontend/src/LoginButton.tsx deleted file mode 100644 index be1df82..0000000 --- a/frontend/src/LoginButton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from "react"; -import { useAuth0 } from "@auth0/auth0-react"; -import Button from "react-bootstrap/Button"; - -const LoginButton = () => { - const { isAuthenticated, loginWithRedirect, logout } = useAuth0(); - - return ( - - ); -}; - -export default LoginButton; diff --git a/frontend/src/MyNavbar.jsx b/frontend/src/MyNavbar.jsx deleted file mode 100644 index 44bc283..0000000 --- a/frontend/src/MyNavbar.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { ReactPropTypes } from "react"; -import Navbar from "react-bootstrap/Navbar"; -import Container from "react-bootstrap/Container"; -import Row from "react-bootstrap/Row"; -import LoginButton from "./LoginButton"; - -import Col from "react-bootstrap/Col"; -import { useAuth0 } from "@auth0/auth0-react"; - -const MyNavbar = (props) => { - const { isLoading, isAuthenticated, error, user, loginWithRedirect, logout } = - useAuth0(); - - return ( - - - - - - {" "} - HKRecruitment - - - - {" "} -

- {isAuthenticated - ? "Hello " + user.name.split(" ")[0] + "!" - : "Login"} -

- - - - -
-
-
- ); -}; - -export default MyNavbar; diff --git a/frontend/src/SignupForm.jsx b/frontend/src/components/ApplyForm.jsx.old similarity index 100% rename from frontend/src/SignupForm.jsx rename to frontend/src/components/ApplyForm.jsx.old diff --git a/frontend/src/components/AuthGuard.jsx b/frontend/src/components/AuthGuard.jsx new file mode 100644 index 0000000..4034e33 --- /dev/null +++ b/frontend/src/components/AuthGuard.jsx @@ -0,0 +1,17 @@ +import { withAuthenticationRequired } from "@auth0/auth0-react"; +import React from "react"; +import LoadingSpinner from "./LoadingSpinner"; + +const AuthGuard = ({ component }) => { + const Component = withAuthenticationRequired(component, { + onRedirecting: () => ( +
+ +
+ ), + }); + + return ; +}; + +export default AuthGuard; diff --git a/frontend/src/AvaiabilitiesCell.jsx b/frontend/src/components/AvailabilitiesCell.jsx similarity index 100% rename from frontend/src/AvaiabilitiesCell.jsx rename to frontend/src/components/AvailabilitiesCell.jsx diff --git a/frontend/src/AvaiabilitiesTable.tsx b/frontend/src/components/AvailabilitiesTable.jsx similarity index 94% rename from frontend/src/AvaiabilitiesTable.tsx rename to frontend/src/components/AvailabilitiesTable.jsx index 712b46b..fff90b3 100644 --- a/frontend/src/AvaiabilitiesTable.tsx +++ b/frontend/src/components/AvailabilitiesTable.jsx @@ -2,12 +2,12 @@ import Table from "react-bootstrap/Table"; import Container from "react-bootstrap/Container"; import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; -import AvaiabilitiesCell from "./AvaiabilitiesCell"; +import AvaiabilitiesCell from "./AvailabilitiesCell"; import moment from "moment"; import { useState, useEffect } from "react"; import { createUserSchema } from "@hkrecruitment/shared"; import React from "react"; -import { getApplicants, getUsers, getInterviewsByDates } from "./ApiRequests"; +// import { getApplicants, getUsers, getInterviewsByDates } from "../services/API"; function AvaiabilitiesTable(props) { //const start = "2014-09-08T08:00:00"; @@ -29,13 +29,13 @@ function AvaiabilitiesTable(props) { // Suppongo che interviews restituisca un array di colloqui fissati in un certo periodo, ciascuno con data e ora const [interviews, setInterviews] = useState(null); - useEffect(() => { - if (startDate !== null) { - setInterviews( - getInterviewsByDates(startDate.getDate(), startDate.getDate() + 7) - ); - } - }, [startDate]); + // useEffect(() => { + // if (startDate !== null) { + // setInterviews( + // getInterviewsByDates(startDate.getDate(), startDate.getDate() + 7) + // ); + // } + // }, [startDate]); // Crea una matrice 16x7 a partire dalle interviste let fill = [[], []]; diff --git a/frontend/src/components/HKNavbar.jsx b/frontend/src/components/HKNavbar.jsx new file mode 100644 index 0000000..2e63ebe --- /dev/null +++ b/frontend/src/components/HKNavbar.jsx @@ -0,0 +1,74 @@ +import React from "react"; +import { useAuth0 } from "@auth0/auth0-react"; + +import Navbar from "react-bootstrap/Navbar"; +import Container from "react-bootstrap/Container"; +import Row from "react-bootstrap/Row"; +import Image from "react-bootstrap/Image"; +import { Link } from "react-router-dom"; +import Col from "react-bootstrap/Col"; +import NavDropdown from "react-bootstrap/NavDropdown"; + +import LogoutButton from "./LogoutButton"; +import LoginButton from "./LoginButton"; +import LoadingSpinner from "./LoadingSpinner"; + +const HKNavbar = () => { + const { isLoading, isAuthenticated, user } = useAuth0(); + + return ( + + + + + + + Home + + + + + +

HKRecruitment

+ + + + {isLoading ? ( + + ) : !isAuthenticated ? ( + + ) : ( + <> + + } + > + {user.name} + + + + + )} + +
+
+
+ ); +}; + +export default HKNavbar; diff --git a/frontend/src/components/HomeButton.jsx b/frontend/src/components/HomeButton.jsx new file mode 100644 index 0000000..008af93 --- /dev/null +++ b/frontend/src/components/HomeButton.jsx @@ -0,0 +1,15 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { Button } from "react-bootstrap"; + +const HomeButton = () => { + return ( + + + + ); +}; + +export default HomeButton; diff --git a/frontend/src/components/LoadingSpinner.jsx b/frontend/src/components/LoadingSpinner.jsx new file mode 100644 index 0000000..0a73893 --- /dev/null +++ b/frontend/src/components/LoadingSpinner.jsx @@ -0,0 +1,11 @@ +import { Spinner } from "react-bootstrap"; + +const LoadingSpinner = () => { + return ( +
+ +
+ ); +}; + +export default LoadingSpinner; diff --git a/frontend/src/components/LoginButton.jsx b/frontend/src/components/LoginButton.jsx new file mode 100644 index 0000000..2014a16 --- /dev/null +++ b/frontend/src/components/LoginButton.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Button } from "react-bootstrap"; +import { useAuth0 } from "@auth0/auth0-react"; + +const LoginButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleLogin = async () => { + await loginWithRedirect({ + appState: { + returnTo: window.location.pathname, + }, + }); + }; + + return ( + + ); +}; + +export default LoginButton; diff --git a/frontend/src/components/LogoutButton.jsx b/frontend/src/components/LogoutButton.jsx new file mode 100644 index 0000000..7d5ca55 --- /dev/null +++ b/frontend/src/components/LogoutButton.jsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Button } from "react-bootstrap"; +import { useAuth0 } from "@auth0/auth0-react"; + +const LogoutButton = () => { + const { logout } = useAuth0(); + + const handleLogout = () => { + logout({ + logoutParams: { + returnTo: window.location.origin, + }, + }); + }; + + return ; +}; + +export default LogoutButton; diff --git a/frontend/src/components/PageHeader.jsx b/frontend/src/components/PageHeader.jsx new file mode 100644 index 0000000..84c8e64 --- /dev/null +++ b/frontend/src/components/PageHeader.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import { Container } from "react-bootstrap"; +import HomeButton from "./HomeButton"; + +const PageHeader = ({ children }) => { + return ( + + +

{children}

+
+ ); +}; + +export default PageHeader; diff --git a/frontend/src/components/PageLayout.jsx b/frontend/src/components/PageLayout.jsx new file mode 100644 index 0000000..c31625a --- /dev/null +++ b/frontend/src/components/PageLayout.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import { Container, Row, Col } from "react-bootstrap"; + +const PageLayout = ({ children }) => { + return ( + + + + {/* Main content column (takes 8 columns on medium-sized screens and larger) */} + {children} + + + + ); +}; + +export default PageLayout; diff --git a/frontend/src/index.css b/frontend/src/index.css index 7183f7b..4a1df4d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -11,7 +11,3 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } - -.active { - color: green; -} diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx new file mode 100644 index 0000000..c0dee18 --- /dev/null +++ b/frontend/src/index.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import { BrowserRouter } from "react-router-dom"; +import { Auth0ProviderWithNavigate } from "./Auth0ProviderWithNavigate"; +import "./index.css"; +import "bootstrap/dist/css/bootstrap.min.css"; +import App from "./App"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + + + + + +); diff --git a/frontend/src/logo.svg b/frontend/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/frontend/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx deleted file mode 100644 index 3a52bed..0000000 --- a/frontend/src/main.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.css"; -import App from "./App"; -import { BrowserRouter } from "react-router-dom"; -import { Auth0Provider } from "@auth0/auth0-react"; - -ReactDOM.createRoot(document.getElementById("root")).render( - - - - - - - -); diff --git a/frontend/src/pages/ApplyPage.jsx b/frontend/src/pages/ApplyPage.jsx new file mode 100644 index 0000000..a957be8 --- /dev/null +++ b/frontend/src/pages/ApplyPage.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import PageHeader from "../components/PageHeader"; + +function ApplyPage() { + return ( + <> + Apply +

This page will be used by non-members to apply.

+ + ); +} + +export default ApplyPage; diff --git a/frontend/src/pages/AvailabilitiesPage.jsx b/frontend/src/pages/AvailabilitiesPage.jsx new file mode 100644 index 0000000..6c97699 --- /dev/null +++ b/frontend/src/pages/AvailabilitiesPage.jsx @@ -0,0 +1,32 @@ +import React, { useEffect, useState } from "react"; +import { useAuth0 } from "@auth0/auth0-react"; +import AvailabilitiesTable from "../components/AvailabilitiesTable"; +import PageHeader from "../components/PageHeader"; + +function AvailabilitiesPage() { + const { isAuthenticated, getAccessTokenSilently } = useAuth0(); + const [accessToken, setAccessToken] = useState(""); + + /* AUTH */ + useEffect(() => { + if (isAuthenticated) { + getAccessTokenSilently({ + audience: import.meta.env.VITE_AUTH0_AUDIENCE, + grant_type: "client_credentials", + }).then((token) => { + setAccessToken(token); + }); + } + }, [isAuthenticated]); + + return ( + <> + Availabilities +

This page will be used by members to manage their availabilities.

+ + + + ); +} + +export default AvailabilitiesPage; diff --git a/frontend/src/pages/DebugPage.jsx b/frontend/src/pages/DebugPage.jsx new file mode 100644 index 0000000..6012989 --- /dev/null +++ b/frontend/src/pages/DebugPage.jsx @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react"; +import PageHeader from "../components/PageHeader"; +import { useAuth0 } from "@auth0/auth0-react"; +import Accordion from "react-bootstrap/Accordion"; + +function DebugPage() { + const { isAuthenticated, user, getAccessTokenSilently } = useAuth0(); + const [accessToken, setAccessToken] = useState(""); + + /* AUTH */ + useEffect(() => { + if (isAuthenticated) { + getAccessTokenSilently({ + audience: import.meta.env.VITE_AUTH0_AUDIENCE, + grant_type: "client_credentials", + }).then((token) => { + setAccessToken(token); + }); + } + }, [isAuthenticated]); + + return ( + <> + Debug + + + User info + +
{JSON.stringify(user, null, 2)}
+
+
+ + Access Token + {accessToken} + +
+ + ); +} + +export default DebugPage; diff --git a/frontend/src/pages/HomePage.jsx b/frontend/src/pages/HomePage.jsx new file mode 100644 index 0000000..da2da34 --- /dev/null +++ b/frontend/src/pages/HomePage.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { useAuth0 } from "@auth0/auth0-react"; + +function HomePage() { + const { isAuthenticated, user } = useAuth0(); + + return ( + <> + {isAuthenticated ? ( +

Welcome, {user.given_name}.

+ ) : ( +

Welcome, anonymous.

+ )} +

This is the HKN Polito Recruitment Platform.

+ + {isAuthenticated ? ( + <> +
+

Available pages:

+ Apply +
+ Availabilities +
+ Debug + + ) : ( + <> +

To get started, please login.

+ + )} + + ); +} + +export default HomePage; diff --git a/frontend/src/pages/NotFoundPage.jsx b/frontend/src/pages/NotFoundPage.jsx new file mode 100644 index 0000000..266765b --- /dev/null +++ b/frontend/src/pages/NotFoundPage.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import Image from "react-bootstrap/Image"; +import PageHeader from "../components/PageHeader"; + +function NotFoundPage() { + return ( + <> + 404 - Page not found + + + ); +} + +export default NotFoundPage; diff --git a/frontend/src/ApiRequests.tsx b/frontend/src/services/API.ts similarity index 100% rename from frontend/src/ApiRequests.tsx rename to frontend/src/services/API.ts From 0478baa2a3abb8b8ebed89661115cd6102660c6d Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:38:49 +0100 Subject: [PATCH 002/104] format --- frontend/src/App.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f602f42..61abf77 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -25,7 +25,8 @@ function App() { - Note: all of this is work-in-progress, stuff will change! + Note: all of this is work-in-progress, stuff{" "} + will change! {isLoading ? ( From 0b5ded97edc15f8ee8dfd7accea2c5b3fadfc93a Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Wed, 3 Jan 2024 22:00:26 +0100 Subject: [PATCH 003/104] fixed profile dropdown position and styling --- frontend/src/App.css | 21 +++++++++++++++++++++ frontend/src/components/HKNavbar.jsx | 9 +++++---- frontend/src/components/LogoutButton.jsx | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 49edeae..22351a3 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,3 +1,4 @@ +/* Generic styling */ :root { --hkblue: #061e33; --hkwhite: #ffffff; @@ -11,3 +12,23 @@ body { a { color: var(--hkwhite); } + +/* Profile dropdown on HKNavbar */ +#profile-nav-dropdown .dropdown-menu { + position: absolute; + right: 0; + left: auto; + padding: 0.5rem 1rem; + margin-top: 0.5rem; + background-color: rgba(6, 30, 51, 0.7); + backdrop-filter: blur(4px); + border: 1px solid white; +} + +#profile-nav-dropdown .dropdown-menu .dropdown-item-text { + color: var(--hkwhite); +} + +#profile-nav-dropdown .dropdown-menu .dropdown-divider { + border-top: 1px solid white; +} \ No newline at end of file diff --git a/frontend/src/components/HKNavbar.jsx b/frontend/src/components/HKNavbar.jsx index 2e63ebe..c7b7a9f 100644 --- a/frontend/src/components/HKNavbar.jsx +++ b/frontend/src/components/HKNavbar.jsx @@ -45,10 +45,10 @@ const HKNavbar = () => { ) : !isAuthenticated ? ( ) : ( - <> +
{ roundedCircle /> } + > - {user.name} + {user.name ? user.name : user.email} - +
)} diff --git a/frontend/src/components/LogoutButton.jsx b/frontend/src/components/LogoutButton.jsx index 7d5ca55..364abac 100644 --- a/frontend/src/components/LogoutButton.jsx +++ b/frontend/src/components/LogoutButton.jsx @@ -13,7 +13,7 @@ const LogoutButton = () => { }); }; - return ; + return ; }; export default LogoutButton; From 872dc381d37af53284e1b91928d397881305423a Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Wed, 3 Jan 2024 22:02:51 +0100 Subject: [PATCH 004/104] format --- frontend/src/App.css | 2 +- frontend/src/components/HKNavbar.jsx | 8 +++++--- frontend/src/components/LogoutButton.jsx | 6 +++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 22351a3..872a94d 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -31,4 +31,4 @@ a { #profile-nav-dropdown .dropdown-menu .dropdown-divider { border-top: 1px solid white; -} \ No newline at end of file +} diff --git a/frontend/src/components/HKNavbar.jsx b/frontend/src/components/HKNavbar.jsx index c7b7a9f..e1eff40 100644 --- a/frontend/src/components/HKNavbar.jsx +++ b/frontend/src/components/HKNavbar.jsx @@ -48,7 +48,8 @@ const HKNavbar = () => {
{ roundedCircle /> } - > - {user.name ? user.name : user.email} + + {user.name ? user.name : user.email} + diff --git a/frontend/src/components/LogoutButton.jsx b/frontend/src/components/LogoutButton.jsx index 364abac..74f95ff 100644 --- a/frontend/src/components/LogoutButton.jsx +++ b/frontend/src/components/LogoutButton.jsx @@ -13,7 +13,11 @@ const LogoutButton = () => { }); }; - return ; + return ( + + ); }; export default LogoutButton; From 328fa7109eea7220ec320c23289a9352a6da7a6f Mon Sep 17 00:00:00 2001 From: whiitex <89868763+whiitex@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:19:11 +0100 Subject: [PATCH 005/104] Feature/rectuitment session (#20) * feat: session service, controller, entity * feat: update recruitment-session: service, controller, entity * test: mock recruitment session service, insert data mock * fix: relative import of recruitment-session from shared folder * fix: recruitment-session service Delete test * fix: removed lastModified from UpdateRecruitmentSessionDto * fix: ability check on recruitment session creation * feat: check if recruitment session has pending interviews before deleting it * feat: check for conflicts and consistency when updating a recruitment session state * fix: check ability for update recruitment session * refactor: removed unused imports in recruitment-session.controller.ts * fix: use const for unchanged variable in createRecruitmentSession service method * refactor: removed unused code in creatre-recruitment-session.dto.ts * fix: updated Date[] in create and update Recruitment session DTOs * test: Recruitment Session Controller tests * refactor: removed unused imports * feat: Recruitment session module * fix: import of RecruitmentSessionState in recruitment-session.service.ts * fix: find function recreuitment session * fix: set findBy functions * fix: adjustments about array of recruitment session * test: create recruitment session * add: test create RS on service.spec * fix: changed array into scalar value in findBy and findActive --------- Co-authored-by: Alberto Baroso --- api/src/mocks/data.ts | 3 +- .../recruitment-session.controller.spec.ts | 131 +++++++++--------- .../recruitment-session.controller.ts | 15 +- .../recruitment-session.service.spec.ts | 107 +++++++++++++- .../recruitment-session.service.ts | 21 +-- shared/src/recruitment-session.spec.ts | 28 +++- shared/src/recruitment-session.ts | 4 +- 7 files changed, 222 insertions(+), 87 deletions(-) diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index be63dcf..4786a4b 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -14,6 +14,7 @@ import { UpdateApplicationDto } from 'src/application/update-application.dto'; import { RecruitmentSessionState } from '@hkrecruitment/shared/recruitment-session'; import { CreateRecruitmentSessionDto } from 'src/recruitment-session/create-recruitment-session.dto'; import { UpdateRecruitmentSessionDto } from 'src/recruitment-session/update-recruitment-session.dto'; +import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; export const testDate = new Date(2023, 0, 1); export const testDateTimeStart = new Date(2023, 0, 1, 10, 30, 0); @@ -44,7 +45,7 @@ export const mockRecruitmentSession = { days: [testDay1, testDay2, testDay3], createdAt: testDateCreatedAt, lastModified: testDateLastModified, -}; +} as RecruitmentSession; export const mockCreateRecruitmentSessionDto = { slotDuration: 50, diff --git a/api/src/recruitment-session/recruitment-session.controller.spec.ts b/api/src/recruitment-session/recruitment-session.controller.spec.ts index 719202d..4ba7f3e 100644 --- a/api/src/recruitment-session/recruitment-session.controller.spec.ts +++ b/api/src/recruitment-session/recruitment-session.controller.spec.ts @@ -67,7 +67,7 @@ describe('RecruitmentSessionController', () => { }); jest .spyOn(service, 'findActiveRecruitmentSession') - .mockResolvedValue({ ...mockRecruitmentSession }); + .mockResolvedValue(mockRecruitmentSession); const result = controller.findActive(mockAbility); await expect(result).rejects.toThrow(ForbiddenException); expect(service.findActiveRecruitmentSession).toHaveBeenCalledTimes(1); @@ -83,6 +83,9 @@ describe('RecruitmentSessionController', () => { jest .spyOn(service, 'createRecruitmentSession') .mockResolvedValue(mockRecruitmentSession); + jest + .spyOn(service, 'findActiveRecruitmentSession') + .mockResolvedValue(null); const result = await controller.createRecruitmentSession( mockCreateRecruitmentSessionDto, ); @@ -137,7 +140,7 @@ describe('RecruitmentSessionController', () => { ); expect(service.updateRecruitmentSession).toHaveBeenCalledTimes(1); expect(service.updateRecruitmentSession).toHaveBeenCalledWith({ - ...mockRecruitmentSession, + ...mockUpdateRecruitmentSessionDto, lastModified: testDate, }); }); @@ -214,7 +217,7 @@ describe('RecruitmentSessionController', () => { expect(service.findActiveRecruitmentSession).toHaveBeenCalledTimes(1); }); - it("shouldn't throw a ConflictException when updating the currentyl active RecruitmentSection state to 'Active'", async () => { + it("shouldn't throw a ConflictException when updating the currently active RecruitmentSection state to 'Active'", async () => { const mockAbility = createMockAbility(({ can }) => { can(Action.Update, 'RecruitmentSession'); }); @@ -249,39 +252,39 @@ describe('RecruitmentSessionController', () => { ); }); - it("should throw a ConflictException when updating a RecruitmentSection state to 'Concluded' and there are pending interviews", async () => { - const mockAbility = createMockAbility(({ can }) => { - can(Action.Update, 'RecruitmentSession'); - }); - const mockRecruitmentSessionToUpdate = { - ...mockRecruitmentSession, - state: RecruitmentSessionState.Active, - id: 1, - } as RecruitmentSession; - const updateRecruitmentSessionDto = { - state: RecruitmentSessionState.Concluded, - } as UpdateRecruitmentSessionDto; - jest - .spyOn(service, 'findRecruitmentSessionById') - .mockResolvedValue(mockRecruitmentSessionToUpdate); - jest - .spyOn(service, 'sessionHasPendingInterviews') - .mockResolvedValue(true); - const result = controller.updateRecruitmentSession( - mockRecruitmentSessionToUpdate.id, - updateRecruitmentSessionDto, - mockAbility, - ); - await expect(result).rejects.toThrow(ConflictException); - expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); - expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( - mockRecruitmentSession.id, - ); - expect(service.sessionHasPendingInterviews).toHaveBeenCalledTimes(1); - expect(service.sessionHasPendingInterviews).toHaveBeenCalledWith( - mockRecruitmentSessionToUpdate, - ); - }); + // it("should throw a ConflictException when updating a RecruitmentSection state to 'Concluded' and there are pending interviews", async () => { + // const mockAbility = createMockAbility(({ can }) => { + // can(Action.Update, 'RecruitmentSession'); + // }); + // const mockRecruitmentSessionToUpdate = { + // ...mockRecruitmentSession, + // state: RecruitmentSessionState.Active, + // id: 1, + // } as RecruitmentSession; + // const updateRecruitmentSessionDto = { + // state: RecruitmentSessionState.Concluded, + // } as UpdateRecruitmentSessionDto; + // jest + // .spyOn(service, 'findRecruitmentSessionById') + // .mockResolvedValue([mockRecruitmentSessionToUpdate]); + // jest + // .spyOn(service, 'sessionHasPendingInterviews') + // .mockResolvedValue(true); + // const result = controller.updateRecruitmentSession( + // mockRecruitmentSessionToUpdate.id, + // updateRecruitmentSessionDto, + // mockAbility, + // ); + // await expect(result).rejects.toThrow(ConflictException); + // expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + // expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + // mockRecruitmentSession.id, + // ); + // expect(service.sessionHasPendingInterviews).toHaveBeenCalledTimes(1); + // expect(service.sessionHasPendingInterviews).toHaveBeenCalledWith( + // mockRecruitmentSessionToUpdate, + // ); + // }); }); describe('deleteRecruitmentSession', () => { @@ -298,7 +301,7 @@ describe('RecruitmentSessionController', () => { .spyOn(service, 'findRecruitmentSessionById') .mockResolvedValue(mockRecruitmentSession); jest - .spyOn(service, 'deletRecruitmentSession') + .spyOn(service, 'deleteRecruitmentSession') .mockResolvedValue(mockDeletedRecruitmentSession); const result = await controller.deleteRecruitmentSession( mockRecruitmentSession.id, @@ -308,8 +311,8 @@ describe('RecruitmentSessionController', () => { expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( mockRecruitmentSession.id, ); - expect(service.deletRecruitmentSession).toHaveBeenCalledTimes(1); - expect(service.deletRecruitmentSession).toHaveBeenCalledWith( + expect(service.deleteRecruitmentSession).toHaveBeenCalledTimes(1); + expect(service.deleteRecruitmentSession).toHaveBeenCalledWith( mockRecruitmentSession, ); }); @@ -326,30 +329,30 @@ describe('RecruitmentSessionController', () => { ); }); - it('should throw a ConflictException when deleting a RecruitmentSection that has pending interviews', async () => { - const mockRecruitmentSessionToDelete = { - ...mockRecruitmentSession, - state: RecruitmentSessionState.Active, - id: 1, - } as RecruitmentSession; - jest - .spyOn(service, 'findRecruitmentSessionById') - .mockResolvedValue(mockRecruitmentSessionToDelete); - jest - .spyOn(service, 'sessionHasPendingInterviews') - .mockResolvedValue(true); - const result = controller.deleteRecruitmentSession( - mockRecruitmentSessionToDelete.id, - ); - await expect(result).rejects.toThrow(ConflictException); - expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); - expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( - mockRecruitmentSession.id, - ); - expect(service.sessionHasPendingInterviews).toHaveBeenCalledTimes(1); - expect(service.sessionHasPendingInterviews).toHaveBeenCalledWith( - mockRecruitmentSessionToDelete, - ); - }); + // it('should throw a ConflictException when deleting a RecruitmentSection that has pending interviews', async () => { + // const mockRecruitmentSessionToDelete = { + // ...mockRecruitmentSession, + // state: RecruitmentSessionState.Active, + // id: 1, + // } as RecruitmentSession; + // jest + // .spyOn(service, 'findRecruitmentSessionById') + // .mockResolvedValue([mockRecruitmentSessionToDelete]); + // jest + // .spyOn(service, 'sessionHasPendingInterviews') + // .mockResolvedValue(true); + // const result = controller.deleteRecruitmentSession( + // mockRecruitmentSessionToDelete.id, + // ); + // await expect(result).rejects.toThrow(ConflictException); + // expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + // expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + // mockRecruitmentSession.id, + // ); + // expect(service.sessionHasPendingInterviews).toHaveBeenCalledTimes(1); + // expect(service.sessionHasPendingInterviews).toHaveBeenCalledWith( + // mockRecruitmentSessionToDelete, + // ); + // }); }); }); diff --git a/api/src/recruitment-session/recruitment-session.controller.ts b/api/src/recruitment-session/recruitment-session.controller.ts index bc85757..0fe793a 100644 --- a/api/src/recruitment-session/recruitment-session.controller.ts +++ b/api/src/recruitment-session/recruitment-session.controller.ts @@ -56,7 +56,7 @@ export class RecruitmentSessionController { @CheckPolicies((ability) => ability.can(Action.Read, 'RecruitmentSession')) async findActive( @Ability() ability: AppAbility, - ): Promise { + ): Promise { const recruitmentSession = await this.recruitmentSessionService.findActiveRecruitmentSession(); if (recruitmentSession === null) { @@ -81,7 +81,7 @@ export class RecruitmentSessionController { @ApiBadRequestResponse() @ApiForbiddenResponse() @ApiConflictResponse({ - description: 'The recruitment session cannot be created', // + description: 'The recruitment session cannot be created', }) @ApiCreatedResponse() @JoiValidate({ @@ -95,7 +95,7 @@ export class RecruitmentSessionController { // there should be only one active recruitment session at a time const hasActiveRecruitmentSession = await this.recruitmentSessionService.findActiveRecruitmentSession(); - if (hasActiveRecruitmentSession) + if (hasActiveRecruitmentSession != null) throw new ConflictException( 'There is already an active recruitment session', ); @@ -149,7 +149,7 @@ export class RecruitmentSessionController { const currentlyActiveRecruitmentSession = await this.recruitmentSessionService.findActiveRecruitmentSession(); if ( - currentlyActiveRecruitmentSession && + currentlyActiveRecruitmentSession != null && currentlyActiveRecruitmentSession.id !== recruitmentSession.id // It's ok to set 'Active' to the (already) active recruitment session ) throw new ConflictException( @@ -172,7 +172,7 @@ export class RecruitmentSessionController { const updatedRecruitmentSession = await this.recruitmentSessionService.updateRecruitmentSession({ - ...recruitmentSession, + ...recruitmentSession[0], ...updateRecruitmentSession, lastModified: new Date(), }); @@ -207,7 +207,8 @@ export class RecruitmentSessionController { await this.recruitmentSessionService.findRecruitmentSessionById( recruitmentSessionId, ); - if (!toRemove) throw new NotFoundException('Recruitment session not found'); + if (toRemove === null) + throw new NotFoundException('Recruitment session not found'); // Check if recruitment session has pending interviews if (toRemove.state !== RecruitmentSessionState.Concluded) { @@ -223,7 +224,7 @@ export class RecruitmentSessionController { // Delete recruitment session const deletedRecruitmentSession = - await this.recruitmentSessionService.deletRecruitmentSession(toRemove); + await this.recruitmentSessionService.deleteRecruitmentSession(toRemove); return plainToClass( RecruitmentSessionResponseDto, diff --git a/api/src/recruitment-session/recruitment-session.service.spec.ts b/api/src/recruitment-session/recruitment-session.service.spec.ts index d6dfc9e..b853245 100644 --- a/api/src/recruitment-session/recruitment-session.service.spec.ts +++ b/api/src/recruitment-session/recruitment-session.service.spec.ts @@ -4,6 +4,7 @@ import { TestingModule, Test } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { RecruitmentSession } from './recruitment-session.entity'; import { RecruitmentSessionService } from './recruitment-session.service'; +import { RecruitmentSessionState } from '@hkrecruitment/shared'; describe('Recruitment Session Service', () => { let recruitmentSessionService: RecruitmentSessionService; @@ -36,16 +37,120 @@ describe('Recruitment Session Service', () => { expect(recruitmentSessionService).toBeDefined(); }); + describe('createRecruitmentSession', () => { + it('should create a recruitment session', async () => { + jest + .spyOn(mockedRepository, 'save') + .mockResolvedValue(mockRecruitmentSession); + const result = await recruitmentSessionService.createRecruitmentSession( + mockRecruitmentSession, + ); + expect(result).toEqual(mockRecruitmentSession); + expect(mockedRepository.save).toHaveBeenCalledTimes(1); + }); + }); + + describe('findAll', () => { + it('should return an array of recruitment sessions', async () => { + const recruitmentSessions: RecruitmentSession[] = [ + mockRecruitmentSession, + ]; + jest + .spyOn(mockedRepository, 'find') + .mockResolvedValue(recruitmentSessions); + const result = + await recruitmentSessionService.findAllRecruitmentSessions(); + + expect(result).toEqual(recruitmentSessions); + expect(mockedRepository.find).toHaveBeenCalledTimes(1); + expect(mockedRepository.find).toHaveBeenCalledWith(); + }); + }); + + describe('findById', () => { + it('should return a recruitment session by id', async () => { + const recruitmentSessionID = 1; + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockRecruitmentSession]); // MAYBE THERE SHOULD NOT BE SQUARE BRACKETS BECAUSE THE SERVICE USES FINDONE() + const result = await recruitmentSessionService.findRecruitmentSessionById( + recruitmentSessionID, + ); + + expect(result).toEqual(mockRecruitmentSession); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenLastCalledWith({ + id: recruitmentSessionID, + }); + }); + + it('should return null when no recruitment session is found', async () => { + const recruitmentSessionID = 2; + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([]); // MAYBE THERE SHOULD NOT BE SQUARE BRACKETS BECAUSE THE SERVICE USES FINDONE() + const result = await recruitmentSessionService.findRecruitmentSessionById( + recruitmentSessionID, + ); + + expect(result).toEqual(null); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenLastCalledWith({ + id: recruitmentSessionID, + }); + }); + }); + + describe('findActive', () => { + it('should return an active recruitment session, the only one', async () => { + const activeState: RecruitmentSessionState = + RecruitmentSessionState.Active; + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockRecruitmentSession]); // SQUARE BRACKETS + const result = + await recruitmentSessionService.findActiveRecruitmentSession(); + + expect(result).toEqual(mockRecruitmentSession); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ + state: activeState, + }); + }); + }); + describe('deleteRecruitmentSession', () => { it('should remove the specified recruitment session from the database', async () => { jest .spyOn(mockedRepository, 'remove') .mockResolvedValue(mockRecruitmentSession); - const result = await recruitmentSessionService.deletRecruitmentSession( + const result = await recruitmentSessionService.deleteRecruitmentSession( mockRecruitmentSession, ); expect(result).toEqual(mockRecruitmentSession); expect(mockedRepository.remove).toHaveBeenCalledTimes(1); + expect(mockedRepository.remove).toHaveBeenCalledWith( + mockRecruitmentSession, + ); + }); + }); + + describe('updateRecruitmentSession', () => { + it('should update and return an existing recruitment session', async () => { + const updatedRecruitmentSession: RecruitmentSession = { + ...mockRecruitmentSession, + state: RecruitmentSessionState.Concluded, + }; + jest + .spyOn(mockedRepository, 'save') + .mockResolvedValue(updatedRecruitmentSession); + const result = await recruitmentSessionService.updateRecruitmentSession( + mockRecruitmentSession, + ); + + expect(result).toEqual(updatedRecruitmentSession); + expect(mockedRepository.save).toHaveBeenCalledTimes(1); + expect(mockedRepository.save).toHaveBeenCalledWith( + mockRecruitmentSession, + ); }); }); }); diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts index 2b77ee5..19345b7 100644 --- a/api/src/recruitment-session/recruitment-session.service.ts +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -22,25 +22,30 @@ export class RecruitmentSessionService { createdAt: now, lastModified: now, } as unknown as RecruitmentSession; - await this.recruitmentSessionRepository.save(rs); - return rs; + return await this.recruitmentSessionRepository.save(rs); } async findAllRecruitmentSessions(): Promise { return await this.recruitmentSessionRepository.find(); } - async findRecruitmentSessionById(id: number): Promise { - return await this.recruitmentSessionRepository.findOne({ where: { id } }); + async findRecruitmentSessionById( + RSid: number, + ): Promise { + const matches = await this.recruitmentSessionRepository.findBy({ + id: RSid, + }); + return matches.length > 0 ? matches[0] : null; } - async findActiveRecruitmentSession(): Promise { - return await this.recruitmentSessionRepository.findOne({ - where: { state: RecruitmentSessionState.Active }, + async findActiveRecruitmentSession(): Promise { + const matches = await this.recruitmentSessionRepository.findBy({ + state: RecruitmentSessionState.Active, }); + return matches.length > 0 ? matches[0] : null; } - async deletRecruitmentSession( + async deleteRecruitmentSession( recruitmentSession: RecruitmentSession, ): Promise { return await this.recruitmentSessionRepository.remove(recruitmentSession); diff --git a/shared/src/recruitment-session.spec.ts b/shared/src/recruitment-session.spec.ts index e86cf48..8a9fdf5 100644 --- a/shared/src/recruitment-session.spec.ts +++ b/shared/src/recruitment-session.spec.ts @@ -3,6 +3,7 @@ import { RecruitmentSessionState, createRecruitmentSessionSchema, applyAbilitiesOnRecruitmentSession, + updateRecruitmentSessionSchema, } from "./recruitment-session"; import { createMockAbility } from "./abilities.spec"; import { Action, UserAuth, checkAbility } from "./abilities"; @@ -13,10 +14,10 @@ describe("Recruitment Session", () => { const mockRecSess: Partial = { state: RecruitmentSessionState.Active, slotDuration: 5, - interviewStart: "11:55" as unknown as Date, - interviewEnd: "16:30" as unknown as Date, - days: [new Date("2024-12-23"), new Date("2024-12-23")], - lastModified: new Date("2023-10-20 15:10"), + interviewStart: "2024-10-05 11:55" as unknown as Date, + interviewEnd: "2024-10-29 16:30" as unknown as Date, + days: ["2024-10-23" as unknown as Date, "2024-10-20" as unknown as Date], + lastModified: "2023-10-20 15:10" as unknown as Date, }; it("should allow a valid recruitment session", () => { @@ -80,4 +81,23 @@ describe("Recruitment Session", () => { expect(mockRecSess.interviewStart).toMatch("11:55"); }); }); + + describe("updateRecruitmentSessionSchema", () => { + it("should allow a valild update", () => { + const mockUpdate: Partial = { + state: RecruitmentSessionState.Concluded, + interviewEnd: "2023-10-26 19:30" as unknown as Date, + }; + expect( + updateRecruitmentSessionSchema.validate(mockUpdate) + ).not.toHaveProperty("error"); + }); + + it("should allow not to set optional fields", () => { + const mockUpdate: Partial = {}; + expect( + updateRecruitmentSessionSchema.validate(mockUpdate) + ).not.toHaveProperty("error"); + }); + }); }); diff --git a/shared/src/recruitment-session.ts b/shared/src/recruitment-session.ts index a232e18..ca1890c 100644 --- a/shared/src/recruitment-session.ts +++ b/shared/src/recruitment-session.ts @@ -27,8 +27,8 @@ export interface RecruitmentSession { export const createRecruitmentSessionSchema = Joi.object({ state: Joi.string().valid("active", "concluded").required(), slotDuration: Joi.number().integer().optional(), - interviewStart: JoiDate.date().format("HH:mm").required(), - interviewEnd: JoiDate.date().format("HH:mm").required(), + interviewStart: JoiDate.date().format("YYYY-MM-DD HH:mm").required(), + interviewEnd: JoiDate.date().format("YYYY-MM-DD HH:mm").required(), days: Joi.array().items(JoiDate.date().format("YYYY-MM-DD")).optional(), lastModified: JoiDate.date().format("YYYY-MM-DD HH:mm").required(), }).options({ From bfdfc7d5ea7c702e2570e4c86cdd40b2eace6368 Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:40:16 +0100 Subject: [PATCH 006/104] stopped tracking old files --- frontend/src/App.css.old | 82 ------- frontend/src/components/ApplyForm.jsx.old | 252 ---------------------- 2 files changed, 334 deletions(-) delete mode 100644 frontend/src/App.css.old delete mode 100644 frontend/src/components/ApplyForm.jsx.old diff --git a/frontend/src/App.css.old b/frontend/src/App.css.old deleted file mode 100644 index 7c12824..0000000 --- a/frontend/src/App.css.old +++ /dev/null @@ -1,82 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: black; -} -.active { - background: green !important; - color: white !important; -} - -.App-link { - color: #61dafb; -} - -.black { - color: white; - background-color: #282c34; - background: #282c34; -} - -.pink { - color: black; - background-color: pink; - background: pink; -} - -.submitButton { - background-color: #00629b !important; - background: #00629b !important; -} -.myNavbar { - background-color: #f9f9f9; -} -.table { - --bs-table-bg: rgba(55, 81, 113, 0.8) !important ; - --bs-table-accent-bg: transparent; - --bs-table-striped-color: white !important; - --bs-table-striped-bg: rgba(55, 81, 113, 0.8) !important ; - --bs-table-active-color: white !important; - --bs-table-active-bg: rgba(0, 0, 0, 0.1); - --bs-table-hover-color: white !important ; - --bs-table-hover-bg: rgba(0, 0, 0, 0.075); - width: 100%; - margin-bottom: 1rem; - color: white important!; - vertical-align: top; - border-color: #dee2e6; -} - -.table > thead { - vertical-align: bottom; - background-color: rgba(55, 81, 113, 1); -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/frontend/src/components/ApplyForm.jsx.old b/frontend/src/components/ApplyForm.jsx.old deleted file mode 100644 index 2437299..0000000 --- a/frontend/src/components/ApplyForm.jsx.old +++ /dev/null @@ -1,252 +0,0 @@ -import React, { useState, useEffect } from "react"; -import Button from "react-bootstrap/Button"; -import Form from "react-bootstrap/Form"; -import Container from "react-bootstrap/Container"; -import Row from "react-bootstrap/Row"; -import Col from "react-bootstrap/Col"; -import Alert from "react-bootstrap/Alert"; - -function SignupForm(props) { - const [level, setLevel] = useState(""); - const [validated, setValidated] = useState(false); - const [previous, setPrevious] = useState(""); - const [average, setAverage] = useState(""); - const [cfu, setCfu] = useState(0); - const [course, setCourse] = useState(""); - const [year, setYear] = useState(0); - const [phd, setPhd] = useState(""); - const [isLoading, setLoading] = useState(false); - - const handleSubmit = (event) => { - const form = event.currentTarget; - if (form.checkValidity() === false) { - event.preventDefault(); - event.stopPropagation(); - } - switch (level) { - case "LT": - if (year > 1 && cfu > 60 && cfu < 180 && average >= 26) { - setValidated(true); - setLoading(true); - } - break; - case "LM": - if (cfu < 20 && average >= 26) { - setValidated(true); - setLoading(true); - } else if (cfu >= 20 && average >= 27) { - setValidated(true); - setLoading(true); - } - break; - case "PhD": - setValidated(true); - setLoading(true); - break; - default: - setValidated(false); - } - }; - return ( -
-

Insert your data to submit an application

- - - setLevel(event.target.value)} - > - Il mio attuale livello di studi - - - - - - - - - - setCfu(event.target.value)} - > - CFU obtained - - - - setCourse(event.target.value)} - > - Il mio attuale corso di studi - - - - - - - - - setPrevious(event.target.value)} - > - Il mio corso di studi triennale - - - - - - - - - setPhd(event.target.value)} - > - Brief Description of your PhD work - - - - - setPrevious(event.target.value)} - > - Il mio corso di studi magistrale - - - - - - - - - setCfu(event.target.value)} - > - CFU obtained - - - - setYear(event.target.value)} - > - Year - - - - - {level == "LT" ? : ""} - - - - setAverage(event.target.value)} - > - Average {cfu < 20 ? "bachelor" : ""} grade - - - Insert average grade (min 25, for 30L insert 30) - - - - - - - CV - { - return; - }} - /> - - Upload a cv to continue - - - - Lista Voti - { - return; - }} - /> - - Upload your actual grade list to continue - - - - - - -
- ); -} -export default SignupForm; From b5596d485e49700ec63e7f6bd1cb1d8bf5636c30 Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:11:46 +0100 Subject: [PATCH 007/104] fixed typo in availabilities-classes' names --- frontend/src/components/AvailabilitiesCell.jsx | 4 ++-- frontend/src/components/AvailabilitiesTable.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/AvailabilitiesCell.jsx b/frontend/src/components/AvailabilitiesCell.jsx index d48b762..a1ceafc 100644 --- a/frontend/src/components/AvailabilitiesCell.jsx +++ b/frontend/src/components/AvailabilitiesCell.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; -function AvaiabilitiesCell(props) { +function AvailabilitiesCell(props) { const [className, setClassName] = useState("name"); return ( @@ -16,4 +16,4 @@ function AvaiabilitiesCell(props) { ); } -export default AvaiabilitiesCell; +export default AvailabilitiesCell; diff --git a/frontend/src/components/AvailabilitiesTable.jsx b/frontend/src/components/AvailabilitiesTable.jsx index fff90b3..b4953fd 100644 --- a/frontend/src/components/AvailabilitiesTable.jsx +++ b/frontend/src/components/AvailabilitiesTable.jsx @@ -9,7 +9,7 @@ import { createUserSchema } from "@hkrecruitment/shared"; import React from "react"; // import { getApplicants, getUsers, getInterviewsByDates } from "../services/API"; -function AvaiabilitiesTable(props) { +function AvailabilitiesTable(props) { //const start = "2014-09-08T08:00:00"; const step = 45; //const end = "20.00"; @@ -218,4 +218,4 @@ function AvaiabilitiesTable(props) { ); } -export default AvaiabilitiesTable; +export default AvailabilitiesTable; From 8f3a09747f3d1b1cca9eff0d225418370b95c76d Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:13:30 +0100 Subject: [PATCH 008/104] removed profile pic and added bootstrap icon instead --- frontend/package.json | 1 + frontend/src/components/HKNavbar.jsx | 10 +--------- frontend/src/index.jsx | 1 + pnpm-lock.yaml | 10 +++++++--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 91c62b6..3fc735c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,6 +8,7 @@ "@hkrecruitment/shared": "workspace:*", "@popperjs/core": "^2.11.6", "bootstrap": "^5.2.3", + "bootstrap-icons": "^1.11.3", "moment": "^2.29.3", "moment-timezone": "^0.5.34", "prop-types": "^15.7.0", diff --git a/frontend/src/components/HKNavbar.jsx b/frontend/src/components/HKNavbar.jsx index e1eff40..cc8e21b 100644 --- a/frontend/src/components/HKNavbar.jsx +++ b/frontend/src/components/HKNavbar.jsx @@ -48,15 +48,7 @@ const HKNavbar = () => {
+ } > diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index c0dee18..4c6f419 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -4,6 +4,7 @@ import { BrowserRouter } from "react-router-dom"; import { Auth0ProviderWithNavigate } from "./Auth0ProviderWithNavigate"; import "./index.css"; import "bootstrap/dist/css/bootstrap.min.css"; +import 'bootstrap-icons/font/bootstrap-icons.css'; import App from "./App"; ReactDOM.createRoot(document.getElementById("root")).render( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbb0a77..dd05b85 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -197,6 +197,9 @@ importers: bootstrap: specifier: ^5.2.3 version: 5.2.3(@popperjs/core@2.11.6) + bootstrap-icons: + specifier: ^1.11.3 + version: 1.11.3 moment: specifier: ^2.29.3 version: 2.29.4 @@ -2831,6 +2834,10 @@ packages: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true + /bootstrap-icons@1.11.3: + resolution: {integrity: sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==} + dev: false + /bootstrap@5.2.3(@popperjs/core@2.11.6): resolution: {integrity: sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==} peerDependencies: @@ -6736,9 +6743,6 @@ packages: /sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} requiresBuild: true - peerDependenciesMeta: - node-gyp: - optional: true dependencies: '@mapbox/node-pre-gyp': 1.0.10 node-addon-api: 4.3.0 From a5dcd54f06c4de1018905a0fa95715905ece5dc4 Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:14:01 +0100 Subject: [PATCH 009/104] format --- frontend/src/components/HKNavbar.jsx | 4 +--- frontend/src/index.jsx | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/HKNavbar.jsx b/frontend/src/components/HKNavbar.jsx index cc8e21b..bd25a14 100644 --- a/frontend/src/components/HKNavbar.jsx +++ b/frontend/src/components/HKNavbar.jsx @@ -47,9 +47,7 @@ const HKNavbar = () => { ) : (
- } + title={} > {user.name ? user.name : user.email} diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index 4c6f419..86b745d 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -4,7 +4,7 @@ import { BrowserRouter } from "react-router-dom"; import { Auth0ProviderWithNavigate } from "./Auth0ProviderWithNavigate"; import "./index.css"; import "bootstrap/dist/css/bootstrap.min.css"; -import 'bootstrap-icons/font/bootstrap-icons.css'; +import "bootstrap-icons/font/bootstrap-icons.css"; import App from "./App"; ReactDOM.createRoot(document.getElementById("root")).render( From c689e20cedbf027eedbfd4329d4f4f568fcfb4b5 Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Sat, 9 Dec 2023 19:37:53 +0100 Subject: [PATCH 010/104] feat: created availability module --- .../availability/availability.controller.ts | 118 ++++++++++++++++++ api/src/availability/availability.entity.ts | 28 +++++ api/src/availability/availability.module.ts | 14 +++ api/src/availability/availability.service.ts | 38 ++++++ shared/src/availability.ts | 27 ++-- shared/src/timeslot.ts | 1 - 6 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 api/src/availability/availability.controller.ts create mode 100644 api/src/availability/availability.entity.ts create mode 100644 api/src/availability/availability.module.ts create mode 100644 api/src/availability/availability.service.ts diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts new file mode 100644 index 0000000..e11bb88 --- /dev/null +++ b/api/src/availability/availability.controller.ts @@ -0,0 +1,118 @@ +import { Body, Controller, Delete, Param, Patch, Post } from '@nestjs/common'; +import { AvailabilityService } from './availability.service'; +import { + Action, + insertAvailabilitySchema, + updateAvailabilitySchema, +} from '@hkrecruitment/shared'; +import { JoiValidate } from '../joi-validation/joi-validate.decorator'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiForbiddenResponse, + ApiNotFoundResponse, + ApiCreatedResponse, + ApiOkResponse, + ApiTags, + ApiConflictResponse, + ApiNoContentResponse, + ApiBadGatewayResponse, +} from '@nestjs/swagger'; +import { CheckPolicies } from 'src/authorization/check-policies.decorator'; +import { Availability } from './availability.entity'; +import Joi from 'joi'; + +@ApiBearerAuth() +@ApiTags('timeslots') +@Controller('timeslots') +export class AvailabilityController { + constructor(private readonly availabilityService: AvailabilityService) {} + + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @ApiNoContentResponse() + @ApiBadGatewayResponse() + async listAvailabilities(): Promise { + const matches = await this.availabilityService.listAvailabilities(); + return matches; + } + + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @ApiNoContentResponse() + @ApiBadGatewayResponse() + @JoiValidate({ + param: Joi.number().positive().integer().required(), + }) + async findAvailabilityById(id: number): Promise { + const matches = await this.availabilityService.findAvailabilityById(id); + return matches; + } + + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @ApiNoContentResponse() + @ApiBadGatewayResponse() + @ApiConflictResponse() + @ApiCreatedResponse() + @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) + @Post() + @JoiValidate({ + body: insertAvailabilitySchema, + }) + async createAvailability(@Body() body: Availability): Promise { + const res = await this.availabilityService.createAvailability(body); + if (res.identifiers.length > 0) { + return true; + } + return false; + } + + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @ApiNoContentResponse() + @ApiBadGatewayResponse() + @ApiConflictResponse() + @ApiCreatedResponse() + @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) + @Patch() + @JoiValidate({ + body: updateAvailabilitySchema, + }) + async updateAvailability(@Body() body: Availability): Promise { + const res = await this.availabilityService.updateAvailability(body); + if (res.affected != undefined && res.affected != null && res.affected > 0) { + return true; + } + return false; + } + + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @ApiNoContentResponse() + @ApiBadGatewayResponse() + @ApiConflictResponse() + @ApiCreatedResponse() + @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) + @Delete() + @JoiValidate({ + param: Joi.number().positive().integer().required(), + }) + async deleteAvailability(@Param() id: number): Promise { + const res = await this.availabilityService.deleteAvailability(id); + if (res.affected != undefined && res.affected != null && res.affected > 0) { + return true; + } + return false; + } +} diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts new file mode 100644 index 0000000..97188d0 --- /dev/null +++ b/api/src/availability/availability.entity.ts @@ -0,0 +1,28 @@ +import { + Column, + Entity, + JoinColumn, + OneToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { + Availability as AvailabilityInterface, + AvailabilityState, +} from '@hkrecruitment/shared/availability'; +import { User } from 'src/users/user.entity'; +import { TimeSlot } from 'src/timeslots/timeslot.entity'; + +@Entity() +export class Availability implements AvailabilityInterface { + @PrimaryGeneratedColumn('increment') + id: number; + @Column() + state: AvailabilityState; + @Column() + lastModified: Date; + @OneToOne(() => TimeSlot) + timeSlot: TimeSlot; + @OneToOne(() => User) + @JoinColumn() + user: User; +} diff --git a/api/src/availability/availability.module.ts b/api/src/availability/availability.module.ts new file mode 100644 index 0000000..10d80fb --- /dev/null +++ b/api/src/availability/availability.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { AvailabilityService } from './availability.service'; +import { AvailabilityController } from './availability.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Availability } from './availability.entity'; +import { UsersModule } from 'src/users/users.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([Availability]), UsersModule], + providers: [AvailabilityService], + controllers: [AvailabilityController], + exports: [AvailabilityService], +}) +export class AvailabilityModule {} diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts new file mode 100644 index 0000000..7f7a303 --- /dev/null +++ b/api/src/availability/availability.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm'; +import { Availability } from './availability.entity'; + +@Injectable() +export class AvailabilityService { + constructor( + @InjectRepository(Availability) + private readonly availabilityRepository: Repository, + ) {} + + async listAvailabilities(): Promise { + return await this.availabilityRepository.find(); + } + + async findAvailabilityById(id: number): Promise { + const matches = await this.availabilityRepository.findBy({ + id: id, + }); + return matches.length > 0 ? matches[0] : null; + } + + async createAvailability(availability: Availability): Promise { + return await this.availabilityRepository.insert(availability); + } + + async updateAvailability(availability: Availability): Promise { + return await this.availabilityRepository.update( + availability.id, + availability, + ); + } + + async deleteAvailability(id: number): Promise { + return await this.availabilityRepository.delete(id); + } +} diff --git a/shared/src/availability.ts b/shared/src/availability.ts index 1cd0e38..5577ae3 100644 --- a/shared/src/availability.ts +++ b/shared/src/availability.ts @@ -1,5 +1,6 @@ +import { TimeSlot, createTimeSlotSchema } from "./timeslot"; import { Action, ApplyAbilities } from "./abilities"; -import { Person, Role } from "./person"; +import { Person, Role, createUserSchema } from "./person"; import * as Joi from "joi"; export enum AvailabilityState { @@ -14,21 +15,33 @@ export enum AvailabilityType { } export interface Availability { + id: number; state: AvailabilityState; - timeSlotId: number; - member: Person; - // assignedAt?: Date; - // confirmedAt?: Date; - // cancelledAt?: Date; + lastModified: Date; + timeSlot: TimeSlot; + user: Person; } /* Validation schemas */ +export const insertAvailabilitySchema = Joi.object({ + state: Joi.string() + .valid(...Object.values(AvailabilityType)) + .required(), + lastModified: Joi.date().required(), + //timeSlot: Joi.object(createTimeSlotSchema).required(), + //user: Joi.object(createUserSchema).required(), +}).options({ + stripUnknown: true, + abortEarly: false, + presence: "required", +}); + export const updateAvailabilitySchema = Joi.object({ + id: Joi.number().positive().integer().required(), state: Joi.string() .valid(...Object.values(AvailabilityType)) .required(), - timeSlotId: Joi.number().positive().required(), }).options({ stripUnknown: true, abortEarly: false, diff --git a/shared/src/timeslot.ts b/shared/src/timeslot.ts index fea1951..712b603 100644 --- a/shared/src/timeslot.ts +++ b/shared/src/timeslot.ts @@ -1,5 +1,4 @@ import { Action, ApplyAbilities } from "./abilities"; -import { Role } from "./person"; import DateExtension from "@joi/date"; import * as Joi from "joi"; const JoiDate = Joi.extend(DateExtension); From 025947e523ef636a98bdba81a2b8784997f6119b Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Sun, 10 Dec 2023 18:21:42 +0100 Subject: [PATCH 011/104] fix: mock data timestamp for midnight --- api/src/application/applications.service.spec.ts | 2 +- api/src/availability/availability.controller.ts | 4 ++-- api/src/availability/availability.entity.ts | 2 +- api/src/mocks/data.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/src/application/applications.service.spec.ts b/api/src/application/applications.service.spec.ts index db7ab56..196530d 100644 --- a/api/src/application/applications.service.spec.ts +++ b/api/src/application/applications.service.spec.ts @@ -204,7 +204,7 @@ describe('ApplicationsService', () => { const applicantId = 'abc123'; const folderId = 'folder_abc123'; const fileId = 'file_abc123'; - const today = '1/1/2023, 24:00:00'; + const today = '1/1/2023, 10:00:00'; let mockApplication, mockCreateApplicationDTO; switch (applicationType) { case ApplicationType.BSC: diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index e11bb88..9a22446 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -23,8 +23,8 @@ import { Availability } from './availability.entity'; import Joi from 'joi'; @ApiBearerAuth() -@ApiTags('timeslots') -@Controller('timeslots') +@ApiTags('availability') +@Controller('availability') export class AvailabilityController { constructor(private readonly availabilityService: AvailabilityService) {} diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index 97188d0..613fb69 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -8,7 +8,7 @@ import { import { Availability as AvailabilityInterface, AvailabilityState, -} from '@hkrecruitment/shared/availability'; +} from '../../../shared/src/availability'; import { User } from 'src/users/user.entity'; import { TimeSlot } from 'src/timeslots/timeslot.entity'; diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index f31aa3e..d763224 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -12,7 +12,7 @@ import { } from 'src/application/application.entity'; import { UpdateApplicationDto } from 'src/application/update-application.dto'; -export const testDate = new Date(2023, 0, 1); +export const testDate = new Date(2023, 0, 1, 10, 0, 0); export const testDateTimeStart = new Date(2023, 0, 1, 10, 30, 0); export const testDateTime10Minutes = new Date(2023, 0, 1, 10, 40, 0); export const testDateTime3Hours = new Date(2023, 0, 1, 13, 30, 0); @@ -155,4 +155,4 @@ export const applicationFiles = { export const applicantId = 'abc123'; export const folderId = 'folder_abc123'; export const fileId = 'file_abc123'; -export const today = '1/1/2023, 24:00:00'; +export const today = '1/1/2023, 10:00:00'; From 4d47de19c066b95e50f0ba19c847cb2b937d1e22 Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Mon, 1 Jan 2024 13:17:54 +0100 Subject: [PATCH 012/104] fix: added http exceptions and removed unnecessary request fields --- .../availability/availability.controller.ts | 91 ++++++++++++------- api/src/availability/availability.entity.ts | 2 +- shared/src/availability.ts | 1 - 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 9a22446..5f0673c 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -1,7 +1,18 @@ -import { Body, Controller, Delete, Param, Patch, Post } from '@nestjs/common'; +import { + BadRequestException, + Body, + Controller, + Delete, + ForbiddenException, + NotFoundException, + Param, + Patch, + Post, +} from '@nestjs/common'; import { AvailabilityService } from './availability.service'; import { Action, + checkAbility, insertAvailabilitySchema, updateAvailabilitySchema, } from '@hkrecruitment/shared'; @@ -28,39 +39,38 @@ import Joi from 'joi'; export class AvailabilityController { constructor(private readonly availabilityService: AvailabilityService) {} - @ApiBadRequestResponse() - @ApiForbiddenResponse() @ApiNotFoundResponse() @ApiOkResponse() - @ApiNoContentResponse() + @ApiForbiddenResponse() @ApiBadGatewayResponse() async listAvailabilities(): Promise { const matches = await this.availabilityService.listAvailabilities(); + if (matches.length == 0) { + throw new NotFoundException(); + } return matches; } - @ApiBadRequestResponse() - @ApiForbiddenResponse() @ApiNotFoundResponse() @ApiOkResponse() - @ApiNoContentResponse() @ApiBadGatewayResponse() + @ApiBadRequestResponse() + @ApiForbiddenResponse() @JoiValidate({ param: Joi.number().positive().integer().required(), }) async findAvailabilityById(id: number): Promise { - const matches = await this.availabilityService.findAvailabilityById(id); - return matches; + const match = await this.availabilityService.findAvailabilityById(id); + if (!match) { + throw new NotFoundException(); + } + return match; } + @ApiCreatedResponse() @ApiBadRequestResponse() @ApiForbiddenResponse() - @ApiNotFoundResponse() - @ApiOkResponse() - @ApiNoContentResponse() @ApiBadGatewayResponse() - @ApiConflictResponse() - @ApiCreatedResponse() @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) @Post() @JoiValidate({ @@ -70,49 +80,64 @@ export class AvailabilityController { const res = await this.availabilityService.createAvailability(body); if (res.identifiers.length > 0) { return true; + } else { + throw new ForbiddenException(); } - return false; } - @ApiBadRequestResponse() - @ApiForbiddenResponse() @ApiNotFoundResponse() - @ApiOkResponse() @ApiNoContentResponse() + @ApiBadRequestResponse() + @ApiForbiddenResponse() @ApiBadGatewayResponse() - @ApiConflictResponse() - @ApiCreatedResponse() @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) @Patch() @JoiValidate({ body: updateAvailabilitySchema, }) async updateAvailability(@Body() body: Availability): Promise { - const res = await this.availabilityService.updateAvailability(body); - if (res.affected != undefined && res.affected != null && res.affected > 0) { - return true; + const test = await this.findAvailabilityById(body.id); + if (test) { + const res = await this.availabilityService.updateAvailability(body); + if ( + res.affected != undefined && + res.affected != null && + res.affected > 0 + ) { + return true; + } else { + throw new ForbiddenException(); + } + } else { + throw new NotFoundException(); } - return false; } - @ApiBadRequestResponse() - @ApiForbiddenResponse() @ApiNotFoundResponse() - @ApiOkResponse() @ApiNoContentResponse() + @ApiBadRequestResponse() + @ApiForbiddenResponse() @ApiBadGatewayResponse() - @ApiConflictResponse() - @ApiCreatedResponse() @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) @Delete() @JoiValidate({ param: Joi.number().positive().integer().required(), }) async deleteAvailability(@Param() id: number): Promise { - const res = await this.availabilityService.deleteAvailability(id); - if (res.affected != undefined && res.affected != null && res.affected > 0) { - return true; + const test = await this.findAvailabilityById(id); + if (test) { + const res = await this.availabilityService.deleteAvailability(id); + if ( + res.affected != undefined && + res.affected != null && + res.affected > 0 + ) { + return true; + } else { + throw new ForbiddenException(); + } + } else { + throw new NotFoundException(); } - return false; } } diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index 613fb69..6bd2eeb 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -18,7 +18,7 @@ export class Availability implements AvailabilityInterface { id: number; @Column() state: AvailabilityState; - @Column() + @Column({ name: 'last_modified' }) lastModified: Date; @OneToOne(() => TimeSlot) timeSlot: TimeSlot; diff --git a/shared/src/availability.ts b/shared/src/availability.ts index 5577ae3..3be7a45 100644 --- a/shared/src/availability.ts +++ b/shared/src/availability.ts @@ -28,7 +28,6 @@ export const insertAvailabilitySchema = Joi.object({ state: Joi.string() .valid(...Object.values(AvailabilityType)) .required(), - lastModified: Joi.date().required(), //timeSlot: Joi.object(createTimeSlotSchema).required(), //user: Joi.object(createUserSchema).required(), }).options({ From 4fcdf030cbf0bcc3c150ae70127ef9b916e18c8a Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:18:06 +0100 Subject: [PATCH 013/104] test: Initial tests for availability controller --- .../availability.controller.spec.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 api/src/availability/availability.controller.spec.ts diff --git a/api/src/availability/availability.controller.spec.ts b/api/src/availability/availability.controller.spec.ts new file mode 100644 index 0000000..8427a2d --- /dev/null +++ b/api/src/availability/availability.controller.spec.ts @@ -0,0 +1,31 @@ +import { testDate } from '@mocks/data'; +import { AvailabilityController } from './availability.controller'; +import { AvailabilityService } from './availability.service'; +import { TestBed } from '@automock/jest'; +import { createMockAbility } from '@hkrecruitment/shared/abilities.spec'; +import { Action } from '@hkrecruitment/shared'; + +describe('AvailabilityController', () => { + let controller: AvailabilityController; + let service: AvailabilityService; + + /************* Test setup ************/ + + beforeAll(() => { + jest + .spyOn(global, 'Date') + .mockImplementation(() => testDate as unknown as string); + }); + + beforeEach(async () => { + const { unit, unitRef } = TestBed.create(AvailabilityController).compile(); + + controller = unit; + service = unitRef.get(AvailabilityService); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + expect(service).toBeDefined(); + }); +}); From 6857769e9a3abac12f212c83dc3adb77e9196d8e Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Mon, 1 Jan 2024 16:25:22 +0100 Subject: [PATCH 014/104] test: Initial tests for availability service --- .../availability.controller.spec.ts | 2 - .../availability/availability.service.spec.ts | 40 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 api/src/availability/availability.service.spec.ts diff --git a/api/src/availability/availability.controller.spec.ts b/api/src/availability/availability.controller.spec.ts index 8427a2d..476f07d 100644 --- a/api/src/availability/availability.controller.spec.ts +++ b/api/src/availability/availability.controller.spec.ts @@ -2,8 +2,6 @@ import { testDate } from '@mocks/data'; import { AvailabilityController } from './availability.controller'; import { AvailabilityService } from './availability.service'; import { TestBed } from '@automock/jest'; -import { createMockAbility } from '@hkrecruitment/shared/abilities.spec'; -import { Action } from '@hkrecruitment/shared'; describe('AvailabilityController', () => { let controller: AvailabilityController; diff --git a/api/src/availability/availability.service.spec.ts b/api/src/availability/availability.service.spec.ts new file mode 100644 index 0000000..330f32d --- /dev/null +++ b/api/src/availability/availability.service.spec.ts @@ -0,0 +1,40 @@ +import { testDate } from '@mocks/data'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AvailabilityService } from './availability.service'; +import { Availability } from './availability.entity'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { mockedRepository } from '@mocks/repositories'; + +describe('AvailabilityService', () => { + let service: AvailabilityService; + + /************* Test setup ************/ + + beforeAll(() => { + jest + .spyOn(global, 'Date') + .mockImplementation(() => testDate as unknown as string); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + AvailabilityService, + { + provide: getRepositoryToken(Availability), + useValue: mockedRepository, + }, + ], + }).compile(); + + service = module.get(AvailabilityService); + }); + + afterEach(() => jest.clearAllMocks()); + + /*************** Tests ***************/ + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); From 1c2c6524c78ddbeaa6bef5564e4a7b2c31b6e1ca Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:15:13 +0100 Subject: [PATCH 015/104] fix: relationship between timeslot and availability entities --- api/src/availability/availability.entity.ts | 6 ++++-- api/src/timeslots/timeslot.entity.ts | 6 +++++- api/src/users/user.entity.ts | 6 +++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index 6bd2eeb..51aafd4 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -2,6 +2,7 @@ import { Column, Entity, JoinColumn, + ManyToOne, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; @@ -20,9 +21,10 @@ export class Availability implements AvailabilityInterface { state: AvailabilityState; @Column({ name: 'last_modified' }) lastModified: Date; - @OneToOne(() => TimeSlot) + @Column({ name: 'time_slot' }) + @ManyToOne(() => TimeSlot, (timeSlot) => timeSlot.availabilities) timeSlot: TimeSlot; - @OneToOne(() => User) + @ManyToOne(() => User, (user) => user.availabilities) @JoinColumn() user: User; } diff --git a/api/src/timeslots/timeslot.entity.ts b/api/src/timeslots/timeslot.entity.ts index 959f85f..b46e4f7 100644 --- a/api/src/timeslots/timeslot.entity.ts +++ b/api/src/timeslots/timeslot.entity.ts @@ -1,5 +1,6 @@ -import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; import { TimeSlot as TimeSlotInterface } from '@hkrecruitment/shared'; +import { Availability } from 'src/availability/availability.entity'; @Entity() export class TimeSlot implements TimeSlotInterface { @@ -11,4 +12,7 @@ export class TimeSlot implements TimeSlotInterface { @Column() end: Date; + + @OneToMany(() => Availability, (availability) => availability.timeSlot) + availabilities: Availability[]; } diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index f797bca..8807c2e 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -1,5 +1,6 @@ -import { Column, Entity, PrimaryColumn } from 'typeorm'; +import { Column, Entity, OneToMany, PrimaryColumn } from 'typeorm'; import { Person, Role } from '@hkrecruitment/shared'; +import { Availability } from 'src/availability/availability.entity'; @Entity() export class User implements Person { @@ -26,4 +27,7 @@ export class User implements Person { @Column() role: Role; + + @OneToMany(() => Availability, (availability) => availability.user) + availabilities: Availability[]; } From f39f04144aec02e5bdb6a41926a9406c761f115b Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:24:06 +0100 Subject: [PATCH 016/104] fix: removed relationship fields in entities --- api/src/availability/availability.entity.ts | 4 ++-- api/src/timeslots/timeslot.entity.ts | 3 --- api/src/users/user.entity.ts | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index 51aafd4..1153fa7 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -22,9 +22,9 @@ export class Availability implements AvailabilityInterface { @Column({ name: 'last_modified' }) lastModified: Date; @Column({ name: 'time_slot' }) - @ManyToOne(() => TimeSlot, (timeSlot) => timeSlot.availabilities) + @ManyToOne(() => TimeSlot) timeSlot: TimeSlot; - @ManyToOne(() => User, (user) => user.availabilities) + @ManyToOne(() => User) @JoinColumn() user: User; } diff --git a/api/src/timeslots/timeslot.entity.ts b/api/src/timeslots/timeslot.entity.ts index b46e4f7..f5f784e 100644 --- a/api/src/timeslots/timeslot.entity.ts +++ b/api/src/timeslots/timeslot.entity.ts @@ -12,7 +12,4 @@ export class TimeSlot implements TimeSlotInterface { @Column() end: Date; - - @OneToMany(() => Availability, (availability) => availability.timeSlot) - availabilities: Availability[]; } diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index 8807c2e..e4e070d 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -27,7 +27,4 @@ export class User implements Person { @Column() role: Role; - - @OneToMany(() => Availability, (availability) => availability.user) - availabilities: Availability[]; } From 789e24672754dd96915f61a1f3110089e339a7c8 Mon Sep 17 00:00:00 2001 From: Marco De Luca <31864038+markdeluk@users.noreply.github.com> Date: Sun, 14 Jan 2024 18:15:09 +0100 Subject: [PATCH 017/104] test: CRUD unit tests for availability --- .../availability.controller.spec.ts | 52 ++++++++++++++++++- .../availability/availability.controller.ts | 36 +++++-------- api/src/availability/availability.entity.ts | 1 - .../availability/availability.service.spec.ts | 46 +++++++++++++++- api/src/availability/availability.service.ts | 24 +++++---- .../availability/create-availability.dto.ts | 18 +++++++ api/src/mocks/data.ts | 9 ++++ .../timeslots/timeslots.controller.spec.ts | 7 +-- shared/src/availability.ts | 2 - 9 files changed, 152 insertions(+), 43 deletions(-) create mode 100644 api/src/availability/create-availability.dto.ts diff --git a/api/src/availability/availability.controller.spec.ts b/api/src/availability/availability.controller.spec.ts index 476f07d..e9f04b9 100644 --- a/api/src/availability/availability.controller.spec.ts +++ b/api/src/availability/availability.controller.spec.ts @@ -1,4 +1,4 @@ -import { testDate } from '@mocks/data'; +import { mockAvailability, testDate } from '@mocks/data'; import { AvailabilityController } from './availability.controller'; import { AvailabilityService } from './availability.service'; import { TestBed } from '@automock/jest'; @@ -26,4 +26,54 @@ describe('AvailabilityController', () => { expect(controller).toBeDefined(); expect(service).toBeDefined(); }); + + // CRUD OPERATIONS + + describe('createAvailability', () => { + it('should allow creating a valid availability', async () => { + const availability = { + state: mockAvailability.state, + lastModified: mockAvailability.lastModified, + timeSlot: mockAvailability.timeSlot, + user: mockAvailability.user, + }; + + jest + .spyOn(service, 'createAvailability') + .mockResolvedValue(mockAvailability); + + const result = await controller.createAvailability(availability); + + expect(result).toEqual(mockAvailability); + }); + }); + + // Read an availability + describe('findAvailabilityById', () => { + it('should allow finding an availability by id', async () => { + jest + .spyOn(service, 'findAvailabilityById') + .mockResolvedValue(mockAvailability); + + const result = await controller.findAvailabilityById(mockAvailability.id); + + expect(result).toEqual(mockAvailability); + }); + }); + + // Delete an availability + describe('deleteAvailability', () => { + it('should allow deleting an availability', async () => { + jest + .spyOn(service, 'findAvailabilityById') + .mockResolvedValue(mockAvailability); + jest + .spyOn(service, 'deleteAvailability') + .mockResolvedValue(mockAvailability); + + await expect( + controller.deleteAvailability(mockAvailability.id), + ).resolves.toEqual(mockAvailability); + }); + }); }); diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 5f0673c..dd2a8f4 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -1,5 +1,4 @@ import { - BadRequestException, Body, Controller, Delete, @@ -12,7 +11,6 @@ import { import { AvailabilityService } from './availability.service'; import { Action, - checkAbility, insertAvailabilitySchema, updateAvailabilitySchema, } from '@hkrecruitment/shared'; @@ -25,13 +23,13 @@ import { ApiCreatedResponse, ApiOkResponse, ApiTags, - ApiConflictResponse, ApiNoContentResponse, ApiBadGatewayResponse, } from '@nestjs/swagger'; import { CheckPolicies } from 'src/authorization/check-policies.decorator'; import { Availability } from './availability.entity'; import Joi from 'joi'; +import { CreateAvailabilityDto } from './create-availability.dto'; @ApiBearerAuth() @ApiTags('availability') @@ -76,10 +74,12 @@ export class AvailabilityController { @JoiValidate({ body: insertAvailabilitySchema, }) - async createAvailability(@Body() body: Availability): Promise { + async createAvailability( + @Body() body: CreateAvailabilityDto, + ): Promise { const res = await this.availabilityService.createAvailability(body); - if (res.identifiers.length > 0) { - return true; + if (res) { + return res; } else { throw new ForbiddenException(); } @@ -95,16 +95,12 @@ export class AvailabilityController { @JoiValidate({ body: updateAvailabilitySchema, }) - async updateAvailability(@Body() body: Availability): Promise { + async updateAvailability(@Body() body: Availability): Promise { const test = await this.findAvailabilityById(body.id); if (test) { - const res = await this.availabilityService.updateAvailability(body); - if ( - res.affected != undefined && - res.affected != null && - res.affected > 0 - ) { - return true; + const res = await this.availabilityService.updateAvailability(test, body); + if (res) { + return res; } else { throw new ForbiddenException(); } @@ -123,16 +119,12 @@ export class AvailabilityController { @JoiValidate({ param: Joi.number().positive().integer().required(), }) - async deleteAvailability(@Param() id: number): Promise { + async deleteAvailability(@Param() id: number): Promise { const test = await this.findAvailabilityById(id); if (test) { - const res = await this.availabilityService.deleteAvailability(id); - if ( - res.affected != undefined && - res.affected != null && - res.affected > 0 - ) { - return true; + const res = await this.availabilityService.deleteAvailability(test); + if (res) { + return res; } else { throw new ForbiddenException(); } diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index 1153fa7..d2e9a8c 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -3,7 +3,6 @@ import { Entity, JoinColumn, ManyToOne, - OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; import { diff --git a/api/src/availability/availability.service.spec.ts b/api/src/availability/availability.service.spec.ts index 330f32d..19cd7ef 100644 --- a/api/src/availability/availability.service.spec.ts +++ b/api/src/availability/availability.service.spec.ts @@ -1,4 +1,4 @@ -import { testDate } from '@mocks/data'; +import { mockAvailability, testDate } from '@mocks/data'; import { Test, TestingModule } from '@nestjs/testing'; import { AvailabilityService } from './availability.service'; import { Availability } from './availability.entity'; @@ -37,4 +37,48 @@ describe('AvailabilityService', () => { it('should be defined', () => { expect(service).toBeDefined(); }); + + // CRUD OPERATIONS + + describe('createAvailability', () => { + it('should create a new availability', async () => { + jest.spyOn(mockedRepository, 'save').mockResolvedValue(mockAvailability); + const result = await service.createAvailability(mockAvailability); + expect(result).toEqual(mockAvailability); + expect(mockedRepository.save).toHaveBeenCalledTimes(1); + }); + }); + + describe('deleteAvailability', () => { + it('should remove the specified availability from the database', async () => { + jest + .spyOn(mockedRepository, 'remove') + .mockResolvedValue(mockAvailability); + const result = await service.deleteAvailability(mockAvailability); + expect(result).toEqual(mockAvailability); + expect(mockedRepository.remove).toHaveBeenCalledTimes(1); + }); + }); + + describe('listAvailabilities', () => { + it('should return all availabilities', async () => { + jest + .spyOn(mockedRepository, 'find') + .mockResolvedValue([mockAvailability]); + const result = await service.listAvailabilities(); + expect(result).toEqual([mockAvailability]); + expect(mockedRepository.find).toHaveBeenCalledTimes(1); + }); + }); + + describe('findAvailabilityById', () => { + it('should return the availability with the specified id', async () => { + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockAvailability]); + const result = await service.findAvailabilityById(mockAvailability.id); + expect(result).toEqual(mockAvailability); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index 7f7a303..aa85a4a 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { DeleteResult, InsertResult, Repository, UpdateResult } from 'typeorm'; +import { Repository } from 'typeorm'; import { Availability } from './availability.entity'; +import { CreateAvailabilityDto } from './create-availability.dto'; @Injectable() export class AvailabilityService { @@ -21,18 +22,21 @@ export class AvailabilityService { return matches.length > 0 ? matches[0] : null; } - async createAvailability(availability: Availability): Promise { - return await this.availabilityRepository.insert(availability); + async createAvailability( + availability: CreateAvailabilityDto, + ): Promise { + return await this.availabilityRepository.save(availability); } - async updateAvailability(availability: Availability): Promise { - return await this.availabilityRepository.update( - availability.id, - availability, - ); + async updateAvailability( + oldAvailability: Availability, + newAvailability: Availability, + ): Promise { + await this.availabilityRepository.remove(oldAvailability); + return await this.availabilityRepository.save(newAvailability); } - async deleteAvailability(id: number): Promise { - return await this.availabilityRepository.delete(id); + async deleteAvailability(availability: Availability): Promise { + return await this.availabilityRepository.remove(availability); } } diff --git a/api/src/availability/create-availability.dto.ts b/api/src/availability/create-availability.dto.ts new file mode 100644 index 0000000..c9cd76f --- /dev/null +++ b/api/src/availability/create-availability.dto.ts @@ -0,0 +1,18 @@ +import { + Availability, + AvailabilityState, + TimeSlot, +} from '@hkrecruitment/shared'; +import { ApiProperty } from '@nestjs/swagger'; +import { User } from 'src/users/user.entity'; + +export class CreateAvailabilityDto implements Availability { + @ApiProperty() + state: AvailabilityState; + @ApiProperty() + lastModified: Date; + @ApiProperty() + timeSlot: TimeSlot; + @ApiProperty() + user: User; +} diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index d763224..96dd1d2 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -4,6 +4,7 @@ import { ApplicationState, LangLevel, Role, + AvailabilityState, } from '@hkrecruitment/shared'; import { BscApplication, @@ -156,3 +157,11 @@ export const applicantId = 'abc123'; export const folderId = 'folder_abc123'; export const fileId = 'file_abc123'; export const today = '1/1/2023, 10:00:00'; + +export const mockAvailability = { + id: 1, + state: AvailabilityState.Confirmed, + lastModified: new Date(), + timeSlot: mockTimeSlot, + user: applicant, +}; diff --git a/api/src/timeslots/timeslots.controller.spec.ts b/api/src/timeslots/timeslots.controller.spec.ts index 0c2d34b..55ec4d6 100644 --- a/api/src/timeslots/timeslots.controller.spec.ts +++ b/api/src/timeslots/timeslots.controller.spec.ts @@ -1,10 +1,5 @@ import { TestBed } from '@automock/jest'; -import { - mockTimeSlot, - testDate, - testDateTimeEnd, - testDateTimeStart, -} from '@mocks/data'; +import { mockTimeSlot, testDateTimeEnd, testDateTimeStart } from '@mocks/data'; import { TimeSlotsController } from './timeslots.controller'; import { TimeSlotsService } from './timeslots.service'; import { testDateTime10Minutes } from '@mocks/data'; diff --git a/shared/src/availability.ts b/shared/src/availability.ts index 3be7a45..144e56e 100644 --- a/shared/src/availability.ts +++ b/shared/src/availability.ts @@ -15,7 +15,6 @@ export enum AvailabilityType { } export interface Availability { - id: number; state: AvailabilityState; lastModified: Date; timeSlot: TimeSlot; @@ -37,7 +36,6 @@ export const insertAvailabilitySchema = Joi.object({ }); export const updateAvailabilitySchema = Joi.object({ - id: Joi.number().positive().integer().required(), state: Joi.string() .valid(...Object.values(AvailabilityType)) .required(), From 7c7954c8c839c28996da19659534e2c676cdf3ba Mon Sep 17 00:00:00 2001 From: whiteOFF <89868763+whiteOFF@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:11:35 +0100 Subject: [PATCH 018/104] RecruitmentSession: controller, service, entity, tests (#15) RecruitmentSessionController: - findActive: Retrieve the active recruitment session if it exists. - createRecruitmentSession - updateRecruitmentSession - deleteRecruitmentSession RecruitmentSessionService: - createRecruitmentSession - findAllRecruitmentSessions - findRecruitmentSessionById - findActiveRecruitmentSession - deletRecruitmentSession - updateRecruitmentSession - sessionHasPendingInterviews: Check if a recruitment session has pending interviews (to be implemented). DTOs: - CreateRecruitmentSessionDTO - UpdateRecruitmentSessionDTO - RecruitmentSessionResponseDTO Tests: - Controller Unit tests: recruitment-session.controller.spec.ts - Service Unit tests: recruitment-session.service.spec.ts Commits: * fix: missing dependencies and imports (#9) * fix: added @joi/date library * fix: added missing useState import fix: removed loading screen when auth token is empty * docs: updated project description, useful links, and contributors in README.md (#10) * feat session: service, controller, entity * feat: update recruitment-session: service, controller, entity * feat: update recruitment-session: service, controller, entity * fix: dependencies in shared/abilities * fix: mock shared -> required/optional fields * fix: mock recruitment session service, insert data mock * fix: relative import of recruitment-session from shared folder * fix: recruitment-session service Delete test * fix: removed lastModified from UpdateRecruitmentSessionDto * fix: ability check on recruitment session creation * feat: check if recruitment session has pending interviews before deleting it * feat: check for conflicts and consistency when updating a recruitment session state * fix: check ability for update recruitment session * refactor: removed unused imports in recruitment-session.controller.ts * fix: use const for unchanged variable in createRecruitmentSession service method * refactor: removed unused code in creatre-recruitment-session.dto.ts * fix: updated Date[] in create and update Recruitment session DTOs * fix: added 'state' to recruitmentSession response DTO * test: Recruitment Session Controller tests * refactor: removed unused imports --------- Co-authored-by: Alberto Baroso --- api/src/mocks/data.ts | 36 ++ .../create-recruitment-session.dto.ts | 18 + .../recruitment-session-response.dto.ts | 14 + .../recruitment-session.controller.spec.ts | 355 ++++++++++++++++++ .../recruitment-session.controller.ts | 231 ++++++++++++ .../recruitment-session.entity.ts | 32 ++ .../recruitment-session.service.spec.ts | 51 +++ .../recruitment-session.service.ts | 61 +++ .../update-recruitment-session.dto.ts | 24 ++ shared/src/abilities.ts | 13 +- shared/src/index.ts | 1 + shared/src/recruitment-session.spec.ts | 83 ++++ shared/src/recruitment-session.ts | 72 ++++ 13 files changed, 989 insertions(+), 2 deletions(-) create mode 100644 api/src/recruitment-session/create-recruitment-session.dto.ts create mode 100644 api/src/recruitment-session/recruitment-session-response.dto.ts create mode 100644 api/src/recruitment-session/recruitment-session.controller.spec.ts create mode 100644 api/src/recruitment-session/recruitment-session.controller.ts create mode 100644 api/src/recruitment-session/recruitment-session.entity.ts create mode 100644 api/src/recruitment-session/recruitment-session.service.spec.ts create mode 100644 api/src/recruitment-session/recruitment-session.service.ts create mode 100644 api/src/recruitment-session/update-recruitment-session.dto.ts create mode 100644 shared/src/recruitment-session.spec.ts create mode 100644 shared/src/recruitment-session.ts diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index 96dd1d2..3c60b65 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -12,6 +12,9 @@ import { PhdApplication, } from 'src/application/application.entity'; import { UpdateApplicationDto } from 'src/application/update-application.dto'; +import { RecruitmentSessionState } from '@hkrecruitment/shared/recruitment-session'; +import { CreateRecruitmentSessionDto } from 'src/recruitment-session/create-recruitment-session.dto'; +import { UpdateRecruitmentSessionDto } from 'src/recruitment-session/update-recruitment-session.dto'; export const testDate = new Date(2023, 0, 1, 10, 0, 0); export const testDateTimeStart = new Date(2023, 0, 1, 10, 30, 0); @@ -25,6 +28,39 @@ export const mockTimeSlot = { id: 1, }; +export const testInterviewStart = '11:55' as unknown as Date; +export const testInterviewEnd = '20:35' as unknown as Date; +export const testDay1 = '2024-10-20' as unknown as Date; +export const testDay2 = '2024-10-21' as unknown as Date; +export const testDay3 = '2024-10-22' as unknown as Date; +export const testDateCreatedAt = '2024-9-10' as unknown as Date; +export const testDateLastModified = '2024-9-12' as unknown as Date; + +export const mockRecruitmentSession = { + id: 1, + state: RecruitmentSessionState.Active, + slotDuration: 50, + interviewStart: testInterviewStart, + interviewEnd: testInterviewEnd, + days: [testDay1, testDay2, testDay3], + createdAt: testDateCreatedAt, + lastModified: testDateLastModified, +}; + +export const mockCreateRecruitmentSessionDto = { + slotDuration: 50, + interviewStart: testInterviewStart, + interviewEnd: testInterviewEnd, + days: [testDay1, testDay2, testDay3], +} as CreateRecruitmentSessionDto; + +export const mockUpdateRecruitmentSessionDto = { + slotDuration: 50, + interviewStart: testInterviewStart, + interviewEnd: testInterviewEnd, + days: [testDay1, testDay2, testDay3], +} as UpdateRecruitmentSessionDto; + export const baseFile = { encoding: '7bit', mimetype: 'application/pdf', diff --git a/api/src/recruitment-session/create-recruitment-session.dto.ts b/api/src/recruitment-session/create-recruitment-session.dto.ts new file mode 100644 index 0000000..81b6399 --- /dev/null +++ b/api/src/recruitment-session/create-recruitment-session.dto.ts @@ -0,0 +1,18 @@ +import { RecruitmentSession } from '@hkrecruitment/shared/recruitment-session'; +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateRecruitmentSessionDto + implements Partial +{ + @ApiProperty() + slotDuration: number; + + @ApiProperty() + interviewStart: Date; + + @ApiProperty() + interviewEnd: Date; + + @ApiProperty({ isArray: true }) + days: Date[]; +} diff --git a/api/src/recruitment-session/recruitment-session-response.dto.ts b/api/src/recruitment-session/recruitment-session-response.dto.ts new file mode 100644 index 0000000..f13d57f --- /dev/null +++ b/api/src/recruitment-session/recruitment-session-response.dto.ts @@ -0,0 +1,14 @@ +import { + RecruitmentSession, + RecruitmentSessionState, +} from '@hkrecruitment/shared/recruitment-session'; +import { Exclude, Expose } from 'class-transformer'; + +@Exclude() +export class RecruitmentSessionResponseDto + implements Partial +{ + @Expose() id: number; + @Expose() createdAt: Date; + @Expose() state: RecruitmentSessionState; +} diff --git a/api/src/recruitment-session/recruitment-session.controller.spec.ts b/api/src/recruitment-session/recruitment-session.controller.spec.ts new file mode 100644 index 0000000..719202d --- /dev/null +++ b/api/src/recruitment-session/recruitment-session.controller.spec.ts @@ -0,0 +1,355 @@ +import { createMockAbility } from '@hkrecruitment/shared/abilities.spec'; +import { RecruitmentSessionController } from './recruitment-session.controller'; +import { RecruitmentSessionService } from './recruitment-session.service'; +import { Action, RecruitmentSessionState } from '@hkrecruitment/shared'; +import { TestBed } from '@automock/jest'; +import { RecruitmentSessionResponseDto } from './recruitment-session-response.dto'; +import { RecruitmentSession } from './recruitment-session.entity'; +import { + mockRecruitmentSession, + mockUpdateRecruitmentSessionDto, + mockCreateRecruitmentSessionDto, + testDate, +} from '@mocks/data'; +import { + ConflictException, + ForbiddenException, + NotFoundException, +} from '@nestjs/common'; +import { UpdateRecruitmentSessionDto } from './update-recruitment-session.dto'; + +describe('RecruitmentSessionController', () => { + let controller: RecruitmentSessionController; + let service: RecruitmentSessionService; + + /************* Test setup ************/ + + beforeAll(() => { + jest + .spyOn(global, 'Date') + .mockImplementation(() => testDate as unknown as string); + }); + + beforeEach(async () => { + const { unit, unitRef } = TestBed.create( + RecruitmentSessionController, + ).compile(); + + controller = unit; + service = unitRef.get(RecruitmentSessionService); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + expect(service).toBeDefined(); + }); + + describe('getActive RecruitmentSession', () => { + it('should return an active recruitment session if it exists', async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Read, 'RecruitmentSession'); + }); + jest + .spyOn(service, 'findActiveRecruitmentSession') + .mockResolvedValue(mockRecruitmentSession); + const result = await controller.findActive(mockAbility); + const expectedApp = { + ...mockRecruitmentSession, + } as RecruitmentSessionResponseDto; + expect(result).toEqual(expectedApp); + expect(service.findActiveRecruitmentSession).toHaveBeenCalledTimes(1); + expect(mockAbility.can).toHaveBeenCalled(); + }); + + it("should throw a ForbiddenException if the user can't read the recruitment session", async () => { + const mockAbility = createMockAbility(({ cannot }) => { + cannot(Action.Read, 'RecruitmentSession'); + }); + jest + .spyOn(service, 'findActiveRecruitmentSession') + .mockResolvedValue({ ...mockRecruitmentSession }); + const result = controller.findActive(mockAbility); + await expect(result).rejects.toThrow(ForbiddenException); + expect(service.findActiveRecruitmentSession).toHaveBeenCalledTimes(1); + expect(mockAbility.can).toHaveBeenCalled(); + }); + }); + + describe('createRecruitmentSession', () => { + it('should create a recruitment session', async () => { + const expectedRecruitmentSession = { + ...mockRecruitmentSession, + } as RecruitmentSessionResponseDto; + jest + .spyOn(service, 'createRecruitmentSession') + .mockResolvedValue(mockRecruitmentSession); + const result = await controller.createRecruitmentSession( + mockCreateRecruitmentSessionDto, + ); + expect(result).toEqual(expectedRecruitmentSession); + expect(service.createRecruitmentSession).toHaveBeenCalledTimes(1); + expect(service.createRecruitmentSession).toHaveBeenCalledWith( + mockCreateRecruitmentSessionDto, + ); + }); + + it('should throw a ConflictException if there is already an active recruitment session', async () => { + jest + .spyOn(service, 'findActiveRecruitmentSession') + .mockResolvedValue(mockRecruitmentSession); + const result = controller.createRecruitmentSession( + mockCreateRecruitmentSessionDto, + ); + await expect(result).rejects.toThrow(ConflictException); + expect(service.findActiveRecruitmentSession).toHaveBeenCalledTimes(1); + }); + }); + + describe('updateRecruitmentSession', () => { + it('should update a recruitment session', async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Update, 'RecruitmentSession'); + }); + const mockUpdatedRecruitmentSession = { + ...mockRecruitmentSession, + ...mockUpdateRecruitmentSessionDto, + } as RecruitmentSession; + const expectedRecruitmentSession = { + id: mockUpdatedRecruitmentSession.id, + state: mockUpdatedRecruitmentSession.state, + createdAt: mockUpdatedRecruitmentSession.createdAt, + } as RecruitmentSessionResponseDto; + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSession); + jest + .spyOn(service, 'updateRecruitmentSession') + .mockResolvedValue(mockUpdatedRecruitmentSession); + const result = await controller.updateRecruitmentSession( + mockRecruitmentSession.id, + mockUpdateRecruitmentSessionDto, + mockAbility, + ); + expect(result).toEqual(expectedRecruitmentSession); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + expect(service.updateRecruitmentSession).toHaveBeenCalledTimes(1); + expect(service.updateRecruitmentSession).toHaveBeenCalledWith({ + ...mockRecruitmentSession, + lastModified: testDate, + }); + }); + + it('should throw a NotFoundException if the recruitment session does not exist', async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Update, 'RecruitmentSession'); + }); + jest.spyOn(service, 'findRecruitmentSessionById').mockResolvedValue(null); + const result = controller.updateRecruitmentSession( + mockRecruitmentSession.id, + mockUpdateRecruitmentSessionDto, + mockAbility, + ); + await expect(result).rejects.toThrow(NotFoundException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + }); + + it("should throw a ForbiddenException if the user can't update the recruitment session", async () => { + const mockAbility = createMockAbility(({ cannot }) => { + cannot(Action.Update, 'RecruitmentSession'); + }); + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSession); + const result = controller.updateRecruitmentSession( + mockRecruitmentSession.id, + mockUpdateRecruitmentSessionDto, + mockAbility, + ); + await expect(result).rejects.toThrow(ForbiddenException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + }); + + it("should throw a ConflictException when updating a RecruitmentSection state to 'Active' and there is already an active recruitment session", async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Update, 'RecruitmentSession'); + }); + const mockRecruitmentSessionToUpdate = { + ...mockRecruitmentSession, + state: RecruitmentSessionState.Concluded, + id: 1, + } as RecruitmentSession; + const activeRecruitmentSession = { + ...mockRecruitmentSession, + id: 2, + state: RecruitmentSessionState.Active, + } as RecruitmentSession; + const updateRecruitmentSessionDto = { + state: RecruitmentSessionState.Active, + } as UpdateRecruitmentSessionDto; + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSessionToUpdate); + jest + .spyOn(service, 'findActiveRecruitmentSession') + .mockResolvedValue(activeRecruitmentSession); + const result = controller.updateRecruitmentSession( + mockRecruitmentSessionToUpdate.id, + updateRecruitmentSessionDto, + mockAbility, + ); + await expect(result).rejects.toThrow(ConflictException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + expect(service.findActiveRecruitmentSession).toHaveBeenCalledTimes(1); + }); + + it("shouldn't throw a ConflictException when updating the currentyl active RecruitmentSection state to 'Active'", async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Update, 'RecruitmentSession'); + }); + const mockRecruitmentSessionToUpdate = { + ...mockRecruitmentSession, + state: RecruitmentSessionState.Concluded, + id: 1, + } as RecruitmentSession; + const activeRecruitmentSession = { + ...mockRecruitmentSession, + id: 1, + state: RecruitmentSessionState.Active, + } as RecruitmentSession; + const updateRecruitmentSessionDto = { + state: RecruitmentSessionState.Active, + } as UpdateRecruitmentSessionDto; + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSessionToUpdate); + jest + .spyOn(service, 'findActiveRecruitmentSession') + .mockResolvedValue(activeRecruitmentSession); + const result = controller.updateRecruitmentSession( + mockRecruitmentSessionToUpdate.id, + updateRecruitmentSessionDto, + mockAbility, + ); + await expect(result).resolves.not.toThrow(ConflictException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + }); + + it("should throw a ConflictException when updating a RecruitmentSection state to 'Concluded' and there are pending interviews", async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Update, 'RecruitmentSession'); + }); + const mockRecruitmentSessionToUpdate = { + ...mockRecruitmentSession, + state: RecruitmentSessionState.Active, + id: 1, + } as RecruitmentSession; + const updateRecruitmentSessionDto = { + state: RecruitmentSessionState.Concluded, + } as UpdateRecruitmentSessionDto; + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSessionToUpdate); + jest + .spyOn(service, 'sessionHasPendingInterviews') + .mockResolvedValue(true); + const result = controller.updateRecruitmentSession( + mockRecruitmentSessionToUpdate.id, + updateRecruitmentSessionDto, + mockAbility, + ); + await expect(result).rejects.toThrow(ConflictException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + expect(service.sessionHasPendingInterviews).toHaveBeenCalledTimes(1); + expect(service.sessionHasPendingInterviews).toHaveBeenCalledWith( + mockRecruitmentSessionToUpdate, + ); + }); + }); + + describe('deleteRecruitmentSession', () => { + it('should delete a recruitment session', async () => { + const mockDeletedRecruitmentSession = { + ...mockRecruitmentSession, + } as RecruitmentSession; + const expectedRecruitmentSession = { + id: mockDeletedRecruitmentSession.id, + state: mockDeletedRecruitmentSession.state, + createdAt: mockDeletedRecruitmentSession.createdAt, + } as RecruitmentSessionResponseDto; + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSession); + jest + .spyOn(service, 'deletRecruitmentSession') + .mockResolvedValue(mockDeletedRecruitmentSession); + const result = await controller.deleteRecruitmentSession( + mockRecruitmentSession.id, + ); + expect(result).toEqual(expectedRecruitmentSession); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + expect(service.deletRecruitmentSession).toHaveBeenCalledTimes(1); + expect(service.deletRecruitmentSession).toHaveBeenCalledWith( + mockRecruitmentSession, + ); + }); + + it('should throw a NotFoundException if the recruitment session does not exist', async () => { + jest.spyOn(service, 'findRecruitmentSessionById').mockResolvedValue(null); + const result = controller.deleteRecruitmentSession( + mockRecruitmentSession.id, + ); + await expect(result).rejects.toThrow(NotFoundException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + }); + + it('should throw a ConflictException when deleting a RecruitmentSection that has pending interviews', async () => { + const mockRecruitmentSessionToDelete = { + ...mockRecruitmentSession, + state: RecruitmentSessionState.Active, + id: 1, + } as RecruitmentSession; + jest + .spyOn(service, 'findRecruitmentSessionById') + .mockResolvedValue(mockRecruitmentSessionToDelete); + jest + .spyOn(service, 'sessionHasPendingInterviews') + .mockResolvedValue(true); + const result = controller.deleteRecruitmentSession( + mockRecruitmentSessionToDelete.id, + ); + await expect(result).rejects.toThrow(ConflictException); + expect(service.findRecruitmentSessionById).toHaveBeenCalledTimes(1); + expect(service.findRecruitmentSessionById).toHaveBeenCalledWith( + mockRecruitmentSession.id, + ); + expect(service.sessionHasPendingInterviews).toHaveBeenCalledTimes(1); + expect(service.sessionHasPendingInterviews).toHaveBeenCalledWith( + mockRecruitmentSessionToDelete, + ); + }); + }); +}); diff --git a/api/src/recruitment-session/recruitment-session.controller.ts b/api/src/recruitment-session/recruitment-session.controller.ts new file mode 100644 index 0000000..9e65523 --- /dev/null +++ b/api/src/recruitment-session/recruitment-session.controller.ts @@ -0,0 +1,231 @@ +import { + Body, + Controller, + NotFoundException, + ConflictException, + Param, + Post, + Delete, + Patch, + ForbiddenException, + Get, +} from '@nestjs/common'; +import { RecruitmentSessionService } from './recruitment-session.service'; +import { + createRecruitmentSessionSchema, + RecruitmentSession, + RecruitmentSessionState, + updateRecruitmentSessionSchema, +} from '@hkrecruitment/shared/recruitment-session'; +import { Action, AppAbility, checkAbility } from '@hkrecruitment/shared'; +import { JoiValidate } from '../joi-validation/joi-validate.decorator'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiForbiddenResponse, + ApiNotFoundResponse, + ApiCreatedResponse, + ApiOkResponse, + ApiTags, + ApiConflictResponse, + ApiNoContentResponse, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { CheckPolicies } from 'src/authorization/check-policies.decorator'; +import { CreateRecruitmentSessionDto } from './create-recruitment-session.dto'; +import { UpdateRecruitmentSessionDto } from './update-recruitment-session.dto'; +import * as Joi from 'joi'; +import { Ability } from 'src/authorization/ability.decorator'; +import { plainToClass } from 'class-transformer'; +import { RecruitmentSessionResponseDto } from './recruitment-session-response.dto'; + +@ApiBearerAuth() +@ApiTags('recruitment-session') +@Controller('recruitment-session') +export class RecruitmentSessionController { + constructor( + private readonly recruitmentSessionService: RecruitmentSessionService, + ) {} + + // FIND ACTIVE RECRUITMENT SESSION + @ApiNotFoundResponse() + @ApiUnauthorizedResponse() + @Get() + @CheckPolicies((ability) => ability.can(Action.Read, 'RecruitmentSession')) + async findActive( + @Ability() ability: AppAbility, + ): Promise { + const recruitmentSession = + await this.recruitmentSessionService.findActiveRecruitmentSession(); + if (recruitmentSession === null) { + throw new NotFoundException(); + } + + if ( + !checkAbility( + ability, + Action.Read, + recruitmentSession, + 'RecruitmentSession', + ) + ) { + throw new ForbiddenException(); + } + + return recruitmentSession; + } + + // CREATE NEW RECRUITMENT SESSION + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiConflictResponse({ + description: 'The recruitment session cannot be created', // + }) + @ApiCreatedResponse() + @JoiValidate({ + body: createRecruitmentSessionSchema, + }) + @CheckPolicies((ability) => ability.can(Action.Create, 'RecruitmentSession')) + @Post() + async createRecruitmentSession( + @Body() recruitmentSession: CreateRecruitmentSessionDto, + ): Promise { + // there should be only one active recruitment session at a time + const hasActiveRecruitmentSession = + await this.recruitmentSessionService.findActiveRecruitmentSession(); + if (hasActiveRecruitmentSession) + throw new ConflictException( + 'There is already an active recruitment session', + ); + + return this.recruitmentSessionService.createRecruitmentSession({ + ...recruitmentSession, + }); + } + + // UPDATE A RECRUITMENT SESSION + @Patch(':session_id') + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiConflictResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @JoiValidate({ + param: Joi.number().positive().integer().required().label('session_id'), + body: updateRecruitmentSessionSchema, + }) + @CheckPolicies((ability) => ability.can(Action.Update, 'RecruitmentSession')) + async updateRecruitmentSession( + @Param('session_id') sessionId: number, + @Body() updateRecruitmentSession: UpdateRecruitmentSessionDto, + @Ability() ability: AppAbility, + ): Promise { + const recruitmentSession = + await this.recruitmentSessionService.findRecruitmentSessionById( + sessionId, + ); + + if (recruitmentSession === null) throw new NotFoundException(); + + const sessionToCheck = { + ...updateRecruitmentSession, + sessionId: recruitmentSession.id, + }; + if ( + !checkAbility( + ability, + Action.Update, + sessionToCheck, + 'RecruitmentSession', + ) + ) + throw new ForbiddenException(); + + if (updateRecruitmentSession.hasOwnProperty('state')) { + // There should be only one active recruitment session at a time + if (updateRecruitmentSession.state === RecruitmentSessionState.Active) { + const currentlyActiveRecruitmentSession = + await this.recruitmentSessionService.findActiveRecruitmentSession(); + if ( + currentlyActiveRecruitmentSession && + currentlyActiveRecruitmentSession.id !== recruitmentSession.id // It's ok to set 'Active' to the (already) active recruitment session + ) + throw new ConflictException( + 'There is already an active recruitment session', + ); + } else if ( + updateRecruitmentSession.state === RecruitmentSessionState.Concluded + ) { + // Recruitment session can't be set to concluded if it has pending interviews + const hasPendingInterviews = + await this.recruitmentSessionService.sessionHasPendingInterviews( + recruitmentSession, + ); + if (hasPendingInterviews) + throw new ConflictException( + "Recruitment session can't be set to inactive because it has pending interviews", + ); + } + } + + const updatedRecruitmentSession = + await this.recruitmentSessionService.updateRecruitmentSession({ + ...recruitmentSession, + ...updateRecruitmentSession, + lastModified: new Date(), + }); + + return plainToClass( + RecruitmentSessionResponseDto, + updatedRecruitmentSession, + ); + } + + // DELETE A RECRUITMENT SESSION + @ApiBadRequestResponse() + @ApiForbiddenResponse() + @ApiNotFoundResponse() + @ApiOkResponse() + @ApiConflictResponse() + @ApiNoContentResponse() + @CheckPolicies((ability) => ability.can(Action.Delete, 'RecruitmentSession')) + @Delete('/:recruitment_session_id') + @JoiValidate({ + param: Joi.number() + .positive() + .integer() + .required() + .label('recruitment_session_id'), + }) + async deleteRecruitmentSession( + @Param('recruitment_session_id') recruitmentSessionId: number, + ): Promise { + // Check if recruitment session exists + const toRemove = + await this.recruitmentSessionService.findRecruitmentSessionById( + recruitmentSessionId, + ); + if (!toRemove) throw new NotFoundException('Recruitment session not found'); + + // Check if recruitment session has pending interviews + if (toRemove.state !== RecruitmentSessionState.Concluded) { + const hasPendingInterviews = + await this.recruitmentSessionService.sessionHasPendingInterviews( + toRemove, + ); + if (hasPendingInterviews) + throw new ConflictException( + "Recruitment session can't be deleted because it has pending interviews", + ); + } + + // Delete recruitment session + const deletedRecruitmentSession = + await this.recruitmentSessionService.deletRecruitmentSession(toRemove); + + return plainToClass( + RecruitmentSessionResponseDto, + deletedRecruitmentSession, + ); + } +} diff --git a/api/src/recruitment-session/recruitment-session.entity.ts b/api/src/recruitment-session/recruitment-session.entity.ts new file mode 100644 index 0000000..df58326 --- /dev/null +++ b/api/src/recruitment-session/recruitment-session.entity.ts @@ -0,0 +1,32 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { + RecruitmentSession as RecruitmentSessionInterface, + RecruitmentSessionState, +} from '@hkrecruitment/shared'; + +@Entity() +export class RecruitmentSession implements RecruitmentSessionInterface { + @PrimaryGeneratedColumn('increment') + id: number; + + @Column() + state: RecruitmentSessionState; + + @Column('int', { name: 'slot_duration' }) + slotDuration: number; + + @Column('date', { name: 'interview_start' }) + interviewStart: Date; + + @Column('date', { name: 'interview_end' }) + interviewEnd: Date; + + @Column('date', { array: true }) + days: Date[]; + + @Column('date', { name: 'created_at' }) + createdAt: Date; + + @Column('date', { name: 'last_modified' }) + lastModified: Date; +} diff --git a/api/src/recruitment-session/recruitment-session.service.spec.ts b/api/src/recruitment-session/recruitment-session.service.spec.ts new file mode 100644 index 0000000..d6dfc9e --- /dev/null +++ b/api/src/recruitment-session/recruitment-session.service.spec.ts @@ -0,0 +1,51 @@ +import { mockRecruitmentSession, testDate } from '@mocks/data'; +import { mockedRepository } from '@mocks/repositories'; +import { TestingModule, Test } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { RecruitmentSession } from './recruitment-session.entity'; +import { RecruitmentSessionService } from './recruitment-session.service'; + +describe('Recruitment Session Service', () => { + let recruitmentSessionService: RecruitmentSessionService; + + beforeAll(() => { + jest + .spyOn(global, 'Date') + .mockImplementation(() => testDate as unknown as string); + }); + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + RecruitmentSessionService, + { + provide: getRepositoryToken(RecruitmentSession), + useValue: mockedRepository, + }, + ], + }).compile(); + + recruitmentSessionService = module.get( + RecruitmentSessionService, + ); + }); + + afterEach(() => jest.clearAllMocks()); + + it('should be defined', () => { + expect(recruitmentSessionService).toBeDefined(); + }); + + describe('deleteRecruitmentSession', () => { + it('should remove the specified recruitment session from the database', async () => { + jest + .spyOn(mockedRepository, 'remove') + .mockResolvedValue(mockRecruitmentSession); + const result = await recruitmentSessionService.deletRecruitmentSession( + mockRecruitmentSession, + ); + expect(result).toEqual(mockRecruitmentSession); + expect(mockedRepository.remove).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts new file mode 100644 index 0000000..3d9ee94 --- /dev/null +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { RecruitmentSession } from './recruitment-session.entity'; +import { CreateRecruitmentSessionDto } from './create-recruitment-session.dto'; +import { RecruitmentSessionState } from '@hkrecruitment/shared/recruitment-session'; + +@Injectable() +export class RecruitmentSessionService { + constructor( + @InjectRepository(RecruitmentSession) + private readonly recruitmentSessionRepository: Repository, + ) {} + + async createRecruitmentSession( + recruitmentSession: CreateRecruitmentSessionDto, + ): Promise { + const now = new Date(); + const rs = { + ...recruitmentSession, + state: RecruitmentSessionState.Active, + createdAt: now, + lastModified: now, + } as unknown as RecruitmentSession; + await this.recruitmentSessionRepository.save(rs); + return rs; + } + + async findAllRecruitmentSessions(): Promise { + return await this.recruitmentSessionRepository.find(); + } + + async findRecruitmentSessionById(id: number): Promise { + return await this.recruitmentSessionRepository.findOne({ where: { id } }); + } + + async findActiveRecruitmentSession(): Promise { + return await this.recruitmentSessionRepository.findOne({ + where: { state: RecruitmentSessionState.Active }, + }); + } + + async deletRecruitmentSession( + recruitmentSession: RecruitmentSession, + ): Promise { + return await this.recruitmentSessionRepository.remove(recruitmentSession); + } + + async updateRecruitmentSession( + recruitmentSession: RecruitmentSession, + ): Promise { + return await this.recruitmentSessionRepository.save(recruitmentSession); + } + + async sessionHasPendingInterviews( + recruitmentSession: RecruitmentSession, + ): Promise { + throw new Error('Method not implemented.'); + // TODO: Return true if recruitmentSession.interviews > 0 where interviw date is in the future + } +} diff --git a/api/src/recruitment-session/update-recruitment-session.dto.ts b/api/src/recruitment-session/update-recruitment-session.dto.ts new file mode 100644 index 0000000..a1595ae --- /dev/null +++ b/api/src/recruitment-session/update-recruitment-session.dto.ts @@ -0,0 +1,24 @@ +import { + RecruitmentSession, + RecruitmentSessionState, +} from '@hkrecruitment/shared/recruitment-session'; +import { ApiProperty } from '@nestjs/swagger'; + +export class UpdateRecruitmentSessionDto + implements Partial +{ + @ApiProperty({ required: false }) + state?: RecruitmentSessionState; + + @ApiProperty({ required: false }) + slotDuration?: number; + + @ApiProperty({ required: false }) + interviewStart?: Date; + + @ApiProperty({ required: false }) + interviewEnd?: Date; + + @ApiProperty({ required: false, isArray: true }) + days?: Date[]; +} diff --git a/shared/src/abilities.ts b/shared/src/abilities.ts index 56a3e5d..23fd5bf 100644 --- a/shared/src/abilities.ts +++ b/shared/src/abilities.ts @@ -9,6 +9,8 @@ import { applyAbilitiesForPerson, Person, Role } from "./person"; import { Application, applyAbilitiesOnApplication } from "./application"; import { applyAbilitiesOnAvailability, Availability } from "./availability"; import { TimeSlot } from "./timeslot"; +import { RecruitmentSession } from "./recruitment-session"; +import { applyAbilitiesOnRecruitmentSession } from "./recruitment-session"; export interface UserAuth { sub: string; @@ -26,8 +28,14 @@ type SubjectsTypes = | Partial | Partial | Partial - | Partial; -type SubjectNames = "Person" | "Application" | "Availability" | "TimeSlot"; + | Partial + | Partial; +type SubjectNames = + | "Person" + | "Application" + | "Availability" + | "TimeSlot" + | "RecruitmentSession"; export type Subjects = SubjectsTypes | SubjectNames; export type AppAbility = PureAbility<[Action, Subjects]>; @@ -44,6 +52,7 @@ export const abilityForUser = (user: UserAuth): AppAbility => { applyAbilitiesForPerson(user, builder); applyAbilitiesOnApplication(user, builder); applyAbilitiesOnAvailability(user, builder); + applyAbilitiesOnRecruitmentSession(user, builder); const { build } = builder; return build(); diff --git a/shared/src/index.ts b/shared/src/index.ts index 3402b34..17e316f 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -4,3 +4,4 @@ export * from "./application"; export * from "./availability"; export * from "./timeslot"; export * from "./slot"; +export * from "./recruitment-session"; diff --git a/shared/src/recruitment-session.spec.ts b/shared/src/recruitment-session.spec.ts new file mode 100644 index 0000000..e86cf48 --- /dev/null +++ b/shared/src/recruitment-session.spec.ts @@ -0,0 +1,83 @@ +import { + RecruitmentSession, + RecruitmentSessionState, + createRecruitmentSessionSchema, + applyAbilitiesOnRecruitmentSession, +} from "./recruitment-session"; +import { createMockAbility } from "./abilities.spec"; +import { Action, UserAuth, checkAbility } from "./abilities"; +import { expression } from "joi"; + +describe("Recruitment Session", () => { + describe("createRecruitmentSessionSchema", () => { + const mockRecSess: Partial = { + state: RecruitmentSessionState.Active, + slotDuration: 5, + interviewStart: "11:55" as unknown as Date, + interviewEnd: "16:30" as unknown as Date, + days: [new Date("2024-12-23"), new Date("2024-12-23")], + lastModified: new Date("2023-10-20 15:10"), + }; + + it("should allow a valid recruitment session", () => { + expect( + createRecruitmentSessionSchema.validate(mockRecSess) + ).not.toHaveProperty("error"); + }); + + it("should allow to not set optional fields", () => { + const session: Partial = { + ...mockRecSess, + days: undefined, + slotDuration: undefined, + }; + expect( + createRecruitmentSessionSchema.validate(session) + ).not.toHaveProperty("error"); + }); + + it("should require state", () => { + const session: Partial = { + ...mockRecSess, + state: undefined, + }; + const { error } = createRecruitmentSessionSchema.validate(session); + expect(error).toBeDefined(); + expect(error.message).toMatch(/.+state.+ is required/); + }); + + it("should require interview start", () => { + const session: Partial = { + ...mockRecSess, + interviewStart: undefined, + }; + const { error } = createRecruitmentSessionSchema.validate(session); + expect(error).toBeDefined(); + expect(error.message).toMatch(/.+interviewStart.+ is required/); + }); + + it("should require interview end", () => { + const session: Partial = { + ...mockRecSess, + interviewEnd: undefined, + }; + const { error } = createRecruitmentSessionSchema.validate(session); + expect(error).toBeDefined(); + expect(error.message).toMatch(/.+interviewEnd.+ is required/); + }); + + it("should require last modified", () => { + const session: Partial = { + ...mockRecSess, + lastModified: undefined, + }; + const { error } = createRecruitmentSessionSchema.validate(session); + expect(error).toBeDefined(); + expect(error.message).toMatch(/.+lastModified.+ is required/); + }); + + it("check interview start type: should be 11:55", () => { + expect(mockRecSess.interviewStart).toMatch("11:55"); + }); + }); +}); diff --git a/shared/src/recruitment-session.ts b/shared/src/recruitment-session.ts new file mode 100644 index 0000000..a232e18 --- /dev/null +++ b/shared/src/recruitment-session.ts @@ -0,0 +1,72 @@ +import { Action, ApplyAbilities } from "./abilities"; +import { Role } from "./person"; +import DateExtension from "@joi/date"; +import * as Joi from "joi"; + +const JoiDate = Joi.extend(DateExtension); + +export enum RecruitmentSessionState { + Active = "active", + Concluded = "concluded", +} + +// import BaseJoi from "joi"; + +export interface RecruitmentSession { + state: RecruitmentSessionState; + slotDuration: number; + interviewStart: Date; + interviewEnd: Date; + days: Date[]; + createdAt: Date; + lastModified: Date; +} + +/* Validation schemas */ + +export const createRecruitmentSessionSchema = Joi.object({ + state: Joi.string().valid("active", "concluded").required(), + slotDuration: Joi.number().integer().optional(), + interviewStart: JoiDate.date().format("HH:mm").required(), + interviewEnd: JoiDate.date().format("HH:mm").required(), + days: Joi.array().items(JoiDate.date().format("YYYY-MM-DD")).optional(), + lastModified: JoiDate.date().format("YYYY-MM-DD HH:mm").required(), +}).options({ + stripUnknown: true, + abortEarly: false, + presence: "required", +}); + +export const updateRecruitmentSessionSchema = Joi.object({ + state: Joi.string().valid("active", "concluded").optional(), + slotDuration: Joi.number().integer().optional(), + interviewStart: JoiDate.date().format("YYYY-MM-DD HH:mm").optional(), + interviewEnd: JoiDate.date().format("YYYY-MM-DD HH:mm").optional(), + days: Joi.array().items(JoiDate.date().format("YYYY-MM-DD")).optional(), + createdAt: JoiDate.date().format("YYYY-MM-DD HH:mm").optional(), + lastModified: JoiDate.date().format("YYYY-MM-DD HH:mm").optional(), +}); + +/* Abilities */ + +export const applyAbilitiesOnRecruitmentSession: ApplyAbilities = ( + user, + { can, cannot } +) => { + can(Action.Manage, "RecruitmentSession"); + switch (user.role) { + case Role.Admin: + case Role.Supervisor: + can(Action.Manage, "RecruitmentSession"); + break; + case Role.Clerk: + case Role.Member: + can(Action.Read, "RecruitmentSession"); + break; + case Role.Applicant: + cannot(Action.Manage, "RecruitmentSession"); + break; + default: + cannot(Action.Manage, "RecruitmentSession"); + } +}; From 7054cde28d5a0e521f28e2e76990600a699601c8 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 22 Jan 2024 09:55:16 +0100 Subject: [PATCH 019/104] fix: updated imports from shared/recruitment-session --- .../recruitment-session/create-recruitment-session.dto.ts | 2 +- .../recruitment-session/recruitment-session-response.dto.ts | 2 +- .../recruitment-session/recruitment-session.controller.ts | 6 ++++-- api/src/recruitment-session/recruitment-session.service.ts | 2 +- .../recruitment-session/update-recruitment-session.dto.ts | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/api/src/recruitment-session/create-recruitment-session.dto.ts b/api/src/recruitment-session/create-recruitment-session.dto.ts index 81b6399..a9d7276 100644 --- a/api/src/recruitment-session/create-recruitment-session.dto.ts +++ b/api/src/recruitment-session/create-recruitment-session.dto.ts @@ -1,4 +1,4 @@ -import { RecruitmentSession } from '@hkrecruitment/shared/recruitment-session'; +import { RecruitmentSession } from '@hkrecruitment/shared'; import { ApiProperty } from '@nestjs/swagger'; export class CreateRecruitmentSessionDto diff --git a/api/src/recruitment-session/recruitment-session-response.dto.ts b/api/src/recruitment-session/recruitment-session-response.dto.ts index f13d57f..4f83647 100644 --- a/api/src/recruitment-session/recruitment-session-response.dto.ts +++ b/api/src/recruitment-session/recruitment-session-response.dto.ts @@ -1,7 +1,7 @@ import { RecruitmentSession, RecruitmentSessionState, -} from '@hkrecruitment/shared/recruitment-session'; +} from '@hkrecruitment/shared'; import { Exclude, Expose } from 'class-transformer'; @Exclude() diff --git a/api/src/recruitment-session/recruitment-session.controller.ts b/api/src/recruitment-session/recruitment-session.controller.ts index 9e65523..bc85757 100644 --- a/api/src/recruitment-session/recruitment-session.controller.ts +++ b/api/src/recruitment-session/recruitment-session.controller.ts @@ -16,8 +16,10 @@ import { RecruitmentSession, RecruitmentSessionState, updateRecruitmentSessionSchema, -} from '@hkrecruitment/shared/recruitment-session'; -import { Action, AppAbility, checkAbility } from '@hkrecruitment/shared'; + Action, + AppAbility, + checkAbility, +} from '@hkrecruitment/shared'; import { JoiValidate } from '../joi-validation/joi-validate.decorator'; import { ApiBadRequestResponse, diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts index 3d9ee94..2b77ee5 100644 --- a/api/src/recruitment-session/recruitment-session.service.ts +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { RecruitmentSession } from './recruitment-session.entity'; import { CreateRecruitmentSessionDto } from './create-recruitment-session.dto'; -import { RecruitmentSessionState } from '@hkrecruitment/shared/recruitment-session'; +import { RecruitmentSessionState } from '@hkrecruitment/shared'; @Injectable() export class RecruitmentSessionService { diff --git a/api/src/recruitment-session/update-recruitment-session.dto.ts b/api/src/recruitment-session/update-recruitment-session.dto.ts index a1595ae..63c72fc 100644 --- a/api/src/recruitment-session/update-recruitment-session.dto.ts +++ b/api/src/recruitment-session/update-recruitment-session.dto.ts @@ -1,7 +1,7 @@ import { RecruitmentSession, RecruitmentSessionState, -} from '@hkrecruitment/shared/recruitment-session'; +} from '@hkrecruitment/shared'; import { ApiProperty } from '@nestjs/swagger'; export class UpdateRecruitmentSessionDto From c44c5bdb3fecf1902117f69d8566bb99dd66e525 Mon Sep 17 00:00:00 2001 From: Alberto Baroso <35893959+AlbertoBaroso@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:57:47 +0100 Subject: [PATCH 020/104] Feature: Rectuitment session module (#17) --- api/src/app.module.ts | 6 ++++-- .../recruitment-session.module.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 api/src/recruitment-session/recruitment-session.module.ts diff --git a/api/src/app.module.ts b/api/src/app.module.ts index cab0441..271ffd0 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -3,6 +3,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { UsersModule } from './users/users.module'; import { ApplicationsModule } from './application/applications.module'; +import { RecruitmentSessionModule } from './recruitment-session/recruitment-session.module'; import { TimeSlotsModule } from './timeslots/timeslots.module'; import { AuthenticationModule } from './authentication/authentication.module'; import { APP_GUARD } from '@nestjs/core'; @@ -30,11 +31,12 @@ import { AuthorizationGuard } from './authorization/authorization.guard'; } as TypeOrmModuleOptions), inject: [ConfigService], }), - UsersModule, ApplicationsModule, - TimeSlotsModule, AuthenticationModule, AuthorizationModule, + RecruitmentSessionModule, + TimeSlotsModule, + UsersModule, ], providers: [ { diff --git a/api/src/recruitment-session/recruitment-session.module.ts b/api/src/recruitment-session/recruitment-session.module.ts new file mode 100644 index 0000000..b53e481 --- /dev/null +++ b/api/src/recruitment-session/recruitment-session.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { RecruitmentSessionService } from './recruitment-session.service'; +import { RecruitmentSessionController } from './recruitment-session.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { RecruitmentSession } from './recruitment-session.entity'; +import { UsersModule } from 'src/users/users.module'; + +@Module({ + imports: [TypeOrmModule.forFeature([RecruitmentSession]), UsersModule], + providers: [RecruitmentSessionService], + controllers: [RecruitmentSessionController], + exports: [RecruitmentSessionService], +}) +export class RecruitmentSessionModule {} From e6b590ff88da75cf75faa802cd57b55636f89e71 Mon Sep 17 00:00:00 2001 From: Alberto Baroso <35893959+AlbertoBaroso@users.noreply.github.com> Date: Fri, 15 Mar 2024 21:53:21 +0100 Subject: [PATCH 021/104] SonarCloud Analysis (#18) * feat: setup coverageDirectory and coveragePathIgnorePatterns * ci: added SonarCloud Analysis job in GitHub actions * ci: sonar-project.properties configuration * Simplified workflow, single task, maximum gain --------- Co-authored-by: Vincenzo Pellegrini --- .github/workflows/fullstack.yml | 35 ++++++++++++++++++++++----------- api/package.json | 17 +++++++++++++--- package.json | 2 +- shared/package.json | 3 ++- sonar-project.properties | 7 +++++++ 5 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 sonar-project.properties diff --git a/.github/workflows/fullstack.yml b/.github/workflows/fullstack.yml index 0c49192..48e3293 100644 --- a/.github/workflows/fullstack.yml +++ b/.github/workflows/fullstack.yml @@ -1,23 +1,27 @@ -name: Fullstack +name: Test on: - - push - - pull_request + push: + branches: + - '*' + pull_request: + branches: + - '*' jobs: - test: + tests: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 20 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v3 name: Install pnpm id: pnpm-install with: @@ -30,7 +34,7 @@ jobs: run: | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} @@ -46,10 +50,13 @@ jobs: - name: Test building run: pnpm run build && pnpm run clean + - name: Run shared unit tests - run: pnpm --filter shared test + run: pnpm --filter shared test:cov + - name: Run api unit tests - run: pnpm --filter api test + run: pnpm --filter api test:cov + - name: Run api e2e tests env: DATABASE_TYPE: sqlite @@ -59,4 +66,10 @@ jobs: AUTH0_AUDIENCE: ${{ secrets.TEST_AUTH0_AUDIENCE }} AUTH0_CLIENT_ID: ${{ secrets.TEST_AUTH0_CLIENT_ID }} AUTH0_CLIENT_SECRET: ${{ secrets.TEST_AUTH0_CLIENT_SECRET }} - run: pnpm --filter api test:e2e + run: pnpm --filter api test-e2e:cov + + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/api/package.json b/api/package.json index 4bb5e3c..b4a347b 100644 --- a/api/package.json +++ b/api/package.json @@ -14,9 +14,10 @@ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest -i --verbose", "test:watch": "jest --watch", - "test:cov": "jest --coverage", + "test:cov": "jest -i --verbose --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --verbose --config ./test/jest-e2e.json", + "test-e2e": "jest --verbose --config ./test/jest-e2e.json", + "test-e2e:cov": "jest --verbose --config ./test/jest-e2e.json --coverage --coverageDirectory=../coverage/api-e2e", "typeorm": "typeorm-ts-node-commonjs", "clean": "rimraf dist" }, @@ -98,10 +99,20 @@ "collectCoverageFrom": [ "**/*.(t|j)s" ], + "coveragePathIgnorePatterns": [ + "node_modules/", + "coverage*/", + "test/", + "dist/", + ".module.", + "google/", + "main.ts", + ".eslintrc.js" + ], "moduleNameMapper": { "^@mocks/(.*)$": "/src/mocks/$1" }, - "coverageDirectory": "../coverage", + "coverageDirectory": "../coverage/api", "testEnvironment": "node" } } \ No newline at end of file diff --git a/package.json b/package.json index 21fa9c6..4fbe562 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "clean": "pnpm --filter frontend clean && pnpm --filter api clean", "format": "pnpm --filter shared format & pnpm --filter frontend format & pnpm --filter api format", "format-check": "pnpm --filter shared format-check && pnpm --filter frontend format-check && pnpm --filter api format-check", - "test": "pnpm --filter api test && pnpm --filter shared test" + "test": "pnpm --filter api test && pnpm --filter shared test && pnpm --filter api test-e2e" }, "keywords": [], "author": "", diff --git a/shared/package.json b/shared/package.json index 9688ff6..6d82609 100644 --- a/shared/package.json +++ b/shared/package.json @@ -31,6 +31,7 @@ ], "transform": { "^.+\\.tsx?$": "@swc/jest" - } + }, + "coverageDirectory": "../coverage/shared" } } diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..895f2fc --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.projectKey=MuNuChapterHKN_HKrecruitment +sonar.organization=munuchapterhkn +sonar.projectName=HKrecruitment + +sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info +sonar.sources=api/, frontend/, shared/ +sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md From 3a42aa3d6c7c53aa7d7d93bec8706c68492636db Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 18 Mar 2024 22:12:51 +0100 Subject: [PATCH 022/104] fix: removed unused avaiability endpoints --- .../availability/availability.controller.ts | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index dd2a8f4..170d4b7 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -5,14 +5,12 @@ import { ForbiddenException, NotFoundException, Param, - Patch, Post, } from '@nestjs/common'; import { AvailabilityService } from './availability.service'; import { Action, insertAvailabilitySchema, - updateAvailabilitySchema, } from '@hkrecruitment/shared'; import { JoiValidate } from '../joi-validation/joi-validate.decorator'; import { @@ -21,7 +19,6 @@ import { ApiForbiddenResponse, ApiNotFoundResponse, ApiCreatedResponse, - ApiOkResponse, ApiTags, ApiNoContentResponse, ApiBadGatewayResponse, @@ -37,34 +34,6 @@ import { CreateAvailabilityDto } from './create-availability.dto'; export class AvailabilityController { constructor(private readonly availabilityService: AvailabilityService) {} - @ApiNotFoundResponse() - @ApiOkResponse() - @ApiForbiddenResponse() - @ApiBadGatewayResponse() - async listAvailabilities(): Promise { - const matches = await this.availabilityService.listAvailabilities(); - if (matches.length == 0) { - throw new NotFoundException(); - } - return matches; - } - - @ApiNotFoundResponse() - @ApiOkResponse() - @ApiBadGatewayResponse() - @ApiBadRequestResponse() - @ApiForbiddenResponse() - @JoiValidate({ - param: Joi.number().positive().integer().required(), - }) - async findAvailabilityById(id: number): Promise { - const match = await this.availabilityService.findAvailabilityById(id); - if (!match) { - throw new NotFoundException(); - } - return match; - } - @ApiCreatedResponse() @ApiBadRequestResponse() @ApiForbiddenResponse() @@ -85,30 +54,6 @@ export class AvailabilityController { } } - @ApiNotFoundResponse() - @ApiNoContentResponse() - @ApiBadRequestResponse() - @ApiForbiddenResponse() - @ApiBadGatewayResponse() - @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) - @Patch() - @JoiValidate({ - body: updateAvailabilitySchema, - }) - async updateAvailability(@Body() body: Availability): Promise { - const test = await this.findAvailabilityById(body.id); - if (test) { - const res = await this.availabilityService.updateAvailability(test, body); - if (res) { - return res; - } else { - throw new ForbiddenException(); - } - } else { - throw new NotFoundException(); - } - } - @ApiNotFoundResponse() @ApiNoContentResponse() @ApiBadRequestResponse() From ce2f05c06f1b86112f189914282c6af074d1b913 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 18 Mar 2024 22:17:31 +0100 Subject: [PATCH 023/104] feat: added existance checks and conflict check upon availability creation --- .../availability/availability.controller.ts | 76 ++++++++++++++----- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 170d4b7..f7255a5 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -1,15 +1,20 @@ import { Body, + ConflictException, Controller, Delete, ForbiddenException, NotFoundException, Param, Post, + Req, } from '@nestjs/common'; import { AvailabilityService } from './availability.service'; +import { TimeSlotsService } from '../timeslots/timeslots.service'; +import { UsersService } from '../users/users.service'; import { Action, + AvailabilityState, insertAvailabilitySchema, } from '@hkrecruitment/shared'; import { JoiValidate } from '../joi-validation/joi-validate.decorator'; @@ -22,36 +27,71 @@ import { ApiTags, ApiNoContentResponse, ApiBadGatewayResponse, + ApiConflictResponse, + ApiUnprocessableEntityResponse, } from '@nestjs/swagger'; import { CheckPolicies } from 'src/authorization/check-policies.decorator'; import { Availability } from './availability.entity'; import Joi from 'joi'; import { CreateAvailabilityDto } from './create-availability.dto'; +import { AuthenticatedRequest } from 'src/authorization/authenticated-request.types'; @ApiBearerAuth() @ApiTags('availability') @Controller('availability') export class AvailabilityController { - constructor(private readonly availabilityService: AvailabilityService) {} + constructor( + private readonly availabilityService: AvailabilityService, + private readonly timeSlotsService: TimeSlotsService, + private readonly userService: UsersService, + ) { } @ApiCreatedResponse() @ApiBadRequestResponse() + @ApiNotFoundResponse() @ApiForbiddenResponse() @ApiBadGatewayResponse() + @ApiConflictResponse() @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) @Post() @JoiValidate({ + param: Joi.number() + .positive() + .integer() + .required() + .label('availability_id'), body: insertAvailabilitySchema, }) async createAvailability( - @Body() body: CreateAvailabilityDto, + @Body() availabilityDto: CreateAvailabilityDto, + @Req() req: AuthenticatedRequest, ): Promise { - const res = await this.availabilityService.createAvailability(body); - if (res) { - return res; - } else { - throw new ForbiddenException(); - } + /* Verify timeslot exists */ + const timeSlot = await this.timeSlotsService.findById( + availabilityDto.timeSlot.id, + ); + if (!timeSlot) throw new NotFoundException(); + + /* Verify user exists */ + const user = await this.userService.findByOauthId(req.user.sub); + if (!user) throw new NotFoundException(); + + /* Verify availability for timeslot does not already exist */ + const existing = await this.availabilityService.findByUserAndTimeSlot( + user, + timeSlot, + ); + if (existing) throw new ConflictException(); + + const availability = { + ...availabilityDto, + state: AvailabilityState.Free, + } as Availability; + const result = await this.availabilityService.createAvailability( + availability, + ); + if (!result) throw new ForbiddenException(); + return result; } @ApiNotFoundResponse() @@ -64,17 +104,13 @@ export class AvailabilityController { @JoiValidate({ param: Joi.number().positive().integer().required(), }) - async deleteAvailability(@Param() id: number): Promise { - const test = await this.findAvailabilityById(id); - if (test) { - const res = await this.availabilityService.deleteAvailability(test); - if (res) { - return res; - } else { - throw new ForbiddenException(); - } - } else { - throw new NotFoundException(); - } + async deleteAvailability( + @Param('availability_id') availabilityId: number, + ): Promise { + const availability = await this.availabilityService.findById( + availabilityId, + ); + if (!availability) throw new NotFoundException(); + return await this.availabilityService.deleteAvailability(availabilityId); } } From 8728e5242d76d6969ddead73a87afe6e5e01d23a Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 18 Mar 2024 22:20:00 +0100 Subject: [PATCH 024/104] feat: return 404 when attempting to delete non-existing availabilities refactor: availability.controller.ts using prettier --- .../availability/availability.controller.ts | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index f7255a5..20466b2 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -40,11 +40,9 @@ import { AuthenticatedRequest } from 'src/authorization/authenticated-request.ty @ApiTags('availability') @Controller('availability') export class AvailabilityController { - constructor( - private readonly availabilityService: AvailabilityService, + constructor(private readonly availabilityService: AvailabilityService, private readonly timeSlotsService: TimeSlotsService, - private readonly userService: UsersService, - ) { } + private readonly userService: UsersService) { } @ApiCreatedResponse() @ApiBadRequestResponse() @@ -55,62 +53,53 @@ export class AvailabilityController { @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) @Post() @JoiValidate({ - param: Joi.number() - .positive() - .integer() - .required() - .label('availability_id'), + param: Joi.number().positive().integer().required().label('availability_id'), body: insertAvailabilitySchema, }) async createAvailability( @Body() availabilityDto: CreateAvailabilityDto, @Req() req: AuthenticatedRequest, ): Promise { + /* Verify timeslot exists */ - const timeSlot = await this.timeSlotsService.findById( - availabilityDto.timeSlot.id, - ); - if (!timeSlot) throw new NotFoundException(); + const timeSlot = await this.timeSlotsService.findById(availabilityDto.timeSlot.id); + if (!timeSlot) + throw new NotFoundException(); /* Verify user exists */ const user = await this.userService.findByOauthId(req.user.sub); - if (!user) throw new NotFoundException(); + if (!user) + throw new NotFoundException(); /* Verify availability for timeslot does not already exist */ - const existing = await this.availabilityService.findByUserAndTimeSlot( - user, - timeSlot, - ); - if (existing) throw new ConflictException(); + const existing = await this.availabilityService.findByUserAndTimeSlot(user, timeSlot); + if (existing) + throw new ConflictException(); const availability = { ...availabilityDto, state: AvailabilityState.Free, } as Availability; - const result = await this.availabilityService.createAvailability( - availability, - ); - if (!result) throw new ForbiddenException(); + const result = await this.availabilityService.createAvailability(availability); + if (!result) + throw new ForbiddenException(); return result; } @ApiNotFoundResponse() @ApiNoContentResponse() - @ApiBadRequestResponse() - @ApiForbiddenResponse() - @ApiBadGatewayResponse() - @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) - @Delete() + @ApiUnprocessableEntityResponse() + @CheckPolicies((ability) => ability.can(Action.Delete, 'Availability')) + @Delete(':availability_id') @JoiValidate({ - param: Joi.number().positive().integer().required(), + param: Joi.number().positive().integer().required().label('availability_id'), }) async deleteAvailability( @Param('availability_id') availabilityId: number, ): Promise { - const availability = await this.availabilityService.findById( - availabilityId, - ); - if (!availability) throw new NotFoundException(); + const availability = await this.availabilityService.findById(availabilityId); + if (!availability) + throw new NotFoundException(); return await this.availabilityService.deleteAvailability(availabilityId); } } From 5a5b0bb4ca57717286ffae5b29fa174de6cfbaf8 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 25 Mar 2024 19:48:52 +0100 Subject: [PATCH 025/104] fix: Availability authorizations and creation schema --- shared/src/availability.ts | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/shared/src/availability.ts b/shared/src/availability.ts index 144e56e..267dc17 100644 --- a/shared/src/availability.ts +++ b/shared/src/availability.ts @@ -1,6 +1,6 @@ -import { TimeSlot, createTimeSlotSchema } from "./timeslot"; +import { TimeSlot } from "./timeslot"; import { Action, ApplyAbilities } from "./abilities"; -import { Person, Role, createUserSchema } from "./person"; +import { Person, Role } from "./person"; import * as Joi from "joi"; export enum AvailabilityState { @@ -15,6 +15,7 @@ export enum AvailabilityType { } export interface Availability { + id: number; state: AvailabilityState; lastModified: Date; timeSlot: TimeSlot; @@ -23,22 +24,8 @@ export interface Availability { /* Validation schemas */ -export const insertAvailabilitySchema = Joi.object({ - state: Joi.string() - .valid(...Object.values(AvailabilityType)) - .required(), - //timeSlot: Joi.object(createTimeSlotSchema).required(), - //user: Joi.object(createUserSchema).required(), -}).options({ - stripUnknown: true, - abortEarly: false, - presence: "required", -}); - -export const updateAvailabilitySchema = Joi.object({ - state: Joi.string() - .valid(...Object.values(AvailabilityType)) - .required(), +export const insertAvailabilitySchema = Joi.object({ + timeSlotId: Joi.number().required(), }).options({ stripUnknown: true, abortEarly: false, @@ -58,13 +45,12 @@ export const applyAbilitiesOnAvailability: ApplyAbilities = ( break; case Role.Member: case Role.Clerk: + can(Action.Create, "Availability"); can(Action.Read, "Availability"); - can(Action.Update, "Availability", { userId: user.sub }); + can(Action.Delete, "Availability"); + cannot(Action.Update, "Availability"); break; case Role.Applicant: - can(Action.Read, "Availability", { userId: user.sub }); - can(Action.Update, "Availability", { userId: user.sub }); - break; default: cannot(Action.Manage, "Availability"); } From 49dd827be1a8891b510ac0757bef7e2e9a052274 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 25 Mar 2024 19:50:09 +0100 Subject: [PATCH 026/104] fix!: updated AvailabilityState enum values --- shared/src/availability.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/src/availability.ts b/shared/src/availability.ts index 267dc17..f21bb2b 100644 --- a/shared/src/availability.ts +++ b/shared/src/availability.ts @@ -4,9 +4,9 @@ import { Person, Role } from "./person"; import * as Joi from "joi"; export enum AvailabilityState { - Subscribed = "subscribed", - Confirmed = "confirmed", - Cancelled = "cancelled", + Free = "free", + Interviewing = "interviewing", + Recovering = "recovering", } export enum AvailabilityType { From 341fe7b464cda878c196db88d5f4e0c8d028b2ae Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Mon, 25 Mar 2024 19:52:35 +0100 Subject: [PATCH 027/104] test: role abilities on Availability test: validate insert Availability schema --- shared/src/availability.spec.ts | 79 ++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/shared/src/availability.spec.ts b/shared/src/availability.spec.ts index 212a245..ac7b256 100644 --- a/shared/src/availability.spec.ts +++ b/shared/src/availability.spec.ts @@ -1,40 +1,75 @@ import { AvailabilityState, - AvailabilityType, Availability, - updateAvailabilitySchema, + insertAvailabilitySchema, + applyAbilitiesOnAvailability, } from "./availability"; import { createMockAbility } from "./abilities.spec"; import { Action, UserAuth, checkAbility } from "./abilities"; import { Role } from "./person"; +const mockAbilityForAvailability = (user: UserAuth) => + createMockAbility((builder) => { + applyAbilitiesOnAvailability(user, builder); + }); + +const mockAvailability = { + id: 1, + state: AvailabilityState.Free, + lastModified: new Date(), + timeSlot: { + start: new Date(2024, 0, 1, 10, 0, 0), + end: new Date(2024, 0, 1, 11, 0, 0), + id: 1, + availabilities: [], + }, + user: { + firstName: 'John', + lastName: 'Doe', + oauthId: '123', + role: Role.Member, + }, +}; + describe("Availability", () => { - describe("updateAvailabilitySchema", () => { - it("should allow a valid update", () => { - const updateAvailability = { - state: AvailabilityState.Confirmed, - timeSlotId: 123, - }; - const { error } = updateAvailabilitySchema.validate(updateAvailability); - expect(error).toBeDefined(); + Object.values(Role).forEach((role: Role) => { + describe("should allow only HKN members to read, create, and delete availabilities", () => { + const mockAbility = mockAbilityForAvailability({ role, sub: "123" }); + it(`should allow only Admin and Supervisors to update availabilities [${role}]`, () => { + expect( + checkAbility(mockAbility, Action.Update, mockAvailability, "Availability") + ).toBe([Role.Admin, Role.Supervisor].includes(role)); + }); + it(`should allow only HKN members to read, create, and delete availabilities [${role}]`, () => { + expect( + checkAbility(mockAbility, Action.Read, mockAvailability, "Availability") + ).toBe(![Role.Applicant, Role.None].includes(role)); + expect( + checkAbility(mockAbility, Action.Create, mockAvailability, "Availability") + ).toBe(![Role.Applicant, Role.None].includes(role)); + expect( + checkAbility(mockAbility, Action.Delete, mockAvailability, "Availability") + ).toBe(![Role.Applicant, Role.None].includes(role)); + }); }); + }); - it("should not allow updating with an invalid state", () => { - const updateAvailability = { - state: "Non_Existent_State", - timeSlotId: 123, + describe("insertAvailabilitySchema", () => { + it('should allow creating a valid availability', () => { + const validAvailability = { + timeSlotId: 1, }; - const { error } = updateAvailabilitySchema.validate(updateAvailability); - expect(error).toBeDefined(); + + const { error } = insertAvailabilitySchema.validate(validAvailability); + expect(error).toBeUndefined(); }); - it("should not allow updating with an invalid timeSlotId", () => { - const updateAvailability = { - state: AvailabilityState.Confirmed, - timeSlotId: -321, - }; - const { error } = updateAvailabilitySchema.validate(updateAvailability); + it('should not allow creating an availability without a timeSlotId', () => { + const invalidAvailability = {}; + + const { error } = insertAvailabilitySchema.validate(invalidAvailability); expect(error).toBeDefined(); }); }); }); + From fd676d1cb2e34aae5a2f69a08ec7902bf6cbde4c Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 27 Mar 2024 21:41:49 +0100 Subject: [PATCH 028/104] feat: additional checks before deleting availability --- .../availability/availability.controller.ts | 24 +++++++--- .../availability/availability.service.spec.ts | 48 ++++++++++++------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 20466b2..22f6aa9 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -14,6 +14,7 @@ import { TimeSlotsService } from '../timeslots/timeslots.service'; import { UsersService } from '../users/users.service'; import { Action, + AppAbility, AvailabilityState, insertAvailabilitySchema, } from '@hkrecruitment/shared'; @@ -35,6 +36,7 @@ import { Availability } from './availability.entity'; import Joi from 'joi'; import { CreateAvailabilityDto } from './create-availability.dto'; import { AuthenticatedRequest } from 'src/authorization/authenticated-request.types'; +import { Ability } from 'src/authorization/ability.decorator'; @ApiBearerAuth() @ApiTags('availability') @@ -62,22 +64,22 @@ export class AvailabilityController { ): Promise { /* Verify timeslot exists */ - const timeSlot = await this.timeSlotsService.findById(availabilityDto.timeSlot.id); + const timeSlot = await this.timeSlotsService.findById(availabilityDto.timeSlotId); if (!timeSlot) - throw new NotFoundException(); + throw new NotFoundException("Timeslot not found"); /* Verify user exists */ const user = await this.userService.findByOauthId(req.user.sub); if (!user) - throw new NotFoundException(); + throw new NotFoundException("User not found"); /* Verify availability for timeslot does not already exist */ const existing = await this.availabilityService.findByUserAndTimeSlot(user, timeSlot); if (existing) - throw new ConflictException(); + throw new ConflictException("Availability already exists for this timeslot"); const availability = { - ...availabilityDto, + timeSlot: timeSlot, state: AvailabilityState.Free, } as Availability; const result = await this.availabilityService.createAvailability(availability); @@ -87,6 +89,7 @@ export class AvailabilityController { } @ApiNotFoundResponse() + @ApiForbiddenResponse() @ApiNoContentResponse() @ApiUnprocessableEntityResponse() @CheckPolicies((ability) => ability.can(Action.Delete, 'Availability')) @@ -95,11 +98,20 @@ export class AvailabilityController { param: Joi.number().positive().integer().required().label('availability_id'), }) async deleteAvailability( + @Ability() ability: AppAbility, @Param('availability_id') availabilityId: number, - ): Promise { + @Req() req: AuthenticatedRequest, + ): Promise { + + // Check if availability exists const availability = await this.availabilityService.findById(availabilityId); if (!availability) throw new NotFoundException(); + + // Check if user has permission to delete availability + if (ability.cannot(Action.Delete, 'Availability') || availability.user.oauthId !== req.user.sub) + throw new ForbiddenException(); + return await this.availabilityService.deleteAvailability(availabilityId); } } diff --git a/api/src/availability/availability.service.spec.ts b/api/src/availability/availability.service.spec.ts index 19cd7ef..c4c7c72 100644 --- a/api/src/availability/availability.service.spec.ts +++ b/api/src/availability/availability.service.spec.ts @@ -1,9 +1,11 @@ -import { mockAvailability, testDate } from '@mocks/data'; +import { mockAvailability, testDate } from 'src/mocks/data'; import { Test, TestingModule } from '@nestjs/testing'; import { AvailabilityService } from './availability.service'; import { Availability } from './availability.entity'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { mockedRepository } from '@mocks/repositories'; +import { getRepositoryToken, getDataSourceToken } from '@nestjs/typeorm'; +import { mockedRepository } from 'src/mocks/repositories'; +import { ConflictException } from '@nestjs/common'; +import { createMockDataSource } from 'src/mocks/data-sources'; describe('AvailabilityService', () => { let service: AvailabilityService; @@ -24,6 +26,12 @@ describe('AvailabilityService', () => { provide: getRepositoryToken(Availability), useValue: mockedRepository, }, + { + provide: getDataSourceToken(), + useValue: createMockDataSource({ + findOne: mockAvailability + }), + } ], }).compile(); @@ -38,8 +46,26 @@ describe('AvailabilityService', () => { expect(service).toBeDefined(); }); + describe('listAvailabilities', () => { + it('should return all availabilities', async () => { + jest.spyOn(mockedRepository, 'find').mockResolvedValue([mockAvailability]); + const result = await service.listAvailabilities(); + expect(result).toEqual([mockAvailability]); + expect(mockedRepository.find).toHaveBeenCalledTimes(1); + }); + }); + // CRUD OPERATIONS + describe('findById', () => { + it('should return the availability with the specified id', async () => { + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([mockAvailability]); + const result = await service.findById(mockAvailability.id); + expect(result).toEqual(mockAvailability); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + }); + }); + describe('createAvailability', () => { it('should create a new availability', async () => { jest.spyOn(mockedRepository, 'save').mockResolvedValue(mockAvailability); @@ -51,32 +77,22 @@ describe('AvailabilityService', () => { describe('deleteAvailability', () => { it('should remove the specified availability from the database', async () => { + jest .spyOn(mockedRepository, 'remove') .mockResolvedValue(mockAvailability); - const result = await service.deleteAvailability(mockAvailability); + const result = await service.deleteAvailability(mockAvailability.id); expect(result).toEqual(mockAvailability); expect(mockedRepository.remove).toHaveBeenCalledTimes(1); }); }); - describe('listAvailabilities', () => { - it('should return all availabilities', async () => { - jest - .spyOn(mockedRepository, 'find') - .mockResolvedValue([mockAvailability]); - const result = await service.listAvailabilities(); - expect(result).toEqual([mockAvailability]); - expect(mockedRepository.find).toHaveBeenCalledTimes(1); - }); - }); - describe('findAvailabilityById', () => { it('should return the availability with the specified id', async () => { jest .spyOn(mockedRepository, 'findBy') .mockResolvedValue([mockAvailability]); - const result = await service.findAvailabilityById(mockAvailability.id); + const result = await service.findById(mockAvailability.id); expect(result).toEqual(mockAvailability); expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); }); From fcdefde042ab40cbbfe303cae87d18114efaca62 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 27 Mar 2024 21:42:15 +0100 Subject: [PATCH 029/104] test: availability controller unit tests --- .../availability.controller.spec.ts | 150 +++++++++++++++--- 1 file changed, 124 insertions(+), 26 deletions(-) diff --git a/api/src/availability/availability.controller.spec.ts b/api/src/availability/availability.controller.spec.ts index e9f04b9..8f2032b 100644 --- a/api/src/availability/availability.controller.spec.ts +++ b/api/src/availability/availability.controller.spec.ts @@ -1,11 +1,20 @@ -import { mockAvailability, testDate } from '@mocks/data'; +import { mockAvailability, mockCreateAvailabilityDto, mockClerk, mockTimeSlot, testDate } from 'src/mocks/data'; +import { createMock } from '@golevelup/ts-jest'; import { AvailabilityController } from './availability.controller'; import { AvailabilityService } from './availability.service'; import { TestBed } from '@automock/jest'; +import { AuthenticatedRequest } from 'src/authorization/authenticated-request.types'; +import { UsersService } from 'src/users/users.service'; +import { TimeSlotsService } from 'src/timeslots/timeslots.service'; +import { Action } from '@hkrecruitment/shared'; +import { createMockAbility } from '@hkrecruitment/shared/abilities.spec'; describe('AvailabilityController', () => { let controller: AvailabilityController; - let service: AvailabilityService; + let availabilityService: AvailabilityService; + let userService: UsersService; + let timeslotService: TimeSlotsService; + /************* Test setup ************/ @@ -19,61 +28,150 @@ describe('AvailabilityController', () => { const { unit, unitRef } = TestBed.create(AvailabilityController).compile(); controller = unit; - service = unitRef.get(AvailabilityService); + availabilityService = unitRef.get(AvailabilityService); + userService = unitRef.get(UsersService); + timeslotService = unitRef.get(TimeSlotsService); }); it('should be defined', () => { expect(controller).toBeDefined(); - expect(service).toBeDefined(); + expect(availabilityService).toBeDefined(); + expect(userService).toBeDefined(); + expect(timeslotService).toBeDefined(); }); // CRUD OPERATIONS describe('createAvailability', () => { it('should allow creating a valid availability', async () => { - const availability = { - state: mockAvailability.state, - lastModified: mockAvailability.lastModified, - timeSlot: mockAvailability.timeSlot, - user: mockAvailability.user, - }; + const mockRequest = getMockRequest(); + + jest.spyOn(userService, 'findByOauthId').mockResolvedValue(mockClerk); + jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); jest - .spyOn(service, 'createAvailability') + .spyOn(availabilityService, 'createAvailability') .mockResolvedValue(mockAvailability); - const result = await controller.createAvailability(availability); + const result = await controller.createAvailability(mockCreateAvailabilityDto, mockRequest); expect(result).toEqual(mockAvailability); }); - }); - // Read an availability - describe('findAvailabilityById', () => { - it('should allow finding an availability by id', async () => { - jest - .spyOn(service, 'findAvailabilityById') - .mockResolvedValue(mockAvailability); + it('should throw an error if the availability is invalid', async () => { + const mockRequest = getMockRequest(); - const result = await controller.findAvailabilityById(mockAvailability.id); + jest.spyOn(userService, 'findByOauthId').mockResolvedValue(mockClerk); + jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); + jest.spyOn(availabilityService, 'createAvailability').mockResolvedValue(undefined); - expect(result).toEqual(mockAvailability); + await expect( + controller.createAvailability(mockCreateAvailabilityDto, mockRequest), + ).rejects.toThrowError(); + }); + + it('should throw an error if the timeslot does not exist', async () => { + const mockRequest = getMockRequest(); + + jest.spyOn(timeslotService, 'findById').mockResolvedValue(null); + + await expect( + controller.createAvailability(mockCreateAvailabilityDto, mockRequest), + ).rejects.toThrowError('Timeslot not found'); + }); + + it('should throw an error if the user does not exist', async () => { + + const mockRequest = getMockRequest(); + + jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); + jest.spyOn(userService, 'findByOauthId').mockResolvedValue(null); + + await expect( + controller.createAvailability(mockCreateAvailabilityDto, mockRequest), + ).rejects.toThrowError('User not found'); + }); + + it('should throw a conflict error if an availability already exists for the user in the same timeslot', async () => { + const mockRequest = getMockRequest(); + + jest.spyOn(userService, 'findByOauthId').mockResolvedValue(mockClerk); + jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); + jest.spyOn(availabilityService, 'findByUserAndTimeSlot').mockResolvedValue(mockAvailability); + + await expect( + controller.createAvailability(mockCreateAvailabilityDto, mockRequest), + ).rejects.toThrowError('Availability already exists for this timeslot'); }); }); // Delete an availability describe('deleteAvailability', () => { it('should allow deleting an availability', async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Delete, 'Availability'); + }); + const mockReq = createMock(); + mockReq.user.sub = mockAvailability.user.oauthId; + + jest.spyOn(availabilityService, 'findById').mockResolvedValue(mockAvailability); jest - .spyOn(service, 'findAvailabilityById') - .mockResolvedValue(mockAvailability); - jest - .spyOn(service, 'deleteAvailability') + .spyOn(availabilityService, 'deleteAvailability') .mockResolvedValue(mockAvailability); await expect( - controller.deleteAvailability(mockAvailability.id), + controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), ).resolves.toEqual(mockAvailability); }); + + it('should throw an error if the user does not have the ability to delete the availability', async () => { + const mockAbility = createMockAbility(({ cannot }) => { + cannot(Action.Delete, 'Availability'); + }); + const mockReq = createMock(); + mockReq.user.sub = '123'; + + jest.spyOn(availabilityService, 'findById').mockResolvedValue(mockAvailability); + await expect( + controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + ).rejects.toThrowError('Forbidden'); + }); + + it("should throw an error if the user tries to delete someone else's availability", async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Delete, 'Availability'); + }); + const mockReq = createMock(); + mockReq.user.sub = '345'; + + jest.spyOn(availabilityService, 'findById').mockResolvedValue(mockAvailability); + await expect( + controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + ).rejects.toThrowError('Forbidden'); + }); + + it('should throw an error if the availability does not exist', async () => { + const mockAbility = createMockAbility(({ can }) => { + can(Action.Create, 'Availability'); + }); + const mockReq = createMock(); + mockReq.user.sub = '123'; + + jest.spyOn(availabilityService, 'findById').mockResolvedValue(undefined); + + await expect( + controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + ).rejects.toThrowError('Not Found'); + }); + + // TODO: Delete an availability that is in use, where the user is optional + // TODO: Delete an availability that is in use, with an existing replacement + // TODO: Delete an availability that is in use, with no existing replacement }); }); + +function getMockRequest() { + const mockRequest = createMock(); + mockRequest.user.sub = '123'; + return mockRequest; +} From 5790d24f3c9528baff9cef90a4ba586da8ffd361 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 27 Mar 2024 21:52:31 +0100 Subject: [PATCH 030/104] feat: added findByUserAndTimeSlot in Availability service fix: used Relation as type of fields in Availability entity --- api/src/availability/availability.entity.ts | 14 ++++- api/src/availability/availability.service.ts | 61 ++++++++++++++++---- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index d2e9a8c..ec82ad0 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -4,6 +4,7 @@ import { JoinColumn, ManyToOne, PrimaryGeneratedColumn, + Relation, } from 'typeorm'; import { Availability as AvailabilityInterface, @@ -16,14 +17,21 @@ import { TimeSlot } from 'src/timeslots/timeslot.entity'; export class Availability implements AvailabilityInterface { @PrimaryGeneratedColumn('increment') id: number; + @Column() state: AvailabilityState; + @Column({ name: 'last_modified' }) lastModified: Date; + @Column({ name: 'time_slot' }) - @ManyToOne(() => TimeSlot) - timeSlot: TimeSlot; + @ManyToOne(() => TimeSlot, (timeSlot) => timeSlot.availabilities) + timeSlot: Relation; + @ManyToOne(() => User) @JoinColumn() - user: User; + user: Relation; + + // @OneToOne(() => Interview) + // interview: Interview; } diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index aa85a4a..814acd1 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -1,42 +1,79 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { ConflictException, Injectable } from '@nestjs/common'; +import { InjectRepository, InjectDataSource } from '@nestjs/typeorm'; +import { Repository, DataSource } from 'typeorm'; import { Availability } from './availability.entity'; -import { CreateAvailabilityDto } from './create-availability.dto'; +import { TimeSlot } from 'src/timeslots/timeslot.entity'; +import { User } from 'src/users/user.entity'; +import { AvailabilityState } from '@hkrecruitment/shared'; +import { transaction } from 'src/utils/database'; @Injectable() export class AvailabilityService { constructor( @InjectRepository(Availability) private readonly availabilityRepository: Repository, - ) {} + @InjectDataSource() + private dataSource: DataSource, + ) { } async listAvailabilities(): Promise { return await this.availabilityRepository.find(); } - async findAvailabilityById(id: number): Promise { + async findById(id: number): Promise { const matches = await this.availabilityRepository.findBy({ id: id, }); return matches.length > 0 ? matches[0] : null; } + async findByUserAndTimeSlot(user: User, timeSlot: TimeSlot) { + const matches = await this.availabilityRepository.findBy({ + user: user, + timeSlot: timeSlot, + }); + return matches.length > 0 ? matches[0] : null; + } + async createAvailability( - availability: CreateAvailabilityDto, + availability: Availability, ): Promise { return await this.availabilityRepository.save(availability); } async updateAvailability( - oldAvailability: Availability, + oldAvailabilityId: number, newAvailability: Availability, ): Promise { - await this.availabilityRepository.remove(oldAvailability); - return await this.availabilityRepository.save(newAvailability); + return await this.availabilityRepository.save({ + ...newAvailability, + id: oldAvailabilityId, + }); } - async deleteAvailability(availability: Availability): Promise { - return await this.availabilityRepository.remove(availability); + async deleteAvailability(availabilityId: number): Promise { + return await transaction(this.dataSource, async (queryRunner) => { + + const availability = await queryRunner.manager + .getRepository(Availability) + .findOneBy({ id: availabilityId }); + + // Check if availability is in use + if (availability.state === AvailabilityState.Interviewing) { + const rescheduled = false; + // TODO: If user was optional, just delete it, otherwise: + // Try to assign a different person to the interview + // TODO: Retrieve availability status + id of interviewer of timeslots in range [timeslot-1, timeslot+1] + // TODO: Search for someone with state = AvailabilityState.Available in timesolt, and state != AvailabilityState.interviewing in t-1 and t+1 + // TODO: If it doesn't exist: rescheduled = False + // If no other availability is found, availability cannot be deleted + if (!rescheduled) + throw new ConflictException( + 'Availability is in use and cannot be deleted.', + ); + } + + return await this.availabilityRepository.remove(availability); + }); } } From 25553d85025fbb8f313ac94ed34a7554c84db18a Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 27 Mar 2024 21:53:16 +0100 Subject: [PATCH 031/104] fix!: removed unnecessary fields in CreateAvailabilityDto --- api/src/availability/create-availability.dto.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/api/src/availability/create-availability.dto.ts b/api/src/availability/create-availability.dto.ts index c9cd76f..cf90fab 100644 --- a/api/src/availability/create-availability.dto.ts +++ b/api/src/availability/create-availability.dto.ts @@ -1,18 +1,6 @@ -import { - Availability, - AvailabilityState, - TimeSlot, -} from '@hkrecruitment/shared'; import { ApiProperty } from '@nestjs/swagger'; -import { User } from 'src/users/user.entity'; -export class CreateAvailabilityDto implements Availability { +export class CreateAvailabilityDto { @ApiProperty() - state: AvailabilityState; - @ApiProperty() - lastModified: Date; - @ApiProperty() - timeSlot: TimeSlot; - @ApiProperty() - user: User; + timeSlotId: number; } From 8dbf64679a80bf60241bd297d49f9aa30e41f133 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Tue, 2 Apr 2024 19:23:55 +0200 Subject: [PATCH 032/104] test: Availability service unit tests --- .../applications.controller.spec.ts | 2 +- .../application/applications.service.spec.ts | 6 +- .../availability.controller.spec.ts | 60 ++++++++--- .../availability/availability.controller.ts | 60 +++++++---- .../availability/availability.service.spec.ts | 102 +++++++++++++++--- api/src/availability/availability.service.ts | 54 +++++----- api/src/mocks/data-sources.ts | 58 ++++++++++ api/src/mocks/data.ts | 32 ++++-- api/src/mocks/services.ts | 16 ++- api/src/utils/database.ts | 27 +++++ 10 files changed, 328 insertions(+), 89 deletions(-) create mode 100644 api/src/mocks/data-sources.ts create mode 100644 api/src/utils/database.ts diff --git a/api/src/application/applications.controller.spec.ts b/api/src/application/applications.controller.spec.ts index 1c04510..e445537 100644 --- a/api/src/application/applications.controller.spec.ts +++ b/api/src/application/applications.controller.spec.ts @@ -16,7 +16,7 @@ import { mockMscApplication, updateApplicationDTO, testDate, -} from '@mocks/data'; +} from 'src/mocks/data'; import { BadRequestException, ConflictException, diff --git a/api/src/application/applications.service.spec.ts b/api/src/application/applications.service.spec.ts index 196530d..976cfeb 100644 --- a/api/src/application/applications.service.spec.ts +++ b/api/src/application/applications.service.spec.ts @@ -4,8 +4,8 @@ import { getRepositoryToken } from '@nestjs/typeorm'; import { Application } from './application.entity'; import { ApplicationState, ApplicationType } from '@hkrecruitment/shared'; import { UsersService } from '../users/users.service'; -import { mockedRepository } from '@mocks/repositories'; -import { mockedUsersService } from '@mocks/services'; +import { mockedRepository } from 'src/mocks/repositories'; +import { mockedUsersService } from 'src/mocks/services'; import { applicant, applicationFiles, @@ -20,7 +20,7 @@ import { folderId, today, testDate, -} from '@mocks/data'; +} from 'src/mocks/data'; import { flattenApplication } from './create-application.dto'; import { InternalServerErrorException } from '@nestjs/common'; diff --git a/api/src/availability/availability.controller.spec.ts b/api/src/availability/availability.controller.spec.ts index 8f2032b..f87083d 100644 --- a/api/src/availability/availability.controller.spec.ts +++ b/api/src/availability/availability.controller.spec.ts @@ -1,4 +1,10 @@ -import { mockAvailability, mockCreateAvailabilityDto, mockClerk, mockTimeSlot, testDate } from 'src/mocks/data'; +import { + mockAvailability, + mockCreateAvailabilityDto, + mockClerk, + mockTimeSlot, + testDate, +} from 'src/mocks/data'; import { createMock } from '@golevelup/ts-jest'; import { AvailabilityController } from './availability.controller'; import { AvailabilityService } from './availability.service'; @@ -15,7 +21,6 @@ describe('AvailabilityController', () => { let userService: UsersService; let timeslotService: TimeSlotsService; - /************* Test setup ************/ beforeAll(() => { @@ -44,7 +49,6 @@ describe('AvailabilityController', () => { describe('createAvailability', () => { it('should allow creating a valid availability', async () => { - const mockRequest = getMockRequest(); jest.spyOn(userService, 'findByOauthId').mockResolvedValue(mockClerk); @@ -53,7 +57,10 @@ describe('AvailabilityController', () => { .spyOn(availabilityService, 'createAvailability') .mockResolvedValue(mockAvailability); - const result = await controller.createAvailability(mockCreateAvailabilityDto, mockRequest); + const result = await controller.createAvailability( + mockCreateAvailabilityDto, + mockRequest, + ); expect(result).toEqual(mockAvailability); }); @@ -63,7 +70,9 @@ describe('AvailabilityController', () => { jest.spyOn(userService, 'findByOauthId').mockResolvedValue(mockClerk); jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); - jest.spyOn(availabilityService, 'createAvailability').mockResolvedValue(undefined); + jest + .spyOn(availabilityService, 'createAvailability') + .mockResolvedValue(undefined); await expect( controller.createAvailability(mockCreateAvailabilityDto, mockRequest), @@ -81,7 +90,6 @@ describe('AvailabilityController', () => { }); it('should throw an error if the user does not exist', async () => { - const mockRequest = getMockRequest(); jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); @@ -97,7 +105,9 @@ describe('AvailabilityController', () => { jest.spyOn(userService, 'findByOauthId').mockResolvedValue(mockClerk); jest.spyOn(timeslotService, 'findById').mockResolvedValue(mockTimeSlot); - jest.spyOn(availabilityService, 'findByUserAndTimeSlot').mockResolvedValue(mockAvailability); + jest + .spyOn(availabilityService, 'findByUserAndTimeSlot') + .mockResolvedValue(mockAvailability); await expect( controller.createAvailability(mockCreateAvailabilityDto, mockRequest), @@ -114,13 +124,19 @@ describe('AvailabilityController', () => { const mockReq = createMock(); mockReq.user.sub = mockAvailability.user.oauthId; - jest.spyOn(availabilityService, 'findById').mockResolvedValue(mockAvailability); + jest + .spyOn(availabilityService, 'findById') + .mockResolvedValue(mockAvailability); jest .spyOn(availabilityService, 'deleteAvailability') .mockResolvedValue(mockAvailability); await expect( - controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + controller.deleteAvailability( + mockAbility, + mockAvailability.id, + mockReq, + ), ).resolves.toEqual(mockAvailability); }); @@ -131,9 +147,15 @@ describe('AvailabilityController', () => { const mockReq = createMock(); mockReq.user.sub = '123'; - jest.spyOn(availabilityService, 'findById').mockResolvedValue(mockAvailability); + jest + .spyOn(availabilityService, 'findById') + .mockResolvedValue(mockAvailability); await expect( - controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + controller.deleteAvailability( + mockAbility, + mockAvailability.id, + mockReq, + ), ).rejects.toThrowError('Forbidden'); }); @@ -144,9 +166,15 @@ describe('AvailabilityController', () => { const mockReq = createMock(); mockReq.user.sub = '345'; - jest.spyOn(availabilityService, 'findById').mockResolvedValue(mockAvailability); + jest + .spyOn(availabilityService, 'findById') + .mockResolvedValue(mockAvailability); await expect( - controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + controller.deleteAvailability( + mockAbility, + mockAvailability.id, + mockReq, + ), ).rejects.toThrowError('Forbidden'); }); @@ -160,7 +188,11 @@ describe('AvailabilityController', () => { jest.spyOn(availabilityService, 'findById').mockResolvedValue(undefined); await expect( - controller.deleteAvailability(mockAbility, mockAvailability.id, mockReq), + controller.deleteAvailability( + mockAbility, + mockAvailability.id, + mockReq, + ), ).rejects.toThrowError('Not Found'); }); diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 22f6aa9..6825882 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -42,9 +42,11 @@ import { Ability } from 'src/authorization/ability.decorator'; @ApiTags('availability') @Controller('availability') export class AvailabilityController { - constructor(private readonly availabilityService: AvailabilityService, + constructor( + private readonly availabilityService: AvailabilityService, private readonly timeSlotsService: TimeSlotsService, - private readonly userService: UsersService) { } + private readonly userService: UsersService, + ) {} @ApiCreatedResponse() @ApiBadRequestResponse() @@ -55,36 +57,45 @@ export class AvailabilityController { @CheckPolicies((ability) => ability.can(Action.Create, 'Availability')) @Post() @JoiValidate({ - param: Joi.number().positive().integer().required().label('availability_id'), + param: Joi.number() + .positive() + .integer() + .required() + .label('availability_id'), body: insertAvailabilitySchema, }) async createAvailability( @Body() availabilityDto: CreateAvailabilityDto, @Req() req: AuthenticatedRequest, ): Promise { - /* Verify timeslot exists */ - const timeSlot = await this.timeSlotsService.findById(availabilityDto.timeSlotId); - if (!timeSlot) - throw new NotFoundException("Timeslot not found"); + const timeSlot = await this.timeSlotsService.findById( + availabilityDto.timeSlotId, + ); + if (!timeSlot) throw new NotFoundException('Timeslot not found'); /* Verify user exists */ const user = await this.userService.findByOauthId(req.user.sub); - if (!user) - throw new NotFoundException("User not found"); + if (!user) throw new NotFoundException('User not found'); /* Verify availability for timeslot does not already exist */ - const existing = await this.availabilityService.findByUserAndTimeSlot(user, timeSlot); + const existing = await this.availabilityService.findByUserAndTimeSlot( + user, + timeSlot, + ); if (existing) - throw new ConflictException("Availability already exists for this timeslot"); + throw new ConflictException( + 'Availability already exists for this timeslot', + ); const availability = { timeSlot: timeSlot, state: AvailabilityState.Free, } as Availability; - const result = await this.availabilityService.createAvailability(availability); - if (!result) - throw new ForbiddenException(); + const result = await this.availabilityService.createAvailability( + availability, + ); + if (!result) throw new ForbiddenException(); return result; } @@ -95,21 +106,28 @@ export class AvailabilityController { @CheckPolicies((ability) => ability.can(Action.Delete, 'Availability')) @Delete(':availability_id') @JoiValidate({ - param: Joi.number().positive().integer().required().label('availability_id'), + param: Joi.number() + .positive() + .integer() + .required() + .label('availability_id'), }) async deleteAvailability( @Ability() ability: AppAbility, @Param('availability_id') availabilityId: number, @Req() req: AuthenticatedRequest, - ): Promise { - + ): Promise { // Check if availability exists - const availability = await this.availabilityService.findById(availabilityId); - if (!availability) - throw new NotFoundException(); + const availability = await this.availabilityService.findById( + availabilityId, + ); + if (!availability) throw new NotFoundException(); // Check if user has permission to delete availability - if (ability.cannot(Action.Delete, 'Availability') || availability.user.oauthId !== req.user.sub) + if ( + ability.cannot(Action.Delete, 'Availability') || + availability.user.oauthId !== req.user.sub + ) throw new ForbiddenException(); return await this.availabilityService.deleteAvailability(availabilityId); diff --git a/api/src/availability/availability.service.spec.ts b/api/src/availability/availability.service.spec.ts index c4c7c72..9f62ba2 100644 --- a/api/src/availability/availability.service.spec.ts +++ b/api/src/availability/availability.service.spec.ts @@ -5,7 +5,8 @@ import { Availability } from './availability.entity'; import { getRepositoryToken, getDataSourceToken } from '@nestjs/typeorm'; import { mockedRepository } from 'src/mocks/repositories'; import { ConflictException } from '@nestjs/common'; -import { createMockDataSource } from 'src/mocks/data-sources'; +import { mockDataSource } from 'src/mocks/data-sources'; +import { AvailabilityState } from '@hkrecruitment/shared'; describe('AvailabilityService', () => { let service: AvailabilityService; @@ -28,10 +29,8 @@ describe('AvailabilityService', () => { }, { provide: getDataSourceToken(), - useValue: createMockDataSource({ - findOne: mockAvailability - }), - } + useValue: mockDataSource, + }, ], }).compile(); @@ -48,7 +47,9 @@ describe('AvailabilityService', () => { describe('listAvailabilities', () => { it('should return all availabilities', async () => { - jest.spyOn(mockedRepository, 'find').mockResolvedValue([mockAvailability]); + jest + .spyOn(mockedRepository, 'find') + .mockResolvedValue([mockAvailability]); const result = await service.listAvailabilities(); expect(result).toEqual([mockAvailability]); expect(mockedRepository.find).toHaveBeenCalledTimes(1); @@ -59,7 +60,9 @@ describe('AvailabilityService', () => { describe('findById', () => { it('should return the availability with the specified id', async () => { - jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([mockAvailability]); + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockAvailability]); const result = await service.findById(mockAvailability.id); expect(result).toEqual(mockAvailability); expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); @@ -77,13 +80,56 @@ describe('AvailabilityService', () => { describe('deleteAvailability', () => { it('should remove the specified availability from the database', async () => { - - jest - .spyOn(mockedRepository, 'remove') - .mockResolvedValue(mockAvailability); + const mockAvailabilityRepository = { + findOneBy: mockAvailability, + remove: mockAvailability, + }; + const mockedRepositories = mockDataSource.setMockResults({ + Availability: mockAvailabilityRepository, + }); const result = await service.deleteAvailability(mockAvailability.id); expect(result).toEqual(mockAvailability); - expect(mockedRepository.remove).toHaveBeenCalledTimes(1); + expect(mockDataSource.createQueryRunner).toHaveBeenCalledTimes(1); + expect( + mockedRepositories['Availability'].findOneBy, + ).toHaveBeenCalledTimes(1); + expect(mockedRepositories['Availability'].findOneBy).toHaveBeenCalledWith( + { id: mockAvailability.id }, + ); + expect(mockedRepositories['Availability'].remove).toHaveBeenCalledTimes( + 1, + ); + expect(mockedRepositories['Availability'].remove).toHaveBeenCalledWith( + mockAvailability, + ); + }); + + it('should throw a conflict error if the availability is in use', async () => { + const mockAvailabilityInUse = { + ...mockAvailability, + state: AvailabilityState.Interviewing, + }; + const mockAvailabilityRepository = { + findOneBy: mockAvailabilityInUse, + remove: mockAvailabilityInUse, + }; + const mockedRepositories = mockDataSource.setMockResults({ + Availability: mockAvailabilityRepository, + }); + jest + .spyOn(mockedRepositories['Availability'], 'findOneBy') + .mockResolvedValue(mockAvailabilityInUse); + jest + .spyOn(mockedRepositories['Availability'], 'remove') + .mockResolvedValue(mockAvailabilityInUse); + const result = service.deleteAvailability(mockAvailabilityInUse.id); + await expect(result).rejects.toThrow(ConflictException); + expect(mockedRepositories['Availability'].findOneBy).toHaveBeenCalledWith( + { id: mockAvailability.id }, + ); + expect( + mockedRepositories['Availability'].findOneBy, + ).toHaveBeenCalledTimes(1); }); }); @@ -97,4 +143,36 @@ describe('AvailabilityService', () => { expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); }); }); + + describe('findByUserAndTimeSlot', () => { + it('should return the availability with the specified user and time slot', async () => { + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockAvailability]); + const result = await service.findByUserAndTimeSlot( + mockAvailability.user, + mockAvailability.timeSlot, + ); + expect(result).toEqual(mockAvailability); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ + user: mockAvailability.user, + timeSlot: mockAvailability.timeSlot, + }); + }); + + it('should return null if no availability is found', async () => { + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([]); + const result = await service.findByUserAndTimeSlot( + mockAvailability.user, + mockAvailability.timeSlot, + ); + expect(result).toBeNull(); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ + user: mockAvailability.user, + timeSlot: mockAvailability.timeSlot, + }); + }); + }); }); diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index 814acd1..6f9904d 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -1,6 +1,6 @@ import { ConflictException, Injectable } from '@nestjs/common'; import { InjectRepository, InjectDataSource } from '@nestjs/typeorm'; -import { Repository, DataSource } from 'typeorm'; +import { Repository, DataSource, QueryRunner } from 'typeorm'; import { Availability } from './availability.entity'; import { TimeSlot } from 'src/timeslots/timeslot.entity'; import { User } from 'src/users/user.entity'; @@ -14,7 +14,7 @@ export class AvailabilityService { private readonly availabilityRepository: Repository, @InjectDataSource() private dataSource: DataSource, - ) { } + ) {} async listAvailabilities(): Promise { return await this.availabilityRepository.find(); @@ -35,9 +35,7 @@ export class AvailabilityService { return matches.length > 0 ? matches[0] : null; } - async createAvailability( - availability: Availability, - ): Promise { + async createAvailability(availability: Availability): Promise { return await this.availabilityRepository.save(availability); } @@ -52,28 +50,32 @@ export class AvailabilityService { } async deleteAvailability(availabilityId: number): Promise { - return await transaction(this.dataSource, async (queryRunner) => { - - const availability = await queryRunner.manager - .getRepository(Availability) - .findOneBy({ id: availabilityId }); + return await transaction( + this.dataSource, + async (queryRunner: QueryRunner) => { + const availability = await queryRunner.manager + .getRepository(Availability) + .findOneBy({ id: availabilityId }); - // Check if availability is in use - if (availability.state === AvailabilityState.Interviewing) { - const rescheduled = false; - // TODO: If user was optional, just delete it, otherwise: - // Try to assign a different person to the interview - // TODO: Retrieve availability status + id of interviewer of timeslots in range [timeslot-1, timeslot+1] - // TODO: Search for someone with state = AvailabilityState.Available in timesolt, and state != AvailabilityState.interviewing in t-1 and t+1 - // TODO: If it doesn't exist: rescheduled = False - // If no other availability is found, availability cannot be deleted - if (!rescheduled) - throw new ConflictException( - 'Availability is in use and cannot be deleted.', - ); - } + // Check if availability is in use + if (availability.state === AvailabilityState.Interviewing) { + const rescheduled = false; + // TODO: If user was optional, just delete it, otherwise: + // Try to assign a different person to the interview + // TODO: Retrieve availability status + id of interviewer of timeslots in range [timeslot-1, timeslot+1] + // TODO: Search for someone with state = AvailabilityState.Available in timesolt, and state != AvailabilityState.interviewing in t-1 and t+1 + // TODO: If it doesn't exist: rescheduled = False + // If no other availability is found, availability cannot be deleted + if (!rescheduled) + throw new ConflictException( + 'Availability is in use and cannot be deleted.', + ); + } - return await this.availabilityRepository.remove(availability); - }); + return await queryRunner.manager + .getRepository(Availability) + .remove(availability); + }, + ); } } diff --git a/api/src/mocks/data-sources.ts b/api/src/mocks/data-sources.ts new file mode 100644 index 0000000..3cf81ca --- /dev/null +++ b/api/src/mocks/data-sources.ts @@ -0,0 +1,58 @@ +interface QueryResults { + findOneBy?: any; + remove?: any; +} + +class MockedDataSource { + results = {}; + + /** + * Sets the mock results for the data source. + * + * @param results - An object containing the query results. + * @returns The mocked results object. + */ + setMockResults(results: { [key: string]: QueryResults }) { + const mockedResults = {}; + for (const key in results) + mockedResults[key] = this.mockResults(results[key]); + this.results = mockedResults; + return mockedResults; + } + + /** + * Creates a mock object with jest.Mock functions for each property of the input object. + * The mock functions will return the corresponding values from the input object. + * + * @param results - The object containing the values to be mocked. + * @returns An object with jest.Mock functions for each property of the input object. + */ + mockResults(results: T): { [K in keyof T]: jest.Mock } { + const mockedResults = {} as { [K in keyof T]: jest.Mock }; + for (const key in results) mockedResults[key] = jest.fn(() => results[key]); + return mockedResults; + } + + /** + * Creates a mock query runner. + * @returns A mock query runner object. + */ + createQueryRunner = jest.fn(() => { + return { + connect: jest.fn(), + startTransaction: jest.fn(), + manager: { + getRepository: jest.fn((entity: any) => { + if (!this.results.hasOwnProperty(entity.name)) + throw new Error(`No results found for entity ${entity.name}`); + return this.results[entity.name]; + }), + }, + rollbackTransaction: jest.fn(), + commitTransaction: jest.fn(), + release: jest.fn(), + }; + }); +} + +export const mockDataSource = new MockedDataSource(); diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index 3c60b65..61266d7 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -1,11 +1,4 @@ import { CreateApplicationDto } from 'src/application/create-application.dto'; -import { - ApplicationType, - ApplicationState, - LangLevel, - Role, - AvailabilityState, -} from '@hkrecruitment/shared'; import { BscApplication, MscApplication, @@ -15,6 +8,13 @@ import { UpdateApplicationDto } from 'src/application/update-application.dto'; import { RecruitmentSessionState } from '@hkrecruitment/shared/recruitment-session'; import { CreateRecruitmentSessionDto } from 'src/recruitment-session/create-recruitment-session.dto'; import { UpdateRecruitmentSessionDto } from 'src/recruitment-session/update-recruitment-session.dto'; +import { CreateAvailabilityDto } from 'src/availability/create-availability.dto'; +import { ApplicationType } from '@hkrecruitment/shared'; +import { ApplicationState } from '@hkrecruitment/shared'; +import { LangLevel } from '@hkrecruitment/shared'; +import { Role } from '@hkrecruitment/shared'; +import { AvailabilityState } from '@hkrecruitment/shared'; +import { TimeSlot } from '@hkrecruitment/shared'; export const testDate = new Date(2023, 0, 1, 10, 0, 0); export const testDateTimeStart = new Date(2023, 0, 1, 10, 30, 0); @@ -26,7 +26,8 @@ export const mockTimeSlot = { start: testDateTimeStart, end: testDateTimeEnd, id: 1, -}; + availabilities: [], +} as TimeSlot & { availabilities: any[] }; export const testInterviewStart = '11:55' as unknown as Date; export const testInterviewEnd = '20:35' as unknown as Date; @@ -170,6 +171,15 @@ export const applicant = { role: Role.Applicant, }; +export const mockClerk = { + firstName: 'Violet', + lastName: 'Red', + oauthId: '321', + sex: 'female', + email: 'email2@example.com', + role: Role.Clerk, +}; + export const applicationFiles = { cv: [ { @@ -196,8 +206,12 @@ export const today = '1/1/2023, 10:00:00'; export const mockAvailability = { id: 1, - state: AvailabilityState.Confirmed, + state: AvailabilityState.Free, lastModified: new Date(), timeSlot: mockTimeSlot, user: applicant, }; + +export const mockCreateAvailabilityDto = { + timeSlotId: mockTimeSlot.id, +} as CreateAvailabilityDto; diff --git a/api/src/mocks/services.ts b/api/src/mocks/services.ts index 6d7d17c..ae0611f 100644 --- a/api/src/mocks/services.ts +++ b/api/src/mocks/services.ts @@ -1,3 +1,13 @@ -export const mockedUsersService = { - findByOauthId: jest.fn(), -}; +import { TimeSlotsService } from 'src/timeslots/timeslots.service'; +import { UsersService } from 'src/users/users.service'; + +function classToMock(classToMock: any): Object { + const mockedService = {}; + Object.getOwnPropertyNames(classToMock.prototype).forEach((methodName) => { + mockedService[methodName] = jest.fn(); + }); + return mockedService; +} + +export const mockedUsersService = classToMock(UsersService); +export const mockedTimeSlotsService = classToMock(TimeSlotsService); diff --git a/api/src/utils/database.ts b/api/src/utils/database.ts new file mode 100644 index 0000000..6c0a1b1 --- /dev/null +++ b/api/src/utils/database.ts @@ -0,0 +1,27 @@ +import { DataSource, QueryRunner } from 'typeorm'; + +export async function transaction( + dataSource: DataSource, + actions: (queryRunner: QueryRunner) => Promise, +) { + // BEGIN TRANSACTION + const queryRunner = dataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + let result: any; + + try { + result = await actions(queryRunner); + // COMMIT TRANSACTION + await queryRunner.commitTransaction(); + } catch (err) { + // ROLLBACK TRANSACTION + await queryRunner.rollbackTransaction(); + throw err; + } finally { + await queryRunner.release(); + } + + return result; +} From 07a17fd768198af7878403f59295516f595ad3e8 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 3 Apr 2024 19:52:35 +0200 Subject: [PATCH 033/104] fix!: removed create/delete timeslot endpoint --- api/src/timeslots/create-timeslot.dto.ts | 2 +- api/src/timeslots/timeslot.entity.ts | 11 ++- .../timeslots/timeslots.controller.spec.ts | 78 ----------------- api/src/timeslots/timeslots.controller.ts | 85 +------------------ 4 files changed, 13 insertions(+), 163 deletions(-) diff --git a/api/src/timeslots/create-timeslot.dto.ts b/api/src/timeslots/create-timeslot.dto.ts index 2b9aaea..2e7836d 100644 --- a/api/src/timeslots/create-timeslot.dto.ts +++ b/api/src/timeslots/create-timeslot.dto.ts @@ -1,7 +1,7 @@ import { TimeSlot } from '@hkrecruitment/shared'; import { ApiProperty } from '@nestjs/swagger'; -export class CreateTimeSlotDto implements TimeSlot { +export class CreateTimeSlotDto implements Partial { @ApiProperty() start: Date; diff --git a/api/src/timeslots/timeslot.entity.ts b/api/src/timeslots/timeslot.entity.ts index f5f784e..54911ca 100644 --- a/api/src/timeslots/timeslot.entity.ts +++ b/api/src/timeslots/timeslot.entity.ts @@ -1,4 +1,10 @@ -import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; +import { + Column, + Entity, + OneToMany, + PrimaryGeneratedColumn, + Relation, +} from 'typeorm'; import { TimeSlot as TimeSlotInterface } from '@hkrecruitment/shared'; import { Availability } from 'src/availability/availability.entity'; @@ -12,4 +18,7 @@ export class TimeSlot implements TimeSlotInterface { @Column() end: Date; + + @OneToMany(() => Availability, (availability) => availability.timeSlot) + availabilities: Relation; } diff --git a/api/src/timeslots/timeslots.controller.spec.ts b/api/src/timeslots/timeslots.controller.spec.ts index 55ec4d6..0c1c16d 100644 --- a/api/src/timeslots/timeslots.controller.spec.ts +++ b/api/src/timeslots/timeslots.controller.spec.ts @@ -1,9 +1,6 @@ import { TestBed } from '@automock/jest'; -import { mockTimeSlot, testDateTimeEnd, testDateTimeStart } from '@mocks/data'; import { TimeSlotsController } from './timeslots.controller'; import { TimeSlotsService } from './timeslots.service'; -import { testDateTime10Minutes } from '@mocks/data'; -import { testDateTime3Hours } from '@mocks/data'; describe('TimeSlotController', () => { let controller: TimeSlotsController; @@ -21,79 +18,4 @@ describe('TimeSlotController', () => { expect(controller).toBeDefined(); expect(service).toBeDefined(); }); - - // Create a time slot - describe('createTimeSlot', () => { - it('should allow creating a valid time slot', async () => { - const timeSlot = { - start: mockTimeSlot.start, - end: mockTimeSlot.end, - }; - - jest.spyOn(service, 'countOverlappingTimeSlots').mockResolvedValue(0); - jest.spyOn(service, 'createTimeSlot').mockResolvedValue(mockTimeSlot); - - const result = await controller.createTimeSlot(timeSlot); - - expect(result).toEqual(mockTimeSlot); - }); - - it('should throw an error if the duration is less than 30 minutes', async () => { - const timeSlot = { - start: testDateTimeStart, - end: testDateTime10Minutes, - }; - - await expect(controller.createTimeSlot(timeSlot)).rejects.toThrow( - 'The duration of the time slot must be at least 30 minutes', - ); - }); - - it('should throw an error if the duration is more than 60 minutes', async () => { - const timeSlot = { - start: testDateTimeStart, - end: testDateTime3Hours, - }; - - await expect(controller.createTimeSlot(timeSlot)).rejects.toThrow( - 'The duration of the time slot must be at most 60 minutes', - ); - }); - - it('should throw an error if the time slot overlaps with an existing time slot', async () => { - const timeSlot = { - start: testDateTimeStart, - end: testDateTimeEnd, - }; - - jest.spyOn(service, 'countOverlappingTimeSlots').mockResolvedValue(1); - - await expect(controller.createTimeSlot(timeSlot)).rejects.toThrow( - 'The time slot overlaps with existing time slots', - ); - }); - }); - - describe('deleteTimeSlot', () => { - it('should allow deleting an existing time slot', async () => { - jest.spyOn(service, 'findById').mockResolvedValue(mockTimeSlot); - jest.spyOn(service, 'deleteTimeSlot').mockResolvedValue(mockTimeSlot); - - await expect(controller.deleteTimeSlot(mockTimeSlot.id)).resolves.toEqual( - mockTimeSlot, - ); - expect(service.deleteTimeSlot).toHaveBeenCalledWith(mockTimeSlot); - expect(service.deleteTimeSlot).toHaveBeenCalledTimes(1); - }); - - it('should throw an error if the time slot does not exist', async () => { - jest.spyOn(service, 'findById').mockResolvedValue(null); - jest.spyOn(service, 'deleteTimeSlot').mockResolvedValue(mockTimeSlot); - - await expect( - controller.deleteTimeSlot(mockTimeSlot.id), - ).rejects.toThrowError('Time slot not found'); - expect(service.deleteTimeSlot).toHaveBeenCalledTimes(0); - }); - }); }); diff --git a/api/src/timeslots/timeslots.controller.ts b/api/src/timeslots/timeslots.controller.ts index 3202071..9580b8d 100644 --- a/api/src/timeslots/timeslots.controller.ts +++ b/api/src/timeslots/timeslots.controller.ts @@ -1,91 +1,10 @@ -import { - Body, - Controller, - BadRequestException, - NotFoundException, - ConflictException, - Param, - Post, - Delete, -} from '@nestjs/common'; +import { Controller } from '@nestjs/common'; import { TimeSlotsService } from './timeslots.service'; -import { Action, createTimeSlotSchema, TimeSlot } from '@hkrecruitment/shared'; -import { JoiValidate } from '../joi-validation/joi-validate.decorator'; -import { - ApiBadRequestResponse, - ApiBearerAuth, - ApiForbiddenResponse, - ApiNotFoundResponse, - ApiCreatedResponse, - ApiOkResponse, - ApiTags, - ApiConflictResponse, - ApiNoContentResponse, -} from '@nestjs/swagger'; -import { CheckPolicies } from 'src/authorization/check-policies.decorator'; -import { CreateTimeSlotDto } from './create-timeslot.dto'; -import * as Joi from 'joi'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; @ApiBearerAuth() @ApiTags('timeslots') @Controller('timeslots') export class TimeSlotsController { constructor(private readonly timeSlotsService: TimeSlotsService) {} - - @ApiBadRequestResponse() - @ApiForbiddenResponse() - @ApiConflictResponse({ - description: 'The time slot overlaps with existing time slots', - }) - @ApiCreatedResponse() - @JoiValidate({ - body: createTimeSlotSchema, - }) - @CheckPolicies((ability) => ability.can(Action.Create, 'TimeSlot')) - @Post() - async createTimeSlot(@Body() timeSlot: CreateTimeSlotDto): Promise { - const startDate = new Date(timeSlot.start); - const endDate = new Date(timeSlot.end); - - // Check duration - const durationInMinutes = - (endDate.getTime() - startDate.getTime()) / (1000 * 60); - if (durationInMinutes < 30) { - throw new BadRequestException( - 'The duration of the time slot must be at least 30 minutes', - ); - } else if (durationInMinutes > 60) { - throw new BadRequestException( - 'The duration of the time slot must be at most 60 minutes', - ); - } - - // Check overlapping timeslots - const overlappingTimeSlots = - await this.timeSlotsService.countOverlappingTimeSlots(startDate, endDate); - if (overlappingTimeSlots > 0) - throw new ConflictException( - 'The time slot overlaps with existing time slots', - ); - - return await this.timeSlotsService.createTimeSlot(timeSlot); - } - - @ApiBadRequestResponse() - @ApiForbiddenResponse() - @ApiNotFoundResponse() - @ApiOkResponse() - @ApiNoContentResponse() - @CheckPolicies((ability) => ability.can(Action.Delete, 'TimeSlot')) - @Delete('/:time_slot_id') - @JoiValidate({ - param: Joi.number().positive().integer().required().label('time_slot_id'), - }) - async deleteTimeSlot( - @Param('time_slot_id') timeSlotId: number, - ): Promise { - const timeSlot = await this.timeSlotsService.findById(timeSlotId); - if (!timeSlot) throw new NotFoundException('Time slot not found'); - return await this.timeSlotsService.deleteTimeSlot(timeSlot); - } } From b076f729a0d2e28666c85f77ab84d10951f5a9f2 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 3 Apr 2024 19:54:26 +0200 Subject: [PATCH 034/104] feat: TimeSlot service generateTimeslots() --- api/src/timeslots/timeslots.service.ts | 56 +++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index ec5e1b9..7eb37c0 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, LessThan, MoreThan } from 'typeorm'; +import { Repository, LessThan, MoreThan, QueryRunner } from 'typeorm'; import { TimeSlot } from './timeslot.entity'; +import { RecruitmentSession } from '@hkrecruitment/shared'; import { CreateTimeSlotDto } from './create-timeslot.dto'; @Injectable() @@ -51,4 +52,57 @@ export class TimeSlotsService { async createTimeSlot(timeSlot: CreateTimeSlotDto): Promise { return await this.timeSlotRepository.save(timeSlot); } + + async createRecruitmentSessionTimeSlots( + queryRunner: QueryRunner, + recruitmentSession: RecruitmentSession, + ): Promise { + const { slotDuration, interviewStart, interviewEnd, days } = + recruitmentSession; + const timeSlots = this.generateTimeSlots( + slotDuration, + interviewStart, + interviewEnd, + days, + ); + return await queryRunner.manager.getRepository(TimeSlot).save(timeSlots); + } + + generateTimeSlots( + slotDuration: number, + interviewStart: Date, + interviewEnd: Date, + days: Date[], + ): TimeSlot[] { + const timeSlots: TimeSlot[] = []; + const interviewStartMinutes = + interviewStart.getHours() * 60 + interviewStart.getMinutes(); + const interviewEndMinutes = + interviewEnd.getHours() * 60 + interviewEnd.getMinutes(); + const dailySlots = Math.floor( + (interviewEndMinutes - interviewStartMinutes) / slotDuration, + ); + + for (let i = 0; i < dailySlots; i++) { + for (let day of days) { + const timeSlotStart = new Date(day); + timeSlotStart.setHours( + interviewStart.getHours() + Math.floor((i * slotDuration) / 60), + interviewStart.getMinutes() + ((i * slotDuration) % 60), + 0, + 0, + ); + const timeSlotEnd = new Date( + timeSlotStart.getTime() + slotDuration * 1000 * 60, + ); + + const timeSlot = new TimeSlot(); + timeSlot.start = timeSlotStart; + timeSlot.end = timeSlotEnd; + timeSlots.push(timeSlot); + } + } + + return timeSlots; + } } From 5f0666245a96bacd8c1a0d1dae928096a1d2a6f2 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 3 Apr 2024 19:54:45 +0200 Subject: [PATCH 035/104] test: TimeSlot service generateTimeslots() --- api/src/timeslots/timeslots.service.spec.ts | 139 +++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index fde00f1..0fd8f78 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -1,17 +1,19 @@ -import { mockTimeSlot, testDate } from '@mocks/data'; -import { mockedRepository } from '@mocks/repositories'; +import { mockGenerateTimeSlots, mockTimeSlot, testDate } from 'src/mocks/data'; +import { mockedRepository } from 'src/mocks/repositories'; import { TestingModule, Test } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { TimeSlot } from './timeslot.entity'; import { TimeSlotsService } from './timeslots.service'; +import { mockDataSource } from 'src/mocks/data-sources'; describe('TimeSlotsService', () => { let timeSlotService: TimeSlotsService; + let mockDate: jest.SpyInstance; /************* Test setup ************/ beforeAll(() => { - jest + mockDate = jest .spyOn(global, 'Date') .mockImplementation(() => testDate as unknown as string); }); @@ -73,4 +75,135 @@ describe('TimeSlotsService', () => { expect(mockedRepository.save).toHaveBeenCalledTimes(1); }); }); + + describe('createRecruitmentSessionTimeSlots', () => { + it('should create recruitment session time slots', async () => { + const expectedTimeSlots: Partial[] = [ + { + start: new Date('2023-01-01T10:30:00'), + end: new Date('2023-01-01T11:00:00'), + }, + { + start: new Date('2023-01-01T11:00:00'), + end: new Date('2023-01-01T11:30:00'), + }, + { + start: new Date('2023-01-01T10:00:00'), + end: new Date('2022-01-01T11:00:00'), + }, + { + start: new Date('2023-01-01T10:00:00'), + end: new Date('2022-01-01T11:00:00'), + }, + ]; + + mockDataSource.setMockResults({ TimeSlot: { save: expectedTimeSlots } }); + const queryRunner = mockDataSource.createQueryRunner(); + const result = await timeSlotService.createRecruitmentSessionTimeSlots( + queryRunner, + mockGenerateTimeSlots, + ); + + expect(result).toEqual(expectedTimeSlots); + expect( + queryRunner.manager.getRepository(TimeSlot).save, + ).toHaveBeenCalledTimes(1); + }); + }); + + describe('generateTimeSlots', () => { + it('should generate time slots', () => { + mockDate.mockRestore(); + const slotDuration = 60; + const interviewStart = new Date('2022-01-01T09:00:00'); + const interviewEnd = new Date('2022-01-01T12:00:00'); + const days = [new Date('2022-01-01'), new Date('2022-01-03')]; + + const expectedTimeSlots: Partial[] = [ + { + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T10:00:00'), + }, + { + start: new Date('2022-01-03T09:00:00'), + end: new Date('2022-01-03T10:00:00'), + }, + { + start: new Date('2022-01-01T10:00:00'), + end: new Date('2022-01-01T11:00:00'), + }, + { + start: new Date('2022-01-03T10:00:00'), + end: new Date('2022-01-03T11:00:00'), + }, + { + start: new Date('2022-01-01T11:00:00'), + end: new Date('2022-01-01T12:00:00'), + }, + { + start: new Date('2022-01-03T11:00:00'), + end: new Date('2022-01-03T12:00:00'), + }, + ].map( + (timeSlot) => + ({ + ...timeSlot, + id: undefined, + availabilities: undefined, + } as TimeSlot), + ); + + const result = timeSlotService.generateTimeSlots( + slotDuration, + interviewStart, + interviewEnd, + days, + ); + + expect(result).toEqual(expectedTimeSlots); + }); + + it("shouldn't generate time slots that overflow interviewEnd time", () => { + mockDate.mockRestore(); + const slotDuration = 50; + const interviewStart = new Date('2022-01-01T09:00:00'); + const interviewEnd = new Date('2022-01-01T11:00:00'); + const days = [new Date('2022-01-01'), new Date('2022-01-03')]; + + const expectedTimeSlots: Partial[] = [ + { + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T09:50:00'), + }, + { + start: new Date('2022-01-03T09:00:00'), + end: new Date('2022-01-03T09:50:00'), + }, + { + start: new Date('2022-01-01T09:50:00'), + end: new Date('2022-01-01T10:40:00'), + }, + { + start: new Date('2022-01-03T09:50:00'), + end: new Date('2022-01-03T10:40:00'), + }, + ].map( + (timeSlot) => + ({ + ...timeSlot, + id: undefined, + availabilities: undefined, + } as TimeSlot), + ); + + const result = timeSlotService.generateTimeSlots( + slotDuration, + interviewStart, + interviewEnd, + days, + ); + + expect(result).toEqual(expectedTimeSlots); + }); + }); }); From 1e831c0362fdeecc9b47737e5e07aa675bea03b3 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Wed, 3 Apr 2024 19:57:34 +0200 Subject: [PATCH 036/104] feat: create recruitment session's timeslots atomically using a transaction --- .../recruitment-session.controller.spec.ts | 5 +- .../recruitment-session.controller.ts | 6 +-- .../recruitment-session.service.spec.ts | 51 +++++++++++++++++-- .../recruitment-session.service.ts | 24 +++++++-- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/api/src/recruitment-session/recruitment-session.controller.spec.ts b/api/src/recruitment-session/recruitment-session.controller.spec.ts index 719202d..679e5d7 100644 --- a/api/src/recruitment-session/recruitment-session.controller.spec.ts +++ b/api/src/recruitment-session/recruitment-session.controller.spec.ts @@ -10,7 +10,7 @@ import { mockUpdateRecruitmentSessionDto, mockCreateRecruitmentSessionDto, testDate, -} from '@mocks/data'; +} from 'src/mocks/data'; import { ConflictException, ForbiddenException, @@ -294,6 +294,9 @@ describe('RecruitmentSessionController', () => { state: mockDeletedRecruitmentSession.state, createdAt: mockDeletedRecruitmentSession.createdAt, } as RecruitmentSessionResponseDto; + jest + .spyOn(global, 'Date') + .mockImplementation(() => testDate as unknown as string); jest .spyOn(service, 'findRecruitmentSessionById') .mockResolvedValue(mockRecruitmentSession); diff --git a/api/src/recruitment-session/recruitment-session.controller.ts b/api/src/recruitment-session/recruitment-session.controller.ts index bc85757..112e7c0 100644 --- a/api/src/recruitment-session/recruitment-session.controller.ts +++ b/api/src/recruitment-session/recruitment-session.controller.ts @@ -100,9 +100,9 @@ export class RecruitmentSessionController { 'There is already an active recruitment session', ); - return this.recruitmentSessionService.createRecruitmentSession({ - ...recruitmentSession, - }); + return this.recruitmentSessionService.createRecruitmentSession( + recruitmentSession, + ); } // UPDATE A RECRUITMENT SESSION diff --git a/api/src/recruitment-session/recruitment-session.service.spec.ts b/api/src/recruitment-session/recruitment-session.service.spec.ts index d6dfc9e..afb7fbc 100644 --- a/api/src/recruitment-session/recruitment-session.service.spec.ts +++ b/api/src/recruitment-session/recruitment-session.service.spec.ts @@ -1,12 +1,16 @@ -import { mockRecruitmentSession, testDate } from '@mocks/data'; -import { mockedRepository } from '@mocks/repositories'; +import { mockRecruitmentSession, testDate } from 'src/mocks/data'; +import { mockedRepository } from 'src/mocks/repositories'; import { TestingModule, Test } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; +import { getRepositoryToken, getDataSourceToken } from '@nestjs/typeorm'; import { RecruitmentSession } from './recruitment-session.entity'; import { RecruitmentSessionService } from './recruitment-session.service'; +import { mockedTimeSlotsService as mockedTimeSlotsServiceClass } from '@mocks/services'; +import { mockDataSource } from 'src/mocks/data-sources'; +import { TimeSlotsService } from 'src/timeslots/timeslots.service'; describe('Recruitment Session Service', () => { let recruitmentSessionService: RecruitmentSessionService; + let mockedTimeSlotsService: TimeSlotsService; beforeAll(() => { jest @@ -22,9 +26,18 @@ describe('Recruitment Session Service', () => { provide: getRepositoryToken(RecruitmentSession), useValue: mockedRepository, }, + { + provide: TimeSlotsService, + useValue: mockedTimeSlotsServiceClass, + }, + { + provide: getDataSourceToken(), + useValue: mockDataSource, + }, ], }).compile(); + mockedTimeSlotsService = module.get(TimeSlotsService); recruitmentSessionService = module.get( RecruitmentSessionService, ); @@ -36,6 +49,38 @@ describe('Recruitment Session Service', () => { expect(recruitmentSessionService).toBeDefined(); }); + describe('createRecruitmentSession', () => { + it('should create a new recruitment session', async () => { + const mockRecruitmentSessionRepository = { + save: mockRecruitmentSession, + }; + const mockedRepositories = mockDataSource.setMockResults({ + RecruitmentSession: mockRecruitmentSessionRepository, + }); + jest + .spyOn(mockedTimeSlotsService, 'createRecruitmentSessionTimeSlots') + .mockResolvedValue([]); + const result = await recruitmentSessionService.createRecruitmentSession( + mockRecruitmentSession, + ); + const expectedRecruitmentSession = { + ...mockRecruitmentSession, + createdAt: testDate, + lastModified: testDate, + }; + expect(result).toEqual(mockRecruitmentSession); + expect( + mockedRepositories['RecruitmentSession'].save, + ).toHaveBeenCalledTimes(1); + expect( + mockedRepositories['RecruitmentSession'].save, + ).toHaveBeenCalledWith(expectedRecruitmentSession); + expect( + mockedTimeSlotsService.createRecruitmentSessionTimeSlots, + ).toHaveBeenCalledTimes(1); + }); + }); + describe('deleteRecruitmentSession', () => { it('should remove the specified recruitment session from the database', async () => { jest diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts index 2b77ee5..ee108e9 100644 --- a/api/src/recruitment-session/recruitment-session.service.ts +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -1,15 +1,20 @@ import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { InjectRepository, InjectDataSource } from '@nestjs/typeorm'; +import { Repository, DataSource } from 'typeorm'; import { RecruitmentSession } from './recruitment-session.entity'; import { CreateRecruitmentSessionDto } from './create-recruitment-session.dto'; import { RecruitmentSessionState } from '@hkrecruitment/shared'; +import { transaction } from 'src/utils/database'; +import { TimeSlotsService } from 'src/timeslots/timeslots.service'; @Injectable() export class RecruitmentSessionService { constructor( @InjectRepository(RecruitmentSession) private readonly recruitmentSessionRepository: Repository, + private readonly timeslotService: TimeSlotsService, + @InjectDataSource() + private dataSource: DataSource, ) {} async createRecruitmentSession( @@ -21,9 +26,18 @@ export class RecruitmentSessionService { state: RecruitmentSessionState.Active, createdAt: now, lastModified: now, - } as unknown as RecruitmentSession; - await this.recruitmentSessionRepository.save(rs); - return rs; + } as RecruitmentSession; + + return transaction(this.dataSource, async (queryRunner) => { + const recruitmentSession = await queryRunner.manager + .getRepository(RecruitmentSession) + .save(rs); + await this.timeslotService.createRecruitmentSessionTimeSlots( + queryRunner, + recruitmentSession, + ); + return recruitmentSession; + }); } async findAllRecruitmentSessions(): Promise { From 1bdb1cc7293c42d89ecc2024abcd5509b2ace115 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Thu, 4 Apr 2024 22:26:28 +0200 Subject: [PATCH 037/104] feat: added jest-mock-extended library to auto mock classes --- api/package.json | 1 + api/src/mocks/data-sources.ts | 33 ++--- pnpm-lock.yaml | 241 ++-------------------------------- 3 files changed, 32 insertions(+), 243 deletions(-) diff --git a/api/package.json b/api/package.json index b4a347b..9dcef79 100644 --- a/api/package.json +++ b/api/package.json @@ -39,6 +39,7 @@ "dotenv": "^16.0.3", "google-auth-library": "^8.7.0", "googleapis": "^118.0.0", + "jest-mock-extended": "^3.0.5", "joi": "^17.7.0", "js-yaml": "^4.1.0", "jwks-rsa": "^3.0.0", diff --git a/api/src/mocks/data-sources.ts b/api/src/mocks/data-sources.ts index 3cf81ca..4371477 100644 --- a/api/src/mocks/data-sources.ts +++ b/api/src/mocks/data-sources.ts @@ -1,9 +1,17 @@ -interface QueryResults { +import { QueryRunner } from 'typeorm'; +import { mock } from 'jest-mock-extended'; + +export interface QueryResults { findOneBy?: any; + findOne?: any; + findBy?: any; + find?: any; + save?: any; + query?: any; remove?: any; } -class MockedDataSource { +export class MockedDataSource { results = {}; /** @@ -38,20 +46,13 @@ class MockedDataSource { * @returns A mock query runner object. */ createQueryRunner = jest.fn(() => { - return { - connect: jest.fn(), - startTransaction: jest.fn(), - manager: { - getRepository: jest.fn((entity: any) => { - if (!this.results.hasOwnProperty(entity.name)) - throw new Error(`No results found for entity ${entity.name}`); - return this.results[entity.name]; - }), - }, - rollbackTransaction: jest.fn(), - commitTransaction: jest.fn(), - release: jest.fn(), - }; + const queryRunner = mock(); + queryRunner.manager.getRepository = jest.fn((entity: any) => { + if (!this.results.hasOwnProperty(entity.name)) + throw new Error(`No results found for entity ${entity.name}`); + return this.results[entity.name]; + }); + return queryRunner; }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbb0a77..1b5024d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,9 @@ importers: googleapis: specifier: ^118.0.0 version: 118.0.0 + jest-mock-extended: + specifier: ^3.0.5 + version: 3.0.5(jest@28.1.3)(typescript@4.5.2) joi: specifier: ^17.7.0 version: 17.7.0 @@ -286,7 +289,6 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.17 - dev: true /@angular-devkit/core@14.2.1(chokidar@3.5.3): resolution: {integrity: sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==} @@ -401,12 +403,10 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 - dev: true /@babel/compat-data@7.20.5: resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==} engines: {node: '>=6.9.0'} - dev: true /@babel/core@7.20.5: resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==} @@ -429,7 +429,6 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: true /@babel/core@7.21.3: resolution: {integrity: sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==} @@ -452,7 +451,6 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: true /@babel/generator@7.20.5: resolution: {integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==} @@ -461,7 +459,6 @@ packages: '@babel/types': 7.20.5 '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 - dev: true /@babel/generator@7.21.3: resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==} @@ -471,7 +468,6 @@ packages: '@jridgewell/gen-mapping': 0.3.2 '@jridgewell/trace-mapping': 0.3.17 jsesc: 2.5.2 - dev: true /@babel/helper-compilation-targets@7.20.0(@babel/core@7.20.5): resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} @@ -484,7 +480,6 @@ packages: '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 semver: 6.3.0 - dev: true /@babel/helper-compilation-targets@7.20.7(@babel/core@7.21.3): resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} @@ -498,12 +493,10 @@ packages: browserslist: 4.21.5 lru-cache: 5.1.1 semver: 6.3.0 - dev: true /@babel/helper-environment-visitor@7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-function-name@7.19.0: resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} @@ -511,7 +504,6 @@ packages: dependencies: '@babel/template': 7.18.10 '@babel/types': 7.20.5 - dev: true /@babel/helper-function-name@7.21.0: resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} @@ -519,21 +511,18 @@ packages: dependencies: '@babel/template': 7.20.7 '@babel/types': 7.21.3 - dev: true /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.5 - dev: true /@babel/helper-module-imports@7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.5 - dev: true /@babel/helper-module-transforms@7.20.2: resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==} @@ -549,7 +538,6 @@ packages: '@babel/types': 7.20.5 transitivePeerDependencies: - supports-color - dev: true /@babel/helper-module-transforms@7.21.2: resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} @@ -565,46 +553,38 @@ packages: '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color - dev: true /@babel/helper-plugin-utils@7.20.2: resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-simple-access@7.20.2: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.5 - dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.20.5 - dev: true /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option@7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-option@7.21.0: resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/helpers@7.20.6: resolution: {integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==} @@ -615,7 +595,6 @@ packages: '@babel/types': 7.20.5 transitivePeerDependencies: - supports-color - dev: true /@babel/helpers@7.21.0: resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} @@ -626,7 +605,6 @@ packages: '@babel/types': 7.21.3 transitivePeerDependencies: - supports-color - dev: true /@babel/highlight@7.18.6: resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} @@ -635,7 +613,6 @@ packages: '@babel/helper-validator-identifier': 7.19.1 chalk: 2.4.2 js-tokens: 4.0.0 - dev: true /@babel/parser@7.20.5: resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==} @@ -643,7 +620,6 @@ packages: hasBin: true dependencies: '@babel/types': 7.20.5 - dev: true /@babel/parser@7.21.3: resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} @@ -651,7 +627,6 @@ packages: hasBin: true dependencies: '@babel/types': 7.21.3 - dev: true /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.3): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} @@ -660,7 +635,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -669,7 +643,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.3): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} @@ -678,7 +651,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.3): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} @@ -687,7 +659,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} @@ -696,7 +667,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.3): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} @@ -705,7 +675,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -714,7 +683,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.3): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} @@ -723,7 +691,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -732,7 +699,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} @@ -741,7 +707,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.3): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} @@ -750,7 +715,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.3): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} @@ -760,7 +724,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.21.3): resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} @@ -770,7 +733,6 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - dev: true /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.3): resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} @@ -806,7 +768,6 @@ packages: '@babel/code-frame': 7.18.6 '@babel/parser': 7.20.5 '@babel/types': 7.20.5 - dev: true /@babel/template@7.20.7: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} @@ -815,7 +776,6 @@ packages: '@babel/code-frame': 7.18.6 '@babel/parser': 7.21.3 '@babel/types': 7.21.3 - dev: true /@babel/traverse@7.20.5: resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==} @@ -833,7 +793,6 @@ packages: globals: 11.12.0 transitivePeerDependencies: - supports-color - dev: true /@babel/traverse@7.21.3: resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} @@ -851,7 +810,6 @@ packages: globals: 11.12.0 transitivePeerDependencies: - supports-color - dev: true /@babel/types@7.20.5: resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==} @@ -860,7 +818,6 @@ packages: '@babel/helper-string-parser': 7.19.4 '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - dev: true /@babel/types@7.21.3: resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} @@ -869,11 +826,9 @@ packages: '@babel/helper-string-parser': 7.19.4 '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - dev: true /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: true /@casl/ability@6.3.3: resolution: {integrity: sha512-UzbqsE9etu6QzZrRmqIyVun2kztAzJ46Tz7lC/2P2buCE6B6Ll7Vptz7JTQtGwapLbeKo2jS7dL966TVOQ7x4g==} @@ -1180,12 +1135,10 @@ packages: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 - dev: true /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - dev: true /@jest/console@28.1.3: resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==} @@ -1197,7 +1150,6 @@ packages: jest-message-util: 28.1.3 jest-util: 28.1.3 slash: 3.0.0 - dev: true /@jest/core@28.1.3(ts-node@10.7.0): resolution: {integrity: sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==} @@ -1240,7 +1192,6 @@ packages: transitivePeerDependencies: - supports-color - ts-node - dev: true /@jest/create-cache-key-function@27.5.1: resolution: {integrity: sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==} @@ -1257,14 +1208,12 @@ packages: '@jest/types': 28.1.3 '@types/node': 16.18.4 jest-mock: 28.1.3 - dev: true /@jest/expect-utils@28.1.3: resolution: {integrity: sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: jest-get-type: 28.0.2 - dev: true /@jest/expect@28.1.3: resolution: {integrity: sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==} @@ -1274,7 +1223,6 @@ packages: jest-snapshot: 28.1.3 transitivePeerDependencies: - supports-color - dev: true /@jest/fake-timers@28.1.3: resolution: {integrity: sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==} @@ -1286,7 +1234,6 @@ packages: jest-message-util: 28.1.3 jest-mock: 28.1.3 jest-util: 28.1.3 - dev: true /@jest/globals@28.1.3: resolution: {integrity: sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==} @@ -1297,7 +1244,6 @@ packages: '@jest/types': 28.1.3 transitivePeerDependencies: - supports-color - dev: true /@jest/reporters@28.1.3: resolution: {integrity: sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==} @@ -1335,14 +1281,12 @@ packages: v8-to-istanbul: 9.0.1 transitivePeerDependencies: - supports-color - dev: true /@jest/schemas@28.1.3: resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@sinclair/typebox': 0.24.51 - dev: true /@jest/source-map@28.1.2: resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} @@ -1351,7 +1295,6 @@ packages: '@jridgewell/trace-mapping': 0.3.17 callsites: 3.1.0 graceful-fs: 4.2.10 - dev: true /@jest/test-result@28.1.3: resolution: {integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==} @@ -1361,7 +1304,6 @@ packages: '@jest/types': 28.1.3 '@types/istanbul-lib-coverage': 2.0.4 collect-v8-coverage: 1.0.1 - dev: true /@jest/test-sequencer@28.1.3: resolution: {integrity: sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==} @@ -1371,7 +1313,6 @@ packages: graceful-fs: 4.2.10 jest-haste-map: 28.1.3 slash: 3.0.0 - dev: true /@jest/transform@28.1.3: resolution: {integrity: sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==} @@ -1394,7 +1335,6 @@ packages: write-file-atomic: 4.0.2 transitivePeerDependencies: - supports-color - dev: true /@jest/types@27.5.1: resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} @@ -1417,7 +1357,6 @@ packages: '@types/node': 16.18.4 '@types/yargs': 17.0.15 chalk: 4.1.2 - dev: true /@joi/date@2.1.0: resolution: {integrity: sha512-2zN5m0LgxZp/cynHGbzEImVmFIa+n+IOb/Nlw5LX/PLJneeCwG1NbiGw7MvPjsAKUGQK8z31Nn6V6lEN+4fZhg==} @@ -1431,7 +1370,6 @@ packages: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.14 - dev: true /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} @@ -1842,19 +1780,16 @@ packages: /@sinclair/typebox@0.24.51: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} - dev: true /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: type-detect: 4.0.8 - dev: true /@sinonjs/fake-timers@9.1.2: resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} dependencies: '@sinonjs/commons': 1.8.6 - dev: true /@sqltools/formatter@1.2.5: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} @@ -2004,26 +1939,22 @@ packages: '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.3 - dev: true /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: '@babel/types': 7.20.5 - dev: true /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.20.5 '@babel/types': 7.20.5 - dev: true /@types/babel__traverse@7.18.3: resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} dependencies: '@babel/types': 7.20.5 - dev: true /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} @@ -2074,23 +2005,19 @@ packages: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: '@types/node': 16.18.4 - dev: true /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - dev: true /@types/istanbul-lib-report@3.0.0: resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} dependencies: '@types/istanbul-lib-coverage': 2.0.4 - dev: true /@types/istanbul-reports@3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: '@types/istanbul-lib-report': 3.0.0 - dev: true /@types/jest@28.1.8: resolution: {integrity: sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==} @@ -2151,7 +2078,6 @@ packages: /@types/prettier@2.7.1: resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} - dev: true /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} @@ -2193,7 +2119,6 @@ packages: /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - dev: true /@types/superagent@4.1.16: resolution: {integrity: sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==} @@ -2214,7 +2139,6 @@ packages: /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - dev: true /@types/yargs@16.0.5: resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==} @@ -2226,7 +2150,6 @@ packages: resolution: {integrity: sha512-ZHc4W2dnEQPfhn06TBEdWaiUHEZAocYaiVMfwOipY5jcJt/251wVrKCBWBetGZWO5CF8tdb7L3DmdxVlZ2BOIg==} dependencies: '@types/yargs-parser': 21.0.0 - dev: true /@typescript-eslint/eslint-plugin@5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.29.0)(typescript@4.5.2): resolution: {integrity: sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==} @@ -2613,7 +2536,6 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 - dev: true /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -2624,7 +2546,6 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -2635,7 +2556,6 @@ packages: /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - dev: true /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -2647,7 +2567,6 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true /app-root-path@3.1.0: resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} @@ -2683,7 +2602,6 @@ packages: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 - dev: true /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2729,7 +2647,6 @@ packages: slash: 3.0.0 transitivePeerDependencies: - supports-color - dev: true /babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} @@ -2742,7 +2659,6 @@ packages: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - dev: true /babel-plugin-jest-hoist@28.1.3: resolution: {integrity: sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==} @@ -2752,7 +2668,6 @@ packages: '@babel/types': 7.20.5 '@types/babel__core': 7.1.20 '@types/babel__traverse': 7.18.3 - dev: true /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.3): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} @@ -2772,7 +2687,6 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.3) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.3) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.3) - dev: true /babel-preset-jest@28.1.3(@babel/core@7.21.3): resolution: {integrity: sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==} @@ -2783,7 +2697,6 @@ packages: '@babel/core': 7.21.3 babel-plugin-jest-hoist: 28.1.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.3) - dev: true /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2855,7 +2768,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browser-tabs-lock@1.2.15: resolution: {integrity: sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==} @@ -2883,7 +2795,6 @@ packages: electron-to-chromium: 1.4.284 node-releases: 2.0.10 update-browserslist-db: 1.0.10(browserslist@4.21.5) - dev: true /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -2896,7 +2807,6 @@ packages: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: node-int64: 0.4.0 - dev: true /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -2970,7 +2880,6 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - dev: true /camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} @@ -2982,19 +2891,16 @@ packages: /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - dev: true /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - dev: true /caniuse-lite@1.0.30001436: resolution: {integrity: sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==} /caniuse-lite@1.0.30001468: resolution: {integrity: sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==} - dev: true /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -3003,7 +2909,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true /chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} @@ -3023,7 +2928,6 @@ packages: /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - dev: true /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -3055,11 +2959,9 @@ packages: /ci-info@3.7.0: resolution: {integrity: sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==} engines: {node: '>=8'} - dev: true /cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - dev: true /class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} @@ -3144,17 +3046,14 @@ packages: /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - dev: true /collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - dev: true /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - dev: true /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -3164,7 +3063,6 @@ packages: /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -3236,7 +3134,6 @@ packages: /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: true /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -3285,7 +3182,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} @@ -3346,7 +3242,6 @@ packages: /dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} - dev: true /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -3355,7 +3250,6 @@ packages: /deepmerge@4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} - dev: true /defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} @@ -3391,7 +3285,6 @@ packages: /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - dev: true /dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} @@ -3403,7 +3296,6 @@ packages: /diff-sequences@28.1.1: resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} @@ -3500,7 +3392,6 @@ packages: /emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} engines: {node: '>=12'} - dev: true /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3548,7 +3439,6 @@ packages: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: is-arrayish: 0.2.1 - dev: true /es-cookie@1.3.2: resolution: {integrity: sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==} @@ -3597,12 +3487,10 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} - dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -3731,7 +3619,6 @@ packages: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - dev: true /esquery@1.4.0: resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} @@ -3804,12 +3691,10 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - dev: true /expect@28.1.3: resolution: {integrity: sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==} @@ -3820,7 +3705,6 @@ packages: jest-matcher-utils: 28.1.3 jest-message-util: 28.1.3 jest-util: 28.1.3 - dev: true /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} @@ -3919,7 +3803,6 @@ packages: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} dependencies: bser: 2.1.1 - dev: true /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} @@ -3946,7 +3829,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} @@ -3968,7 +3850,6 @@ packages: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -4070,7 +3951,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind@1.1.1: @@ -4132,7 +4012,6 @@ packages: /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - dev: true /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -4148,7 +4027,6 @@ packages: /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - dev: true /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} @@ -4160,7 +4038,6 @@ packages: /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -4203,7 +4080,6 @@ packages: /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - dev: true /globals@13.18.0: resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==} @@ -4298,7 +4174,6 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -4333,7 +4208,6 @@ packages: /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: true /html-minifier-terser@6.1.0: resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} @@ -4393,7 +4267,6 @@ packages: /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: true /humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} @@ -4443,7 +4316,6 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - dev: true /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -4531,7 +4403,6 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: true /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} @@ -4544,7 +4415,6 @@ packages: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 - dev: true /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -4558,7 +4428,6 @@ packages: /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -4580,7 +4449,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} @@ -4606,7 +4474,6 @@ packages: /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} - dev: true /istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} @@ -4619,7 +4486,6 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: true /istanbul-lib-report@3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} @@ -4628,7 +4494,6 @@ packages: istanbul-lib-coverage: 3.2.0 make-dir: 3.1.0 supports-color: 7.2.0 - dev: true /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} @@ -4639,7 +4504,6 @@ packages: source-map: 0.6.1 transitivePeerDependencies: - supports-color - dev: true /istanbul-reports@3.1.5: resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} @@ -4647,7 +4511,6 @@ packages: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.0 - dev: true /iterare@1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} @@ -4670,7 +4533,6 @@ packages: dependencies: execa: 5.1.1 p-limit: 3.1.0 - dev: true /jest-circus@28.1.3: resolution: {integrity: sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==} @@ -4697,7 +4559,6 @@ packages: stack-utils: 2.0.6 transitivePeerDependencies: - supports-color - dev: true /jest-cli@28.1.3(@types/node@16.18.4)(ts-node@10.7.0): resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==} @@ -4725,7 +4586,6 @@ packages: - '@types/node' - supports-color - ts-node - dev: true /jest-config@28.1.3(@types/node@16.18.4)(ts-node@10.7.0): resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} @@ -4765,7 +4625,6 @@ packages: ts-node: 10.7.0(@swc/core@1.3.56)(@types/node@16.18.4)(typescript@4.5.2) transitivePeerDependencies: - supports-color - dev: true /jest-diff@28.1.3: resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==} @@ -4775,14 +4634,12 @@ packages: diff-sequences: 28.1.1 jest-get-type: 28.0.2 pretty-format: 28.1.3 - dev: true /jest-docblock@28.1.1: resolution: {integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: detect-newline: 3.1.0 - dev: true /jest-each@28.1.3: resolution: {integrity: sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==} @@ -4793,7 +4650,6 @@ packages: jest-get-type: 28.0.2 jest-util: 28.1.3 pretty-format: 28.1.3 - dev: true /jest-environment-node@28.1.3: resolution: {integrity: sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==} @@ -4805,12 +4661,10 @@ packages: '@types/node': 16.18.4 jest-mock: 28.1.3 jest-util: 28.1.3 - dev: true /jest-get-type@28.0.2: resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true /jest-haste-map@28.1.3: resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==} @@ -4829,7 +4683,6 @@ packages: walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 - dev: true /jest-leak-detector@28.1.3: resolution: {integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==} @@ -4837,7 +4690,6 @@ packages: dependencies: jest-get-type: 28.0.2 pretty-format: 28.1.3 - dev: true /jest-matcher-utils@28.1.3: resolution: {integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==} @@ -4847,7 +4699,6 @@ packages: jest-diff: 28.1.3 jest-get-type: 28.0.2 pretty-format: 28.1.3 - dev: true /jest-message-util@28.1.3: resolution: {integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==} @@ -4862,7 +4713,6 @@ packages: pretty-format: 28.1.3 slash: 3.0.0 stack-utils: 2.0.6 - dev: true /jest-mock-extended@2.0.9(jest@28.1.3)(typescript@4.5.2): resolution: {integrity: sha512-eRZq7/FgwHbxOMm3Lo4DpQX6S2zi4OvwMVFHEb3FgDLp0Xy3P1WARkF93xxO5uD4nAHiEPYHZ25qVU9mAVxoLQ==} @@ -4875,13 +4725,23 @@ packages: typescript: 4.5.2 dev: true + /jest-mock-extended@3.0.5(jest@28.1.3)(typescript@4.5.2): + resolution: {integrity: sha512-/eHdaNPUAXe7f65gHH5urc8SbRVWjYxBqmCgax2uqOBJy8UUcCBMN1upj1eZ8y/i+IqpyEm4Kq0VKss/GCCTdw==} + peerDependencies: + jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 + typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 + dependencies: + jest: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + ts-essentials: 7.0.3(typescript@4.5.2) + typescript: 4.5.2 + dev: false + /jest-mock@28.1.3: resolution: {integrity: sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 '@types/node': 16.18.4 - dev: true /jest-pnp-resolver@1.2.3(jest-resolve@28.1.3): resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} @@ -4893,12 +4753,10 @@ packages: optional: true dependencies: jest-resolve: 28.1.3 - dev: true /jest-regex-util@28.0.2: resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true /jest-resolve-dependencies@28.1.3: resolution: {integrity: sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==} @@ -4908,7 +4766,6 @@ packages: jest-snapshot: 28.1.3 transitivePeerDependencies: - supports-color - dev: true /jest-resolve@28.1.3: resolution: {integrity: sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==} @@ -4923,7 +4780,6 @@ packages: resolve: 1.22.1 resolve.exports: 1.1.0 slash: 3.0.0 - dev: true /jest-runner@28.1.3: resolution: {integrity: sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==} @@ -4952,7 +4808,6 @@ packages: source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - dev: true /jest-runtime@28.1.3: resolution: {integrity: sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==} @@ -4982,7 +4837,6 @@ packages: strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - dev: true /jest-snapshot@28.1.3: resolution: {integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==} @@ -5013,7 +4867,6 @@ packages: semver: 7.3.8 transitivePeerDependencies: - supports-color - dev: true /jest-util@28.1.3: resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==} @@ -5025,7 +4878,6 @@ packages: ci-info: 3.7.0 graceful-fs: 4.2.10 picomatch: 2.3.1 - dev: true /jest-validate@28.1.3: resolution: {integrity: sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==} @@ -5037,7 +4889,6 @@ packages: jest-get-type: 28.0.2 leven: 3.1.0 pretty-format: 28.1.3 - dev: true /jest-watcher@28.1.3: resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} @@ -5051,7 +4902,6 @@ packages: emittery: 0.10.2 jest-util: 28.1.3 string-length: 4.0.2 - dev: true /jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} @@ -5068,7 +4918,6 @@ packages: '@types/node': 16.18.4 merge-stream: 2.0.0 supports-color: 8.1.1 - dev: true /jest@28.1.3(@types/node@16.18.4)(ts-node@10.7.0): resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==} @@ -5088,7 +4937,6 @@ packages: - '@types/node' - supports-color - ts-node - dev: true /joi@17.7.0: resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==} @@ -5116,7 +4964,6 @@ packages: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} @@ -5128,7 +4975,6 @@ packages: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true - dev: true /json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} @@ -5160,7 +5006,6 @@ packages: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - dev: true /jsonc-parser@3.1.0: resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==} @@ -5241,12 +5086,10 @@ packages: /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - dev: true /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - dev: true /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} @@ -5262,7 +5105,6 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true /loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} @@ -5273,7 +5115,6 @@ packages: engines: {node: '>=8'} dependencies: p-locate: 4.1.0 - dev: true /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -5357,7 +5198,6 @@ packages: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: yallist: 3.1.1 - dev: true /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} @@ -5430,7 +5270,6 @@ packages: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 - dev: true /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} @@ -5464,7 +5303,6 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -5490,7 +5328,6 @@ packages: /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - dev: true /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -5633,7 +5470,6 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} @@ -5708,11 +5544,9 @@ packages: /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - dev: true /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - dev: true /node-releases@2.0.6: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} @@ -5751,14 +5585,12 @@ packages: /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} dependencies: path-key: 3.1.1 - dev: true /npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} @@ -5812,7 +5644,6 @@ packages: engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 - dev: true /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} @@ -5859,7 +5690,6 @@ packages: engines: {node: '>=6'} dependencies: p-try: 2.2.0 - dev: true /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -5872,7 +5702,6 @@ packages: engines: {node: '>=8'} dependencies: p-limit: 2.3.0 - dev: true /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} @@ -5892,7 +5721,6 @@ packages: /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - dev: true /packet-reader@1.0.0: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} @@ -5920,7 +5748,6 @@ packages: error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - dev: true /parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} @@ -5971,7 +5798,6 @@ packages: /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} @@ -5980,11 +5806,9 @@ packages: /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -6067,19 +5891,16 @@ packages: /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pirates@4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} - dev: true /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} dependencies: find-up: 4.1.0 - dev: true /pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} @@ -6143,7 +5964,6 @@ packages: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 18.2.0 - dev: true /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -6182,7 +6002,6 @@ packages: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 - dev: true /prop-types-extra@1.1.1(react@18.2.0): resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==} @@ -6298,7 +6117,6 @@ packages: /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - dev: true /react-lifecycles-compat@3.0.4: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} @@ -6439,7 +6257,6 @@ packages: engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 - dev: true /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -6449,12 +6266,10 @@ packages: /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - dev: true /resolve.exports@1.1.0: resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} engines: {node: '>=10'} - dev: true /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} @@ -6463,7 +6278,6 @@ packages: is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true /restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} @@ -6623,12 +6437,10 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shelljs@0.8.5: resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} @@ -6659,12 +6471,10 @@ packages: /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - dev: true /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} @@ -6703,7 +6513,6 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -6731,7 +6540,6 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true /sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} @@ -6763,7 +6571,6 @@ packages: engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 - dev: true /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} @@ -6779,7 +6586,6 @@ packages: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - dev: true /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -6813,17 +6619,14 @@ packages: /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} - dev: true /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: true /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - dev: true /superagent@8.0.5: resolution: {integrity: sha512-lQVE0Praz7nHiSaJLKBM/cZyi7J0E4io8tWnGSBdBrqAzhzrjQ/F5iGP9Zr29CJC8N5zYdhG2kKaNcB6dKxp7g==} @@ -6858,7 +6661,6 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -6878,12 +6680,10 @@ packages: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - dev: true /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true /swagger-ui-dist@4.15.1: resolution: {integrity: sha512-DlZARu6ckUFqDe0j5IPayO4k0gQvYQw9Un02MhxAgaMtVnTH2vmyyDe+yKeV0r1LiiPx3JbasdS/5Yyb/AV3iw==} @@ -6915,7 +6715,6 @@ packages: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - dev: true /terser-webpack-plugin@5.3.6(@swc/core@1.3.56)(webpack@5.74.0): resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} @@ -6983,7 +6782,6 @@ packages: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - dev: true /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -7015,19 +6813,16 @@ packages: /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: true /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} - dev: true /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} @@ -7054,7 +6849,6 @@ packages: typescript: '>=3.7.0' dependencies: typescript: 4.5.2 - dev: true /ts-jest@28.0.8(@babel/core@7.21.3)(jest@28.1.3)(typescript@4.5.2): resolution: {integrity: sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==} @@ -7215,7 +7009,6 @@ packages: /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - dev: true /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} @@ -7225,7 +7018,6 @@ packages: /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - dev: true /type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} @@ -7407,7 +7199,6 @@ packages: browserslist: 4.21.5 escalade: 3.1.1 picocolors: 1.0.0 - dev: true /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -7444,7 +7235,6 @@ packages: '@jridgewell/trace-mapping': 0.3.17 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.9.0 - dev: true /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} @@ -7507,7 +7297,6 @@ packages: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: makeerror: 1.0.12 - dev: true /warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} @@ -7670,7 +7459,6 @@ packages: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 - dev: true /xml2js@0.4.23: resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} @@ -7699,7 +7487,6 @@ packages: /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} From 4d1590e9ade15bef0b349df1597b3835627fc9a3 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Thu, 4 Apr 2024 22:32:40 +0200 Subject: [PATCH 038/104] fix: added DbAwareColumn to overcome sqlite column type limitation --- api/src/availability/availability.entity.ts | 7 +++---- api/src/users/user.entity.ts | 5 ++++- api/src/utils/database.ts | 8 +++++++- api/src/utils/db-aware-column.ts | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 api/src/utils/db-aware-column.ts diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index ec82ad0..384669e 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -1,7 +1,6 @@ import { Column, Entity, - JoinColumn, ManyToOne, PrimaryGeneratedColumn, Relation, @@ -12,6 +11,7 @@ import { } from '../../../shared/src/availability'; import { User } from 'src/users/user.entity'; import { TimeSlot } from 'src/timeslots/timeslot.entity'; +import { DbAwareColumn } from 'src/utils/db-aware-column'; @Entity() export class Availability implements AvailabilityInterface { @@ -24,12 +24,11 @@ export class Availability implements AvailabilityInterface { @Column({ name: 'last_modified' }) lastModified: Date; - @Column({ name: 'time_slot' }) + @DbAwareColumn(() => TimeSlot, { name: 'time_slot' }) @ManyToOne(() => TimeSlot, (timeSlot) => timeSlot.availabilities) timeSlot: Relation; - @ManyToOne(() => User) - @JoinColumn() + @ManyToOne(() => User, (user) => user.availabilities) user: Relation; // @OneToOne(() => Interview) diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index e4e070d..eae431a 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -1,4 +1,4 @@ -import { Column, Entity, OneToMany, PrimaryColumn } from 'typeorm'; +import { Column, Entity, OneToMany, PrimaryColumn, Relation } from 'typeorm'; import { Person, Role } from '@hkrecruitment/shared'; import { Availability } from 'src/availability/availability.entity'; @@ -27,4 +27,7 @@ export class User implements Person { @Column() role: Role; + + @OneToMany(() => Availability, (availability) => availability.user) + availabilities?: Relation; } diff --git a/api/src/utils/database.ts b/api/src/utils/database.ts index 6c0a1b1..f9bdeb9 100644 --- a/api/src/utils/database.ts +++ b/api/src/utils/database.ts @@ -1,9 +1,15 @@ import { DataSource, QueryRunner } from 'typeorm'; +/** + * Executes a transaction using the provided data source and actions. + * @param dataSource The connection configuration to a specific database to use for the transaction. + * @param actions The actions to perform within the transaction. + * @returns The result of the executed actions. + */ export async function transaction( dataSource: DataSource, actions: (queryRunner: QueryRunner) => Promise, -) { +): Promise { // BEGIN TRANSACTION const queryRunner = dataSource.createQueryRunner(); diff --git a/api/src/utils/db-aware-column.ts b/api/src/utils/db-aware-column.ts new file mode 100644 index 0000000..1cac047 --- /dev/null +++ b/api/src/utils/db-aware-column.ts @@ -0,0 +1,16 @@ +import { Column, ColumnOptions } from 'typeorm'; + +/** + * Decorator function that uses 'varchar' as the column type in the testing environment. + * This is necessary as sqlite does not support custom classes as column types. + * If not in a test environment, it returns a column with its original type. + * + * @param type - The type of the column. + * @param columnOptions - The options for the column. + * @returns The column definition. + */ +export function DbAwareColumn(type: (t?: any) => Function, columnOptions: ColumnOptions) { + if (process.env.NODE_ENV === 'test') + return Column('varchar', columnOptions); + return Column(type, columnOptions); +} \ No newline at end of file From 0b89084549c0fbe8e8ab333bbf567b89778a8e08 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Thu, 4 Apr 2024 22:36:14 +0200 Subject: [PATCH 039/104] fix: apply abilities on TimeSlots --- shared/src/abilities.ts | 3 ++- shared/src/timeslot.ts | 46 ++++++++++++++--------------------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/shared/src/abilities.ts b/shared/src/abilities.ts index 23fd5bf..dd277ee 100644 --- a/shared/src/abilities.ts +++ b/shared/src/abilities.ts @@ -8,7 +8,7 @@ import { import { applyAbilitiesForPerson, Person, Role } from "./person"; import { Application, applyAbilitiesOnApplication } from "./application"; import { applyAbilitiesOnAvailability, Availability } from "./availability"; -import { TimeSlot } from "./timeslot"; +import { applyAbilitiesOnTimeSlot, TimeSlot } from "./timeslot"; import { RecruitmentSession } from "./recruitment-session"; import { applyAbilitiesOnRecruitmentSession } from "./recruitment-session"; @@ -53,6 +53,7 @@ export const abilityForUser = (user: UserAuth): AppAbility => { applyAbilitiesOnApplication(user, builder); applyAbilitiesOnAvailability(user, builder); applyAbilitiesOnRecruitmentSession(user, builder); + applyAbilitiesOnTimeSlot(user, builder); const { build } = builder; return build(); diff --git a/shared/src/timeslot.ts b/shared/src/timeslot.ts index 712b603..6343754 100644 --- a/shared/src/timeslot.ts +++ b/shared/src/timeslot.ts @@ -1,27 +1,12 @@ import { Action, ApplyAbilities } from "./abilities"; -import DateExtension from "@joi/date"; -import * as Joi from "joi"; -const JoiDate = Joi.extend(DateExtension); - -// import BaseJoi from "joi"; -// const Joi = BaseJoi.extend(JoiDate); +import { Role } from "./person"; export interface TimeSlot { + id: number; start: Date; end: Date; } -/* Validation schemas */ - -export const createTimeSlotSchema = Joi.object({ - start: JoiDate.date().format("YYYY-MM-DD HH:mm").required(), - end: JoiDate.date().format("YYYY-MM-DD HH:mm").required(), -}).options({ - stripUnknown: true, - abortEarly: false, - presence: "required", -}); - /* Abilities */ export const applyAbilitiesOnTimeSlot: ApplyAbilities = ( @@ -29,18 +14,17 @@ export const applyAbilitiesOnTimeSlot: ApplyAbilities = ( { can, cannot } ) => { can(Action.Manage, "TimeSlot"); - // switch (user.role) { - // case Role.Admin: - // case Role.Supervisor: - // case Role.Clerk: - // // TODO: Decide who can create/delete timeslots - // can(Action.Manage, "TimeSlot"); - // break; - // case Role.Member: - // case Role.Applicant: - // can(Action.Read, "TimeSlot"); - // break; - // default: - // cannot(Action.Manage, "TimeSlot"); - // } + switch (user.role) { + case Role.Admin: + case Role.Supervisor: + can(Action.Manage, "TimeSlot"); + break; + case Role.Clerk: + case Role.Member: + case Role.Applicant: + can(Action.Read, "TimeSlot"); + break; + default: + cannot(Action.Manage, "TimeSlot"); + } }; From 54a2a5152f8718e7edaf302be30406eab9fc22ed Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Fri, 5 Apr 2024 08:45:10 +0200 Subject: [PATCH 040/104] fix: imported missing modules --- api/src/app.module.ts | 2 ++ api/src/availability/availability.module.ts | 3 ++- api/src/recruitment-session/recruitment-session.module.ts | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/app.module.ts b/api/src/app.module.ts index 271ffd0..607aeb4 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -10,6 +10,7 @@ import { APP_GUARD } from '@nestjs/core'; import { JwtGuard } from './authentication/jwt-guard.guard'; import { AuthorizationModule } from './authorization/authorization.module'; import { AuthorizationGuard } from './authorization/authorization.guard'; +import { AvailabilityModule } from './availability/availability.module'; @Module({ imports: [ @@ -34,6 +35,7 @@ import { AuthorizationGuard } from './authorization/authorization.guard'; ApplicationsModule, AuthenticationModule, AuthorizationModule, + AvailabilityModule, RecruitmentSessionModule, TimeSlotsModule, UsersModule, diff --git a/api/src/availability/availability.module.ts b/api/src/availability/availability.module.ts index 10d80fb..ac3273a 100644 --- a/api/src/availability/availability.module.ts +++ b/api/src/availability/availability.module.ts @@ -4,9 +4,10 @@ import { AvailabilityController } from './availability.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Availability } from './availability.entity'; import { UsersModule } from 'src/users/users.module'; +import { TimeSlotsModule } from 'src/timeslots/timeslots.module'; @Module({ - imports: [TypeOrmModule.forFeature([Availability]), UsersModule], + imports: [TypeOrmModule.forFeature([Availability]), UsersModule, TimeSlotsModule], providers: [AvailabilityService], controllers: [AvailabilityController], exports: [AvailabilityService], diff --git a/api/src/recruitment-session/recruitment-session.module.ts b/api/src/recruitment-session/recruitment-session.module.ts index b53e481..714207a 100644 --- a/api/src/recruitment-session/recruitment-session.module.ts +++ b/api/src/recruitment-session/recruitment-session.module.ts @@ -4,9 +4,10 @@ import { RecruitmentSessionController } from './recruitment-session.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { RecruitmentSession } from './recruitment-session.entity'; import { UsersModule } from 'src/users/users.module'; +import { TimeSlotsModule } from 'src/timeslots/timeslots.module'; @Module({ - imports: [TypeOrmModule.forFeature([RecruitmentSession]), UsersModule], + imports: [TypeOrmModule.forFeature([RecruitmentSession]), UsersModule, TimeSlotsModule], providers: [RecruitmentSessionService], controllers: [RecruitmentSessionController], exports: [RecruitmentSessionService], From e45e7776e1de4e9bdf56043e12480e2e46b4ce91 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Sat, 6 Apr 2024 15:57:08 +0200 Subject: [PATCH 041/104] fix: import Joi in availability controller --- api/src/availability/availability.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 6825882..80bd194 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -33,7 +33,7 @@ import { } from '@nestjs/swagger'; import { CheckPolicies } from 'src/authorization/check-policies.decorator'; import { Availability } from './availability.entity'; -import Joi from 'joi'; +import * as Joi from 'joi'; import { CreateAvailabilityDto } from './create-availability.dto'; import { AuthenticatedRequest } from 'src/authorization/authenticated-request.types'; import { Ability } from 'src/authorization/ability.decorator'; From dad284ac1c5a781c5d3248ff7d20e3eceebc6b1e Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Sat, 6 Apr 2024 15:58:21 +0200 Subject: [PATCH 042/104] test: mock recruitment session for timeslot generation --- api/src/mocks/data.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index 61266d7..01100f9 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -5,7 +5,10 @@ import { PhdApplication, } from 'src/application/application.entity'; import { UpdateApplicationDto } from 'src/application/update-application.dto'; -import { RecruitmentSessionState } from '@hkrecruitment/shared/recruitment-session'; +import { + RecruitmentSession, + RecruitmentSessionState, +} from '@hkrecruitment/shared/recruitment-session'; import { CreateRecruitmentSessionDto } from 'src/recruitment-session/create-recruitment-session.dto'; import { UpdateRecruitmentSessionDto } from 'src/recruitment-session/update-recruitment-session.dto'; import { CreateAvailabilityDto } from 'src/availability/create-availability.dto'; @@ -19,6 +22,7 @@ import { TimeSlot } from '@hkrecruitment/shared'; export const testDate = new Date(2023, 0, 1, 10, 0, 0); export const testDateTimeStart = new Date(2023, 0, 1, 10, 30, 0); export const testDateTime10Minutes = new Date(2023, 0, 1, 10, 40, 0); +export const testDateTime1Hour = new Date(2023, 0, 1, 11, 30, 0); export const testDateTime3Hours = new Date(2023, 0, 1, 13, 30, 0); export const testDateTimeEnd = new Date(2023, 0, 1, 11, 30, 0); @@ -62,6 +66,13 @@ export const mockUpdateRecruitmentSessionDto = { days: [testDay1, testDay2, testDay3], } as UpdateRecruitmentSessionDto; +export const mockGenerateTimeSlots = { + slotDuration: 30, + interviewStart: testDateTimeStart, + interviewEnd: testDateTime1Hour, + days: [testDay1, testDay3], +} as RecruitmentSession; + export const baseFile = { encoding: '7bit', mimetype: 'application/pdf', From 20223ca75e075f77ccc2e9893353f90fad8f11bf Mon Sep 17 00:00:00 2001 From: ozerodb <50593663+ozerodb@users.noreply.github.com> Date: Tue, 9 Apr 2024 11:50:10 +0200 Subject: [PATCH 043/104] trying to fix sonarcloud warning using typescript instead - also ran 'pnpm add @types/react' to solve typing-related warning - if this works, other scripts will be adapted to TS too --- frontend/package.json | 1 + ...gate.jsx => Auth0ProviderWithNavigate.tsx} | 2 +- pnpm-lock.yaml | 23 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) rename frontend/src/{Auth0ProviderWithNavigate.jsx => Auth0ProviderWithNavigate.tsx} (90%) diff --git a/frontend/package.json b/frontend/package.json index 3fc735c..8dffd35 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,6 +7,7 @@ "@auth0/auth0-react": "^1.12.0", "@hkrecruitment/shared": "workspace:*", "@popperjs/core": "^2.11.6", + "@types/react": "^18.2.75", "bootstrap": "^5.2.3", "bootstrap-icons": "^1.11.3", "moment": "^2.29.3", diff --git a/frontend/src/Auth0ProviderWithNavigate.jsx b/frontend/src/Auth0ProviderWithNavigate.tsx similarity index 90% rename from frontend/src/Auth0ProviderWithNavigate.jsx rename to frontend/src/Auth0ProviderWithNavigate.tsx index 24ab643..d58ee08 100644 --- a/frontend/src/Auth0ProviderWithNavigate.jsx +++ b/frontend/src/Auth0ProviderWithNavigate.tsx @@ -2,7 +2,7 @@ import { Auth0Provider } from "@auth0/auth0-react"; import React from "react"; import { useNavigate } from "react-router-dom"; -export const Auth0ProviderWithNavigate = ({ children }) => { +export const Auth0ProviderWithNavigate = ({ children }): JSX.Element | null => { const navigate = useNavigate(); const domain = import.meta.env.VITE_AUTH0_DOMAIN; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd05b85..858e3e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -194,6 +194,9 @@ importers: '@popperjs/core': specifier: ^2.11.6 version: 2.11.6 + '@types/react': + specifier: ^18.2.75 + version: 18.2.75 bootstrap: specifier: ^5.2.3 version: 5.2.3(@popperjs/core@2.11.6) @@ -214,7 +217,7 @@ importers: version: 18.2.0 react-bootstrap: specifier: ^2.2.3 - version: 2.7.2(react-dom@18.2.0)(react@18.2.0) + version: 2.7.2(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) @@ -2169,21 +2172,16 @@ packages: /@types/react-transition-group@4.4.5: resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} dependencies: - '@types/react': 18.0.28 + '@types/react': 18.2.75 dev: false - /@types/react@18.0.28: - resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} + /@types/react@18.2.75: + resolution: {integrity: sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==} dependencies: '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.2 csstype: 3.1.1 dev: false - /@types/scheduler@0.16.2: - resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} - dev: false - /@types/semver@7.3.13: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true @@ -6263,7 +6261,7 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 - /react-bootstrap@2.7.2(react-dom@18.2.0)(react@18.2.0): + /react-bootstrap@2.7.2(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-WDSln+mG4RLLFO01stkj2bEx/3MF4YihK9D/dWnHaSxOiQZLbhhlf95D2Jb20X3t2m7vMxRe888FVrfLJoGmmA==} peerDependencies: '@types/react': '>=16.14.8' @@ -6276,6 +6274,7 @@ packages: '@babel/runtime': 7.21.0 '@restart/hooks': 0.4.9(react@18.2.0) '@restart/ui': 1.6.2(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.75 '@types/react-transition-group': 4.4.5 classnames: 2.3.2 dom-helpers: 5.2.1 @@ -7346,7 +7345,7 @@ packages: react: '>=15.0.0' dependencies: '@babel/runtime': 7.21.0 - '@types/react': 18.0.28 + '@types/react': 18.2.75 invariant: 2.2.4 react: 18.2.0 react-lifecycles-compat: 3.0.4 @@ -7357,7 +7356,7 @@ packages: peerDependencies: react: '>=17.0.0' dependencies: - '@types/react': 18.0.28 + '@types/react': 18.2.75 react: 18.2.0 dev: false From 74d804543860fafd0a79db64a6f028e893469e73 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Tue, 9 Apr 2024 13:02:42 +0200 Subject: [PATCH 044/104] fix: added coverage exclusions in sonar-project.properties --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 895f2fc..edb0d90 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -4,4 +4,4 @@ sonar.projectName=HKrecruitment sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info sonar.sources=api/, frontend/, shared/ -sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md +sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md From 52999debe88ad0119b0a7b54344899e6d73514d5 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Tue, 9 Apr 2024 14:09:12 +0200 Subject: [PATCH 045/104] refactor: format according to prettier rules, reduced code duplication in timeslots.service.spec.ts --- api/src/availability/availability.module.ts | 6 +- api/src/mocks/data.ts | 16 ++-- .../recruitment-session.module.ts | 6 +- api/src/timeslots/timeslots.service.spec.ts | 83 ++++++++++--------- api/src/utils/db-aware-column.ts | 14 ++-- api/test/app.e2e-spec.ts | 2 +- shared/src/availability.spec.ts | 39 ++++++--- 7 files changed, 103 insertions(+), 63 deletions(-) diff --git a/api/src/availability/availability.module.ts b/api/src/availability/availability.module.ts index ac3273a..a120215 100644 --- a/api/src/availability/availability.module.ts +++ b/api/src/availability/availability.module.ts @@ -7,7 +7,11 @@ import { UsersModule } from 'src/users/users.module'; import { TimeSlotsModule } from 'src/timeslots/timeslots.module'; @Module({ - imports: [TypeOrmModule.forFeature([Availability]), UsersModule, TimeSlotsModule], + imports: [ + TypeOrmModule.forFeature([Availability]), + UsersModule, + TimeSlotsModule, + ], providers: [AvailabilityService], controllers: [AvailabilityController], exports: [AvailabilityService], diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index 86fdecc..64b1c36 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -12,13 +12,15 @@ import { import { CreateRecruitmentSessionDto } from 'src/recruitment-session/create-recruitment-session.dto'; import { UpdateRecruitmentSessionDto } from 'src/recruitment-session/update-recruitment-session.dto'; import { CreateAvailabilityDto } from 'src/availability/create-availability.dto'; -import { ApplicationType } from '@hkrecruitment/shared'; -import { ApplicationState } from '@hkrecruitment/shared'; -import { LangLevel } from '@hkrecruitment/shared'; -import { Role } from '@hkrecruitment/shared'; -import { AvailabilityState } from '@hkrecruitment/shared'; -import { TimeSlot } from '@hkrecruitment/shared'; - +import { + ApplicationType, + ApplicationState, + LangLevel, + Role, + AvailabilityState, + TimeSlot, +} from '@hkrecruitment/shared'; + export const testDate = new Date(2023, 0, 1, 10, 0, 0); export const testDateTimeStart = new Date(2023, 0, 1, 10, 30, 0); export const testDateTime10Minutes = new Date(2023, 0, 1, 10, 40, 0); diff --git a/api/src/recruitment-session/recruitment-session.module.ts b/api/src/recruitment-session/recruitment-session.module.ts index 714207a..48200b0 100644 --- a/api/src/recruitment-session/recruitment-session.module.ts +++ b/api/src/recruitment-session/recruitment-session.module.ts @@ -7,7 +7,11 @@ import { UsersModule } from 'src/users/users.module'; import { TimeSlotsModule } from 'src/timeslots/timeslots.module'; @Module({ - imports: [TypeOrmModule.forFeature([RecruitmentSession]), UsersModule, TimeSlotsModule], + imports: [ + TypeOrmModule.forFeature([RecruitmentSession]), + UsersModule, + TimeSlotsModule, + ], providers: [RecruitmentSessionService], controllers: [RecruitmentSessionController], exports: [RecruitmentSessionService], diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index 0fd8f78..6284724 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -118,7 +118,6 @@ describe('TimeSlotsService', () => { const interviewStart = new Date('2022-01-01T09:00:00'); const interviewEnd = new Date('2022-01-01T12:00:00'); const days = [new Date('2022-01-01'), new Date('2022-01-03')]; - const expectedTimeSlots: Partial[] = [ { start: new Date('2022-01-01T09:00:00'), @@ -144,66 +143,76 @@ describe('TimeSlotsService', () => { start: new Date('2022-01-03T11:00:00'), end: new Date('2022-01-03T12:00:00'), }, - ].map( - (timeSlot) => - ({ - ...timeSlot, - id: undefined, - availabilities: undefined, - } as TimeSlot), - ); - - const result = timeSlotService.generateTimeSlots( + ]; + testTimeSlotsGeneration( + timeSlotService, slotDuration, interviewStart, interviewEnd, days, + expectedTimeSlots, ); - - expect(result).toEqual(expectedTimeSlots); }); it("shouldn't generate time slots that overflow interviewEnd time", () => { mockDate.mockRestore(); const slotDuration = 50; - const interviewStart = new Date('2022-01-01T09:00:00'); - const interviewEnd = new Date('2022-01-01T11:00:00'); - const days = [new Date('2022-01-01'), new Date('2022-01-03')]; - + const interviewStart = new Date('2022-02-02T09:00:00'); + const interviewEnd = new Date('2022-02-02T11:00:00'); + const days = [new Date('2022-02-02'), new Date('2022-02-04')]; const expectedTimeSlots: Partial[] = [ { - start: new Date('2022-01-01T09:00:00'), - end: new Date('2022-01-01T09:50:00'), + start: new Date('2022-02-02T09:00:00'), + end: new Date('2022-02-02T09:50:00'), }, { - start: new Date('2022-01-03T09:00:00'), - end: new Date('2022-01-03T09:50:00'), + start: new Date('2022-02-04T09:00:00'), + end: new Date('2022-02-04T09:50:00'), }, { - start: new Date('2022-01-01T09:50:00'), - end: new Date('2022-01-01T10:40:00'), + start: new Date('2022-02-02T09:50:00'), + end: new Date('2022-02-02T10:40:00'), }, { - start: new Date('2022-01-03T09:50:00'), - end: new Date('2022-01-03T10:40:00'), + start: new Date('2022-02-04T09:50:00'), + end: new Date('2022-02-04T10:40:00'), }, - ].map( - (timeSlot) => - ({ - ...timeSlot, - id: undefined, - availabilities: undefined, - } as TimeSlot), - ); - - const result = timeSlotService.generateTimeSlots( + ]; + testTimeSlotsGeneration( + timeSlotService, slotDuration, interviewStart, interviewEnd, days, + expectedTimeSlots, ); - - expect(result).toEqual(expectedTimeSlots); }); }); }); + +function testTimeSlotsGeneration( + timeSlotService: TimeSlotsService, + slotDuration: number, + interviewStart: Date, + interviewEnd: Date, + days: Date[], + expectedResult: Partial[], +) { + const expectedTimeSlots: Partial[] = expectedResult.map( + (timeSlot) => + ({ + ...timeSlot, + id: undefined, + availabilities: undefined, + } as TimeSlot), + ); + + const result = timeSlotService.generateTimeSlots( + slotDuration, + interviewStart, + interviewEnd, + days, + ); + + expect(result).toEqual(expectedTimeSlots); +} diff --git a/api/src/utils/db-aware-column.ts b/api/src/utils/db-aware-column.ts index 1cac047..85aca90 100644 --- a/api/src/utils/db-aware-column.ts +++ b/api/src/utils/db-aware-column.ts @@ -4,13 +4,15 @@ import { Column, ColumnOptions } from 'typeorm'; * Decorator function that uses 'varchar' as the column type in the testing environment. * This is necessary as sqlite does not support custom classes as column types. * If not in a test environment, it returns a column with its original type. - * + * * @param type - The type of the column. * @param columnOptions - The options for the column. * @returns The column definition. */ -export function DbAwareColumn(type: (t?: any) => Function, columnOptions: ColumnOptions) { - if (process.env.NODE_ENV === 'test') - return Column('varchar', columnOptions); - return Column(type, columnOptions); -} \ No newline at end of file +export function DbAwareColumn( + type: (t?: any) => Function, + columnOptions: ColumnOptions, +) { + if (process.env.NODE_ENV === 'test') return Column('varchar', columnOptions); + return Column(type, columnOptions); +} diff --git a/api/test/app.e2e-spec.ts b/api/test/app.e2e-spec.ts index a67c64c..db63ae9 100644 --- a/api/test/app.e2e-spec.ts +++ b/api/test/app.e2e-spec.ts @@ -47,7 +47,7 @@ export const getAccessToken = async (key: string): Promise => { headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), }); - + const json = await response.json(); return json.access_token; diff --git a/shared/src/availability.spec.ts b/shared/src/availability.spec.ts index ac7b256..53f5939 100644 --- a/shared/src/availability.spec.ts +++ b/shared/src/availability.spec.ts @@ -24,9 +24,9 @@ const mockAvailability = { availabilities: [], }, user: { - firstName: 'John', - lastName: 'Doe', - oauthId: '123', + firstName: "John", + lastName: "Doe", + oauthId: "123", role: Role.Member, }, }; @@ -37,25 +37,45 @@ describe("Availability", () => { const mockAbility = mockAbilityForAvailability({ role, sub: "123" }); it(`should allow only Admin and Supervisors to update availabilities [${role}]`, () => { expect( - checkAbility(mockAbility, Action.Update, mockAvailability, "Availability") + checkAbility( + mockAbility, + Action.Update, + mockAvailability, + "Availability" + ) ).toBe([Role.Admin, Role.Supervisor].includes(role)); }); it(`should allow only HKN members to read, create, and delete availabilities [${role}]`, () => { expect( - checkAbility(mockAbility, Action.Read, mockAvailability, "Availability") + checkAbility( + mockAbility, + Action.Read, + mockAvailability, + "Availability" + ) ).toBe(![Role.Applicant, Role.None].includes(role)); expect( - checkAbility(mockAbility, Action.Create, mockAvailability, "Availability") + checkAbility( + mockAbility, + Action.Create, + mockAvailability, + "Availability" + ) ).toBe(![Role.Applicant, Role.None].includes(role)); expect( - checkAbility(mockAbility, Action.Delete, mockAvailability, "Availability") + checkAbility( + mockAbility, + Action.Delete, + mockAvailability, + "Availability" + ) ).toBe(![Role.Applicant, Role.None].includes(role)); }); }); }); describe("insertAvailabilitySchema", () => { - it('should allow creating a valid availability', () => { + it("should allow creating a valid availability", () => { const validAvailability = { timeSlotId: 1, }; @@ -64,7 +84,7 @@ describe("Availability", () => { expect(error).toBeUndefined(); }); - it('should not allow creating an availability without a timeSlotId', () => { + it("should not allow creating an availability without a timeSlotId", () => { const invalidAvailability = {}; const { error } = insertAvailabilitySchema.validate(invalidAvailability); @@ -72,4 +92,3 @@ describe("Availability", () => { }); }); }); - From 25610df92a393e49752f355e5a5b1d745a5c6cf2 Mon Sep 17 00:00:00 2001 From: Alberto Baroso Date: Thu, 11 Apr 2024 13:26:43 +0200 Subject: [PATCH 046/104] feat: User is_board and is_expert flags --- api/src/users/user.entity.ts | 6 ++++++ api/src/users/users.controller.spec.ts | 4 ++++ api/src/users/users.controller.ts | 2 ++ api/src/users/users.e2e-spec.ts | 10 ++++++++++ shared/src/person.ts | 2 ++ 5 files changed, 24 insertions(+) diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index eae431a..81bd96c 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -30,4 +30,10 @@ export class User implements Person { @OneToMany(() => Availability, (availability) => availability.user) availabilities?: Relation; + + @Column() + is_board: boolean; + + @Column() + is_expert: boolean; } diff --git a/api/src/users/users.controller.spec.ts b/api/src/users/users.controller.spec.ts index 90f5ca7..845f281 100644 --- a/api/src/users/users.controller.spec.ts +++ b/api/src/users/users.controller.spec.ts @@ -31,6 +31,8 @@ describe('UsersController', () => { sex: 'M', email: 'example@example.com', role: Role.Applicant, + is_board: false, + is_expert: false, }; const mockMember: Person = { @@ -40,6 +42,8 @@ describe('UsersController', () => { sex: 'F', email: 'jane@hknpolito.org', role: Role.Member, + is_board: false, + is_expert: false, }; const mockUsers = [mockApplicant, mockMember]; diff --git a/api/src/users/users.controller.ts b/api/src/users/users.controller.ts index d3af4d9..21e986b 100644 --- a/api/src/users/users.controller.ts +++ b/api/src/users/users.controller.ts @@ -96,6 +96,8 @@ export class UsersController { return this.usersService.create({ ...user, role: !!user.role ? user.role : defaultRole, + is_board: false, + is_expert: false, }); } diff --git a/api/src/users/users.e2e-spec.ts b/api/src/users/users.e2e-spec.ts index 2291a5a..06f35b3 100644 --- a/api/src/users/users.e2e-spec.ts +++ b/api/src/users/users.e2e-spec.ts @@ -27,6 +27,8 @@ describe('UsersController (e2e)', () => { sex: 'F', email: 'known-superuser-test@example.com', role: Role.Admin, + is_board: true, + is_expert: true, }, { oauthId: getSub(newApplicantToken), @@ -35,6 +37,8 @@ describe('UsersController (e2e)', () => { sex: 'F', email: 'test-applicant@example.com', role: Role.Applicant, + is_board: false, + is_expert: false, }, { oauthId: getSub(newMemberToken), @@ -43,6 +47,8 @@ describe('UsersController (e2e)', () => { sex: 'M', email: 'hknrecruitment-test@hknpolito.org', role: Role.Member, + is_board: false, + is_expert: true, }, ]; }); @@ -138,6 +144,8 @@ describe('UsersController (e2e)', () => { role: Role.Applicant, phone_no: null, telegramId: null, + is_board: false, + is_expert: false, }; await request(app.getHttpServer()) @@ -165,6 +173,8 @@ describe('UsersController (e2e)', () => { role: Role.Member, phone_no: null, telegramId: null, + is_board: false, + is_expert: false, }; await request(app.getHttpServer()) diff --git a/shared/src/person.ts b/shared/src/person.ts index b393cd4..ceeca23 100644 --- a/shared/src/person.ts +++ b/shared/src/person.ts @@ -19,6 +19,8 @@ export interface Person { phone_no?: string; telegramId?: string; role: Role; + is_board: boolean; + is_expert: boolean; } export const createUserSchema = Joi.object({ From 5ffb921fa665a7c4fdbb0b8f3ab77aa330da1f35 Mon Sep 17 00:00:00 2001 From: white Date: Wed, 1 May 2024 18:26:27 +0200 Subject: [PATCH 047/104] add: relations between User, Availabilities --- api/src/availability/availability.entity.ts | 3 +++ api/src/recruitment-session/recruitment-session.entity.ts | 8 +++++++- api/src/timeslots/timeslot.entity.ts | 8 ++++++++ api/src/users/user.entity.ts | 4 +++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index 384669e..a3e5f08 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -1,6 +1,7 @@ import { Column, Entity, + JoinColumn, ManyToOne, PrimaryGeneratedColumn, Relation, @@ -26,9 +27,11 @@ export class Availability implements AvailabilityInterface { @DbAwareColumn(() => TimeSlot, { name: 'time_slot' }) @ManyToOne(() => TimeSlot, (timeSlot) => timeSlot.availabilities) + @JoinColumn({ name: 'time_slot' }) timeSlot: Relation; @ManyToOne(() => User, (user) => user.availabilities) + @JoinColumn({ name: 'user_id' }) user: Relation; // @OneToOne(() => Interview) diff --git a/api/src/recruitment-session/recruitment-session.entity.ts b/api/src/recruitment-session/recruitment-session.entity.ts index df58326..6c0d65b 100644 --- a/api/src/recruitment-session/recruitment-session.entity.ts +++ b/api/src/recruitment-session/recruitment-session.entity.ts @@ -1,8 +1,9 @@ -import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, Entity, JoinColumn, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; import { RecruitmentSession as RecruitmentSessionInterface, RecruitmentSessionState, } from '@hkrecruitment/shared'; +import { TimeSlot } from 'src/timeslots/timeslot.entity'; @Entity() export class RecruitmentSession implements RecruitmentSessionInterface { @@ -29,4 +30,9 @@ export class RecruitmentSession implements RecruitmentSessionInterface { @Column('date', { name: 'last_modified' }) lastModified: Date; + + @OneToMany(() => TimeSlot, (timeSlot) => timeSlot.recruitmentSession) + @JoinColumn({ name: 'time_slots' }) + timeSlots: TimeSlot[]; + } diff --git a/api/src/timeslots/timeslot.entity.ts b/api/src/timeslots/timeslot.entity.ts index 54911ca..dadccae 100644 --- a/api/src/timeslots/timeslot.entity.ts +++ b/api/src/timeslots/timeslot.entity.ts @@ -1,12 +1,15 @@ import { Column, Entity, + JoinColumn, + ManyToOne, OneToMany, PrimaryGeneratedColumn, Relation, } from 'typeorm'; import { TimeSlot as TimeSlotInterface } from '@hkrecruitment/shared'; import { Availability } from 'src/availability/availability.entity'; +import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; @Entity() export class TimeSlot implements TimeSlotInterface { @@ -20,5 +23,10 @@ export class TimeSlot implements TimeSlotInterface { end: Date; @OneToMany(() => Availability, (availability) => availability.timeSlot) + @JoinColumn({ name: 'availabilities' }) availabilities: Relation; + + @ManyToOne(() => RecruitmentSession, (recruitmentSession) => recruitmentSession.timeSlots) + @JoinColumn({ name: 'recruitment_session' }) + recruitmentSession: Relation; } diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index 81bd96c..a9b5ccc 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -1,6 +1,7 @@ -import { Column, Entity, OneToMany, PrimaryColumn, Relation } from 'typeorm'; +import { Column, Entity, OneToMany, PrimaryColumn, Relation, JoinColumn } from 'typeorm'; import { Person, Role } from '@hkrecruitment/shared'; import { Availability } from 'src/availability/availability.entity'; +import Joi from 'joi'; @Entity() export class User implements Person { @@ -29,6 +30,7 @@ export class User implements Person { role: Role; @OneToMany(() => Availability, (availability) => availability.user) + @JoinColumn({ name: 'availabilities' }) availabilities?: Relation; @Column() From 8036e539c03e37263b55fb395089a67bdd899e45 Mon Sep 17 00:00:00 2001 From: Vincenzo Pellegrini Date: Sun, 12 May 2024 20:54:01 +0200 Subject: [PATCH 048/104] Chore: update dependencies --- api/package.json | 86 +- frontend/package.json | 31 +- pnpm-lock.yaml | 3896 ++++++++++++++++++++++------------------- shared/package.json | 14 +- 4 files changed, 2131 insertions(+), 1896 deletions(-) diff --git a/api/package.json b/api/package.json index 9dcef79..773c168 100644 --- a/api/package.json +++ b/api/package.json @@ -22,63 +22,63 @@ "clean": "rimraf dist" }, "dependencies": { - "@casl/ability": "^6.3.3", - "@fastify/static": "^6.6.0", + "@casl/ability": "^6.7.1", + "@fastify/static": "^6.12.0", "@hkrecruitment/shared": "workspace:*", - "@joi/date": "^2.1.0", - "@nestjs/common": "^9.0.0", - "@nestjs/config": "^2.2.0", - "@nestjs/core": "^9.0.0", - "@nestjs/passport": "^9.0.0", - "@nestjs/platform-express": "^9.0.0", - "@nestjs/swagger": "^6.1.3", + "@joi/date": "^2.1.1", + "@nestjs/common": "^9.4.3", + "@nestjs/config": "^2.3.4", + "@nestjs/core": "^9.4.3", + "@nestjs/passport": "^9.0.3", + "@nestjs/platform-express": "^9.4.3", + "@nestjs/swagger": "^6.3.0", "@nestjs/typeorm": "^9.0.1", - "@types/js-yaml": "^4.0.5", - "@types/passport-jwt": "^3.0.7", + "@types/js-yaml": "^4.0.9", + "@types/passport-jwt": "^3.0.13", "class-transformer": "^0.5.1", - "dotenv": "^16.0.3", - "google-auth-library": "^8.7.0", + "dotenv": "^16.4.5", + "google-auth-library": "^8.9.0", "googleapis": "^118.0.0", - "jest-mock-extended": "^3.0.5", - "joi": "^17.7.0", + "jest-mock-extended": "^3.0.7", + "joi": "^17.13.1", "js-yaml": "^4.1.0", - "jwks-rsa": "^3.0.0", + "jwks-rsa": "^3.1.0", "passport": "^0.6.0", - "passport-jwt": "^4.0.0", - "pg": "^8.4.0", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", + "passport-jwt": "^4.0.1", + "pg": "^8.11.5", + "reflect-metadata": "^0.1.14", + "rxjs": "^7.8.1", "typeorm": "0.3.11", - "webpack": "^5.75.0" + "webpack": "^5.91.0" }, "devDependencies": { - "@automock/jest": "^1.0.1", - "@golevelup/ts-jest": "^0.3.6", - "@nestjs/cli": "^9.0.0", - "@nestjs/schematics": "^9.0.0", - "@nestjs/testing": "^9.0.0", - "@swc/core": "^1.3.56", - "@swc/jest": "^0.2.26", - "@types/express": "^4.17.14", + "@automock/jest": "^1.4.0", + "@golevelup/ts-jest": "^0.3.8", + "@nestjs/cli": "^9.5.0", + "@nestjs/schematics": "^9.2.0", + "@nestjs/testing": "^9.4.3", + "@swc/core": "^1.5.5", + "@swc/jest": "^0.2.36", + "@types/express": "^4.17.21", "@types/jest": "28.1.8", - "@types/multer": "^1.4.7", - "@types/node": "^16.11.10", - "@types/supertest": "^2.0.11", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^8.0.1", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^4.0.0", + "@types/multer": "^1.4.11", + "@types/node": "^16.18.97", + "@types/supertest": "^2.0.16", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^8.10.0", + "eslint-plugin-prettier": "^4.2.1", "jest": "28.1.3", - "jose": "^4.14.4", + "jose": "^4.15.5", "nodemon": "^2.0.22", - "prettier": "^2.8.5", + "prettier": "^2.8.8", "rimraf": "^3.0.2", - "source-map-support": "^0.5.20", - "sqlite3": "^5.1.6", - "supertest": "^6.1.3", + "source-map-support": "^0.5.21", + "sqlite3": "^5.1.7", + "supertest": "^6.3.4", "ts-jest": "28.0.8", - "ts-loader": "^9.2.3", + "ts-loader": "^9.5.1", "ts-node": "10.7.0", "tsconfig-paths": "4.1.0", "typescript": "4.5.2" diff --git a/frontend/package.json b/frontend/package.json index 8dffd35..32fd132 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,20 +4,21 @@ "private": true, "type": "module", "dependencies": { - "@auth0/auth0-react": "^1.12.0", + "@auth0/auth0-react": "^1.12.1", "@hkrecruitment/shared": "workspace:*", - "@popperjs/core": "^2.11.6", - "@types/react": "^18.2.75", - "bootstrap": "^5.2.3", + "@popperjs/core": "^2.11.8", + "@types/react": "^18.3.2", + "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", - "moment": "^2.29.3", - "moment-timezone": "^0.5.34", - "prop-types": "^15.7.0", - "react": "^18.2.0", - "react-bootstrap": "^2.2.3", - "react-dom": "^18.2.0", + "moment": "^2.30.1", + "moment-timezone": "^0.5.45", + "prop-types": "^15.8.1", + "react": "^18.3.1", + "react-bootstrap": "^2.10.2", + "react-dom": "^18.3.1", + "react-hook-form": "^7.51.4", "react-moment": "^1.1.3", - "react-router-dom": "^6.9.0", + "react-router-dom": "^6.23.1", "web-vitals": "^2.1.4" }, "scripts": { @@ -48,10 +49,10 @@ }, "devDependencies": { "@vitejs/plugin-react": "^3.1.0", - "prettier": "^2.8.5", + "prettier": "^2.8.8", "rimraf": "^3.0.2", - "typescript": "^4.6.4", - "vite": "^4.2.1", - "vite-plugin-html": "^3.2.0" + "typescript": "^4.9.5", + "vite": "^4.5.3", + "vite-plugin-html": "^3.2.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 550644d..1fbd3a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,174 +11,174 @@ importers: api: dependencies: '@casl/ability': - specifier: ^6.3.3 - version: 6.3.3 + specifier: ^6.7.1 + version: 6.7.1 '@fastify/static': - specifier: ^6.6.0 - version: 6.6.0 + specifier: ^6.12.0 + version: 6.12.0 '@hkrecruitment/shared': specifier: workspace:* version: link:../shared '@joi/date': - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^2.1.1 + version: 2.1.1 '@nestjs/common': - specifier: ^9.0.0 - version: 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) + specifier: ^9.4.3 + version: 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/config': - specifier: ^2.2.0 - version: 2.2.0(@nestjs/common@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) + specifier: ^2.3.4 + version: 2.3.4(@nestjs/common@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/core': - specifier: ^9.0.0 - version: 9.2.1(@nestjs/common@9.2.1)(@nestjs/platform-express@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) + specifier: ^9.4.3 + version: 9.4.3(@nestjs/common@9.4.3)(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/passport': - specifier: ^9.0.0 - version: 9.0.0(@nestjs/common@9.2.1)(passport@0.6.0) + specifier: ^9.0.3 + version: 9.0.3(@nestjs/common@9.4.3)(passport@0.6.0) '@nestjs/platform-express': - specifier: ^9.0.0 - version: 9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1) + specifier: ^9.4.3 + version: 9.4.3(@nestjs/common@9.4.3)(@nestjs/core@9.4.3) '@nestjs/swagger': - specifier: ^6.1.3 - version: 6.1.3(@fastify/static@6.6.0)(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(class-transformer@0.5.1)(reflect-metadata@0.1.13) + specifier: ^6.3.0 + version: 6.3.0(@fastify/static@6.12.0)(@nestjs/common@9.4.3)(@nestjs/core@9.4.3)(class-transformer@0.5.1)(reflect-metadata@0.1.14) '@nestjs/typeorm': specifier: ^9.0.1 - version: 9.0.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7)(typeorm@0.3.11) + version: 9.0.1(@nestjs/common@9.4.3)(@nestjs/core@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.11) '@types/js-yaml': - specifier: ^4.0.5 - version: 4.0.5 + specifier: ^4.0.9 + version: 4.0.9 '@types/passport-jwt': - specifier: ^3.0.7 - version: 3.0.7 + specifier: ^3.0.13 + version: 3.0.13 class-transformer: specifier: ^0.5.1 version: 0.5.1 dotenv: - specifier: ^16.0.3 - version: 16.0.3 + specifier: ^16.4.5 + version: 16.4.5 google-auth-library: - specifier: ^8.7.0 - version: 8.7.0 + specifier: ^8.9.0 + version: 8.9.0 googleapis: specifier: ^118.0.0 version: 118.0.0 jest-mock-extended: - specifier: ^3.0.5 - version: 3.0.5(jest@28.1.3)(typescript@4.5.2) + specifier: ^3.0.7 + version: 3.0.7(jest@28.1.3)(typescript@4.5.2) joi: - specifier: ^17.7.0 - version: 17.7.0 + specifier: ^17.13.1 + version: 17.13.1 js-yaml: specifier: ^4.1.0 version: 4.1.0 jwks-rsa: - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^3.1.0 + version: 3.1.0 passport: specifier: ^0.6.0 version: 0.6.0 passport-jwt: - specifier: ^4.0.0 - version: 4.0.0 + specifier: ^4.0.1 + version: 4.0.1 pg: - specifier: ^8.4.0 - version: 8.8.0 + specifier: ^8.11.5 + version: 8.11.5 reflect-metadata: - specifier: ^0.1.13 - version: 0.1.13 + specifier: ^0.1.14 + version: 0.1.14 rxjs: - specifier: ^7.2.0 - version: 7.5.7 + specifier: ^7.8.1 + version: 7.8.1 typeorm: specifier: 0.3.11 - version: 0.3.11(pg@8.8.0)(sqlite3@5.1.6)(ts-node@10.7.0) + version: 0.3.11(pg@8.11.5)(sqlite3@5.1.7)(ts-node@10.7.0) webpack: - specifier: ^5.75.0 - version: 5.75.0(@swc/core@1.3.56) + specifier: ^5.91.0 + version: 5.91.0(@swc/core@1.5.5) devDependencies: '@automock/jest': - specifier: ^1.0.1 - version: 1.0.1(jest@28.1.3)(typescript@4.5.2) + specifier: ^1.4.0 + version: 1.4.0(@nestjs/common@9.4.3)(jest@28.1.3)(typescript@4.5.2) '@golevelup/ts-jest': - specifier: ^0.3.6 - version: 0.3.6 + specifier: ^0.3.8 + version: 0.3.8 '@nestjs/cli': - specifier: ^9.0.0 - version: 9.1.5(@swc/core@1.3.56) + specifier: ^9.5.0 + version: 9.5.0(@swc/core@1.5.5) '@nestjs/schematics': - specifier: ^9.0.0 - version: 9.0.3(typescript@4.5.2) + specifier: ^9.2.0 + version: 9.2.0(typescript@4.5.2) '@nestjs/testing': - specifier: ^9.0.0 - version: 9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(@nestjs/platform-express@9.2.1) + specifier: ^9.4.3 + version: 9.4.3(@nestjs/common@9.4.3)(@nestjs/core@9.4.3)(@nestjs/platform-express@9.4.3) '@swc/core': - specifier: ^1.3.56 - version: 1.3.56 + specifier: ^1.5.5 + version: 1.5.5 '@swc/jest': - specifier: ^0.2.26 - version: 0.2.26(@swc/core@1.3.56) + specifier: ^0.2.36 + version: 0.2.36(@swc/core@1.5.5) '@types/express': - specifier: ^4.17.14 - version: 4.17.14 + specifier: ^4.17.21 + version: 4.17.21 '@types/jest': specifier: 28.1.8 version: 28.1.8 '@types/multer': - specifier: ^1.4.7 - version: 1.4.7 + specifier: ^1.4.11 + version: 1.4.11 '@types/node': - specifier: ^16.11.10 - version: 16.18.4 + specifier: ^16.18.97 + version: 16.18.97 '@types/supertest': - specifier: ^2.0.11 - version: 2.0.12 + specifier: ^2.0.16 + version: 2.0.16 '@typescript-eslint/eslint-plugin': - specifier: ^5.0.0 - version: 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.29.0)(typescript@4.5.2) + specifier: ^5.62.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@4.5.2) '@typescript-eslint/parser': - specifier: ^5.0.0 - version: 5.45.0(eslint@8.29.0)(typescript@4.5.2) + specifier: ^5.62.0 + version: 5.62.0(eslint@8.57.0)(typescript@4.5.2) eslint: - specifier: ^8.0.1 - version: 8.29.0 + specifier: ^8.57.0 + version: 8.57.0 eslint-config-prettier: - specifier: ^8.3.0 - version: 8.5.0(eslint@8.29.0) + specifier: ^8.10.0 + version: 8.10.0(eslint@8.57.0) eslint-plugin-prettier: - specifier: ^4.0.0 - version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.29.0)(prettier@2.8.8) + specifier: ^4.2.1 + version: 4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@2.8.8) jest: specifier: 28.1.3 - version: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + version: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) jose: - specifier: ^4.14.4 - version: 4.14.4 + specifier: ^4.15.5 + version: 4.15.5 nodemon: specifier: ^2.0.22 version: 2.0.22 prettier: - specifier: ^2.8.5 + specifier: ^2.8.8 version: 2.8.8 rimraf: specifier: ^3.0.2 version: 3.0.2 source-map-support: - specifier: ^0.5.20 + specifier: ^0.5.21 version: 0.5.21 sqlite3: - specifier: ^5.1.6 - version: 5.1.6 + specifier: ^5.1.7 + version: 5.1.7 supertest: - specifier: ^6.1.3 - version: 6.3.2 + specifier: ^6.3.4 + version: 6.3.4 ts-jest: specifier: 28.0.8 - version: 28.0.8(@babel/core@7.21.3)(jest@28.1.3)(typescript@4.5.2) + version: 28.0.8(@babel/core@7.24.5)(jest@28.1.3)(typescript@4.5.2) ts-loader: - specifier: ^9.2.3 - version: 9.4.2(typescript@4.5.2)(webpack@5.75.0) + specifier: ^9.5.1 + version: 9.5.1(typescript@4.5.2)(webpack@5.91.0) ts-node: specifier: 10.7.0 - version: 10.7.0(@swc/core@1.3.56)(@types/node@16.18.4)(typescript@4.5.2) + version: 10.7.0(@swc/core@1.5.5)(@types/node@16.18.97)(typescript@4.5.2) tsconfig-paths: specifier: 4.1.0 version: 4.1.0 @@ -189,154 +189,140 @@ importers: frontend: dependencies: '@auth0/auth0-react': - specifier: ^1.12.0 - version: 1.12.0(react-dom@18.2.0)(react@18.2.0) + specifier: ^1.12.1 + version: 1.12.1(react-dom@18.3.1)(react@18.3.1) '@hkrecruitment/shared': specifier: workspace:* version: link:../shared '@popperjs/core': - specifier: ^2.11.6 - version: 2.11.6 + specifier: ^2.11.8 + version: 2.11.8 '@types/react': - specifier: ^18.2.75 - version: 18.2.75 + specifier: ^18.3.2 + version: 18.3.2 bootstrap: - specifier: ^5.2.3 - version: 5.2.3(@popperjs/core@2.11.6) + specifier: ^5.3.3 + version: 5.3.3(@popperjs/core@2.11.8) bootstrap-icons: specifier: ^1.11.3 version: 1.11.3 moment: - specifier: ^2.29.3 - version: 2.29.4 + specifier: ^2.30.1 + version: 2.30.1 moment-timezone: - specifier: ^0.5.34 - version: 0.5.41 + specifier: ^0.5.45 + version: 0.5.45 prop-types: - specifier: ^15.7.0 + specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-bootstrap: - specifier: ^2.2.3 - version: 2.7.2(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0) + specifier: ^2.10.2 + version: 2.10.2(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1) react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-hook-form: + specifier: ^7.51.4 + version: 7.51.4(react@18.3.1) react-moment: specifier: ^1.1.3 - version: 1.1.3(moment@2.29.4)(prop-types@15.8.1)(react@18.2.0) + version: 1.1.3(moment@2.30.1)(prop-types@15.8.1)(react@18.3.1) react-router-dom: - specifier: ^6.9.0 - version: 6.9.0(react-dom@18.2.0)(react@18.2.0) + specifier: ^6.23.1 + version: 6.23.1(react-dom@18.3.1)(react@18.3.1) web-vitals: specifier: ^2.1.4 version: 2.1.4 devDependencies: '@vitejs/plugin-react': specifier: ^3.1.0 - version: 3.1.0(vite@4.2.1) + version: 3.1.0(vite@4.5.3) prettier: - specifier: ^2.8.5 + specifier: ^2.8.8 version: 2.8.8 rimraf: specifier: ^3.0.2 version: 3.0.2 typescript: - specifier: ^4.6.4 - version: 4.9.3 + specifier: ^4.9.5 + version: 4.9.5 vite: - specifier: ^4.2.1 - version: 4.2.1 + specifier: ^4.5.3 + version: 4.5.3 vite-plugin-html: - specifier: ^3.2.0 - version: 3.2.0(vite@4.2.1) + specifier: ^3.2.2 + version: 3.2.2(vite@4.5.3) shared: dependencies: '@casl/ability': - specifier: ^6.3.3 - version: 6.3.3 + specifier: ^6.7.1 + version: 6.7.1 '@joi/date': - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^2.1.1 + version: 2.1.1 joi: - specifier: ^17.7.0 - version: 17.7.0 + specifier: ^17.13.1 + version: 17.13.1 devDependencies: '@swc/core': - specifier: ^1.3.56 - version: 1.3.56 + specifier: ^1.5.5 + version: 1.5.5 '@swc/jest': - specifier: ^0.2.26 - version: 0.2.26(@swc/core@1.3.56) + specifier: ^0.2.36 + version: 0.2.36(@swc/core@1.5.5) '@types/jest': specifier: 28.1.8 version: 28.1.8 jest: specifier: 28.1.3 - version: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + version: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) prettier: - specifier: ^2.8.5 + specifier: ^2.8.8 version: 2.8.8 ts-jest: specifier: 28.0.8 - version: 28.0.8(@babel/core@7.21.3)(jest@28.1.3)(typescript@4.9.3) + version: 28.0.8(@babel/core@7.24.5)(jest@28.1.3)(typescript@4.9.5) typescript: - specifier: ^4.9.3 - version: 4.9.3 + specifier: ^4.9.5 + version: 4.9.5 packages: - /@ampproject/remapping@2.2.0: - resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 - /@angular-devkit/core@14.2.1(chokidar@3.5.3): - resolution: {integrity: sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==} - engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + /@angular-devkit/core@16.0.1(chokidar@3.5.3): + resolution: {integrity: sha512-2uz98IqkKJlgnHbWQ7VeL4pb+snGAZXIama2KXi+k9GsRntdcw+udX8rL3G9SdUGUF+m6+147Y1oRBMHsO/v4w==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^3.5.2 peerDependenciesMeta: chokidar: optional: true dependencies: - ajv: 8.11.0 - ajv-formats: 2.1.1(ajv@8.11.0) + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) chokidar: 3.5.3 - jsonc-parser: 3.1.0 - rxjs: 6.6.7 - source-map: 0.7.4 - dev: true - - /@angular-devkit/core@14.2.2(chokidar@3.5.3): - resolution: {integrity: sha512-ofDhTmJqoAkmkJP0duwUaCxDBMxPlc+AWYwgs3rKKZeJBb0d+tchEXHXevD5bYbbRfXtnwM+Vye2XYHhA4nWAA==} - engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - chokidar: ^3.5.2 - peerDependenciesMeta: - chokidar: - optional: true - dependencies: - ajv: 8.11.0 - ajv-formats: 2.1.1(ajv@8.11.0) - chokidar: 3.5.3 - jsonc-parser: 3.1.0 - rxjs: 6.6.7 + jsonc-parser: 3.2.0 + rxjs: 7.8.1 source-map: 0.7.4 dev: true - /@angular-devkit/schematics-cli@14.2.2(chokidar@3.5.3): - resolution: {integrity: sha512-timCty5tO1A5VOcy8nVJ+jL98i6+ct5/Hg+4rQxc3J6agmmNL9fALboJBEz1ckTt7MewlGtrpohMMy+YGhuWOg==} - engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + /@angular-devkit/schematics-cli@16.0.1(chokidar@3.5.3): + resolution: {integrity: sha512-6KLA125dpgd6oJGtiO2JpZAb92uOG3njQGIt7NFcuQGW/5GO7J41vMXH9cBAfdtbV8SIggSmR/cIEE9ijfj6YQ==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true dependencies: - '@angular-devkit/core': 14.2.2(chokidar@3.5.3) - '@angular-devkit/schematics': 14.2.2(chokidar@3.5.3) + '@angular-devkit/core': 16.0.1(chokidar@3.5.3) + '@angular-devkit/schematics': 16.0.1(chokidar@3.5.3) ansi-colors: 4.1.3 inquirer: 8.2.4 symbol-observable: 4.0.0 @@ -345,270 +331,219 @@ packages: - chokidar dev: true - /@angular-devkit/schematics@14.2.1(chokidar@3.5.3): - resolution: {integrity: sha512-0U18FwDYt4zROBPrvewH6iBTkf2ozVHN4/gxUb9jWrqVw8mPU5AWc/iYxQLHBSinkr2Egjo1H/i9aBqgJSeh3g==} - engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - dependencies: - '@angular-devkit/core': 14.2.1(chokidar@3.5.3) - jsonc-parser: 3.1.0 - magic-string: 0.26.2 - ora: 5.4.1 - rxjs: 6.6.7 - transitivePeerDependencies: - - chokidar - dev: true - - /@angular-devkit/schematics@14.2.2(chokidar@3.5.3): - resolution: {integrity: sha512-90hseNg1yQ2AR+lVr/NByZRHnYAlzCL6hr9p9q1KPHxA3Owo04yX6n6dvR/xf27hCopXInXKPsasR59XCx5ZOQ==} - engines: {node: ^14.15.0 || >=16.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + /@angular-devkit/schematics@16.0.1(chokidar@3.5.3): + resolution: {integrity: sha512-A9D0LTYmiqiBa90GKcSuWb7hUouGIbm/AHbJbjL85WLLRbQA2PwKl7P5Mpd6nS/ZC0kfG4VQY3VOaDvb3qpI9g==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} dependencies: - '@angular-devkit/core': 14.2.2(chokidar@3.5.3) - jsonc-parser: 3.1.0 - magic-string: 0.26.2 + '@angular-devkit/core': 16.0.1(chokidar@3.5.3) + jsonc-parser: 3.2.0 + magic-string: 0.30.0 ora: 5.4.1 - rxjs: 6.6.7 + rxjs: 7.8.1 transitivePeerDependencies: - chokidar dev: true - /@auth0/auth0-react@1.12.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Cny2RyHvr0GrKKKV8PMh6GU0vkWNSgd6mp/YHYJynnYCs9yFduNo9hdpHPxXbdDX5CB6wc2PqK6aL8leDlnl/A==} + /@auth0/auth0-react@1.12.1(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-8+ecK/4rE0AGsxLW2IDcr1oPbT55tuE6cQEzEIOkQjB6QGQxxWMzQy0D4nMKw3JUAc7nYcFVOABNFNbc471n9Q==} peerDependencies: react: ^16.11.0 || ^17 || ^18 react-dom: ^16.11.0 || ^17 || ^18 dependencies: - '@auth0/auth0-spa-js': 1.22.5 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@auth0/auth0-spa-js': 1.22.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@auth0/auth0-spa-js@1.22.5: - resolution: {integrity: sha512-6gaQcd+Eb8ZBcdQkrrm9undM7dY/rPvVdQN8s7rxxrviUCs7OopEygsfSkHf67IP4HtlCiE8dSW5/AipRUOw/A==} + /@auth0/auth0-spa-js@1.22.6: + resolution: {integrity: sha512-iL3O0vWanfKFVgy1J2ZHDPlAUK6EVHWEHWS6mUXwHEuPiK39tjlQtyUKQIJI1F5YsZB75ijGgRWMTawSDXlwCA==} dependencies: abortcontroller-polyfill: 1.7.5 - browser-tabs-lock: 1.2.15 - core-js: 3.26.1 + browser-tabs-lock: 1.3.0 + core-js: 3.37.0 es-cookie: 1.3.2 fast-text-encoding: 1.0.6 - promise-polyfill: 8.2.3 + promise-polyfill: 8.3.0 unfetch: 4.2.0 dev: false - /@automock/jest@1.0.1(jest@28.1.3)(typescript@4.5.2): - resolution: {integrity: sha512-ig9Kx6eaQfpjRiiz8nU1P+t9rw+OsorNfSz160i/QxIHKRnZSopD1hCrxJtottluIbrOEkozKCKYwDw2O+oqKw==} + /@automock/adapters.nestjs@1.4.0(@nestjs/common@9.4.3): + resolution: {integrity: sha512-1BEh9glv23P1SdCrQ3fA7+mCgoRRiDXoHgtuCG2LUFSj6HdZ7unV/U6BGzKLabi017L2C8zOCelYQnMUdCMaGg==} + peerDependencies: + '@nestjs/common': '>= 8.x' + dependencies: + '@automock/common': 2.2.0 + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + dev: true + + /@automock/common@2.2.0: + resolution: {integrity: sha512-aAzMrOIDrhCoWutichpFcSC2TK+tr1tZTq1qxGXZVKpGILtJs/X5/cOOVkUr1AEOsfhW4rHVC4IroIsANn9ZEA==} + dev: true + + /@automock/core@1.4.0: + resolution: {integrity: sha512-okDvKznSAt4Ly3D3RBKCbZ0JYD1YwJ4zFaKv2SUmbIL6m/kEk/cVXvNLspOIyxd1r8WVwhTLafiL9VPvYXkZsg==} + dev: true + + /@automock/jest@1.4.0(@nestjs/common@9.4.3)(jest@28.1.3)(typescript@4.5.2): + resolution: {integrity: sha512-2+yjdWLXMzuH6vrjX4Us2FzuPOQc/ma2D25FnS2sVtcfhHgVauexNo+W3Jq90l1al93YkSE/ULzlVMkqQcGPfA==} + peerDependencies: + jest: '>= 25.x' dependencies: + '@automock/adapters.nestjs': 1.4.0(@nestjs/common@9.4.3) + '@automock/core': 1.4.0 + '@automock/types': 1.2.0 + jest: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) jest-mock-extended: 2.0.9(jest@28.1.3)(typescript@4.5.2) - ts-essentials: 7.0.3(typescript@4.5.2) transitivePeerDependencies: - - jest + - '@nestjs/common' - typescript dev: true + /@automock/types@1.2.0: + resolution: {integrity: sha512-kkGf1PvAb8aLjCXyX4OsIncQFTVAwCQUtsEpsbyH6hhpJ6ape8Lhor9Y5DgunajhuD4oE6D64+4jxSuRSwWNmQ==} + dev: true + /@babel/code-frame@7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.18.6 - /@babel/compat-data@7.20.5: - resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==} + /@babel/code-frame@7.24.2: + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.24.5 + picocolors: 1.0.0 - /@babel/core@7.20.5: - resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==} + /@babel/compat-data@7.24.4: + resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) - '@babel/helper-module-transforms': 7.20.2 - '@babel/helpers': 7.20.6 - '@babel/parser': 7.20.5 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - /@babel/core@7.21.3: - resolution: {integrity: sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==} + /@babel/core@7.24.5: + resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.3 - '@babel/helper-compilation-targets': 7.20.7(@babel/core@7.21.3) - '@babel/helper-module-transforms': 7.21.2 - '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.3 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - convert-source-map: 1.9.0 + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.5 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helpers': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 + convert-source-map: 2.0.0 debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color - /@babel/generator@7.20.5: - resolution: {integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==} + /@babel/generator@7.24.5: + resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 - '@jridgewell/gen-mapping': 0.3.2 + '@babel/types': 7.24.5 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - /@babel/generator@7.21.3: - resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==} + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.21.3 - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 - jsesc: 2.5.2 - - /@babel/helper-compilation-targets@7.20.0(@babel/core@7.20.5): - resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.4 - semver: 6.3.0 - - /@babel/helper-compilation-targets@7.20.7(@babel/core@7.21.3): - resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.21.3 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.5 + '@babel/compat-data': 7.24.4 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 lru-cache: 5.1.1 - semver: 6.3.0 + semver: 6.3.1 - /@babel/helper-environment-visitor@7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} - /@babel/helper-function-name@7.19.0: - resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.20.5 - - /@babel/helper-function-name@7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.3 - - /@babel/helper-hoist-variables@7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.20.5 + '@babel/template': 7.24.0 + '@babel/types': 7.24.5 - /@babel/helper-module-imports@7.18.6: - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 - /@babel/helper-module-transforms@7.20.2: - resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==} + /@babel/helper-module-imports@7.24.3: + resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.20.2 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 - transitivePeerDependencies: - - supports-color + '@babel/types': 7.24.5 - /@babel/helper-module-transforms@7.21.2: - resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} + /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.20.2 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.24.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.24.3 + '@babel/helper-simple-access': 7.24.5 + '@babel/helper-split-export-declaration': 7.24.5 + '@babel/helper-validator-identifier': 7.24.5 /@babel/helper-plugin-utils@7.20.2: resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} engines: {node: '>=6.9.0'} - /@babel/helper-simple-access@7.20.2: - resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + /@babel/helper-plugin-utils@7.24.5: + resolution: {integrity: sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==} + engines: {node: '>=6.9.0'} + + /@babel/helper-simple-access@7.24.5: + resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + /@babel/helper-split-export-declaration@7.24.5: + resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} + /@babel/helper-string-parser@7.24.1: + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + /@babel/helper-validator-identifier@7.24.5: + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} - /@babel/helpers@7.20.6: - resolution: {integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==} + /@babel/helpers@7.24.5: + resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 - transitivePeerDependencies: - - supports-color - - /@babel/helpers@7.21.0: - resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 transitivePeerDependencies: - supports-color @@ -620,6 +555,15 @@ packages: chalk: 2.4.2 js-tokens: 4.0.0 + /@babel/highlight@7.24.5: + resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.24.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 + /@babel/parser@7.20.5: resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==} engines: {node: '>=6.0.0'} @@ -627,191 +571,174 @@ packages: dependencies: '@babel/types': 7.20.5 - /@babel/parser@7.21.3: - resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} + /@babel/parser@7.24.5: + resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.21.3 + '@babel/types': 7.24.5 - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.3): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.5): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.3): + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.3): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.5): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.3): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.3): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.3): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.3): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.3): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.3): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.3): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.3): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.3): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.5): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.21.3): - resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} + /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.5): + resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 - /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.3): - resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} + /@babel/plugin-transform-react-jsx-self@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-RtCJoUO2oYrYwFPtR1/jkoBEcFuI1ae9a9IMxeyAVa3a1Ap4AnxmyIKG2b2FaJKqkidw/0cxRbWN+HOs6ZWd1w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.21.3): - resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} + /@babel/plugin-transform-react-jsx-source@7.24.1(@babel/core@7.24.5): + resolution: {integrity: sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.21.3 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/runtime@7.21.0: - resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} + /@babel/runtime@7.24.5: + resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} engines: {node: '>=6.9.0'} dependencies: - regenerator-runtime: 0.13.11 + regenerator-runtime: 0.14.1 dev: false /@babel/template@7.18.10: resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.24.2 '@babel/parser': 7.20.5 '@babel/types': 7.20.5 - /@babel/template@7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - - /@babel/traverse@7.20.5: - resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==} + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/code-frame': 7.24.2 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 - /@babel/traverse@7.21.3: - resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} + /@babel/traverse@7.24.5: + resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.3 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: @@ -825,21 +752,21 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - /@babel/types@7.21.3: - resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} + /@babel/types@7.24.5: + resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 + '@babel/helper-string-parser': 7.24.1 + '@babel/helper-validator-identifier': 7.24.5 to-fast-properties: 2.0.0 /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - /@casl/ability@6.3.3: - resolution: {integrity: sha512-UzbqsE9etu6QzZrRmqIyVun2kztAzJ46Tz7lC/2P2buCE6B6Ll7Vptz7JTQtGwapLbeKo2jS7dL966TVOQ7x4g==} + /@casl/ability@6.7.1: + resolution: {integrity: sha512-e+Vgrehd1/lzOSwSqKHtmJ6kmIuZbGBlM2LBS5IuYGGKmVHuhUuyh3XgTn1VIw9+TO4gqU+uptvxfIRBUEdJuw==} dependencies: - '@ucast/mongo2js': 1.3.3 + '@ucast/mongo2js': 1.3.4 dev: false /@colors/colors@1.5.0: @@ -859,8 +786,8 @@ packages: dependencies: '@cspotcode/source-map-consumer': 0.8.0 - /@esbuild/android-arm64@0.17.12: - resolution: {integrity: sha512-WQ9p5oiXXYJ33F2EkE3r0FRDFVpEdcDiwNX3u7Xaibxfx6vQE0Sb8ytrfQsA5WO6kDn6mDfKLh6KrPBjvkk7xA==} + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -868,8 +795,8 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.17.12: - resolution: {integrity: sha512-E/sgkvwoIfj4aMAPL2e35VnUJspzVYl7+M1B2cqeubdBhADV4uPon0KCc8p2G+LqSJ6i8ocYPCqY3A4GGq0zkQ==} + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -877,8 +804,8 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.17.12: - resolution: {integrity: sha512-m4OsaCr5gT+se25rFPHKQXARMyAehHTQAz4XX1Vk3d27VtqiX0ALMBPoXZsGaB6JYryCLfgGwUslMqTfqeLU0w==} + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -886,8 +813,8 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.17.12: - resolution: {integrity: sha512-O3GCZghRIx+RAN0NDPhyyhRgwa19MoKlzGonIb5hgTj78krqp9XZbYCvFr9N1eUxg0ZQEpiiZ4QvsOQwBpP+lg==} + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -895,8 +822,8 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.17.12: - resolution: {integrity: sha512-5D48jM3tW27h1qjaD9UNRuN+4v0zvksqZSPZqeSWggfMlsVdAhH3pwSfQIFJwcs9QJ9BRibPS4ViZgs3d2wsCA==} + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -904,8 +831,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.17.12: - resolution: {integrity: sha512-OWvHzmLNTdF1erSvrfoEBGlN94IE6vCEaGEkEH29uo/VoONqPnoDFfShi41Ew+yKimx4vrmmAJEGNoyyP+OgOQ==} + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -913,8 +840,8 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.17.12: - resolution: {integrity: sha512-A0Xg5CZv8MU9xh4a+7NUpi5VHBKh1RaGJKqjxe4KG87X+mTjDE6ZvlJqpWoeJxgfXHT7IMP9tDFu7IZ03OtJAw==} + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -922,8 +849,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.17.12: - resolution: {integrity: sha512-cK3AjkEc+8v8YG02hYLQIQlOznW+v9N+OI9BAFuyqkfQFR+DnDLhEM5N8QRxAUz99cJTo1rLNXqRrvY15gbQUg==} + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -931,8 +858,8 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.17.12: - resolution: {integrity: sha512-WsHyJ7b7vzHdJ1fv67Yf++2dz3D726oO3QCu8iNYik4fb5YuuReOI9OtA+n7Mk0xyQivNTPbl181s+5oZ38gyA==} + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -940,8 +867,8 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.17.12: - resolution: {integrity: sha512-jdOBXJqcgHlah/nYHnj3Hrnl9l63RjtQ4vn9+bohjQPI2QafASB5MtHAoEv0JQHVb/xYQTFOeuHnNYE1zF7tYw==} + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -949,8 +876,8 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.17.12: - resolution: {integrity: sha512-GTOEtj8h9qPKXCyiBBnHconSCV9LwFyx/gv3Phw0pa25qPYjVuuGZ4Dk14bGCfGX3qKF0+ceeQvwmtI+aYBbVA==} + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -958,8 +885,8 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.17.12: - resolution: {integrity: sha512-o8CIhfBwKcxmEENOH9RwmUejs5jFiNoDw7YgS0EJTF6kgPgcqLFjgoc5kDey5cMHRVCIWc6kK2ShUePOcc7RbA==} + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -967,8 +894,8 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.17.12: - resolution: {integrity: sha512-biMLH6NR/GR4z+ap0oJYb877LdBpGac8KfZoEnDiBKd7MD/xt8eaw1SFfYRUeMVx519kVkAOL2GExdFmYnZx3A==} + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -976,8 +903,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.17.12: - resolution: {integrity: sha512-jkphYUiO38wZGeWlfIBMB72auOllNA2sLfiZPGDtOBb1ELN8lmqBrlMiucgL8awBw1zBXN69PmZM6g4yTX84TA==} + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -985,8 +912,8 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.17.12: - resolution: {integrity: sha512-j3ucLdeY9HBcvODhCY4b+Ds3hWGO8t+SAidtmWu/ukfLLG/oYDMaA+dnugTVAg5fnUOGNbIYL9TOjhWgQB8W5g==} + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -994,8 +921,8 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.17.12: - resolution: {integrity: sha512-uo5JL3cgaEGotaqSaJdRfFNSCUJOIliKLnDGWaVCgIKkHxwhYMm95pfMbWZ9l7GeW9kDg0tSxcy9NYdEtjwwmA==} + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -1003,8 +930,8 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.17.12: - resolution: {integrity: sha512-DNdoRg8JX+gGsbqt2gPgkgb00mqOgOO27KnrWZtdABl6yWTST30aibGJ6geBq3WM2TIeW6COs5AScnC7GwtGPg==} + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -1012,8 +939,8 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.17.12: - resolution: {integrity: sha512-aVsENlr7B64w8I1lhHShND5o8cW6sB9n9MUtLumFlPhG3elhNWtE7M1TFpj3m7lT3sKQUMkGFjTQBrvDDO1YWA==} + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -1021,8 +948,8 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.17.12: - resolution: {integrity: sha512-qbHGVQdKSwi0JQJuZznS4SyY27tYXYF0mrgthbxXrZI3AHKuRvU+Eqbg/F0rmLDpW/jkIZBlCO1XfHUBMNJ1pg==} + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -1030,8 +957,8 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.17.12: - resolution: {integrity: sha512-zsCp8Ql+96xXTVTmm6ffvoTSZSV2B/LzzkUXAY33F/76EajNw1m+jZ9zPfNJlJ3Rh4EzOszNDHsmG/fZOhtqDg==} + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -1039,8 +966,8 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.17.12: - resolution: {integrity: sha512-FfrFjR4id7wcFYOdqbDfDET3tjxCozUgbqdkOABsSFzoZGFC92UK7mg4JKRc/B3NNEf1s2WHxJ7VfTdVDPN3ng==} + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -1048,8 +975,8 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.17.12: - resolution: {integrity: sha512-JOOxw49BVZx2/5tW3FqkdjSD/5gXYeVGPDcB0lvap0gLQshkh1Nyel1QazC+wNxus3xPlsYAgqU1BUmrmCvWtw==} + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1057,15 +984,30 @@ packages: dev: true optional: true - /@eslint/eslintrc@1.3.3: - resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==} + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.4.1 - globals: 13.18.0 - ignore: 5.2.1 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -1074,23 +1016,35 @@ packages: - supports-color dev: true + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@fastify/accept-negotiator@1.1.0: resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==} engines: {node: '>=14'} dev: false - /@fastify/static@6.6.0: - resolution: {integrity: sha512-UiYSN2dUmDZ48M40xdIwY1dPwSSYD7c+wtoIQP8y7wyxCwcUtf1YT5/Q4n1uJsBF1fySvuo9njQZKlHeiKy4HQ==} + /@fastify/send@2.1.0: + resolution: {integrity: sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==} + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.0 + mime: 3.0.0 + dev: false + + /@fastify/static@6.12.0: + resolution: {integrity: sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==} dependencies: '@fastify/accept-negotiator': 1.1.0 + '@fastify/send': 2.1.0 content-disposition: 0.5.4 - fastify-plugin: 4.4.0 - glob: 8.0.3 + fastify-plugin: 4.5.1 + glob: 8.1.0 p-limit: 3.1.0 - readable-stream: 4.2.0 - send: 0.18.0 - transitivePeerDependencies: - - supports-color dev: false /@gar/promisify@1.1.3: @@ -1098,8 +1052,8 @@ packages: requiresBuild: true optional: true - /@golevelup/ts-jest@0.3.6: - resolution: {integrity: sha512-gLAMARxD1JtdPJchogfHqThNw7+mOh1hjfktUZts/2eh6p2SONfYiw7PyNAo6QUXh5EH3NxYGJD6kLXs//k8CA==} + /@golevelup/ts-jest@0.3.8: + resolution: {integrity: sha512-2H4XzpCHwoUs2P13tzwVzj+AYspbFGKwMr94WK5eHiDBKgx0j4QGgLQh1wgM18DjhN7jdntrzVMoQRie6kZhnw==} dev: true /@hapi/hoek@9.3.0: @@ -1112,11 +1066,11 @@ packages: '@hapi/hoek': 9.3.0 dev: false - /@humanwhocodes/config-array@0.11.7: - resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==} + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 1.2.1 + '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -1128,8 +1082,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + /@humanwhocodes/object-schema@2.0.3: + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} dev: true /@istanbuljs/load-nyc-config@1.1.0: @@ -1151,7 +1105,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 chalk: 4.1.2 jest-message-util: 28.1.3 jest-util: 28.1.3 @@ -1171,14 +1125,14 @@ packages: '@jest/test-result': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 3.7.0 + ci-info: 3.9.0 exit: 0.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-changed-files: 28.1.3 - jest-config: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + jest-config: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) jest-haste-map: 28.1.3 jest-message-util: 28.1.3 jest-regex-util: 28.0.2 @@ -1199,11 +1153,11 @@ packages: - supports-color - ts-node - /@jest/create-cache-key-function@27.5.1: - resolution: {integrity: sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/create-cache-key-function@29.7.0: + resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 27.5.1 + '@jest/types': 29.6.3 dev: true /@jest/environment@28.1.3: @@ -1212,7 +1166,7 @@ packages: dependencies: '@jest/fake-timers': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 jest-mock: 28.1.3 /@jest/expect-utils@28.1.3: @@ -1236,7 +1190,7 @@ packages: dependencies: '@jest/types': 28.1.3 '@sinonjs/fake-timers': 9.1.2 - '@types/node': 16.18.4 + '@types/node': 16.18.97 jest-message-util: 28.1.3 jest-mock: 28.1.3 jest-util: 28.1.3 @@ -1265,18 +1219,18 @@ packages: '@jest/test-result': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 - '@jridgewell/trace-mapping': 0.3.17 - '@types/node': 16.18.4 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 16.18.97 chalk: 4.1.2 - collect-v8-coverage: 1.0.1 + collect-v8-coverage: 1.0.2 exit: 0.1.2 glob: 7.2.3 - graceful-fs: 4.2.10 - istanbul-lib-coverage: 3.2.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 + istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 + istanbul-reports: 3.1.7 jest-message-util: 28.1.3 jest-util: 28.1.3 jest-worker: 28.1.3 @@ -1284,7 +1238,7 @@ packages: string-length: 4.0.2 strip-ansi: 6.0.1 terminal-link: 2.1.1 - v8-to-istanbul: 9.0.1 + v8-to-istanbul: 9.2.0 transitivePeerDependencies: - supports-color @@ -1294,11 +1248,18 @@ packages: dependencies: '@sinclair/typebox': 0.24.51 + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jest/source-map@28.1.2: resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.25 callsites: 3.1.0 graceful-fs: 4.2.10 @@ -1316,7 +1277,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/test-result': 28.1.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 28.1.3 slash: 3.0.0 @@ -1324,144 +1285,129 @@ packages: resolution: {integrity: sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.24.5 '@jest/types': 28.1.3 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.25 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 1.9.0 fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 28.1.3 jest-regex-util: 28.0.2 jest-util: 28.1.3 micromatch: 4.0.5 - pirates: 4.0.5 + pirates: 4.0.6 slash: 3.0.0 write-file-atomic: 4.0.2 transitivePeerDependencies: - supports-color - /@jest/types@27.5.1: - resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.4 - '@types/yargs': 16.0.5 - chalk: 4.1.2 - dev: true - /@jest/types@28.1.3: resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/schemas': 28.1.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.4 - '@types/yargs': 17.0.15 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 16.18.97 + '@types/yargs': 17.0.32 chalk: 4.1.2 - /@joi/date@2.1.0: - resolution: {integrity: sha512-2zN5m0LgxZp/cynHGbzEImVmFIa+n+IOb/Nlw5LX/PLJneeCwG1NbiGw7MvPjsAKUGQK8z31Nn6V6lEN+4fZhg==} + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - moment: 2.29.4 - dev: false + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 16.18.97 + '@types/yargs': 17.0.32 + chalk: 4.1.2 + dev: true - /@jridgewell/gen-mapping@0.1.1: - resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} - engines: {node: '>=6.0.0'} + /@joi/date@2.1.1: + resolution: {integrity: sha512-oXF8vU8M+O9a6tuItgtTQeboO3+Ed6xunLatt6gq7WEFJ7HjawPH64OmrsX0ch3TEsUgQkU8v4MlOGEsf6PHSQ==} dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 + moment: 2.30.1 + dev: false - /@jridgewell/gen-mapping@0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - /@jridgewell/source-map@0.3.2: - resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} + /@jridgewell/source-map@0.3.6: + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} dependencies: - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.17: - resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 - /@mapbox/node-pre-gyp@1.0.10: - resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==} - hasBin: true - dependencies: - detect-libc: 2.0.1 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.6.7 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.3.8 - tar: 6.1.13 - transitivePeerDependencies: - - encoding - - supports-color + /@lukeed/csprng@1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} - /@nestjs/cli@9.1.5(@swc/core@1.3.56): - resolution: {integrity: sha512-rSp26+Nv7PFtYrRSP18Gv5ZK8rRSc2SCCF5wh4SdZaVGgkxShpNq9YEfI+ik/uziN3KC5o74ppYRXGj+aHGVsA==} + /@lukeed/ms@2.0.2: + resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} + engines: {node: '>=8'} + dev: false + + /@nestjs/cli@9.5.0(@swc/core@1.5.5): + resolution: {integrity: sha512-Z7q+3vNsQSG2d2r2Hl/OOj5EpfjVx3OfnJ9+KuAsOdw1sKLm7+Zc6KbhMFTd/eIvfx82ww3Nk72xdmfPYCulWA==} engines: {node: '>= 12.9.0'} hasBin: true dependencies: - '@angular-devkit/core': 14.2.2(chokidar@3.5.3) - '@angular-devkit/schematics': 14.2.2(chokidar@3.5.3) - '@angular-devkit/schematics-cli': 14.2.2(chokidar@3.5.3) - '@nestjs/schematics': 9.0.3(chokidar@3.5.3)(typescript@4.8.4) - chalk: 3.0.0 + '@angular-devkit/core': 16.0.1(chokidar@3.5.3) + '@angular-devkit/schematics': 16.0.1(chokidar@3.5.3) + '@angular-devkit/schematics-cli': 16.0.1(chokidar@3.5.3) + '@nestjs/schematics': 9.2.0(chokidar@3.5.3)(typescript@4.9.5) + chalk: 4.1.2 chokidar: 3.5.3 - cli-table3: 0.6.2 + cli-table3: 0.6.3 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 7.2.13(typescript@4.8.4)(webpack@5.74.0) - inquirer: 7.3.3 + fork-ts-checker-webpack-plugin: 8.0.0(typescript@4.9.5)(webpack@5.82.1) + inquirer: 8.2.5 node-emoji: 1.11.0 ora: 5.4.1 os-name: 4.0.1 - rimraf: 3.0.2 + rimraf: 4.4.1 shelljs: 0.8.5 source-map-support: 0.5.21 tree-kill: 1.2.2 - tsconfig-paths: 4.1.0 - tsconfig-paths-webpack-plugin: 4.0.0 - typescript: 4.8.4 - webpack: 5.74.0(@swc/core@1.3.56) + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.0.1 + typescript: 4.9.5 + webpack: 5.82.1(@swc/core@1.5.5) webpack-node-externals: 3.0.0 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - - vue-template-compiler - webpack-cli dev: true - /@nestjs/common@9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7): - resolution: {integrity: sha512-nZuo3oDsSSlC5mti/M2aCWTEIfHPGDXmBwWgPeCpRbrNz3IWd109rkajll+yxgidVjznAdBS9y00JkAVJblNYw==} + /@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1): + resolution: {integrity: sha512-Gd6D4IaYj01o14Bwv81ukidn4w3bPHCblMUq+SmUmWLyosK+XQmInCS09SbDDZyL8jy86PngtBLTdhJ2bXSUig==} peerDependencies: cache-manager: <=5 class-transformer: '*' @@ -1478,29 +1424,29 @@ packages: dependencies: class-transformer: 0.5.1 iterare: 1.2.1 - reflect-metadata: 0.1.13 - rxjs: 7.5.7 - tslib: 2.4.1 - uuid: 9.0.0 + reflect-metadata: 0.1.14 + rxjs: 7.8.1 + tslib: 2.5.3 + uid: 2.0.2 - /@nestjs/config@2.2.0(@nestjs/common@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7): - resolution: {integrity: sha512-78Eg6oMbCy3D/YvqeiGBTOWei1Jwi3f2pSIZcZ1QxY67kYsJzTRTkwRT8Iv30DbK0sGKc1mcloDLD5UXgZAZtg==} + /@nestjs/config@2.3.4(@nestjs/common@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1): + resolution: {integrity: sha512-IGdSF+0F9MJO6dCRTEahdxPz4iVijjtolcFBxnY+2QYM3bXYQvAgzskGZi+WkAFJN/VzR3TEp60gN5sI74GxPA==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 reflect-metadata: ^0.1.13 rxjs: ^6.0.0 || ^7.2.0 dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - dotenv: 16.0.1 - dotenv-expand: 8.0.3 + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + dotenv: 16.1.4 + dotenv-expand: 10.0.0 lodash: 4.17.21 - reflect-metadata: 0.1.13 - rxjs: 7.5.7 - uuid: 8.3.2 + reflect-metadata: 0.1.14 + rxjs: 7.8.1 + uuid: 9.0.0 dev: false - /@nestjs/core@9.2.1(@nestjs/common@9.2.1)(@nestjs/platform-express@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7): - resolution: {integrity: sha512-a9GkXuu8uXgNgCVW+17iI8kLCltO+HwHpU2IhR+32JKnN2WEQ1YEWU4t3GJ2MNq44YkjIw9zrKvFkjJBlYrNbQ==} + /@nestjs/core@9.4.3(@nestjs/common@9.4.3)(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1): + resolution: {integrity: sha512-Qi63+wi55Jh4sDyaj5Hhx2jOpKqT386aeo+VOKsxnd+Ql9VvkO/FjmuwBGUyzkJt29ENYc+P0Sx/k5LtstNpPQ==} requiresBuild: true peerDependencies: '@nestjs/common': ^9.0.0 @@ -1517,26 +1463,25 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/platform-express': 9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3)(@nestjs/core@9.4.3) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 - object-hash: 3.0.0 path-to-regexp: 3.2.0 - reflect-metadata: 0.1.13 - rxjs: 7.5.7 - tslib: 2.4.1 - uuid: 9.0.0 + reflect-metadata: 0.1.14 + rxjs: 7.8.1 + tslib: 2.5.3 + uid: 2.0.2 transitivePeerDependencies: - encoding - /@nestjs/mapped-types@1.2.0(@nestjs/common@9.2.1)(class-transformer@0.5.1)(reflect-metadata@0.1.13): - resolution: {integrity: sha512-NTFwPZkQWsArQH8QSyFWGZvJ08gR+R4TofglqZoihn/vU+ktHEJjMqsIsADwb7XD97DhiD+TVv5ac+jG33BHrg==} + /@nestjs/mapped-types@1.2.2(@nestjs/common@9.4.3)(class-transformer@0.5.1)(reflect-metadata@0.1.14): + resolution: {integrity: sha512-3dHxLXs3M0GPiriAcCFFJQHoDFUuzTD5w6JDhE7TyfT89YKpe6tcCCIqOZWdXmt9AZjjK30RkHRSFF+QEnWFQg==} peerDependencies: '@nestjs/common': ^7.0.8 || ^8.0.0 || ^9.0.0 class-transformer: ^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0 - class-validator: ^0.11.1 || ^0.12.0 || ^0.13.0 + class-validator: ^0.11.1 || ^0.12.0 || ^0.13.0 || ^0.14.0 reflect-metadata: ^0.1.12 peerDependenciesMeta: class-transformer: @@ -1544,60 +1489,58 @@ packages: class-validator: optional: true dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) class-transformer: 0.5.1 - reflect-metadata: 0.1.13 + reflect-metadata: 0.1.14 dev: false - /@nestjs/passport@9.0.0(@nestjs/common@9.2.1)(passport@0.6.0): - resolution: {integrity: sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA==} + /@nestjs/passport@9.0.3(@nestjs/common@9.4.3)(passport@0.6.0): + resolution: {integrity: sha512-HplSJaimEAz1IOZEu+pdJHHJhQyBOPAYWXYHfAPQvRqWtw4FJF1VXl1Qtk9dcXQX1eKytDtH+qBzNQc19GWNEg==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 passport: ^0.4.0 || ^0.5.0 || ^0.6.0 dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) passport: 0.6.0 dev: false - /@nestjs/platform-express@9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1): - resolution: {integrity: sha512-7PecaXt8lrdS1p6Vb1X/am3GGv+EO1VahyDzaEGOK6C0zwhc0VPfLtwihkjjfhS6BjpRIXXgviwEjONUvxVZnA==} + /@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3)(@nestjs/core@9.4.3): + resolution: {integrity: sha512-FpdczWoRSC0zz2dNL9u2AQLXKXRVtq4HgHklAhbL59X0uy+mcxhlSThG7DHzDMkoSnuuHY8ojDVf7mDxk+GtCw==} peerDependencies: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/core': 9.2.1(@nestjs/common@9.2.1)(@nestjs/platform-express@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - body-parser: 1.20.1 + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3)(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 multer: 1.4.4-lts.1 - tslib: 2.4.1 + tslib: 2.5.3 transitivePeerDependencies: - supports-color - /@nestjs/schematics@9.0.3(chokidar@3.5.3)(typescript@4.8.4): - resolution: {integrity: sha512-kZrU/lrpVd2cnK8I3ibDb3Wi1ppl3wX3U3lVWoL+DzRRoezWKkh8upEL4q0koKmuXnsmLiu3UPxFeMOrJV7TSA==} + /@nestjs/schematics@9.2.0(chokidar@3.5.3)(typescript@4.9.5): + resolution: {integrity: sha512-wHpNJDPzM6XtZUOB3gW0J6mkFCSJilzCM3XrHI1o0C8vZmFE1snbmkIXNyoi1eV0Nxh1BMymcgz5vIMJgQtTqw==} peerDependencies: - typescript: ^4.3.5 + typescript: '>=4.3.5' dependencies: - '@angular-devkit/core': 14.2.1(chokidar@3.5.3) - '@angular-devkit/schematics': 14.2.1(chokidar@3.5.3) - fs-extra: 10.1.0 + '@angular-devkit/core': 16.0.1(chokidar@3.5.3) + '@angular-devkit/schematics': 16.0.1(chokidar@3.5.3) jsonc-parser: 3.2.0 pluralize: 8.0.0 - typescript: 4.8.4 + typescript: 4.9.5 transitivePeerDependencies: - chokidar dev: true - /@nestjs/schematics@9.0.3(typescript@4.5.2): - resolution: {integrity: sha512-kZrU/lrpVd2cnK8I3ibDb3Wi1ppl3wX3U3lVWoL+DzRRoezWKkh8upEL4q0koKmuXnsmLiu3UPxFeMOrJV7TSA==} + /@nestjs/schematics@9.2.0(typescript@4.5.2): + resolution: {integrity: sha512-wHpNJDPzM6XtZUOB3gW0J6mkFCSJilzCM3XrHI1o0C8vZmFE1snbmkIXNyoi1eV0Nxh1BMymcgz5vIMJgQtTqw==} peerDependencies: - typescript: ^4.3.5 + typescript: '>=4.3.5' dependencies: - '@angular-devkit/core': 14.2.1(chokidar@3.5.3) - '@angular-devkit/schematics': 14.2.1(chokidar@3.5.3) - fs-extra: 10.1.0 + '@angular-devkit/core': 16.0.1(chokidar@3.5.3) + '@angular-devkit/schematics': 16.0.1(chokidar@3.5.3) jsonc-parser: 3.2.0 pluralize: 8.0.0 typescript: 4.5.2 @@ -1605,33 +1548,37 @@ packages: - chokidar dev: true - /@nestjs/swagger@6.1.3(@fastify/static@6.6.0)(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(class-transformer@0.5.1)(reflect-metadata@0.1.13): - resolution: {integrity: sha512-H9C/yRgLFb5QrAt6iGrYmIX9X7Q0zXkgZaTNUATljUBra+RCWrEUbLHBcGjTAOtcIyGV/vmyCLv68YSVcZoE0Q==} + /@nestjs/swagger@6.3.0(@fastify/static@6.12.0)(@nestjs/common@9.4.3)(@nestjs/core@9.4.3)(class-transformer@0.5.1)(reflect-metadata@0.1.14): + resolution: {integrity: sha512-Gnig189oa1tD+h0BYIfUwhp/wvvmTn6iO3csR2E4rQrDTgCxSxZDlNdfZo3AC+Rmf8u0KX4ZAX1RZN1qXTtC7A==} peerDependencies: '@fastify/static': ^6.0.0 '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 + class-transformer: '*' + class-validator: '*' reflect-metadata: ^0.1.12 peerDependenciesMeta: '@fastify/static': optional: true + class-transformer: + optional: true + class-validator: + optional: true dependencies: - '@fastify/static': 6.6.0 - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/core': 9.2.1(@nestjs/common@9.2.1)(@nestjs/platform-express@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/mapped-types': 1.2.0(@nestjs/common@9.2.1)(class-transformer@0.5.1)(reflect-metadata@0.1.13) + '@fastify/static': 6.12.0 + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3)(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/mapped-types': 1.2.2(@nestjs/common@9.4.3)(class-transformer@0.5.1)(reflect-metadata@0.1.14) + class-transformer: 0.5.1 js-yaml: 4.1.0 lodash: 4.17.21 path-to-regexp: 3.2.0 - reflect-metadata: 0.1.13 - swagger-ui-dist: 4.15.1 - transitivePeerDependencies: - - class-transformer - - class-validator + reflect-metadata: 0.1.14 + swagger-ui-dist: 4.18.2 dev: false - /@nestjs/testing@9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(@nestjs/platform-express@9.2.1): - resolution: {integrity: sha512-lemXZdRSuqoZ87l0orCrS/c7gqwxeduIFOd21g9g2RUeQ4qlWPegbQDKASzbfC28klPyrgJLW4MNq7uv2JwV8w==} + /@nestjs/testing@9.4.3(@nestjs/common@9.4.3)(@nestjs/core@9.4.3)(@nestjs/platform-express@9.4.3): + resolution: {integrity: sha512-LDT8Ai2eKnTzvnPaJwWOK03qTaFap5uHHsJCv6dL0uKWk6hyF9jms8DjyVaGsaujCaXDG8izl1mDEER0OmxaZA==} peerDependencies: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 @@ -1643,13 +1590,13 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/core': 9.2.1(@nestjs/common@9.2.1)(@nestjs/platform-express@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/platform-express': 9.2.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1) - tslib: 2.4.1 + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3)(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3)(@nestjs/core@9.4.3) + tslib: 2.5.3 dev: true - /@nestjs/typeorm@9.0.1(@nestjs/common@9.2.1)(@nestjs/core@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7)(typeorm@0.3.11): + /@nestjs/typeorm@9.0.1(@nestjs/common@9.4.3)(@nestjs/core@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.11): resolution: {integrity: sha512-A2BgLIPsMtmMI0bPKEf4bmzgFPsnvHqNBx3KkvaJ7hJrBQy0OqYOb+Rr06ifblKWDWS2tUPNrAFQbZjtk3PI+g==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 @@ -1658,11 +1605,11 @@ packages: rxjs: ^7.2.0 typeorm: ^0.3.0 dependencies: - '@nestjs/common': 9.2.1(class-transformer@0.5.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - '@nestjs/core': 9.2.1(@nestjs/common@9.2.1)(@nestjs/platform-express@9.2.1)(reflect-metadata@0.1.13)(rxjs@7.5.7) - reflect-metadata: 0.1.13 - rxjs: 7.5.7 - typeorm: 0.3.11(pg@8.8.0)(sqlite3@5.1.6)(ts-node@10.7.0) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3)(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + reflect-metadata: 0.1.14 + rxjs: 7.8.1 + typeorm: 0.3.11(pg@8.11.5)(sqlite3@5.1.7)(ts-node@10.7.0) uuid: 8.3.2 dev: false @@ -1684,7 +1631,7 @@ packages: engines: {node: '>= 8'} dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.14.0 + fastq: 1.17.1 dev: true /@npmcli/fs@1.1.1: @@ -1692,7 +1639,7 @@ packages: requiresBuild: true dependencies: '@gar/promisify': 1.1.3 - semver: 7.3.8 + semver: 7.6.2 optional: true /@npmcli/move-file@1.1.2: @@ -1712,53 +1659,54 @@ packages: dependencies: chalk: 4.1.2 consola: 2.15.3 - node-fetch: 2.6.7 + node-fetch: 2.7.0 transitivePeerDependencies: - encoding - /@popperjs/core@2.11.6: - resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} + /@popperjs/core@2.11.8: + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false - /@react-aria/ssr@3.5.0(react@18.2.0): - resolution: {integrity: sha512-h0MJdSWOd1qObLnJ8mprU31wI8tmKFJMuwT22MpWq6psisOOZaga6Ml4u6Ee6M6duWWISjXvqO4Sb/J0PBA+nQ==} + /@react-aria/ssr@3.9.3(react@18.3.1): + resolution: {integrity: sha512-5bUZ93dmvHFcmfUcEN7qzYe8yQQ8JY+nHN6m9/iSDCQ/QmCiE0kWXYwhurjw5ch6I8WokQzx66xKIMHBAa4NNA==} + engines: {node: '>= 12'} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 dependencies: - '@swc/helpers': 0.4.14 - react: 18.2.0 + '@swc/helpers': 0.5.11 + react: 18.3.1 dev: false - /@remix-run/router@1.4.0: - resolution: {integrity: sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==} - engines: {node: '>=14'} + /@remix-run/router@1.16.1: + resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} + engines: {node: '>=14.0.0'} dev: false - /@restart/hooks@0.4.9(react@18.2.0): - resolution: {integrity: sha512-3BekqcwB6Umeya+16XPooARn4qEPW6vNvwYnlofIYe6h9qG1/VeD7UvShCWx11eFz5ELYmwIEshz+MkPX3wjcQ==} + /@restart/hooks@0.4.16(react@18.3.1): + resolution: {integrity: sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==} peerDependencies: react: '>=16.8.0' dependencies: dequal: 2.0.3 - react: 18.2.0 + react: 18.3.1 dev: false - /@restart/ui@1.6.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-hcYs8PwpmHEtwjihLVn2Jr89yrYajfhxN5HtTq3HA9U3+feg1SC3swBM8/qibMTCFsXWToEEtzJMV+LWE+Qjpg==} + /@restart/ui@1.6.9(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-mUbygUsJcRurjZCt1f77gg4DpheD1D+Sc7J3JjAkysUj7t8m4EBJVOqWC9788Qtbc69cJ+HlJc6jBguKwS8Mcw==} peerDependencies: react: '>=16.14.0' react-dom: '>=16.14.0' dependencies: - '@babel/runtime': 7.21.0 - '@popperjs/core': 2.11.6 - '@react-aria/ssr': 3.5.0(react@18.2.0) - '@restart/hooks': 0.4.9(react@18.2.0) - '@types/warning': 3.0.0 + '@babel/runtime': 7.24.5 + '@popperjs/core': 2.11.8 + '@react-aria/ssr': 3.9.3(react@18.3.1) + '@restart/hooks': 0.4.16(react@18.3.1) + '@types/warning': 3.0.3 dequal: 2.0.3 dom-helpers: 5.2.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - uncontrollable: 8.0.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + uncontrollable: 8.0.4(react@18.3.1) warning: 4.0.3 dev: false @@ -1770,14 +1718,14 @@ packages: picomatch: 2.3.1 dev: true - /@sideway/address@4.1.4: - resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==} + /@sideway/address@4.1.5: + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} dependencies: '@hapi/hoek': 9.3.0 dev: false - /@sideway/formula@3.0.0: - resolution: {integrity: sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==} + /@sideway/formula@3.0.1: + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} dev: false /@sideway/pinpoint@2.0.0: @@ -1787,6 +1735,10 @@ packages: /@sinclair/typebox@0.24.51: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: @@ -1801,88 +1753,88 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@swc/core-darwin-arm64@1.3.56: - resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==} + /@swc/core-darwin-arm64@1.5.5: + resolution: {integrity: sha512-Ol5ZwZYdTOZsv2NwjcT/qVVALKzVFeh+IJ4GNarr3P99+38Dkwi81OqCI1o/WaDXQYKAQC/V+CzMbkEuJJfq9Q==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@swc/core-darwin-x64@1.3.56: - resolution: {integrity: sha512-VH5saqYFasdRXJy6RAT+MXm0+IjkMZvOkohJwUei+oA65cKJofQwrJ1jZro8yOJFYvUSI3jgNRGsdBkmo/4hMw==} + /@swc/core-darwin-x64@1.5.5: + resolution: {integrity: sha512-XHWpKBIPKYLgh5/lV2PYjO84lkzf5JR51kjiloyz2Pa9HIV8tHoAP8bYdJwm4nUp2I7KcEh3pPH0AVu5LpxMKw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@swc/core-linux-arm-gnueabihf@1.3.56: - resolution: {integrity: sha512-LWwPo6NnJkH01+ukqvkoNIOpMdw+Zundm4vBeicwyVrkP+mC3kwVfi03TUFpQUz3kRKdw/QEnxGTj+MouCPbtw==} + /@swc/core-linux-arm-gnueabihf@1.5.5: + resolution: {integrity: sha512-vtoWNCWAe+CNSqtqIwFnIH48qgPPlUZKoQ4EVFeMM+7/kDi6SeNxoh5TierJs5bKAWxD49VkPvRoWFCk6V62mA==} engines: {node: '>=10'} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-arm64-gnu@1.3.56: - resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==} + /@swc/core-linux-arm64-gnu@1.5.5: + resolution: {integrity: sha512-L4l7M78U6h/rCAxId+y5Vu+1KfDRF6dJZtitFcaT293guiUQFwJv8gLxI4Jh5wFtZ0fYd0QaCuvh2Ip79CzGMg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-arm64-musl@1.3.56: - resolution: {integrity: sha512-9gxL09BIiAv8zY0DjfnFf19bo8+P4T9tdhzPwcm+1yPJcY5yr1+YFWLNFzz01agtOj6VlZ2/wUJTaOfdjjtc+A==} + /@swc/core-linux-arm64-musl@1.5.5: + resolution: {integrity: sha512-DkzJc13ukXa7oJpyn24BjIgsiOybYrc+IxjsQyfNlDrrs1QXP4elStcpkD02SsIuSyHjZV8Hw2HFBMQB3OHPrA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-x64-gnu@1.3.56: - resolution: {integrity: sha512-n0ORNknl50vMRkll3BDO1E4WOqY6iISlPV1ZQCRLWQ6YQ2q8/WAryBxc2OAybcGHBUFkxyACpJukeU1QZ/9tNw==} + /@swc/core-linux-x64-gnu@1.5.5: + resolution: {integrity: sha512-kj4ZwWJGeBEUzHrRQP2VudN+kkkYH7OI1dPVDc6kWQx5X4329JeKOas4qY0l7gDVjBbRwN9IbbPI6TIn2KfAug==} engines: {node: '>=10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@swc/core-linux-x64-musl@1.3.56: - resolution: {integrity: sha512-r+D34WLAOAlJtfw1gaVWpHRwCncU9nzW9i7w9kSw4HpWYnHJOz54jLGSEmNsrhdTCz1VK2ar+V2ktFUsrlGlDA==} + /@swc/core-linux-x64-musl@1.5.5: + resolution: {integrity: sha512-6pTorCs4mYhPhYtC4jNOnhGgjNd3DZcRoZ9P0tzXXP69aCbYjvlgNH/NRvAROp9AaVFeZ7a7PmCWb6+Rbe7NKg==} engines: {node: '>=10'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@swc/core-win32-arm64-msvc@1.3.56: - resolution: {integrity: sha512-29Yt75Is6X24z3x8h/xZC1HnDPkPpyLH9mDQiM6Cuc0I9mVr1XSriPEUB2N/awf5IE4SA8c+3IVq1DtKWbkJIw==} + /@swc/core-win32-arm64-msvc@1.5.5: + resolution: {integrity: sha512-o0/9pstmEjwZyrY/bA+mymF0zH7E+GT/XCVqdKeWW9Wn3gTTyWa5MZnrFgI2THQ+AXwdglMB/Zo76ARQPaz/+A==} engines: {node: '>=10'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@swc/core-win32-ia32-msvc@1.3.56: - resolution: {integrity: sha512-mplp0zbYDrcHtfvkniXlXdB04e2qIjz2Gq/XHKr4Rnc6xVORJjjXF91IemXKpavx2oZYJws+LNJL7UFQ8jyCdQ==} + /@swc/core-win32-ia32-msvc@1.5.5: + resolution: {integrity: sha512-B+nypUwsmCuaH6RtKWgiPCb+ENjxstJPPJeMJvBqlJqyCaIkZzN4M07Ozi3xVv1VG21SRkd6G3xIqRoalrNc0Q==} engines: {node: '>=10'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@swc/core-win32-x64-msvc@1.3.56: - resolution: {integrity: sha512-zp8MBnrw/bjdLenO/ifYzHrImSjKunqL0C2IF4LXYNRfcbYFh2NwobsVQMZ20IT0474lKRdlP8Oxdt+bHuXrzA==} + /@swc/core-win32-x64-msvc@1.5.5: + resolution: {integrity: sha512-ry83ki9ZX0Q+GWGnqc2J618Z+FvKE8Ajn42F8EYi8Wj0q6Jz3mj+pJzgzakk2INm2ldEZ+FaRPipn4ozsZDcBg==} engines: {node: '>=10'} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@swc/core@1.3.56: - resolution: {integrity: sha512-yz/EeXT+PMZucUNrYceRUaTfuNS4IIu5EDZSOlvCEvm4jAmZi7CYH1B/kvzEzoAOzr7zkQiDPNJftcQXLkjbjA==} + /@swc/core@1.5.5: + resolution: {integrity: sha512-M8O22EEgdSONLd+7KRrXj8pn+RdAZZ7ISnPjE9KCQQlI0kkFNEquWR+uFdlFxQfwlyCe/Zb6uGXGDvtcov4IMg==} engines: {node: '>=10'} requiresBuild: true peerDependencies: @@ -1890,43 +1842,55 @@ packages: peerDependenciesMeta: '@swc/helpers': optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.6 optionalDependencies: - '@swc/core-darwin-arm64': 1.3.56 - '@swc/core-darwin-x64': 1.3.56 - '@swc/core-linux-arm-gnueabihf': 1.3.56 - '@swc/core-linux-arm64-gnu': 1.3.56 - '@swc/core-linux-arm64-musl': 1.3.56 - '@swc/core-linux-x64-gnu': 1.3.56 - '@swc/core-linux-x64-musl': 1.3.56 - '@swc/core-win32-arm64-msvc': 1.3.56 - '@swc/core-win32-ia32-msvc': 1.3.56 - '@swc/core-win32-x64-msvc': 1.3.56 + '@swc/core-darwin-arm64': 1.5.5 + '@swc/core-darwin-x64': 1.5.5 + '@swc/core-linux-arm-gnueabihf': 1.5.5 + '@swc/core-linux-arm64-gnu': 1.5.5 + '@swc/core-linux-arm64-musl': 1.5.5 + '@swc/core-linux-x64-gnu': 1.5.5 + '@swc/core-linux-x64-musl': 1.5.5 + '@swc/core-win32-arm64-msvc': 1.5.5 + '@swc/core-win32-ia32-msvc': 1.5.5 + '@swc/core-win32-x64-msvc': 1.5.5 - /@swc/helpers@0.4.14: - resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + /@swc/helpers@0.5.11: + resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} dependencies: - tslib: 2.4.1 + tslib: 2.6.2 dev: false - /@swc/jest@0.2.26(@swc/core@1.3.56): - resolution: {integrity: sha512-7lAi7q7ShTO3E5Gt1Xqf3pIhRbERxR1DUxvtVa9WKzIB+HGQ7wZP5sYx86zqnaEoKKGhmOoZ7gyW0IRu8Br5+A==} + /@swc/jest@0.2.36(@swc/core@1.5.5): + resolution: {integrity: sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: - '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.56 - jsonc-parser: 3.2.0 + '@jest/create-cache-key-function': 29.7.0 + '@swc/core': 1.5.5 + '@swc/counter': 0.1.3 + jsonc-parser: 3.2.1 dev: true + /@swc/types@0.1.6: + resolution: {integrity: sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==} + dependencies: + '@swc/counter': 0.1.3 + /@tootallnate/once@1.1.2: resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} requiresBuild: true optional: true - /@tsconfig/node10@1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + /@tsconfig/node10@1.0.11: + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -1934,96 +1898,123 @@ packages: /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - /@tsconfig/node16@1.0.3: - resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} /@types/babel__core@7.1.20: resolution: {integrity: sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==} dependencies: '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.3 + '@types/babel__traverse': 7.20.5 + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.5 /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 + + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + dependencies: + '@babel/types': 7.24.5 /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 - /@types/babel__traverse@7.18.3: - resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/types': 7.20.5 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 - /@types/body-parser@1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} + /@types/babel__traverse@7.20.5: + resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} dependencies: - '@types/connect': 3.4.35 - '@types/node': 16.18.4 + '@babel/types': 7.24.5 - /@types/connect@3.4.35: - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: - '@types/node': 16.18.4 + '@types/connect': 3.4.38 + '@types/node': 16.18.97 - /@types/cookiejar@2.1.2: - resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==} + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 16.18.97 + + /@types/cookiejar@2.1.5: + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} dev: true - /@types/eslint-scope@3.7.4: - resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} + /@types/eslint-scope@3.7.7: + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} dependencies: - '@types/eslint': 8.4.10 - '@types/estree': 0.0.51 + '@types/eslint': 8.56.10 + '@types/estree': 1.0.5 - /@types/eslint@8.4.10: - resolution: {integrity: sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==} + /@types/eslint@8.56.10: + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} dependencies: - '@types/estree': 0.0.51 - '@types/json-schema': 7.0.11 + '@types/estree': 1.0.5 + '@types/json-schema': 7.0.15 - /@types/estree@0.0.51: - resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - /@types/express-serve-static-core@4.17.31: - resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} + /@types/express-serve-static-core@4.19.0: + resolution: {integrity: sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==} dependencies: - '@types/node': 16.18.4 - '@types/qs': 6.9.7 - '@types/range-parser': 1.2.4 + '@types/node': 16.18.97 + '@types/qs': 6.9.15 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 - /@types/express@4.17.14: - resolution: {integrity: sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==} + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} dependencies: - '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.31 - '@types/qs': 6.9.7 - '@types/serve-static': 1.15.0 + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.19.0 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 - /@types/graceful-fs@4.1.5: - resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} + /@types/graceful-fs@4.1.9: + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 16.18.4 + '@types/node': 16.18.97 + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + /@types/istanbul-lib-coverage@2.0.6: + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + /@types/istanbul-lib-report@3.0.3: + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} dependencies: - '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-coverage': 2.0.6 - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + /@types/istanbul-reports@3.0.4: + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-report': 3.0.3 /@types/jest@28.1.8: resolution: {integrity: sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==} @@ -2032,128 +2023,134 @@ packages: pretty-format: 28.1.3 dev: true - /@types/js-yaml@4.0.5: - resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} + /@types/js-yaml@4.0.9: + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} dev: false - /@types/json-schema@7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - /@types/jsonwebtoken@8.5.9: - resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} + /@types/jsonwebtoken@9.0.6: + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} dependencies: - '@types/node': 16.18.4 + '@types/node': 16.18.97 dev: false - /@types/mime@3.0.1: - resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} + /@types/methods@1.1.4: + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - /@types/multer@1.4.7: - resolution: {integrity: sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==} + /@types/multer@1.4.11: + resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} dependencies: - '@types/express': 4.17.14 + '@types/express': 4.17.21 dev: true - /@types/node@16.18.4: - resolution: {integrity: sha512-9qGjJ5GyShZjUfx2ArBIGM+xExdfLvvaCyQR0t6yRXKPcWCVYF/WemtX/uIU3r7FYECXRXkIiw2Vnhn6y8d+pw==} + /@types/node@16.18.97: + resolution: {integrity: sha512-4muilE1Lbfn57unR+/nT9AFjWk0MtWi5muwCEJqnOvfRQDbSfLCUdN7vCIg8TYuaANfhLOV85ve+FNpiUsbSRg==} - /@types/parse-json@4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + /@types/parse-json@4.0.2: + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} dev: true - /@types/passport-jwt@3.0.7: - resolution: {integrity: sha512-qRQ4qlww1Yhs3IaioDKrsDNmKy6gLDLgFsGwpCnc2YqWovO2Oxu9yCQdWHMJafQ7UIuOba4C4/TNXcGkQfEjlQ==} + /@types/passport-jwt@3.0.13: + resolution: {integrity: sha512-fjHaC6Bv8EpMMqzTnHP32SXlZGaNfBPC/Po5dmRGYi2Ky7ljXPbGnOy+SxZqa6iZvFgVhoJ1915Re3m93zmcfA==} dependencies: - '@types/express': 4.17.14 - '@types/jsonwebtoken': 8.5.9 - '@types/passport-strategy': 0.2.35 + '@types/express': 4.17.21 + '@types/jsonwebtoken': 9.0.6 + '@types/passport-strategy': 0.2.38 dev: false - /@types/passport-strategy@0.2.35: - resolution: {integrity: sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==} + /@types/passport-strategy@0.2.38: + resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==} dependencies: - '@types/express': 4.17.14 - '@types/passport': 1.0.11 + '@types/express': 4.17.21 + '@types/passport': 1.0.16 dev: false - /@types/passport@1.0.11: - resolution: {integrity: sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==} + /@types/passport@1.0.16: + resolution: {integrity: sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==} dependencies: - '@types/express': 4.17.14 + '@types/express': 4.17.21 dev: false - /@types/prettier@2.7.1: - resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} + /@types/prettier@2.7.3: + resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} - /@types/prop-types@15.7.5: - resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + /@types/prop-types@15.7.12: + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} dev: false - /@types/qs@6.9.7: - resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + /@types/qs@6.9.15: + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - /@types/range-parser@1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - /@types/react-transition-group@4.4.5: - resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} + /@types/react-transition-group@4.4.10: + resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} dependencies: - '@types/react': 18.2.75 + '@types/react': 18.3.2 dev: false - /@types/react@18.2.75: - resolution: {integrity: sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==} + /@types/react@18.3.2: + resolution: {integrity: sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==} dependencies: - '@types/prop-types': 15.7.5 - csstype: 3.1.1 + '@types/prop-types': 15.7.12 + csstype: 3.1.3 dev: false - /@types/semver@7.3.13: - resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true - /@types/serve-static@1.15.0: - resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 16.18.97 + + /@types/serve-static@1.15.7: + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} dependencies: - '@types/mime': 3.0.1 - '@types/node': 16.18.4 + '@types/http-errors': 2.0.4 + '@types/node': 16.18.97 + '@types/send': 0.17.4 /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - /@types/superagent@4.1.16: - resolution: {integrity: sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==} + /@types/superagent@8.1.7: + resolution: {integrity: sha512-NmIsd0Yj4DDhftfWvvAku482PZum4DBW7U51OvS8gvOkDDY0WT1jsVyDV3hK+vplrsYw8oDwi9QxOM7U68iwww==} dependencies: - '@types/cookiejar': 2.1.2 - '@types/node': 16.18.4 + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 16.18.97 dev: true - /@types/supertest@2.0.12: - resolution: {integrity: sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==} + /@types/supertest@2.0.16: + resolution: {integrity: sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==} dependencies: - '@types/superagent': 4.1.16 + '@types/superagent': 8.1.7 dev: true - /@types/warning@3.0.0: - resolution: {integrity: sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==} + /@types/warning@3.0.3: + resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} dev: false - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - /@types/yargs@16.0.5: - resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==} + /@types/yargs@17.0.32: + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} dependencies: - '@types/yargs-parser': 21.0.0 - dev: true - - /@types/yargs@17.0.15: - resolution: {integrity: sha512-ZHc4W2dnEQPfhn06TBEdWaiUHEZAocYaiVMfwOipY5jcJt/251wVrKCBWBetGZWO5CF8tdb7L3DmdxVlZ2BOIg==} - dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.3 - /@typescript-eslint/eslint-plugin@5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.29.0)(typescript@4.5.2): - resolution: {integrity: sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==} + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@4.5.2): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -2163,24 +2160,25 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.45.0(eslint@8.29.0)(typescript@4.5.2) - '@typescript-eslint/scope-manager': 5.45.0 - '@typescript-eslint/type-utils': 5.45.0(eslint@8.29.0)(typescript@4.5.2) - '@typescript-eslint/utils': 5.45.0(eslint@8.29.0)(typescript@4.5.2) + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@4.5.2) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@4.5.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@4.5.2) debug: 4.3.4 - eslint: 8.29.0 - ignore: 5.2.1 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.1 natural-compare-lite: 1.4.0 - regexpp: 3.2.0 - semver: 7.3.8 + semver: 7.6.2 tsutils: 3.21.0(typescript@4.5.2) typescript: 4.5.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.45.0(eslint@8.29.0)(typescript@4.5.2): - resolution: {integrity: sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==} + /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@4.5.2): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2189,26 +2187,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.45.0 - '@typescript-eslint/types': 5.45.0 - '@typescript-eslint/typescript-estree': 5.45.0(typescript@4.5.2) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.5.2) debug: 4.3.4 - eslint: 8.29.0 + eslint: 8.57.0 typescript: 4.5.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@5.45.0: - resolution: {integrity: sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==} + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.45.0 - '@typescript-eslint/visitor-keys': 5.45.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils@5.45.0(eslint@8.29.0)(typescript@4.5.2): - resolution: {integrity: sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==} + /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@4.5.2): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -2217,23 +2215,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.45.0(typescript@4.5.2) - '@typescript-eslint/utils': 5.45.0(eslint@8.29.0)(typescript@4.5.2) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.5.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@4.5.2) debug: 4.3.4 - eslint: 8.29.0 + eslint: 8.57.0 tsutils: 3.21.0(typescript@4.5.2) typescript: 4.5.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@5.45.0: - resolution: {integrity: sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==} + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.45.0(typescript@4.5.2): - resolution: {integrity: sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==} + /@typescript-eslint/typescript-estree@5.62.0(typescript@4.5.2): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -2241,175 +2239,179 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.45.0 - '@typescript-eslint/visitor-keys': 5.45.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.8 + semver: 7.6.2 tsutils: 3.21.0(typescript@4.5.2) typescript: 4.5.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.45.0(eslint@8.29.0)(typescript@4.5.2): - resolution: {integrity: sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==} + /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@4.5.2): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@types/json-schema': 7.0.11 - '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.45.0 - '@typescript-eslint/types': 5.45.0 - '@typescript-eslint/typescript-estree': 5.45.0(typescript@4.5.2) - eslint: 8.29.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.5.2) + eslint: 8.57.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.29.0) - semver: 7.3.8 + semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@5.45.0: - resolution: {integrity: sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==} + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.45.0 - eslint-visitor-keys: 3.3.0 + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 dev: true - /@ucast/core@1.10.1: - resolution: {integrity: sha512-sXKbvQiagjFh2JCpaHUa64P4UdJbOxYeC5xiZFn8y6iYdb0WkismduE+RmiJrIjw/eLDYmIEXiQeIYYowmkcAw==} + /@ucast/core@1.10.2: + resolution: {integrity: sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==} dev: false - /@ucast/js@3.0.2: - resolution: {integrity: sha512-zxNkdIPVvqJjHI7D/iK8Aai1+59yqU+N7bpHFodVmiTN7ukeNiGGpNmmSjQgsUw7eNcEBnPrZHNzp5UBxwmaPw==} + /@ucast/js@3.0.4: + resolution: {integrity: sha512-TgG1aIaCMdcaEyckOZKQozn1hazE0w90SVdlpIJ/er8xVumE11gYAtSbw/LBeUnA4fFnFWTcw3t6reqseeH/4Q==} dependencies: - '@ucast/core': 1.10.1 + '@ucast/core': 1.10.2 dev: false - /@ucast/mongo2js@1.3.3: - resolution: {integrity: sha512-sBPtMUYg+hRnYeVYKL+ATm8FaRPdlU9PijMhGYKgsPGjV9J4Ks41ytIjGayvKUnBOEhiCaKUUnY4qPeifdqATw==} + /@ucast/mongo2js@1.3.4: + resolution: {integrity: sha512-ahazOr1HtelA5AC1KZ9x0UwPMqqimvfmtSm/PRRSeKKeE5G2SCqTgwiNzO7i9jS8zA3dzXpKVPpXMkcYLnyItA==} dependencies: - '@ucast/core': 1.10.1 - '@ucast/js': 3.0.2 - '@ucast/mongo': 2.4.2 + '@ucast/core': 1.10.2 + '@ucast/js': 3.0.4 + '@ucast/mongo': 2.4.3 dev: false - /@ucast/mongo@2.4.2: - resolution: {integrity: sha512-/zH1TdBJlYGKKD+Wh0oyD+aBvDSWrwHcD8b4tUL9UgHLhzHtkEnMVFuxbw3SRIRsAa01wmy06+LWt+WoZdj1Bw==} + /@ucast/mongo@2.4.3: + resolution: {integrity: sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==} dependencies: - '@ucast/core': 1.10.1 + '@ucast/core': 1.10.2 dev: false - /@vitejs/plugin-react@3.1.0(vite@4.2.1): + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitejs/plugin-react@3.1.0(vite@4.5.3): resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.1.0-beta.0 dependencies: - '@babel/core': 7.21.3 - '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.3) - '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.3) + '@babel/core': 7.24.5 + '@babel/plugin-transform-react-jsx-self': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.5) magic-string: 0.27.0 - react-refresh: 0.14.0 - vite: 4.2.1 + react-refresh: 0.14.2 + vite: 4.5.3 transitivePeerDependencies: - supports-color dev: true - /@webassemblyjs/ast@1.11.1: - resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} + /@webassemblyjs/ast@1.12.1: + resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} dependencies: - '@webassemblyjs/helper-numbers': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - /@webassemblyjs/floating-point-hex-parser@1.11.1: - resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==} + /@webassemblyjs/floating-point-hex-parser@1.11.6: + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} - /@webassemblyjs/helper-api-error@1.11.1: - resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==} + /@webassemblyjs/helper-api-error@1.11.6: + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} - /@webassemblyjs/helper-buffer@1.11.1: - resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==} + /@webassemblyjs/helper-buffer@1.12.1: + resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} - /@webassemblyjs/helper-numbers@1.11.1: - resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==} + /@webassemblyjs/helper-numbers@1.11.6: + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.1 - '@webassemblyjs/helper-api-error': 1.11.1 + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 '@xtuc/long': 4.2.2 - /@webassemblyjs/helper-wasm-bytecode@1.11.1: - resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==} + /@webassemblyjs/helper-wasm-bytecode@1.11.6: + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} - /@webassemblyjs/helper-wasm-section@1.11.1: - resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==} + /@webassemblyjs/helper-wasm-section@1.12.1: + resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-buffer': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/wasm-gen': 1.11.1 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.12.1 - /@webassemblyjs/ieee754@1.11.1: - resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==} + /@webassemblyjs/ieee754@1.11.6: + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} dependencies: '@xtuc/ieee754': 1.2.0 - /@webassemblyjs/leb128@1.11.1: - resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==} + /@webassemblyjs/leb128@1.11.6: + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} dependencies: '@xtuc/long': 4.2.2 - /@webassemblyjs/utf8@1.11.1: - resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==} + /@webassemblyjs/utf8@1.11.6: + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} - /@webassemblyjs/wasm-edit@1.11.1: - resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==} + /@webassemblyjs/wasm-edit@1.12.1: + resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-buffer': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/helper-wasm-section': 1.11.1 - '@webassemblyjs/wasm-gen': 1.11.1 - '@webassemblyjs/wasm-opt': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 - '@webassemblyjs/wast-printer': 1.11.1 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-opt': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/wast-printer': 1.12.1 - /@webassemblyjs/wasm-gen@1.11.1: - resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==} + /@webassemblyjs/wasm-gen@1.12.1: + resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/ieee754': 1.11.1 - '@webassemblyjs/leb128': 1.11.1 - '@webassemblyjs/utf8': 1.11.1 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 - /@webassemblyjs/wasm-opt@1.11.1: - resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==} + /@webassemblyjs/wasm-opt@1.12.1: + resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-buffer': 1.11.1 - '@webassemblyjs/wasm-gen': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 - /@webassemblyjs/wasm-parser@1.11.1: - resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==} + /@webassemblyjs/wasm-parser@1.12.1: + resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-api-error': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/ieee754': 1.11.1 - '@webassemblyjs/leb128': 1.11.1 - '@webassemblyjs/utf8': 1.11.1 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 - /@webassemblyjs/wast-printer@1.11.1: - resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==} + /@webassemblyjs/wast-printer@1.12.1: + resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} dependencies: - '@webassemblyjs/ast': 1.11.1 + '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 /@xtuc/ieee754@1.2.0: @@ -2421,13 +2423,6 @@ packages: /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - /abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - dependencies: - event-target-shim: 5.0.1 - dev: false - /abortcontroller-polyfill@1.7.5: resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} dev: false @@ -2439,27 +2434,27 @@ packages: mime-types: 2.1.35 negotiator: 0.6.3 - /acorn-import-assertions@1.8.0(acorn@8.8.1): - resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} + /acorn-import-assertions@1.9.0(acorn@8.11.3): + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} peerDependencies: acorn: ^8 dependencies: - acorn: 8.8.1 + acorn: 8.11.3 - /acorn-jsx@5.3.2(acorn@8.8.1): + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.8.1 + acorn: 8.11.3 dev: true - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - /acorn@8.8.1: - resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} hasBin: true @@ -2471,16 +2466,12 @@ packages: transitivePeerDependencies: - supports-color - /agentkeepalive@4.3.0: - resolution: {integrity: sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==} + /agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} engines: {node: '>= 8.0.0'} requiresBuild: true dependencies: - debug: 4.3.4 - depd: 2.0.0 humanize-ms: 1.2.1 - transitivePeerDependencies: - - supports-color optional: true /aggregate-error@3.1.0: @@ -2492,7 +2483,7 @@ packages: indent-string: 4.0.0 optional: true - /ajv-formats@2.1.1(ajv@8.11.0): + /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: ajv: ^8.0.0 @@ -2500,7 +2491,7 @@ packages: ajv: optional: true dependencies: - ajv: 8.11.0 + ajv: 8.12.0 dev: true /ajv-keywords@3.5.2(ajv@6.12.6): @@ -2518,8 +2509,8 @@ packages: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - /ajv@8.11.0: - resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 @@ -2579,13 +2570,8 @@ packages: /aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - - /are-we-there-yet@2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.0 + requiresBuild: true + optional: true /are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} @@ -2593,7 +2579,7 @@ packages: requiresBuild: true dependencies: delegates: 1.0.0 - readable-stream: 3.6.0 + readable-stream: 3.6.2 optional: true /arg@4.1.3: @@ -2624,27 +2610,27 @@ packages: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} dev: true - /async@3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} dev: true /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true - /babel-jest@28.1.3(@babel/core@7.21.3): + /babel-jest@28.1.3(@babel/core@7.24.5): resolution: {integrity: sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@jest/transform': 28.1.3 - '@types/babel__core': 7.1.20 + '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 28.1.3(@babel/core@7.21.3) + babel-preset-jest: 28.1.3(@babel/core@7.24.5) chalk: 4.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color @@ -2653,7 +2639,7 @@ packages: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} dependencies: - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.24.5 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -2666,38 +2652,38 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@babel/template': 7.18.10 - '@babel/types': 7.20.5 + '@babel/types': 7.24.5 '@types/babel__core': 7.1.20 - '@types/babel__traverse': 7.18.3 + '@types/babel__traverse': 7.20.5 - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.3): + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.5): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.3 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.3) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.3) - - /babel-preset-jest@28.1.3(@babel/core@7.21.3): + '@babel/core': 7.24.5 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.5) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.5) + + /babel-preset-jest@28.1.3(@babel/core@7.24.5): resolution: {integrity: sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 babel-plugin-jest-hoist: 28.1.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.3) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.5) /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2705,29 +2691,33 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - /bignumber.js@9.1.1: - resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} + /bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} dev: false - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} dev: true + /bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + dependencies: + file-uri-to-path: 1.0.0 + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: buffer: 5.7.1 inherits: 2.0.4 - readable-stream: 3.6.0 - dev: true + readable-stream: 3.6.2 /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dependencies: bytes: 3.1.2 - content-type: 1.0.4 + content-type: 1.0.5 debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 @@ -2741,6 +2731,25 @@ packages: transitivePeerDependencies: - supports-color + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true @@ -2749,12 +2758,12 @@ packages: resolution: {integrity: sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==} dev: false - /bootstrap@5.2.3(@popperjs/core@2.11.6): - resolution: {integrity: sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==} + /bootstrap@5.3.3(@popperjs/core@2.11.8): + resolution: {integrity: sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==} peerDependencies: - '@popperjs/core': ^2.11.6 + '@popperjs/core': ^2.11.8 dependencies: - '@popperjs/core': 2.11.6 + '@popperjs/core': 2.11.8 dev: false /brace-expansion@1.1.11: @@ -2774,32 +2783,22 @@ packages: dependencies: fill-range: 7.0.1 - /browser-tabs-lock@1.2.15: - resolution: {integrity: sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==} + /browser-tabs-lock@1.3.0: + resolution: {integrity: sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==} requiresBuild: true dependencies: lodash: 4.17.21 dev: false - /browserslist@4.21.4: - resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001436 - electron-to-chromium: 1.4.284 - node-releases: 2.0.6 - update-browserslist-db: 1.0.10(browserslist@4.21.4) - - /browserslist@4.21.5: - resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001468 - electron-to-chromium: 1.4.284 - node-releases: 2.0.10 - update-browserslist-db: 1.0.10(browserslist@4.21.5) + caniuse-lite: 1.0.30001617 + electron-to-chromium: 1.4.763 + node-releases: 2.0.14 + update-browserslist-db: 1.0.15(browserslist@4.23.0) /bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} @@ -2820,17 +2819,11 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - /buffer-writer@2.0.0: - resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} - engines: {node: '>=4'} - dev: false - /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -2870,17 +2863,21 @@ packages: promise-inflight: 1.0.1 rimraf: 3.0.2 ssri: 8.0.1 - tar: 6.1.13 + tar: 6.2.1 unique-filename: 1.1.1 transitivePeerDependencies: - bluebird optional: true - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.1.3 + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -2890,7 +2887,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.4.1 + tslib: 2.6.2 dev: true /camelcase@5.3.1: @@ -2901,11 +2898,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - /caniuse-lite@1.0.30001436: - resolution: {integrity: sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==} - - /caniuse-lite@1.0.30001468: - resolution: {integrity: sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==} + /caniuse-lite@1.0.30001617: + resolution: {integrity: sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==} /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -2915,14 +2909,6 @@ packages: escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2950,9 +2936,27 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -2961,22 +2965,22 @@ packages: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} - /ci-info@3.7.0: - resolution: {integrity: sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==} + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - /cjs-module-lexer@1.2.2: - resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} + /cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} /class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} - /classnames@2.3.2: - resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} + /classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} dev: false - /clean-css@5.3.2: - resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} + /clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} dependencies: source-map: 0.6.1 @@ -3008,13 +3012,13 @@ packages: yargs: 16.2.0 dev: false - /cli-spinners@2.7.0: - resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} dev: true - /cli-table3@0.6.2: - resolution: {integrity: sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==} + /cli-table3@0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} engines: {node: 10.* || >= 12.*} dependencies: string-width: 4.2.3 @@ -3055,6 +3059,9 @@ packages: /collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -3075,9 +3082,11 @@ packages: /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true + requiresBuild: true + optional: true - /colorette@2.0.19: - resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true /combined-stream@1.0.8: @@ -3100,8 +3109,8 @@ packages: engines: {node: '>= 12'} dev: true - /component-emitter@1.3.0: - resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} + /component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} dev: true /concat-map@0.0.1: @@ -3113,7 +3122,7 @@ packages: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 - readable-stream: 2.3.7 + readable-stream: 2.3.8 typedarray: 0.0.6 /connect-history-api-fallback@1.6.0: @@ -3126,6 +3135,8 @@ packages: /console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + requiresBuild: true + optional: true /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} @@ -3133,13 +3144,16 @@ packages: dependencies: safe-buffer: 5.2.1 - /content-type@1.0.4: - resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -3147,12 +3161,12 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - /cookiejar@2.1.3: - resolution: {integrity: sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==} + /cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} dev: true - /core-js@3.26.1: - resolution: {integrity: sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==} + /core-js@3.37.0: + resolution: {integrity: sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==} requiresBuild: true dev: false @@ -3170,7 +3184,7 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} dependencies: - '@types/parse-json': 4.0.0 + '@types/parse-json': 4.0.2 import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 @@ -3203,13 +3217,15 @@ packages: engines: {node: '>= 6'} dev: true - /csstype@3.1.1: - resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} dev: false - /date-fns@2.29.3: - resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.24.5 dev: false /debug@2.6.9: @@ -3245,15 +3261,25 @@ packages: dependencies: ms: 2.1.2 + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + /dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge@4.2.2: - resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} /defaults@1.0.4: @@ -3262,6 +3288,14 @@ packages: clone: 1.0.4 dev: true + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -3269,6 +3303,8 @@ packages: /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + requiresBuild: true + optional: true /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} @@ -3283,8 +3319,8 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - /detect-libc@2.0.1: - resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + /detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} /detect-newline@3.1.0: @@ -3323,8 +3359,8 @@ packages: /dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: - '@babel/runtime': 7.21.0 - csstype: 3.1.1 + '@babel/runtime': 7.24.5 + csstype: 3.1.3 dev: false /dom-serializer@1.4.1: @@ -3358,20 +3394,26 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.6.2 dev: true + /dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dev: false + /dotenv-expand@8.0.3: resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==} engines: {node: '>=12'} + dev: true - /dotenv@16.0.1: - resolution: {integrity: sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==} + /dotenv@16.1.4: + resolution: {integrity: sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==} engines: {node: '>=12'} dev: false - /dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} /ecdsa-sig-formatter@1.0.11: @@ -3383,16 +3425,16 @@ packages: /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /ejs@3.1.9: - resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + /ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true dependencies: - jake: 10.8.5 + jake: 10.9.1 dev: true - /electron-to-chromium@1.4.284: - resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} + /electron-to-chromium@1.4.763: + resolution: {integrity: sha512-k4J8NrtJ9QrvHLRo8Q18OncqBCB7tIUyqxRcJnlonQ0ioHKYB988GcDFF3ZePmnb8eHEopDs/wPHR/iGAFgoUQ==} /emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} @@ -3416,13 +3458,12 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true - /enhanced-resolve@5.12.0: - resolution: {integrity: sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==} + /enhanced-resolve@5.16.1: + resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==} engines: {node: '>=10.13.0'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 tapable: 2.2.1 /entities@2.2.0: @@ -3449,41 +3490,51 @@ packages: resolution: {integrity: sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==} dev: false - /es-module-lexer@0.9.3: - resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + /es-module-lexer@1.5.2: + resolution: {integrity: sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==} - /esbuild@0.17.12: - resolution: {integrity: sha512-bX/zHl7Gn2CpQwcMtRogTTBf9l1nl+H6R8nUbjk+RuKqAE3+8FDulLA+pHvX7aA7Xe07Iwa+CWvy9I8Y2qqPKQ==} + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.17.12 - '@esbuild/android-arm64': 0.17.12 - '@esbuild/android-x64': 0.17.12 - '@esbuild/darwin-arm64': 0.17.12 - '@esbuild/darwin-x64': 0.17.12 - '@esbuild/freebsd-arm64': 0.17.12 - '@esbuild/freebsd-x64': 0.17.12 - '@esbuild/linux-arm': 0.17.12 - '@esbuild/linux-arm64': 0.17.12 - '@esbuild/linux-ia32': 0.17.12 - '@esbuild/linux-loong64': 0.17.12 - '@esbuild/linux-mips64el': 0.17.12 - '@esbuild/linux-ppc64': 0.17.12 - '@esbuild/linux-riscv64': 0.17.12 - '@esbuild/linux-s390x': 0.17.12 - '@esbuild/linux-x64': 0.17.12 - '@esbuild/netbsd-x64': 0.17.12 - '@esbuild/openbsd-x64': 0.17.12 - '@esbuild/sunos-x64': 0.17.12 - '@esbuild/win32-arm64': 0.17.12 - '@esbuild/win32-ia32': 0.17.12 - '@esbuild/win32-x64': 0.17.12 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} /escape-html@1.0.3: @@ -3502,16 +3553,16 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-prettier@8.5.0(eslint@8.29.0): - resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} + /eslint-config-prettier@8.10.0(eslint@8.57.0): + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.29.0 + eslint: 8.57.0 dev: true - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.29.0)(prettier@2.8.8): + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.0)(prettier@2.8.8): resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -3522,8 +3573,8 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.29.0 - eslint-config-prettier: 8.5.0(eslint@8.29.0) + eslint: 8.57.0 + eslint-config-prettier: 8.10.0(eslint@8.57.0) prettier: 2.8.8 prettier-linter-helpers: 1.0.0 dev: true @@ -3535,89 +3586,73 @@ packages: esrecurse: 4.3.0 estraverse: 4.3.0 - /eslint-scope@7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 dev: true - /eslint-utils@3.0.0(eslint@8.29.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.29.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - dev: true - - /eslint-visitor-keys@3.3.0: - resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.29.0: - resolution: {integrity: sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint/eslintrc': 1.3.3 - '@humanwhocodes/config-array': 0.11.7 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0(eslint@8.29.0) - eslint-visitor-keys: 3.3.0 - espree: 9.4.1 - esquery: 1.4.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.18.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.1 - import-fresh: 3.3.0 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-sdsl: 4.2.0 js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 + optionator: 0.9.4 strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 text-table: 0.2.0 transitivePeerDependencies: - supports-color dev: true - /espree@9.4.1: - resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.8.1 - acorn-jsx: 5.3.2(acorn@8.8.1) - eslint-visitor-keys: 3.3.0 + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 dev: true /esprima@4.0.1: @@ -3625,8 +3660,8 @@ packages: engines: {node: '>=4'} hasBin: true - /esquery@1.4.0: - resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 @@ -3659,11 +3694,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - /event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - dev: false - /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -3701,6 +3731,10 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + /expect@28.1.3: resolution: {integrity: sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -3719,7 +3753,7 @@ packages: array-flatten: 1.1.1 body-parser: 1.20.1 content-disposition: 0.5.4 - content-type: 1.0.4 + content-type: 1.0.5 cookie: 0.5.0 cookie-signature: 1.0.6 debug: 2.6.9 @@ -3762,15 +3796,19 @@ packages: tmp: 0.0.33 dev: true + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: false + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - /fast-diff@1.2.0: - resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true - /fast-glob@3.2.12: - resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3794,12 +3832,12 @@ packages: resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} dev: false - /fastify-plugin@4.4.0: - resolution: {integrity: sha512-ovwFQG2qNy3jcCROiWpr94Hs0le+c7N/3t7m9aVwbFhkxcR/esp2xu25dP8e617HpQdmeDv+gFX4zagdUhDByw==} + /fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} dev: false - /fastq@1.14.0: - resolution: {integrity: sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==} + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 dev: true @@ -3820,13 +3858,16 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.0.4 + flat-cache: 3.2.0 dev: true + /file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + /filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} dependencies: - minimatch: 5.1.1 + minimatch: 5.1.6 dev: true /fill-range@7.0.1: @@ -3864,43 +3905,40 @@ packages: path-exists: 4.0.0 dev: true - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flatted: 3.2.7 + flatted: 3.3.1 + keyv: 4.5.4 rimraf: 3.0.2 dev: true - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /fork-ts-checker-webpack-plugin@7.2.13(typescript@4.8.4)(webpack@5.74.0): - resolution: {integrity: sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==} + /fork-ts-checker-webpack-plugin@8.0.0(typescript@4.9.5)(webpack@5.82.1): + resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==} engines: {node: '>=12.13.0', yarn: '>=1.0.0'} peerDependencies: typescript: '>3.6.0' - vue-template-compiler: '*' - webpack: ^5.11.0 - peerDependenciesMeta: - vue-template-compiler: - optional: true + webpack: ^5.11.0 dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.24.2 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 7.1.0 - deepmerge: 4.2.2 + deepmerge: 4.3.1 fs-extra: 10.1.0 - memfs: 3.4.12 + memfs: 3.5.3 minimatch: 3.1.2 - node-abort-controller: 3.0.1 - schema-utils: 3.1.1 - semver: 7.3.8 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.6.2 tapable: 2.2.1 - typescript: 4.8.4 - webpack: 5.74.0(@swc/core@1.3.56) + typescript: 4.9.5 + webpack: 5.82.1(@swc/core@1.5.5) dev: true /form-data@4.0.0: @@ -3912,13 +3950,13 @@ packages: mime-types: 2.1.35 dev: true - /formidable@2.1.1: - resolution: {integrity: sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==} + /formidable@2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} dependencies: dezalgo: 1.0.4 hexoid: 1.0.0 once: 1.4.0 - qs: 6.11.0 + qs: 6.12.1 dev: true /forwarded@0.2.0: @@ -3929,13 +3967,16 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 - universalify: 2.0.0 + universalify: 2.0.1 dev: true /fs-minipass@2.1.0: @@ -3944,36 +3985,22 @@ packages: dependencies: minipass: 3.3.6 - /fs-monkey@1.0.3: - resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} + /fs-monkey@1.0.6: + resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} dev: true /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - - /gauge@3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} /gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} @@ -3990,24 +4017,24 @@ packages: wide-align: 1.1.5 optional: true - /gaxios@5.1.0: - resolution: {integrity: sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==} + /gaxios@5.1.3: + resolution: {integrity: sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==} engines: {node: '>=12'} dependencies: extend: 3.0.2 https-proxy-agent: 5.0.1 is-stream: 2.0.1 - node-fetch: 2.6.7 + node-fetch: 2.7.0 transitivePeerDependencies: - encoding - supports-color dev: false - /gcp-metadata@5.2.0: - resolution: {integrity: sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==} + /gcp-metadata@5.3.0: + resolution: {integrity: sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==} engines: {node: '>=12'} dependencies: - gaxios: 5.1.0 + gaxios: 5.1.3 json-bigint: 1.0.0 transitivePeerDependencies: - encoding @@ -4022,12 +4049,15 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-intrinsic@1.1.3: - resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - has: 1.0.3 + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 has-symbols: 1.0.3 + hasown: 2.0.2 /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} @@ -4044,6 +4074,9 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -4071,23 +4104,33 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /glob@8.0.3: - resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==} + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 5.1.1 + minimatch: 5.1.6 once: 1.4.0 dev: false + /glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.11.1 + dev: true + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - /globals@13.18.0: - resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==} + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -4099,22 +4142,22 @@ packages: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.2.12 - ignore: 5.2.1 + fast-glob: 3.3.2 + ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 dev: true - /google-auth-library@8.7.0: - resolution: {integrity: sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==} + /google-auth-library@8.9.0: + resolution: {integrity: sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==} engines: {node: '>=12'} dependencies: arrify: 2.0.1 base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 fast-text-encoding: 1.0.6 - gaxios: 5.1.0 - gcp-metadata: 5.2.0 + gaxios: 5.1.3 + gcp-metadata: 5.3.0 gtoken: 6.1.2 jws: 4.0.0 lru-cache: 6.0.0 @@ -4136,11 +4179,11 @@ packages: engines: {node: '>=12.0.0'} dependencies: extend: 3.0.2 - gaxios: 5.1.0 - google-auth-library: 8.7.0 - qs: 6.11.0 + gaxios: 5.1.3 + google-auth-library: 8.9.0 + qs: 6.12.1 url-template: 2.0.8 - uuid: 9.0.0 + uuid: 9.0.1 transitivePeerDependencies: - encoding - supports-color @@ -4150,25 +4193,33 @@ packages: resolution: {integrity: sha512-Ny6zJOGn5P/YDT6GQbJU6K0lSzEu4Yuxnsn45ZgBIeSQ1RM0FolEjUToLXquZd89DU9wUfqA5XYHPEctk1TFWg==} engines: {node: '>=12.0.0'} dependencies: - google-auth-library: 8.7.0 + google-auth-library: 8.9.0 googleapis-common: 6.0.4 transitivePeerDependencies: - encoding - supports-color dev: false + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + /graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - /grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true /gtoken@6.1.2: resolution: {integrity: sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==} engines: {node: '>=12.0.0'} dependencies: - gaxios: 5.1.0 + gaxios: 5.1.3 google-p12-pem: 4.0.1 jws: 4.0.0 transitivePeerDependencies: @@ -4184,18 +4235,29 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} /has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + requiresBuild: true + optional: true - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} @@ -4220,12 +4282,12 @@ packages: hasBin: true dependencies: camel-case: 4.1.2 - clean-css: 5.3.2 + clean-css: 5.3.3 commander: 8.3.0 he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.16.1 + terser: 5.31.0 dev: true /http-cache-semantics@4.1.1: @@ -4301,8 +4363,8 @@ packages: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} dev: true - /ignore@5.2.1: - resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==} + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} dev: true @@ -4346,9 +4408,12 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /inquirer@7.3.3: - resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} - engines: {node: '>=8.0.0'} + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + /inquirer@8.2.4: + resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==} + engines: {node: '>=12.0.0'} dependencies: ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -4358,15 +4423,17 @@ packages: figures: 3.2.0 lodash: 4.17.21 mute-stream: 0.0.8 + ora: 5.4.1 run-async: 2.4.1 - rxjs: 6.6.7 + rxjs: 7.8.1 string-width: 4.2.3 strip-ansi: 6.0.1 through: 2.3.8 + wrap-ansi: 7.0.0 dev: true - /inquirer@8.2.4: - resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==} + /inquirer@8.2.5: + resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} engines: {node: '>=12.0.0'} dependencies: ansi-escapes: 4.3.2 @@ -4379,7 +4446,7 @@ packages: mute-stream: 0.0.8 ora: 5.4.1 run-async: 2.4.1 - rxjs: 7.5.7 + rxjs: 7.8.1 string-width: 4.2.3 strip-ansi: 6.0.1 through: 2.3.8 @@ -4397,9 +4464,13 @@ packages: loose-envify: 1.4.0 dev: false - /ip@2.0.0: - resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + /ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} requiresBuild: true + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 optional: true /ipaddr.js@1.9.1: @@ -4413,13 +4484,13 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 dev: true - /is-core-module@2.11.0: - resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - has: 1.0.3 + hasown: 2.0.2 /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -4476,28 +4547,28 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} requiresBuild: true - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} /istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.21.3 - '@babel/parser': 7.21.3 + '@babel/core': 7.24.5 + '@babel/parser': 7.24.5 '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 transitivePeerDependencies: - supports-color - /istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 3.1.0 + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 supports-color: 7.2.0 /istanbul-lib-source-maps@4.0.1: @@ -4505,28 +4576,28 @@ packages: engines: {node: '>=10'} dependencies: debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: - supports-color - /istanbul-reports@3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} + /istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} dependencies: html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 + istanbul-lib-report: 3.0.1 /iterare@1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} - /jake@10.8.5: - resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} + /jake@10.9.1: + resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} engines: {node: '>=10'} hasBin: true dependencies: - async: 3.2.4 + async: 3.2.5 chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 @@ -4547,7 +4618,7 @@ packages: '@jest/expect': 28.1.3 '@jest/test-result': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -4565,7 +4636,7 @@ packages: transitivePeerDependencies: - supports-color - /jest-cli@28.1.3(@types/node@16.18.4)(ts-node@10.7.0): + /jest-cli@28.1.3(@types/node@16.18.97)(ts-node@10.7.0): resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true @@ -4580,19 +4651,19 @@ packages: '@jest/types': 28.1.3 chalk: 4.1.2 exit: 0.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + jest-config: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) jest-util: 28.1.3 jest-validate: 28.1.3 prompts: 2.4.2 - yargs: 17.6.2 + yargs: 17.7.2 transitivePeerDependencies: - '@types/node' - supports-color - ts-node - /jest-config@28.1.3(@types/node@16.18.4)(ts-node@10.7.0): + /jest-config@28.1.3(@types/node@16.18.97)(ts-node@10.7.0): resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: @@ -4604,16 +4675,16 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 '@jest/test-sequencer': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 - babel-jest: 28.1.3(@babel/core@7.21.3) + '@types/node': 16.18.97 + babel-jest: 28.1.3(@babel/core@7.24.5) chalk: 4.1.2 - ci-info: 3.7.0 - deepmerge: 4.2.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-circus: 28.1.3 jest-environment-node: 28.1.3 jest-get-type: 28.0.2 @@ -4627,7 +4698,7 @@ packages: pretty-format: 28.1.3 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.7.0(@swc/core@1.3.56)(@types/node@16.18.4)(typescript@4.5.2) + ts-node: 10.7.0(@swc/core@1.5.5)(@types/node@16.18.97)(typescript@4.5.2) transitivePeerDependencies: - supports-color @@ -4663,7 +4734,7 @@ packages: '@jest/environment': 28.1.3 '@jest/fake-timers': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 jest-mock: 28.1.3 jest-util: 28.1.3 @@ -4676,18 +4747,18 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/graceful-fs': 4.1.5 - '@types/node': 16.18.4 + '@types/graceful-fs': 4.1.9 + '@types/node': 16.18.97 anymatch: 3.1.3 fb-watchman: 2.0.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-regex-util: 28.0.2 jest-util: 28.1.3 jest-worker: 28.1.3 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /jest-leak-detector@28.1.3: resolution: {integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==} @@ -4725,19 +4796,19 @@ packages: jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 typescript: ^3.0.0 || ^4.0.0 dependencies: - jest: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + jest: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) ts-essentials: 7.0.3(typescript@4.5.2) typescript: 4.5.2 dev: true - /jest-mock-extended@3.0.5(jest@28.1.3)(typescript@4.5.2): - resolution: {integrity: sha512-/eHdaNPUAXe7f65gHH5urc8SbRVWjYxBqmCgax2uqOBJy8UUcCBMN1upj1eZ8y/i+IqpyEm4Kq0VKss/GCCTdw==} + /jest-mock-extended@3.0.7(jest@28.1.3)(typescript@4.5.2): + resolution: {integrity: sha512-7lsKdLFcW9B9l5NzZ66S/yTQ9k8rFtnwYdCNuRU/81fqDWicNDVhitTSPnrGmNeNm0xyw0JHexEOShrIKRCIRQ==} peerDependencies: jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 dependencies: - jest: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) - ts-essentials: 7.0.3(typescript@4.5.2) + jest: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) + ts-essentials: 10.0.0(typescript@4.5.2) typescript: 4.5.2 dev: false @@ -4746,7 +4817,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 /jest-pnp-resolver@1.2.3(jest-resolve@28.1.3): resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} @@ -4777,13 +4848,13 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: chalk: 4.1.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 28.1.3 jest-pnp-resolver: 1.2.3(jest-resolve@28.1.3) jest-util: 28.1.3 jest-validate: 28.1.3 - resolve: 1.22.1 - resolve.exports: 1.1.0 + resolve: 1.22.8 + resolve.exports: 1.1.1 slash: 3.0.0 /jest-runner@28.1.3: @@ -4795,10 +4866,10 @@ packages: '@jest/test-result': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 chalk: 4.1.2 emittery: 0.10.2 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-docblock: 28.1.1 jest-environment-node: 28.1.3 jest-haste-map: 28.1.3 @@ -4826,11 +4897,11 @@ packages: '@jest/transform': 28.1.3 '@jest/types': 28.1.3 chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 + cjs-module-lexer: 1.3.1 + collect-v8-coverage: 1.0.2 execa: 5.1.1 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-haste-map: 28.1.3 jest-message-util: 28.1.3 jest-mock: 28.1.3 @@ -4847,20 +4918,20 @@ packages: resolution: {integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@babel/core': 7.21.3 - '@babel/generator': 7.21.3 - '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.21.3) - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 + '@babel/core': 7.24.5 + '@babel/generator': 7.24.5 + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.5) + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 '@jest/expect-utils': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 - '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.1 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.3) + '@types/babel__traverse': 7.20.5 + '@types/prettier': 2.7.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.5) chalk: 4.1.2 expect: 28.1.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jest-diff: 28.1.3 jest-get-type: 28.0.2 jest-haste-map: 28.1.3 @@ -4869,7 +4940,7 @@ packages: jest-util: 28.1.3 natural-compare: 1.4.0 pretty-format: 28.1.3 - semver: 7.3.8 + semver: 7.6.2 transitivePeerDependencies: - supports-color @@ -4878,10 +4949,10 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 chalk: 4.1.2 - ci-info: 3.7.0 - graceful-fs: 4.2.10 + ci-info: 3.9.0 + graceful-fs: 4.2.11 picomatch: 2.3.1 /jest-validate@28.1.3: @@ -4901,7 +4972,7 @@ packages: dependencies: '@jest/test-result': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.4 + '@types/node': 16.18.97 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.10.2 @@ -4912,7 +4983,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.18.4 + '@types/node': 16.18.97 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -4920,11 +4991,11 @@ packages: resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@types/node': 16.18.4 + '@types/node': 16.18.97 merge-stream: 2.0.0 supports-color: 8.1.1 - /jest@28.1.3(@types/node@16.18.4)(ts-node@10.7.0): + /jest@28.1.3(@types/node@16.18.97)(ts-node@10.7.0): resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true @@ -4937,28 +5008,24 @@ packages: '@jest/core': 28.1.3(ts-node@10.7.0) '@jest/types': 28.1.3 import-local: 3.1.0 - jest-cli: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + jest-cli: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) transitivePeerDependencies: - '@types/node' - supports-color - ts-node - /joi@17.7.0: - resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==} + /joi@17.13.1: + resolution: {integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==} dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 - '@sideway/address': 4.1.4 - '@sideway/formula': 3.0.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 dev: false - /jose@4.14.4: - resolution: {integrity: sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==} - - /js-sdsl@4.2.0: - resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} - dev: true + /jose@4.15.5: + resolution: {integrity: sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==} /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4976,6 +5043,11 @@ packages: dependencies: argparse: 2.0.1 + /jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + requiresBuild: true + optional: true + /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} @@ -4984,9 +5056,13 @@ packages: /json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} dependencies: - bignumber.js: 9.1.1 + bignumber.js: 9.1.2 dev: false + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -5001,25 +5077,19 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json5@2.2.1: - resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} - engines: {node: '>=6'} - hasBin: true - dev: true - /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - /jsonc-parser@3.1.0: - resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==} - dev: true - /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true + /jsonc-parser@3.2.1: + resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + dev: true + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -5028,9 +5098,9 @@ packages: graceful-fs: 4.2.10 dev: true - /jsonwebtoken@8.5.1: - resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} - engines: {node: '>=4', npm: '>=1.4.28'} + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} dependencies: jws: 3.2.2 lodash.includes: 4.3.0 @@ -5041,7 +5111,7 @@ packages: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 5.7.1 + semver: 7.6.2 dev: false /jwa@1.4.1: @@ -5060,16 +5130,16 @@ packages: safe-buffer: 5.2.1 dev: false - /jwks-rsa@3.0.0: - resolution: {integrity: sha512-x9qNrP/kD6tOfrLzBVC5HaneBTR+fCEGIjwk/xSdl+KA7Tzf+R3oiY9ibrONKVLF9fR0V03enkitYPZkO65fAQ==} + /jwks-rsa@3.1.0: + resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} engines: {node: '>=14'} dependencies: - '@types/express': 4.17.14 - '@types/jsonwebtoken': 8.5.9 + '@types/express': 4.17.21 + '@types/jsonwebtoken': 9.0.6 debug: 4.3.4 - jose: 4.14.4 + jose: 4.15.5 limiter: 1.1.5 - lru-memoizer: 2.1.4 + lru-memoizer: 2.2.0 transitivePeerDependencies: - supports-color dev: false @@ -5088,6 +5158,12 @@ packages: safe-buffer: 5.2.1 dev: false + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + /kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -5189,7 +5265,12 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.4.1 + tslib: 2.6.2 + dev: true + + /lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + engines: {node: 14 || >=16.14} dev: true /lru-cache@4.0.2: @@ -5210,37 +5291,37 @@ packages: dependencies: yallist: 4.0.0 - /lru-memoizer@2.1.4: - resolution: {integrity: sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==} + /lru-memoizer@2.2.0: + resolution: {integrity: sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==} dependencies: lodash.clonedeep: 4.5.0 lru-cache: 4.0.2 dev: false - /macos-release@2.5.0: - resolution: {integrity: sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==} + /macos-release@2.5.1: + resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} engines: {node: '>=6'} dev: true - /magic-string@0.26.2: - resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==} + /magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} dependencies: - sourcemap-codec: 1.4.8 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /magic-string@0.27.0: - resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + /magic-string@0.30.0: + resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} engines: {node: '>=12'} dependencies: - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} dependencies: - semver: 6.3.0 + semver: 7.6.2 /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -5250,7 +5331,7 @@ packages: engines: {node: '>= 10'} requiresBuild: true dependencies: - agentkeepalive: 4.3.0 + agentkeepalive: 4.5.0 cacache: 15.3.0 http-cache-semantics: 4.1.1 http-proxy-agent: 4.0.1 @@ -5280,11 +5361,11 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - /memfs@3.4.12: - resolution: {integrity: sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==} + /memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} dependencies: - fs-monkey: 1.0.3 + fs-monkey: 1.0.6 dev: true /merge-descriptors@1.0.1: @@ -5330,23 +5411,40 @@ packages: hasBin: true dev: true + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - /minimatch@5.1.1: - resolution: {integrity: sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==} + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 - /minimist@1.2.7: - resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + /minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} /minipass-collect@1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} @@ -5401,6 +5499,16 @@ packages: /minipass@4.2.8: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} + dev: true + + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + /minipass@7.1.1: + resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true /minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} @@ -5409,25 +5517,28 @@ packages: minipass: 3.3.6 yallist: 4.0.0 + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true dependencies: - minimist: 1.2.7 + minimist: 1.2.8 /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - /moment-timezone@0.5.41: - resolution: {integrity: sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==} + /moment-timezone@0.5.45: + resolution: {integrity: sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==} dependencies: - moment: 2.29.4 + moment: 2.30.1 dev: false - /moment@2.29.4: - resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + /moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} dev: false /ms@2.0.0: @@ -5463,12 +5574,15 @@ packages: thenify-all: 1.6.0 dev: false - /nanoid@3.3.4: - resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true @@ -5487,15 +5601,22 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.4.1 + tslib: 2.6.2 dev: true - /node-abort-controller@3.0.1: - resolution: {integrity: sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==} + /node-abi@3.62.0: + resolution: {integrity: sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.2 + + /node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} dev: true - /node-addon-api@4.3.0: - resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + /node-addon-api@7.1.0: + resolution: {integrity: sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==} + engines: {node: ^16 || ^18 || >= 20} /node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} @@ -5503,8 +5624,8 @@ packages: lodash: 4.17.21 dev: true - /node-fetch@2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 @@ -5527,13 +5648,13 @@ packages: dependencies: env-paths: 2.2.1 glob: 7.2.3 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 make-fetch-happen: 9.1.0 nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.3.8 - tar: 6.1.13 + semver: 7.6.2 + tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: - bluebird @@ -5550,23 +5671,20 @@ packages: /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - /node-releases@2.0.10: - resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - - /node-releases@2.0.6: - resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} /nodemon@2.0.22: resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} engines: {node: '>=8.10.0'} hasBin: true dependencies: - chokidar: 3.5.3 + chokidar: 3.6.0 debug: 3.2.7(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 - semver: 5.7.1 + semver: 5.7.2 simple-update-notifier: 1.1.0 supports-color: 5.5.0 touch: 3.1.0 @@ -5584,8 +5702,10 @@ packages: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} engines: {node: '>=6'} hasBin: true + requiresBuild: true dependencies: abbrev: 1.1.1 + optional: true /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -5597,14 +5717,6 @@ packages: dependencies: path-key: 3.1.1 - /npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - /npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -5626,12 +5738,8 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - /object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - - /object-inspect@1.12.2: - resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} /on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} @@ -5650,8 +5758,8 @@ packages: dependencies: mimic-fn: 2.1.0 - /optionator@0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + /optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} dependencies: deep-is: 0.1.4 @@ -5659,7 +5767,7 @@ packages: levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - word-wrap: 1.2.3 + word-wrap: 1.2.5 dev: true /ora@5.4.1: @@ -5669,7 +5777,7 @@ packages: bl: 4.1.0 chalk: 4.1.2 cli-cursor: 3.1.0 - cli-spinners: 2.7.0 + cli-spinners: 2.9.2 is-interactive: 1.0.0 is-unicode-supported: 0.1.0 log-symbols: 4.1.0 @@ -5681,7 +5789,7 @@ packages: resolution: {integrity: sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==} engines: {node: '>=10'} dependencies: - macos-release: 2.5.0 + macos-release: 2.5.1 windows-release: 4.0.0 dev: true @@ -5727,15 +5835,11 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - /packet-reader@1.0.0: - resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} - dev: false - /param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.6.2 dev: true /parent-module@1.0.1: @@ -5749,7 +5853,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.24.2 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -5776,13 +5880,13 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.6.2 dev: true - /passport-jwt@4.0.0: - resolution: {integrity: sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==} + /passport-jwt@4.0.1: + resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==} dependencies: - jsonwebtoken: 8.5.1 + jsonwebtoken: 9.0.2 passport-strategy: 1.0.0 dev: false @@ -5815,6 +5919,14 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + /path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + dependencies: + lru-cache: 10.2.2 + minipass: 7.1.1 + dev: true + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -5834,8 +5946,14 @@ packages: resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} dev: false - /pg-connection-string@2.5.0: - resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} + /pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + requiresBuild: true + dev: false + optional: true + + /pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} dev: false /pg-int8@1.0.1: @@ -5843,16 +5961,16 @@ packages: engines: {node: '>=4.0.0'} dev: false - /pg-pool@3.5.2(pg@8.8.0): - resolution: {integrity: sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==} + /pg-pool@3.6.2(pg@8.11.5): + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} peerDependencies: pg: '>=8.0' dependencies: - pg: 8.8.0 + pg: 8.11.5 dev: false - /pg-protocol@1.5.0: - resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==} + /pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} dev: false /pg-types@2.2.0: @@ -5866,8 +5984,8 @@ packages: postgres-interval: 1.2.0 dev: false - /pg@8.8.0: - resolution: {integrity: sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==} + /pg@8.11.5: + resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} engines: {node: '>= 8.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -5875,19 +5993,19 @@ packages: pg-native: optional: true dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.5.0 - pg-pool: 3.5.2(pg@8.8.0) - pg-protocol: 1.5.0 + pg-connection-string: 2.6.4 + pg-pool: 3.6.2(pg@8.11.5) + pg-protocol: 1.6.1 pg-types: 2.2.0 pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 dev: false /pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} dependencies: - split2: 4.1.0 + split2: 4.2.0 dev: false /picocolors@1.0.0: @@ -5897,8 +6015,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - /pirates@4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} /pkg-dir@4.2.0: @@ -5912,13 +6030,13 @@ packages: engines: {node: '>=4'} dev: true - /postcss@8.4.21: - resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} dependencies: - nanoid: 3.3.4 + nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 dev: true /postgres-array@2.0.0: @@ -5943,6 +6061,24 @@ packages: xtend: 4.0.2 dev: false + /prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.62.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -5952,7 +6088,7 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} dependencies: - fast-diff: 1.2.0 + fast-diff: 1.3.0 dev: true /prettier@2.8.8: @@ -5973,11 +6109,6 @@ packages: /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - /process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - dev: false - /promise-inflight@1.0.1: resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} requiresBuild: true @@ -5988,8 +6119,8 @@ packages: optional: true optional: true - /promise-polyfill@8.2.3: - resolution: {integrity: sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg==} + /promise-polyfill@8.3.0: + resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==} dev: false /promise-retry@2.0.1: @@ -6008,12 +6139,12 @@ packages: kleur: 3.0.3 sisteransi: 1.0.5 - /prop-types-extra@1.1.1(react@18.2.0): + /prop-types-extra@1.1.1(react@18.3.1): resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==} peerDependencies: react: '>=0.14.0' dependencies: - react: 18.2.0 + react: 18.3.1 react-is: 16.13.1 warning: 4.0.3 dev: false @@ -6046,17 +6177,22 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true - /punycode@2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} /qs@6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} dependencies: - side-channel: 1.0.4 + side-channel: 1.0.6 + + /qs@6.12.1: + resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.6 /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -6080,8 +6216,26 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 - /react-bootstrap@2.7.2(@types/react@18.2.75)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-WDSln+mG4RLLFO01stkj2bEx/3MF4YihK9D/dWnHaSxOiQZLbhhlf95D2Jb20X3t2m7vMxRe888FVrfLJoGmmA==} + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + /react-bootstrap@2.10.2(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-UvB7mRqQjivdZNxJNEA2yOQRB7L9N43nBnKc33K47+cH90/ujmnMwatTCwQLu83gLhrzAl8fsa6Lqig/KLghaA==} peerDependencies: '@types/react': '>=16.14.8' react: '>=16.14.0' @@ -6090,31 +6244,40 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.21.0 - '@restart/hooks': 0.4.9(react@18.2.0) - '@restart/ui': 1.6.2(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.75 - '@types/react-transition-group': 4.4.5 - classnames: 2.3.2 + '@babel/runtime': 7.24.5 + '@restart/hooks': 0.4.16(react@18.3.1) + '@restart/ui': 1.6.9(react-dom@18.3.1)(react@18.3.1) + '@types/react': 18.3.2 + '@types/react-transition-group': 4.4.10 + classnames: 2.5.1 dom-helpers: 5.2.1 invariant: 2.2.4 prop-types: 15.8.1 - prop-types-extra: 1.1.1(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - uncontrollable: 7.2.1(react@18.2.0) + prop-types-extra: 1.1.1(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) + uncontrollable: 7.2.1(react@18.3.1) warning: 4.0.3 dev: false - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + /react-dom@18.3.1(react@18.3.1): + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: - react: ^18.2.0 + react: ^18.3.1 dependencies: loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 + react: 18.3.1 + scheduler: 0.23.2 + dev: false + + /react-hook-form@7.51.4(react@18.3.1): + resolution: {integrity: sha512-V14i8SEkh+V1gs6YtD0hdHYnoL4tp/HX/A45wWQN15CYr9bFRmmRdYStSO5L65lCCZRF+kYiSKhm9alqbcdiVA==} + engines: {node: '>=12.22.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + dependencies: + react: 18.3.1 dev: false /react-is@16.13.1: @@ -6128,69 +6291,69 @@ packages: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} dev: false - /react-moment@1.1.3(moment@2.29.4)(prop-types@15.8.1)(react@18.2.0): + /react-moment@1.1.3(moment@2.30.1)(prop-types@15.8.1)(react@18.3.1): resolution: {integrity: sha512-8EPvlUL8u6EknPp1ISF5MQ3wx2OHJVXIP/iZc4wRh3iV3XozftZERDv9ANZeAtMlhNNQHdFoqcZHFUkBSTONfA==} peerDependencies: moment: ^2.29.0 prop-types: ^15.7.0 react: ^16.0 || ^17.0.0 || ^18.0.0 dependencies: - moment: 2.29.4 + moment: 2.30.1 prop-types: 15.8.1 - react: 18.2.0 + react: 18.3.1 dev: false - /react-refresh@0.14.0: - resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + /react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} dev: true - /react-router-dom@6.9.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==} - engines: {node: '>=14'} + /react-router-dom@6.23.1(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==} + engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' dependencies: - '@remix-run/router': 1.4.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router: 6.9.0(react@18.2.0) + '@remix-run/router': 1.16.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.23.1(react@18.3.1) dev: false - /react-router@6.9.0(react@18.2.0): - resolution: {integrity: sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==} - engines: {node: '>=14'} + /react-router@6.23.1(react@18.3.1): + resolution: {integrity: sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==} + engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' dependencies: - '@remix-run/router': 1.4.0 - react: 18.2.0 + '@remix-run/router': 1.16.1 + react: 18.3.1 dev: false - /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): + /react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: react: '>=16.6.0' react-dom: '>=16.6.0' dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.24.5 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + /react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 dev: false - /readable-stream@2.3.7: - resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -6200,24 +6363,14 @@ packages: string_decoder: 1.1.1 util-deprecate: 1.0.2 - /readable-stream@3.6.0: - resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readable-stream@4.2.0: - resolution: {integrity: sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - dev: false - /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -6229,21 +6382,16 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} dependencies: - resolve: 1.22.1 + resolve: 1.22.8 dev: true - /reflect-metadata@0.1.13: - resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /reflect-metadata@0.1.14: + resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} - /regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} dev: false - /regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - dev: true - /relateurl@0.2.7: resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} engines: {node: '>= 0.10'} @@ -6273,15 +6421,15 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - /resolve.exports@1.1.0: - resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} + /resolve.exports@1.1.1: + resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==} engines: {node: '>=10'} - /resolve@1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.11.0 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -6310,12 +6458,20 @@ packages: dependencies: glob: 7.2.3 - /rollup@3.20.0: - resolution: {integrity: sha512-YsIfrk80NqUDrxrjWPXUa7PWvAfegZEXHuPsEZg58fGCdjL1I9C1i/NaG+L+27kxxwkrG/QEDEQc8s/ynXWWGQ==} + /rimraf@4.4.1: + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 9.3.5 + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /run-async@2.4.1: @@ -6329,17 +6485,10 @@ packages: queue-microtask: 1.2.3 dev: true - /rxjs@6.6.7: - resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} - engines: {npm: '>=2.0.0'} - dependencies: - tslib: 1.14.1 - dev: true - - /rxjs@7.5.7: - resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==} + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.4.1 + tslib: 2.6.2 /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -6350,30 +6499,31 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sax@1.2.4: - resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + /sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} dev: false - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + /scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} dependencies: loose-envify: 1.4.0 dev: false - /schema-utils@3.1.1: - resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==} + /schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - /semver@5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true + dev: true - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true /semver@7.0.0: @@ -6381,12 +6531,10 @@ packages: hasBin: true dev: true - /semver@7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + /semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} hasBin: true - dependencies: - lru-cache: 6.0.0 /send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} @@ -6408,8 +6556,8 @@ packages: transitivePeerDependencies: - supports-color - /serialize-javascript@6.0.0: - resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + /serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} dependencies: randombytes: 2.1.0 @@ -6426,6 +6574,19 @@ packages: /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + requiresBuild: true + optional: true + + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -6458,16 +6619,28 @@ packages: rechoir: 0.6.2 dev: true - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.3 - object-inspect: 1.12.2 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + /simple-update-notifier@1.1.0: resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} engines: {node: '>=8.10.0'} @@ -6495,22 +6668,22 @@ packages: dependencies: agent-base: 6.0.2 debug: 4.3.4 - socks: 2.7.1 + socks: 2.8.3 transitivePeerDependencies: - supports-color optional: true - /socks@2.7.1: - resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + /socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} requiresBuild: true dependencies: - ip: 2.0.0 + ip-address: 9.0.5 smart-buffer: 4.2.0 optional: true - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} dev: true @@ -6535,30 +6708,34 @@ packages: engines: {node: '>= 8'} dev: true - /sourcemap-codec@1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - dev: true - - /split2@4.1.0: - resolution: {integrity: sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==} + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} dev: false /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - /sqlite3@5.1.6: - resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + requiresBuild: true + optional: true + + /sqlite3@5.1.7: + resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} requiresBuild: true + peerDependenciesMeta: + node-gyp: + optional: true dependencies: - '@mapbox/node-pre-gyp': 1.0.10 - node-addon-api: 4.3.0 - tar: 6.1.13 + bindings: 1.5.0 + node-addon-api: 7.1.0 + prebuild-install: 7.1.2 + tar: 6.2.1 optionalDependencies: node-gyp: 8.4.1 transitivePeerDependencies: - bluebird - - encoding - supports-color /ssri@8.0.1: @@ -6627,34 +6804,39 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - /superagent@8.0.5: - resolution: {integrity: sha512-lQVE0Praz7nHiSaJLKBM/cZyi7J0E4io8tWnGSBdBrqAzhzrjQ/F5iGP9Zr29CJC8N5zYdhG2kKaNcB6dKxp7g==} + /superagent@8.1.2: + resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net dependencies: - component-emitter: 1.3.0 - cookiejar: 2.1.3 + component-emitter: 1.3.1 + cookiejar: 2.1.4 debug: 4.3.4 fast-safe-stringify: 2.1.1 form-data: 4.0.0 - formidable: 2.1.1 + formidable: 2.1.2 methods: 1.1.2 mime: 2.6.0 - qs: 6.11.0 - semver: 7.3.8 + qs: 6.12.1 + semver: 7.6.2 transitivePeerDependencies: - supports-color dev: true - /supertest@6.3.2: - resolution: {integrity: sha512-mSmbW/sPpBU6K8w8189ZiHdc62zMe7dCHpC2ktS9tc0/d2DN0FaxNbDJJNFknZD4jCrGJpxkiFoVyemvKgOdwA==} + /supertest@6.3.4: + resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} engines: {node: '>=6.4.0'} dependencies: methods: 1.1.2 - superagent: 8.0.5 + superagent: 8.1.2 transitivePeerDependencies: - supports-color dev: true @@ -6688,8 +6870,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - /swagger-ui-dist@4.15.1: - resolution: {integrity: sha512-DlZARu6ckUFqDe0j5IPayO4k0gQvYQw9Un02MhxAgaMtVnTH2vmyyDe+yKeV0r1LiiPx3JbasdS/5Yyb/AV3iw==} + /swagger-ui-dist@4.18.2: + resolution: {integrity: sha512-oVBoBl9Dg+VJw8uRWDxlyUyHoNEDC0c1ysT6+Boy6CTgr2rUcLcfPon4RvxgS2/taNW6O0+US+Z/dlAsWFjOAQ==} dev: false /symbol-observable@4.0.0: @@ -6701,13 +6883,31 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} - /tar@6.1.13: - resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + /tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.2.8 + minipass: 5.0.0 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 @@ -6719,8 +6919,8 @@ packages: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - /terser-webpack-plugin@5.3.6(@swc/core@1.3.56)(webpack@5.74.0): - resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} + /terser-webpack-plugin@5.3.10(@swc/core@1.5.5)(webpack@5.82.1): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -6735,17 +6935,17 @@ packages: uglify-js: optional: true dependencies: - '@jridgewell/trace-mapping': 0.3.17 - '@swc/core': 1.3.56 + '@jridgewell/trace-mapping': 0.3.25 + '@swc/core': 1.5.5 jest-worker: 27.5.1 - schema-utils: 3.1.1 - serialize-javascript: 6.0.0 - terser: 5.16.1 - webpack: 5.74.0(@swc/core@1.3.56) + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.0 + webpack: 5.82.1(@swc/core@1.5.5) dev: true - /terser-webpack-plugin@5.3.6(@swc/core@1.3.56)(webpack@5.75.0): - resolution: {integrity: sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==} + /terser-webpack-plugin@5.3.10(@swc/core@1.5.5)(webpack@5.91.0): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -6760,21 +6960,21 @@ packages: uglify-js: optional: true dependencies: - '@jridgewell/trace-mapping': 0.3.17 - '@swc/core': 1.3.56 + '@jridgewell/trace-mapping': 0.3.25 + '@swc/core': 1.5.5 jest-worker: 27.5.1 - schema-utils: 3.1.1 - serialize-javascript: 6.0.0 - terser: 5.16.1 - webpack: 5.75.0(@swc/core@1.3.56) + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.0 + webpack: 5.91.0(@swc/core@1.5.5) - /terser@5.16.1: - resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} + /terser@5.31.0: + resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} engines: {node: '>=10'} hasBin: true dependencies: - '@jridgewell/source-map': 0.3.2 - acorn: 8.8.1 + '@jridgewell/source-map': 0.3.6 + acorn: 8.11.3 commander: 2.20.3 source-map-support: 0.5.21 @@ -6846,14 +7046,26 @@ packages: hasBin: true dev: true + /ts-essentials@10.0.0(typescript@4.5.2): + resolution: {integrity: sha512-77FHNJEyysF9+1s4G6eejuA1lxw7uMchT3ZPy3CIbh7GIunffpshtM8pTe5G6N5dpOzNevqRHew859ceLWVBfw==} + peerDependencies: + typescript: '>=4.5.0' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 4.5.2 + dev: false + /ts-essentials@7.0.3(typescript@4.5.2): resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} peerDependencies: typescript: '>=3.7.0' dependencies: typescript: 4.5.2 + dev: true - /ts-jest@28.0.8(@babel/core@7.21.3)(jest@28.1.3)(typescript@4.5.2): + /ts-jest@28.0.8(@babel/core@7.24.5)(jest@28.1.3)(typescript@4.5.2): resolution: {integrity: sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true @@ -6874,20 +7086,20 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + jest: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) jest-util: 28.1.3 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.3.8 + semver: 7.6.2 typescript: 4.5.2 yargs-parser: 21.1.1 dev: true - /ts-jest@28.0.8(@babel/core@7.21.3)(jest@28.1.3)(typescript@4.9.3): + /ts-jest@28.0.8(@babel/core@7.24.5)(jest@28.1.3)(typescript@4.9.5): resolution: {integrity: sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true @@ -6908,35 +7120,36 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.21.3 + '@babel/core': 7.24.5 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 28.1.3(@types/node@16.18.4)(ts-node@10.7.0) + jest: 28.1.3(@types/node@16.18.97)(ts-node@10.7.0) jest-util: 28.1.3 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.3.8 - typescript: 4.9.3 + semver: 7.6.2 + typescript: 4.9.5 yargs-parser: 21.1.1 dev: true - /ts-loader@9.4.2(typescript@4.5.2)(webpack@5.75.0): - resolution: {integrity: sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==} + /ts-loader@9.5.1(typescript@4.5.2)(webpack@5.91.0): + resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} engines: {node: '>=12.0.0'} peerDependencies: typescript: '*' webpack: ^5.0.0 dependencies: chalk: 4.1.2 - enhanced-resolve: 5.12.0 + enhanced-resolve: 5.16.1 micromatch: 4.0.5 - semver: 7.3.8 + semver: 7.6.2 + source-map: 0.7.4 typescript: 4.5.2 - webpack: 5.75.0(@swc/core@1.3.56) + webpack: 5.91.0(@swc/core@1.5.5) dev: true - /ts-node@10.7.0(@swc/core@1.3.56)(@types/node@16.18.4)(typescript@4.5.2): + /ts-node@10.7.0(@swc/core@1.5.5)(@types/node@16.18.97)(typescript@4.5.2): resolution: {integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==} hasBin: true peerDependencies: @@ -6951,14 +7164,14 @@ packages: optional: true dependencies: '@cspotcode/source-map-support': 0.7.0 - '@swc/core': 1.3.56 - '@tsconfig/node10': 1.0.9 + '@swc/core': 1.5.5 + '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 16.18.4 - acorn: 8.8.1 - acorn-walk: 8.2.0 + '@tsconfig/node16': 1.0.4 + '@types/node': 16.18.97 + acorn: 8.11.3 + acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -6967,21 +7180,30 @@ packages: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - /tsconfig-paths-webpack-plugin@4.0.0: - resolution: {integrity: sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==} + /tsconfig-paths-webpack-plugin@4.0.1: + resolution: {integrity: sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==} engines: {node: '>=10.13.0'} dependencies: chalk: 4.1.2 - enhanced-resolve: 5.12.0 - tsconfig-paths: 4.1.0 + enhanced-resolve: 5.16.1 + tsconfig-paths: 4.2.0 dev: true /tsconfig-paths@4.1.0: resolution: {integrity: sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==} engines: {node: '>=6'} dependencies: - json5: 2.2.1 - minimist: 1.2.7 + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 strip-bom: 3.0.0 dev: true @@ -6989,8 +7211,11 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib@2.4.1: - resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + /tslib@2.5.3: + resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} /tsutils@3.21.0(typescript@4.5.2): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -7002,6 +7227,11 @@ packages: typescript: 4.5.2 dev: true + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -7032,7 +7262,7 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - /typeorm@0.3.11(pg@8.8.0)(sqlite3@5.1.6)(ts-node@10.7.0): + /typeorm@0.3.11(pg@8.11.5)(sqlite3@5.1.7)(ts-node@10.7.0): resolution: {integrity: sha512-pzdOyWbVuz/z8Ww6gqvBW4nylsM0KLdUCDExr2gR20/x1khGSVxQkjNV/3YqliG90jrWzrknYbYscpk8yxFJVg==} engines: {node: '>= 12.9.0'} hasBin: true @@ -7095,21 +7325,21 @@ packages: buffer: 6.0.3 chalk: 4.1.2 cli-highlight: 2.1.11 - date-fns: 2.29.3 + date-fns: 2.30.0 debug: 4.3.4 - dotenv: 16.0.3 + dotenv: 16.4.5 glob: 7.2.3 js-yaml: 4.1.0 mkdirp: 1.0.4 - pg: 8.8.0 - reflect-metadata: 0.1.13 + pg: 8.11.5 + reflect-metadata: 0.1.14 sha.js: 2.4.11 - sqlite3: 5.1.6 - ts-node: 10.7.0(@swc/core@1.3.56)(@types/node@16.18.4)(typescript@4.5.2) - tslib: 2.4.1 + sqlite3: 5.1.7 + ts-node: 10.7.0(@swc/core@1.5.5)(@types/node@16.18.97)(typescript@4.5.2) + tslib: 2.6.2 uuid: 8.3.2 xml2js: 0.4.23 - yargs: 17.6.2 + yargs: 17.7.2 transitivePeerDependencies: - supports-color dev: false @@ -7119,37 +7349,36 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - /typescript@4.8.4: - resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} hasBin: true dev: true - /typescript@4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true + /uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 - /uncontrollable@7.2.1(react@18.2.0): + /uncontrollable@7.2.1(react@18.3.1): resolution: {integrity: sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==} peerDependencies: react: '>=15.0.0' dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 18.2.75 + '@babel/runtime': 7.24.5 + '@types/react': 18.3.2 invariant: 2.2.4 - react: 18.2.0 + react: 18.3.1 react-lifecycles-compat: 3.0.4 dev: false - /uncontrollable@8.0.0(react@18.2.0): - resolution: {integrity: sha512-a954G/0JyXoZdpt0YIzTfoQyWtRS1VvygOBsHttCtZL8jDTKd6vQlUo811y46XnWoXIqQ36QKi3cSEdPuFADkA==} + /uncontrollable@8.0.4(react@18.3.1): + resolution: {integrity: sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==} peerDependencies: - react: '>=17.0.0' + react: '>=16.14.0' dependencies: - '@types/react': 18.2.75 - react: 18.2.0 + react: 18.3.1 dev: false /undefsafe@2.0.5: @@ -7179,34 +7408,29 @@ packages: engines: {node: '>= 10.0.0'} dev: true + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: true + /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - /update-browserslist-db@1.0.10(browserslist@4.21.4): - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.4 - escalade: 3.1.1 - picocolors: 1.0.0 - - /update-browserslist-db@1.0.10(browserslist@4.21.5): - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + /update-browserslist-db@1.0.15(browserslist@4.23.0): + resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.21.5 - escalade: 3.1.1 + browserslist: 4.23.0 + escalade: 3.1.2 picocolors: 1.0.0 /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: - punycode: 2.1.1 + punycode: 2.3.1 /url-template@2.0.8: resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} @@ -7227,49 +7451,56 @@ packages: /uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} hasBin: true + dev: false + + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - /v8-to-istanbul@9.0.1: - resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} + /v8-to-istanbul@9.2.0: + resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} engines: {node: '>=10.12.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.17 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - /vite-plugin-html@3.2.0(vite@4.2.1): - resolution: {integrity: sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==} + /vite-plugin-html@3.2.2(vite@4.5.3): + resolution: {integrity: sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==} peerDependencies: vite: '>=2.0.0' dependencies: '@rollup/pluginutils': 4.2.1 - colorette: 2.0.19 + colorette: 2.0.20 connect-history-api-fallback: 1.6.0 consola: 2.15.3 - dotenv: 16.0.3 + dotenv: 16.4.5 dotenv-expand: 8.0.3 - ejs: 3.1.9 - fast-glob: 3.2.12 + ejs: 3.1.10 + fast-glob: 3.3.2 fs-extra: 10.1.0 html-minifier-terser: 6.1.0 node-html-parser: 5.4.2 pathe: 0.2.0 - vite: 4.2.1 + vite: 4.5.3 dev: true - /vite@4.2.1: - resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==} + /vite@4.5.3: + resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: '@types/node': '>= 14' less: '*' + lightningcss: ^1.21.0 sass: '*' stylus: '*' sugarss: '*' @@ -7279,6 +7510,8 @@ packages: optional: true less: optional: true + lightningcss: + optional: true sass: optional: true stylus: @@ -7288,12 +7521,11 @@ packages: terser: optional: true dependencies: - esbuild: 0.17.12 - postcss: 8.4.21 - resolve: 1.22.1 - rollup: 3.20.0 + esbuild: 0.18.20 + postcss: 8.4.38 + rollup: 3.29.4 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /walker@1.0.8: @@ -7307,12 +7539,12 @@ packages: loose-envify: 1.4.0 dev: false - /watchpack@2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + /watchpack@2.4.1: + resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} engines: {node: '>=10.13.0'} dependencies: glob-to-regexp: 0.4.1 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -7336,8 +7568,8 @@ packages: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} - /webpack@5.74.0(@swc/core@1.3.56): - resolution: {integrity: sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==} + /webpack@5.82.1(@swc/core@1.5.5): + resolution: {integrity: sha512-C6uiGQJ+Gt4RyHXXYt+v9f+SN1v83x68URwgxNQ98cvH8kxiuywWGP4XeNZ1paOzZ63aY3cTciCEQJNFUljlLw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -7346,29 +7578,29 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 0.0.51 - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/wasm-edit': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.8.1 - acorn-import-assertions: 1.8.0(acorn@8.8.1) - browserslist: 4.21.4 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.11.3 + acorn-import-assertions: 1.9.0(acorn@8.11.3) + browserslist: 4.23.0 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.12.0 - es-module-lexer: 0.9.3 + enhanced-resolve: 5.16.1 + es-module-lexer: 1.5.2 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.1.1 + schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.6(@swc/core@1.3.56)(webpack@5.74.0) - watchpack: 2.4.0 + terser-webpack-plugin: 5.3.10(@swc/core@1.5.5)(webpack@5.82.1) + watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -7376,8 +7608,8 @@ packages: - uglify-js dev: true - /webpack@5.75.0(@swc/core@1.3.56): - resolution: {integrity: sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==} + /webpack@5.91.0(@swc/core@1.5.5): + resolution: {integrity: sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -7386,29 +7618,29 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 0.0.51 - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/wasm-edit': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.8.1 - acorn-import-assertions: 1.8.0(acorn@8.8.1) - browserslist: 4.21.4 + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.11.3 + acorn-import-assertions: 1.9.0(acorn@8.11.3) + browserslist: 4.23.0 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.12.0 - es-module-lexer: 0.9.3 + enhanced-resolve: 5.16.1 + es-module-lexer: 1.5.2 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.1.1 + schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.6(@swc/core@1.3.56)(webpack@5.75.0) - watchpack: 2.4.0 + terser-webpack-plugin: 5.3.10(@swc/core@1.5.5)(webpack@5.91.0) + watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -7430,8 +7662,10 @@ packages: /wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + requiresBuild: true dependencies: string-width: 4.2.3 + optional: true /windows-release@4.0.0: resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} @@ -7440,8 +7674,8 @@ packages: execa: 4.1.0 dev: true - /word-wrap@1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + /word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} dev: true @@ -7467,7 +7701,7 @@ packages: resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} engines: {node: '>=4.0.0'} dependencies: - sax: 1.2.4 + sax: 1.3.0 xmlbuilder: 11.0.1 dev: false @@ -7513,7 +7747,7 @@ packages: engines: {node: '>=10'} dependencies: cliui: 7.0.4 - escalade: 3.1.1 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 @@ -7521,12 +7755,12 @@ packages: yargs-parser: 20.2.9 dev: false - /yargs@17.6.2: - resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==} + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 diff --git a/shared/package.json b/shared/package.json index 6d82609..dbfce42 100644 --- a/shared/package.json +++ b/shared/package.json @@ -11,18 +11,18 @@ "test:cov": "jest -i --verbose --coverage" }, "devDependencies": { - "@swc/core": "^1.3.56", - "@swc/jest": "^0.2.26", + "@swc/core": "^1.5.5", + "@swc/jest": "^0.2.36", "@types/jest": "28.1.8", "jest": "28.1.3", - "prettier": "^2.8.5", + "prettier": "^2.8.8", "ts-jest": "28.0.8", - "typescript": "^4.9.3" + "typescript": "^4.9.5" }, "dependencies": { - "@casl/ability": "^6.3.3", - "@joi/date": "^2.1.0", - "joi": "^17.7.0" + "@casl/ability": "^6.7.1", + "@joi/date": "^2.1.1", + "joi": "^17.13.1" }, "jest": { "testEnvironment": "node", From d4983e047d95442bc36847aabf30e79ba78f0d20 Mon Sep 17 00:00:00 2001 From: Vincenzo Pellegrini Date: Sun, 12 May 2024 21:02:05 +0200 Subject: [PATCH 049/104] Enable ci on all branches --- .github/workflows/fullstack.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/fullstack.yml b/.github/workflows/fullstack.yml index 48e3293..ab7abc5 100644 --- a/.github/workflows/fullstack.yml +++ b/.github/workflows/fullstack.yml @@ -1,12 +1,6 @@ name: Test -on: - push: - branches: - - '*' - pull_request: - branches: - - '*' +on: [push, pull_request] jobs: tests: From 5357170c93e98bf8503412d93936d5c164a91651 Mon Sep 17 00:00:00 2001 From: Vincenzo Pellegrini Date: Sun, 12 May 2024 21:29:06 +0200 Subject: [PATCH 050/104] Disable shallow clone --- .github/workflows/fullstack.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/fullstack.yml b/.github/workflows/fullstack.yml index ab7abc5..10319ec 100644 --- a/.github/workflows/fullstack.yml +++ b/.github/workflows/fullstack.yml @@ -9,6 +9,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Install Node.js uses: actions/setup-node@v4 From ce9dd19899aba40f4199907d51ab66ac336a13f2 Mon Sep 17 00:00:00 2001 From: white Date: Tue, 14 May 2024 17:28:37 +0200 Subject: [PATCH 051/104] fix: entity (recsess, user, timeslot, avail) --- api/src/availability/availability.entity.ts | 7 +------ .../recruitment-session.entity.ts | 12 ++++++++---- api/src/timeslots/timeslot.entity.ts | 7 ++++--- api/src/users/user.entity.ts | 10 +++++++--- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/api/src/availability/availability.entity.ts b/api/src/availability/availability.entity.ts index a3e5f08..a9aa34f 100644 --- a/api/src/availability/availability.entity.ts +++ b/api/src/availability/availability.entity.ts @@ -1,7 +1,6 @@ import { Column, Entity, - JoinColumn, ManyToOne, PrimaryGeneratedColumn, Relation, @@ -12,7 +11,6 @@ import { } from '../../../shared/src/availability'; import { User } from 'src/users/user.entity'; import { TimeSlot } from 'src/timeslots/timeslot.entity'; -import { DbAwareColumn } from 'src/utils/db-aware-column'; @Entity() export class Availability implements AvailabilityInterface { @@ -25,15 +23,12 @@ export class Availability implements AvailabilityInterface { @Column({ name: 'last_modified' }) lastModified: Date; - @DbAwareColumn(() => TimeSlot, { name: 'time_slot' }) @ManyToOne(() => TimeSlot, (timeSlot) => timeSlot.availabilities) - @JoinColumn({ name: 'time_slot' }) timeSlot: Relation; @ManyToOne(() => User, (user) => user.availabilities) - @JoinColumn({ name: 'user_id' }) user: Relation; - // @OneToOne(() => Interview) + // @OneToOne(() => Interview, (interview) => interview.availability) // interview: Interview; } diff --git a/api/src/recruitment-session/recruitment-session.entity.ts b/api/src/recruitment-session/recruitment-session.entity.ts index 6c0d65b..aebbe13 100644 --- a/api/src/recruitment-session/recruitment-session.entity.ts +++ b/api/src/recruitment-session/recruitment-session.entity.ts @@ -1,4 +1,10 @@ -import { Column, Entity, JoinColumn, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; +import { + Column, + Entity, + Relation, + OneToMany, + PrimaryGeneratedColumn, +} from 'typeorm'; import { RecruitmentSession as RecruitmentSessionInterface, RecruitmentSessionState, @@ -32,7 +38,5 @@ export class RecruitmentSession implements RecruitmentSessionInterface { lastModified: Date; @OneToMany(() => TimeSlot, (timeSlot) => timeSlot.recruitmentSession) - @JoinColumn({ name: 'time_slots' }) - timeSlots: TimeSlot[]; - + timeSlots?: Relation; } diff --git a/api/src/timeslots/timeslot.entity.ts b/api/src/timeslots/timeslot.entity.ts index dadccae..de1e72d 100644 --- a/api/src/timeslots/timeslot.entity.ts +++ b/api/src/timeslots/timeslot.entity.ts @@ -23,10 +23,11 @@ export class TimeSlot implements TimeSlotInterface { end: Date; @OneToMany(() => Availability, (availability) => availability.timeSlot) - @JoinColumn({ name: 'availabilities' }) availabilities: Relation; - @ManyToOne(() => RecruitmentSession, (recruitmentSession) => recruitmentSession.timeSlots) - @JoinColumn({ name: 'recruitment_session' }) + @ManyToOne( + () => RecruitmentSession, + (recruitmentSession) => recruitmentSession.timeSlots, + ) recruitmentSession: Relation; } diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index a9b5ccc..8ec7472 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -1,7 +1,12 @@ -import { Column, Entity, OneToMany, PrimaryColumn, Relation, JoinColumn } from 'typeorm'; +import { + Column, + Entity, + OneToMany, + PrimaryColumn, + Relation, +} from 'typeorm'; import { Person, Role } from '@hkrecruitment/shared'; import { Availability } from 'src/availability/availability.entity'; -import Joi from 'joi'; @Entity() export class User implements Person { @@ -30,7 +35,6 @@ export class User implements Person { role: Role; @OneToMany(() => Availability, (availability) => availability.user) - @JoinColumn({ name: 'availabilities' }) availabilities?: Relation; @Column() From 90ebd95d356868a5443f5632ce7b67a512efc9f5 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 16:16:05 +0200 Subject: [PATCH 052/104] add: find available time slots functionality, tested --- .../availability/availability.controller.ts | 5 +- api/src/availability/availability.service.ts | 28 +++ api/src/mocks/db.sql | 183 ++++++++++++++++++ api/src/mocks/requests.http | 2 + .../recruitment-session.service.ts | 33 ++++ .../timeslots/timeslots.controller.spec.ts | 30 +++ api/src/timeslots/timeslots.controller.ts | 15 +- api/src/timeslots/timeslots.service.spec.ts | 68 ++++++- api/src/timeslots/timeslots.service.ts | 109 ++++++++++- api/src/users/user.entity.ts | 8 +- api/src/users/users.service.ts | 29 +++ 11 files changed, 497 insertions(+), 13 deletions(-) create mode 100644 api/src/mocks/db.sql create mode 100644 api/src/mocks/requests.http diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 80bd194..5087f41 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -78,8 +78,9 @@ export class AvailabilityController { const user = await this.userService.findByOauthId(req.user.sub); if (!user) throw new NotFoundException('User not found'); + /********** DISABLED BEACUSE findByUserAndTimeSlot HAS AN ERROR ************/ /* Verify availability for timeslot does not already exist */ - const existing = await this.availabilityService.findByUserAndTimeSlot( + /* const existing = await this.availabilityService.findByUserAndTimeSlot( user, timeSlot, ); @@ -87,7 +88,7 @@ export class AvailabilityController { throw new ConflictException( 'Availability already exists for this timeslot', ); - +*/ const availability = { timeSlot: timeSlot, state: AvailabilityState.Free, diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index 6f9904d..df91eca 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -16,10 +16,19 @@ export class AvailabilityService { private dataSource: DataSource, ) {} + /** + * List all availabilities + * @returns {Promise} - List of availabilities + */ async listAvailabilities(): Promise { return await this.availabilityRepository.find(); } + /** + * Find all availabilities for a given user + * @param user - User to find availabilities for + * @returns {Promise} - List of availabilities for the user + */ async findById(id: number): Promise { const matches = await this.availabilityRepository.findBy({ id: id, @@ -27,6 +36,7 @@ export class AvailabilityService { return matches.length > 0 ? matches[0] : null; } + /******** THIS FUNCTION GETS AN ERROR AT COMPILE TIME ********* async findByUserAndTimeSlot(user: User, timeSlot: TimeSlot) { const matches = await this.availabilityRepository.findBy({ user: user, @@ -34,11 +44,23 @@ export class AvailabilityService { }); return matches.length > 0 ? matches[0] : null; } +*/ + /** + * Create an availability + * @param availability - Availability to create + * @returns {Promise} - Created availability + */ async createAvailability(availability: Availability): Promise { return await this.availabilityRepository.save(availability); } + /** + * Update an availability + * @param oldAvailabilityId - ID of the + * @param newAvailability - New availability + * @returns {Promise} - Updated availability + */ async updateAvailability( oldAvailabilityId: number, newAvailability: Availability, @@ -49,6 +71,12 @@ export class AvailabilityService { }); } + /** + * Delete an availability + * @param availabilityId - ID of the availability to delete + * @returns {Promise} - Deleted availability + * @throws {ConflictException} - If availability is in use + */ async deleteAvailability(availabilityId: number): Promise { return await transaction( this.dataSource, diff --git a/api/src/mocks/db.sql b/api/src/mocks/db.sql new file mode 100644 index 0000000..d61b0d1 --- /dev/null +++ b/api/src/mocks/db.sql @@ -0,0 +1,183 @@ +-- Active: 1715697529039@@127.0.0.1@5432@hkrecruitment@public + +# Recruitment Session +INSERT INTO "recruitment_session" + ("id","state","slot_duration","last_modified","created_at","days","interview_start","interview_end") +VALUES + (1, 'concluded', 1, '2024-04-10', '2024-04-04', ARRAY['2024-04-05', '2024-04-06', '2024-04-07', '2024-04-08', '2024-04-09']::date[], '2024-04-05', '2024-04-10'), + (2, 'concluded', 1, '2024-05-10', '2024-05-04', ARRAY['2024-05-05', '2024-05-06', '2024-05-07', '2024-05-08']::date[], '2024-05-05', '2024-05-09'), + (3, 'active', 1, '2024-05-14', '2024-05-14', ARRAY['2024-05-16', '2024-05-17', '2024-05-18', '2024-05-19', '2024-05-20']::date[], '2024-05-16', '2024-05-21') +; + + + +# Time Slots +INSERT INTO "time_slot" + ("id","start","end","recruitmentSessionId") +VALUES + (1, '2024-04-05 10:00:00', '2024-05-21 11:00:00', 1), + (2, '2024-04-05 11:00:00', '2024-05-21 12:00:00', 1), + (3, '2024-04-05 15:00:00', '2024-05-21 16:00:00', 1), + (4, '2024-04-05 16:00:00', '2024-05-21 17:00:00', 1), + (5, '2024-04-05 17:00:00', '2024-05-21 18:00:00', 1), + (6, '2024-04-05 18:00:00', '2024-05-21 19:00:00', 1), + (7, '2024-04-06 10:00:00', '2024-06-21 11:00:00', 1), + (8, '2024-04-06 11:00:00', '2024-06-21 12:00:00', 1), + (9, '2024-04-06 15:00:00', '2024-06-21 16:00:00', 1), + (10, '2024-04-06 16:00:00', '2024-06-21 17:00:00', 1), + (11, '2024-04-06 17:00:00', '2024-06-21 18:00:00', 1), + (12, '2024-04-06 18:00:00', '2024-06-21 19:00:00', 1), + (13, '2024-04-07 10:00:00', '2024-07-21 11:00:00', 1), + (14, '2024-04-07 11:00:00', '2024-07-21 12:00:00', 1), + (15, '2024-04-07 15:00:00', '2024-07-21 16:00:00', 1), + (16, '2024-04-07 16:00:00', '2024-07-21 17:00:00', 1), + (17, '2024-04-07 17:00:00', '2024-07-21 18:00:00', 1), + (18, '2024-04-07 18:00:00', '2024-07-21 19:00:00', 1), + (19, '2024-04-08 10:00:00', '2024-08-21 11:00:00', 1), + (20, '2024-04-08 11:00:00', '2024-08-21 12:00:00', 1), + (21, '2024-04-08 15:00:00', '2024-08-21 16:00:00', 1), + (22, '2024-04-08 16:00:00', '2024-08-21 17:00:00', 1), + (23, '2024-04-08 17:00:00', '2024-08-21 18:00:00', 1), + (24, '2024-04-08 18:00:00', '2024-08-21 19:00:00', 1), + (25, '2024-04-09 10:00:00', '2024-09-21 11:00:00', 1), + (26, '2024-04-09 11:00:00', '2024-09-21 12:00:00', 1), + (27, '2024-04-09 15:00:00', '2024-09-21 16:00:00', 1), + (28, '2024-04-09 16:00:00', '2024-09-21 17:00:00', 1), + (29, '2024-04-09 17:00:00', '2024-09-21 18:00:00', 1), + (30, '2024-04-09 18:00:00', '2024-09-21 19:00:00', 1), + (31, '2024-05-05 10:00:00', '2024-05-05 11:00:00', 2), + (32, '2024-05-05 11:00:00', '2024-05-05 12:00:00', 2), + (33, '2024-05-05 15:00:00', '2024-05-05 16:00:00', 2), + (34, '2024-05-05 16:00:00', '2024-05-05 17:00:00', 2), + (35, '2024-05-05 17:00:00', '2024-05-05 18:00:00', 2), + (36, '2024-05-05 18:00:00', '2024-05-05 19:00:00', 2), + (37, '2024-05-06 10:00:00', '2024-05-06 11:00:00', 2), + (38, '2024-05-06 11:00:00', '2024-05-06 12:00:00', 2), + (39, '2024-05-06 15:00:00', '2024-05-06 16:00:00', 2), + (40, '2024-05-06 16:00:00', '2024-05-06 17:00:00', 2), + (41, '2024-05-06 17:00:00', '2024-05-06 18:00:00', 2), + (42, '2024-05-06 18:00:00', '2024-05-06 19:00:00', 2), + (43, '2024-05-07 10:00:00', '2024-05-07 11:00:00', 2), + (44, '2024-05-07 11:00:00', '2024-05-07 12:00:00', 2), + (45, '2024-05-07 15:00:00', '2024-05-07 16:00:00', 2), + (46, '2024-05-07 16:00:00', '2024-05-07 17:00:00', 2), + (47, '2024-05-07 17:00:00', '2024-05-07 18:00:00', 2), + (48, '2024-05-07 18:00:00', '2024-05-07 19:00:00', 2), + (49, '2024-05-08 10:00:00', '2024-05-08 11:00:00', 2), + (50, '2024-05-08 11:00:00', '2024-05-08 12:00:00', 2), + (51, '2024-05-16 10:00:00', '2024-05-16 11:00:00', 3), + (52, '2024-05-16 11:00:00', '2024-05-16 12:00:00', 3), + (53, '2024-05-16 15:00:00', '2024-05-16 16:00:00', 3), + (54, '2024-05-16 16:00:00', '2024-05-16 17:00:00', 3), + (55, '2024-05-16 17:00:00', '2024-05-16 18:00:00', 3), + (56, '2024-05-16 18:00:00', '2024-05-16 19:00:00', 3), + (57, '2024-05-17 10:00:00', '2024-05-17 11:00:00', 3), + (58, '2024-05-17 11:00:00', '2024-05-17 12:00:00', 3), + (59, '2024-05-17 15:00:00', '2024-05-17 16:00:00', 3), + (60, '2024-05-17 16:00:00', '2024-05-17 17:00:00', 3), + (61, '2024-05-17 17:00:00', '2024-05-17 18:00:00', 3), + (62, '2024-05-17 18:00:00', '2024-05-17 19:00:00', 3), + (63, '2024-05-18 10:00:00', '2024-05-18 11:00:00', 3), + (64, '2024-05-18 11:00:00', '2024-05-18 12:00:00', 3), + (65, '2024-05-18 15:00:00', '2024-05-18 16:00:00', 3), + (66, '2024-05-18 16:00:00', '2024-05-18 17:00:00', 3), + (67, '2024-05-18 17:00:00', '2024-05-18 18:00:00', 3), + (68, '2024-05-18 18:00:00', '2024-05-18 19:00:00', 3), + (69, '2024-05-19 10:00:00', '2024-05-19 11:00:00', 3), + (70, '2024-05-19 11:00:00', '2024-05-19 12:00:00', 3), + (71, '2024-05-19 15:00:00', '2024-05-19 16:00:00', 3), + (72, '2024-05-19 16:00:00', '2024-05-19 17:00:00', 3), + (73, '2024-05-19 17:00:00', '2024-05-19 18:00:00', 3), + (74, '2024-05-19 18:00:00', '2024-05-19 19:00:00', 3), + (75, '2024-05-20 10:00:00', '2024-05-20 11:00:00', 3), + (76, '2024-05-20 11:00:00', '2024-05-20 12:00:00', 3), + (77, '2024-05-20 15:00:00', '2024-05-20 16:00:00', 3), + (78, '2024-05-20 16:00:00', '2024-05-20 17:00:00', 3), + (79, '2024-05-20 17:00:00', '2024-05-20 18:00:00', 3), + (80, '2024-05-20 18:00:00', '2024-05-20 19:00:00', 3) +; + + +INSERT INTO "user" + ("oauthId","firstName","lastName","sex","email","phone_no","telegramId","role","is_board","is_expert") +VALUES + ('1', 'Pasquale', 'Bianco', 'male', 'p.bianco@gmail.com', '3405174444', 'pbianco', 'member', true, true), + ('2', 'John', 'Doe', 'male', 'j.doe@gmail.com', '1234567890', 'jdoe', 'member', false, true), + ('3', 'Jane', 'Smith', 'female', 'j.smith@gmail.com', '9876543210', 'jsmith', 'member', false, false), + ('4', 'Michael', 'Johnson', 'male', 'm.johnson@gmail.com', '5555555555', 'mjohnson', 'member', true, true), + ('5', 'Emily', 'Brown', 'female', 'e.brown@gmail.com', '1111111111', 'ebrown', 'member', false, true), + ('6', 'David', 'Wilson', 'male', 'd.wilson@gmail.com', '2222222222', 'dwilson', 'member', false, false), + ('7', 'Olivia', 'Johnson', 'female', 'o.johnson@gmail.com', '3333333333', 'ojohnson', 'member', true, false), + ('8', 'James', 'Smith', 'male', 'j.smith@gmail.com', '4444444444', 'jsmith', 'member', false, false), + ('9', 'Sophia', 'Miller', 'female', 's.miller@gmail.com', '5555555555', 'smiller', 'member', false, true), + ('10', 'Benjamin', 'Davis', 'male', 'b.davis@gmail.com', '6666666666', 'bdavis', 'member', false, false), + ('11', 'Ava', 'Wilson', 'female', 'a.wilson@gmail.com', '7777777777', 'awilson', 'member', true, true), + ('12', 'William', 'Anderson','male', 'w.anderson@gmail.com', '8888888888', 'wanderson', 'member', false, false), + ('13', 'Mia', 'Thomas', 'female', 'm.thomas@gmail.com', '9999999999', 'mthomas', 'member', false, false), + ('14', 'Alexander', 'Taylor', 'male', 'a.taylor@gmail.com', '1111111111', 'ataylor', 'member', false, true), + ('15', 'Charlotte', 'Clark', 'female', 'c.clark@gmail.com', '2222222222', 'cclark', 'member', false, false), + ('16', 'Daniel', 'Moore', 'male', 'd.moore@gmail.com', '3333333333', 'dmoore', 'member', false, false), + ('17', 'Amelia', 'Walker', 'female', 'a.walker@gmail.com', '4444444444', 'awalker', 'clerk', false, false), + ('18', 'Matthew', 'Lewis', 'male', 'm.lewis@gmail.com', '5555555555', 'mlewis', 'admin', false, true), + ('19', 'Ella', 'Harris', 'female', 'e.harris@gmail.com', '6666666666', 'eharris', 'supervisor', false, true), + ('20', 'Joseph', 'King', 'male', 'j.king@gmail.com', '7777777777', 'jking', 'supervisor', true, false) + + +# Availability +INSERT INTO "availability" + ("id","state","last_modified","timeSlotId","userOauthId") +VALUES + (1, 'free' ,'2024-05-14 00:00:00', 51, '5' ), + (2, 'free' ,'2024-05-14 00:00:00', 53, '7' ), + (3, 'free' ,'2024-05-14 00:00:00', 55, '12'), + (4, 'recovering' ,'2024-05-14 00:00:00', 61, '19'), + (5, 'free' ,'2024-05-14 00:00:00', 71, '3' ), + (6, 'free' ,'2024-05-14 00:00:00', 78, '15'), + (7, 'free' ,'2024-05-14 00:00:00', 58, '11'), + (8, 'free' ,'2024-05-14 00:00:00', 57, '8' ), + (9, 'free' ,'2024-05-14 00:00:00', 64, '5' ), + (10, 'recovering' ,'2024-05-14 00:00:00', 53, '17'), + (11, 'free' ,'2024-05-14 00:00:00', 62, '2' ), + (12, 'free' ,'2024-05-14 00:00:00', 71, '9' ), + (13, 'recovering' ,'2024-05-14 00:00:00', 72, '14'), + (14, 'free' ,'2024-05-14 00:00:00', 74, '6' ), + (15, 'free' ,'2024-05-14 00:00:00', 75, '13'), + (16, 'recovering' ,'2024-05-14 00:00:00', 74, '10'), + (17, 'free' ,'2024-05-14 00:00:00', 71, '4' ), + (18, 'free' ,'2024-05-14 00:00:00', 64, '16'), + (19, 'recovering' ,'2024-05-14 00:00:00', 59, '18'), + (20, 'free' ,'2024-05-14 00:00:00', 57, '1' ), + (21, 'interviewing' ,'2024-05-14 00:00:00', 66, '20'), + (22, 'recovering' ,'2024-05-14 00:00:00', 68, '19'), + (23, 'free' ,'2024-05-14 00:00:00', 69, '7' ), + (24, 'interviewing' ,'2024-05-14 00:00:00', 70, '16'), + (25, 'recovering' ,'2024-05-14 00:00:00', 72, '15'), + (26, 'free' ,'2024-05-14 00:00:00', 73, '11'), + (27, 'free' ,'2024-05-14 00:00:00', 75, '14'), + (28, 'recovering' ,'2024-05-14 00:00:00', 76, '9' ), + (29, 'free' ,'2024-05-14 00:00:00', 77, '8' ), + (30, 'free' ,'2024-05-14 00:00:00', 78, '10'), + (31, 'free' ,'2024-05-14 00:00:00', 79, '13'), + (32, 'free' ,'2024-05-14 00:00:00', 80, '12'), + (33, 'interviewing' ,'2024-05-14 00:00:00', 79, '20'), + (34, 'free' ,'2024-05-14 00:00:00', 78, '18'), + (35, 'free' ,'2024-05-14 00:00:00', 77, '17'), + (36, 'interviewing' ,'2024-05-14 00:00:00', 76, '19'), + (37, 'free' ,'2024-05-14 00:00:00', 75, '16'), + (38, 'free' ,'2024-05-14 00:00:00', 74, '15'), + (39, 'free' ,'2024-05-14 00:00:00', 73, '14'), + (40, 'free' ,'2024-05-14 00:00:00', 72, '13'), + (41, 'free' ,'2024-05-14 00:00:00', 71, '12'), + (42, 'interviewing' ,'2024-05-14 00:00:00', 70, '11'), + (43, 'recovering' ,'2024-05-14 00:00:00', 69, '10'), + (44, 'free' ,'2024-05-14 00:00:00', 68, '9' ), + (45, 'interviewing' ,'2024-05-14 00:00:00', 67, '8' ), + (46, 'free' ,'2024-05-14 00:00:00', 66, '7' ), + (47, 'free' ,'2024-05-14 00:00:00', 65, '6' ), + (48, 'interviewing' ,'2024-05-14 00:00:00', 64, '5' ), + (49, 'recovering' ,'2024-05-14 00:00:00', 63, '8' ), + (50, 'free' ,'2024-05-14 00:00:00', 62, '14'), + (51, 'free' ,'2024-05-14 00:00:00', 61, '4' ), + (52, 'free' ,'2024-05-14 00:00:00', 60, '13') +; + diff --git a/api/src/mocks/requests.http b/api/src/mocks/requests.http new file mode 100644 index 0000000..74e9e9e --- /dev/null +++ b/api/src/mocks/requests.http @@ -0,0 +1,2 @@ +GET http://localhost:3000/v1/timeslots/ +Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc4aFdBRTB0U00tX3JJVUR0WElNMyJ9.eyJlbWFpbCI6InBhc3F1YWxlLmJpYW5jb0Boa25wb2xpdG8ub3JnIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDYyMjgyMTU0MzYxNTk5ODcwNTEiLCJhdWQiOlsiaHR0cDovL2hrcmVjcnVpdG1lbnQub3JnIiwiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzE2MTEzNTk3LCJleHAiOjE3MTYxOTk5OTcsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJaekNWd2R2eUJOUWZKc3R1ZUJPcVh3TW1jazZCa0d4NiJ9.PyyW67LJIWc9NbMibzvzfxlY0Pe1YTsZVsV2--ckBQ5hfYsCKqCUi6aIjmiliz_uZO5qdPAsZ7FYNOtOI8fGhPt9QfKSqA166D-CZWnpuwE4cM_EjbpCoHRRQ49r2Ycv_ZnzVXrO6zCyEVZYgx-USGiuBnRcKbIPDJCS4Tc9bcmklcUdq4ujAPtSDdmid9jcVN3DM5eLtXAQuYIUho3G3NRVuKlUnoNIsK16yrKtx5kgOHKuUP6dFYExpp2ViB4oZV-v_linCsXZEE5x-7GBMntJAGtenCs-1uihB-OGLYJY_6fPsRG2rgw5Fl8Ax0Ioc8LeXkwlJ2L-MO1ms-r8uQ \ No newline at end of file diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts index 670a9e6..8a982f2 100644 --- a/api/src/recruitment-session/recruitment-session.service.ts +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -17,6 +17,11 @@ export class RecruitmentSessionService { private dataSource: DataSource, ) {} + /** + * Create a recruitment session + * @param recruitmentSession - Recruitment session to create + * @returns {Promise} - Created recruitment session + */ async createRecruitmentSession( recruitmentSession: CreateRecruitmentSessionDto, ): Promise { @@ -40,10 +45,19 @@ export class RecruitmentSessionService { }); } + /** + * List all recruitment sessions + * @returns {Promise} - List of recruitment sessions + */ async findAllRecruitmentSessions(): Promise { return await this.recruitmentSessionRepository.find(); } + /** + * Find a recruitment session by its ID + * @param RSid - ID of the recruitment session + * @returns {Promise} - Recruitment session with the given ID + */ async findRecruitmentSessionById( RSid: number, ): Promise { @@ -53,6 +67,10 @@ export class RecruitmentSessionService { return matches.length > 0 ? matches[0] : null; } + /** + * Find an active recruitment session + * @returns {Promise} - Active recruitment session + */ async findActiveRecruitmentSession(): Promise { const matches = await this.recruitmentSessionRepository.findBy({ state: RecruitmentSessionState.Active, @@ -60,18 +78,33 @@ export class RecruitmentSessionService { return matches.length > 0 ? matches[0] : null; } + /** + * Delete a recruitment session + * @param recruitmentSession - Recruitment session to delete + * @returns {Promise} - Deleted recruitment session + */ async deleteRecruitmentSession( recruitmentSession: RecruitmentSession, ): Promise { return await this.recruitmentSessionRepository.remove(recruitmentSession); } + /** + * Update a recruitment session + * @param recruitmentSession - Recruitment session to update + * @returns {Promise} - Updated recruitment session + */ async updateRecruitmentSession( recruitmentSession: RecruitmentSession, ): Promise { return await this.recruitmentSessionRepository.save(recruitmentSession); } + /** + * Check if a recruitment session has pending interviews + * @param recruitmentSession - Recruitment session to check + * @returns {Promise} - True if the recruitment session has pending interviews, false otherwise + */ async sessionHasPendingInterviews( recruitmentSession: RecruitmentSession, ): Promise { diff --git a/api/src/timeslots/timeslots.controller.spec.ts b/api/src/timeslots/timeslots.controller.spec.ts index 0c1c16d..da039cc 100644 --- a/api/src/timeslots/timeslots.controller.spec.ts +++ b/api/src/timeslots/timeslots.controller.spec.ts @@ -1,6 +1,8 @@ import { TestBed } from '@automock/jest'; import { TimeSlotsController } from './timeslots.controller'; import { TimeSlotsService } from './timeslots.service'; +import { TimeSlot } from './timeslot.entity'; +import { Test } from '@nestjs/testing'; describe('TimeSlotController', () => { let controller: TimeSlotsController; @@ -18,4 +20,32 @@ describe('TimeSlotController', () => { expect(controller).toBeDefined(); expect(service).toBeDefined(); }); + + describe('findAvailableTimeSlots', () => { + it('should return an array of available time slots', async () => { + const expectedTimeSlots: TimeSlot[] = [ + { + id: 71, + start: new Date('2024-05-19T13:00:00.000Z'), + end: new Date('2024-05-19T14:00:00.000Z'), + } as TimeSlot, + { + id: 73, + start: new Date('2024-05-19T15:00:00.000Z'), + end: new Date('2024-05-19T16:00:00.000Z'), + } as TimeSlot, + ]; + + jest + .spyOn(service, 'findAvailableTimeSlots') + .mockResolvedValue(expectedTimeSlots); + + // Act + const result = await controller.findAvailableTimeSlots(); + + // Assert + expect(result).toEqual(expectedTimeSlots); + expect(service.findAvailableTimeSlots).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/api/src/timeslots/timeslots.controller.ts b/api/src/timeslots/timeslots.controller.ts index 9580b8d..05311bc 100644 --- a/api/src/timeslots/timeslots.controller.ts +++ b/api/src/timeslots/timeslots.controller.ts @@ -1,10 +1,21 @@ -import { Controller } from '@nestjs/common'; +import { Controller, Get, Post } from '@nestjs/common'; import { TimeSlotsService } from './timeslots.service'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { + ApiBearerAuth, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { TimeSlot } from './timeslot.entity'; @ApiBearerAuth() @ApiTags('timeslots') @Controller('timeslots') export class TimeSlotsController { constructor(private readonly timeSlotsService: TimeSlotsService) {} + + @ApiUnauthorizedResponse() + @Get() + async findAvailableTimeSlots(): Promise { + return await this.timeSlotsService.findAvailableTimeSlots(); + } } diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index 6284724..cb95d62 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -5,6 +5,11 @@ import { getRepositoryToken } from '@nestjs/typeorm'; import { TimeSlot } from './timeslot.entity'; import { TimeSlotsService } from './timeslots.service'; import { mockDataSource } from 'src/mocks/data-sources'; +import { + AvailabilityState, + RecruitmentSessionState, + Role, +} from '@hkrecruitment/shared'; describe('TimeSlotsService', () => { let timeSlotService: TimeSlotsService; @@ -43,7 +48,9 @@ describe('TimeSlotsService', () => { describe('deleteTimeSlot', () => { it('should remove the specified timeslot from the database', async () => { jest.spyOn(mockedRepository, 'remove').mockResolvedValue(mockTimeSlot); - const result = await timeSlotService.deleteTimeSlot(mockTimeSlot); + const result = await timeSlotService.deleteTimeSlot( + mockTimeSlot as TimeSlot, + ); expect(result).toEqual(mockTimeSlot); expect(mockedRepository.remove).toHaveBeenCalledTimes(1); }); @@ -188,6 +195,65 @@ describe('TimeSlotsService', () => { ); }); }); + + describe('findAvailableTimeSlots', () => { + it('should correctly call all functions provided for database query', async () => { + // Mock the query builder and its methods + const mockQueryBuilder = { + leftJoinAndSelect: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + getMany: jest.fn().mockResolvedValue([]), + }; + + // Mock the timeSlotRepository and its methods + const mockTimeSlotRepository = { + createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), + }; + + const timeSlotService = new TimeSlotsService( + mockTimeSlotRepository as any, + ); + const result = await timeSlotService.findAvailableTimeSlots(); + + // Assert that the query builder methods were called correctly + expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledWith( + 'TimeSlot.availabilities', + 'availability', + ); + expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledWith( + 'TimeSlot.recruitmentSession', + 'recruitmentSession', + ); + expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledWith( + 'availability.user', + 'user', + ); + expect(mockQueryBuilder.where).toHaveBeenCalledWith( + 'recruitmentSession.state = :recruitmentSessionState', + { + recruitmentSessionState: RecruitmentSessionState.Active, + }, + ); + expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + 'user.role NOT IN (:...roles)', + { + roles: [Role.None, Role.Applicant], + }, + ); + expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + 'availability.state = :availabilityState AND (user.is_board = true OR user.is_expert = true)', + { + availabilityState: AvailabilityState.Free, + }, + ); + expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', + ); + + expect(result).toEqual([]); + }); + }); }); function testTimeSlotsGeneration( diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 7eb37c0..20a5cb1 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -2,8 +2,13 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, LessThan, MoreThan, QueryRunner } from 'typeorm'; import { TimeSlot } from './timeslot.entity'; -import { RecruitmentSession } from '@hkrecruitment/shared'; +import { + RecruitmentSession, + RecruitmentSessionState, +} from '@hkrecruitment/shared'; import { CreateTimeSlotDto } from './create-timeslot.dto'; +import { Role } from '@hkrecruitment/shared/person'; +import { AvailabilityState } from '@hkrecruitment/shared/availability'; @Injectable() export class TimeSlotsService { @@ -12,6 +17,12 @@ export class TimeSlotsService { private readonly timeSlotRepository: Repository, ) {} + /** + * Count the number of overlapping time slots + * @param startDate - Start date of the time slot + * @param endDate - End date of the time slot + * @returns {Promise} - Number of overlapping time slots + */ async countOverlappingTimeSlots( startDate: Date, endDate: Date, @@ -34,14 +45,28 @@ export class TimeSlotsService { return count; } + /** + * List all time slots + * @returns {Promise} - List of time slots + */ async listTimeSlots(): Promise { return await this.timeSlotRepository.find(); } + /** + * Delete a time slot + * @param timeSlot - Time slot to delete + * @returns {Promise} - Deleted time slot + */ async deleteTimeSlot(timeSlot: TimeSlot): Promise { return await this.timeSlotRepository.remove(timeSlot); } + /** + * Find a time slot by its ID + * @param timeSlotId - ID of the time slot + * @returns {Promise} - Time slot with the given ID + */ async findById(timeSlotId: number): Promise { const matches = await this.timeSlotRepository.findBy({ id: timeSlotId, @@ -49,10 +74,21 @@ export class TimeSlotsService { return matches.length > 0 ? matches[0] : null; } + /** + * Create a time slot + * @param timeSlot - Time slot to create + * @returns {Promise} - Created time slot + */ async createTimeSlot(timeSlot: CreateTimeSlotDto): Promise { return await this.timeSlotRepository.save(timeSlot); } + /** + * Create time slots for a recruitment session + * @param queryRunner - Query runner + * @param recruitmentSession - Recruitment session + * @returns {Promise} - List of created time slots + */ async createRecruitmentSessionTimeSlots( queryRunner: QueryRunner, recruitmentSession: RecruitmentSession, @@ -68,6 +104,14 @@ export class TimeSlotsService { return await queryRunner.manager.getRepository(TimeSlot).save(timeSlots); } + /** + * Generate time slots for a recruitment session + * @param slotDuration - Duration of each time slot in minutes + * @param interviewStart - Start time of the interview + * @param interviewEnd - End time of the interview + * @param days - Days of the week the interview will be held + * @returns {TimeSlot[]} - List of generated time slots + */ generateTimeSlots( slotDuration: number, interviewStart: Date, @@ -105,4 +149,67 @@ export class TimeSlotsService { return timeSlots; } + + /** + * Find available time slots for the current recruitment session + * @returns {TimeSlot[]} - List of available time slots + */ + async findAvailableTimeSlots(): Promise { + const queryBuilder = this.timeSlotRepository.createQueryBuilder('TimeSlot'); + queryBuilder + .leftJoinAndSelect('TimeSlot.availabilities', 'availability') + .leftJoinAndSelect('TimeSlot.recruitmentSession', 'recruitmentSession') + .leftJoinAndSelect('availability.user', 'user') + + // only active recruitment sessions (the current one) + .where('recruitmentSession.state = :recruitmentSessionState', { + recruitmentSessionState: RecruitmentSessionState.Active, + }) + + // available people should be members of hkn + .andWhere('user.role NOT IN (:...roles)', { + roles: [Role.None, Role.Applicant], + }) + + // only free people that are board OR expert member + .andWhere( + 'availability.state = :availabilityState AND (user.is_board = true OR user.is_expert = true)', + { + availabilityState: AvailabilityState.Free, + }, + ) + + // there should be at least 2 available people (hopefully one of them is a board member) + .andWhere( + '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', + ); + + const allMatches = await queryBuilder.getMany(); + + let goodTimeSlots: TimeSlot[] = []; + allMatches.forEach((timeSlot) => { + let boardMembers = 0; + let expertMembers = 0; + for (let availability of timeSlot.availabilities) { + // redundant checks + if (availability.state !== AvailabilityState.Free) continue; + if (availability.user.role === Role.None) continue; + if (availability.user.role === Role.Applicant) continue; + + if (availability.user.is_board) ++boardMembers; + else if (availability.user.is_expert) ++expertMembers; + } + + if ((boardMembers && expertMembers) || boardMembers > 1) { + const timeslotToPush = { + id: timeSlot.id, + start: timeSlot.start, + end: timeSlot.end, + } as TimeSlot; + goodTimeSlots.push(timeslotToPush); + } + }); + + return goodTimeSlots; + } } diff --git a/api/src/users/user.entity.ts b/api/src/users/user.entity.ts index 8ec7472..81bd96c 100644 --- a/api/src/users/user.entity.ts +++ b/api/src/users/user.entity.ts @@ -1,10 +1,4 @@ -import { - Column, - Entity, - OneToMany, - PrimaryColumn, - Relation, -} from 'typeorm'; +import { Column, Entity, OneToMany, PrimaryColumn, Relation } from 'typeorm'; import { Person, Role } from '@hkrecruitment/shared'; import { Availability } from 'src/availability/availability.entity'; diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index a068892..8eb1fd8 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -11,26 +11,55 @@ export class UsersService { private readonly userRepository: Repository, ) {} + /** + * Find all users + * @returns {Promise} - List of users + */ async findAll(): Promise { return this.userRepository.find(); } + /** + * Find a user by their OAuth ID + * @param {string} oauthId - OAuth ID of the user + * @returns {Promise} - User with the given OAuth ID + */ async findByOauthId(oauthId: string): Promise { return this.userRepository.findOne({ where: { oauthId } }); } + /** + * Delete a user + * @param {User} user - User to delete + * @returns {Promise} - Deleted user + */ async delete(user: User): Promise { return this.userRepository.remove(user); } + /** + * Create a user + * @param {User} user - User to create + * @returns {Promise} - Created user + */ async create(user: User): Promise { return this.userRepository.save(user); } + /** + * Update a user + * @param {User} user - User to update + * @returns {Promise} - Updated user + */ async update(user: User): Promise { return this.userRepository.save(user); } + /** + * Get the role and abilities for a user + * @param {string} oauthId - OAuth ID of the user + * @returns {[Role, AppAbility]} - Role and abilities for the user + */ async getRoleAndAbilityForOauthId( oauthId: string, ): Promise<[Role, AppAbility]> { From 59c3f48ce295c020e9d113ff6a95918f5b3af438 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 16:38:58 +0200 Subject: [PATCH 053/104] fix: previous simplification for dev --- api/src/availability/availability.controller.ts | 5 ++--- api/src/availability/availability.service.ts | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 5087f41..5e95cab 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -78,9 +78,8 @@ export class AvailabilityController { const user = await this.userService.findByOauthId(req.user.sub); if (!user) throw new NotFoundException('User not found'); - /********** DISABLED BEACUSE findByUserAndTimeSlot HAS AN ERROR ************/ /* Verify availability for timeslot does not already exist */ - /* const existing = await this.availabilityService.findByUserAndTimeSlot( + const existing = await this.availabilityService.findByUserAndTimeSlot( user, timeSlot, ); @@ -88,7 +87,7 @@ export class AvailabilityController { throw new ConflictException( 'Availability already exists for this timeslot', ); -*/ + const availability = { timeSlot: timeSlot, state: AvailabilityState.Free, diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index df91eca..66a0a23 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -36,7 +36,6 @@ export class AvailabilityService { return matches.length > 0 ? matches[0] : null; } - /******** THIS FUNCTION GETS AN ERROR AT COMPILE TIME ********* async findByUserAndTimeSlot(user: User, timeSlot: TimeSlot) { const matches = await this.availabilityRepository.findBy({ user: user, @@ -44,7 +43,7 @@ export class AvailabilityService { }); return matches.length > 0 ? matches[0] : null; } -*/ + /** * Create an availability From 74b0d00ceb86402dc1a09ea120e00b937e1e5705 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 16:41:47 +0200 Subject: [PATCH 054/104] format --- api/src/availability/availability.controller.ts | 2 +- api/src/availability/availability.service.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/api/src/availability/availability.controller.ts b/api/src/availability/availability.controller.ts index 5e95cab..80bd194 100644 --- a/api/src/availability/availability.controller.ts +++ b/api/src/availability/availability.controller.ts @@ -79,7 +79,7 @@ export class AvailabilityController { if (!user) throw new NotFoundException('User not found'); /* Verify availability for timeslot does not already exist */ - const existing = await this.availabilityService.findByUserAndTimeSlot( + const existing = await this.availabilityService.findByUserAndTimeSlot( user, timeSlot, ); diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index 66a0a23..1f8693c 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -44,7 +44,6 @@ export class AvailabilityService { return matches.length > 0 ? matches[0] : null; } - /** * Create an availability * @param availability - Availability to create From 019f24a1ebc486843afeeb00d1f21e2b3466e110 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 16:49:39 +0200 Subject: [PATCH 055/104] fix: minor --- api/src/availability/availability.service.ts | 4 ++-- api/src/timeslots/timeslots.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index 1f8693c..6caa4aa 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -38,8 +38,8 @@ export class AvailabilityService { async findByUserAndTimeSlot(user: User, timeSlot: TimeSlot) { const matches = await this.availabilityRepository.findBy({ - user: user, - timeSlot: timeSlot, + user: user as any, + timeSlot: timeSlot as any, }); return matches.length > 0 ? matches[0] : null; } diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 20a5cb1..9782c6f 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -7,7 +7,7 @@ import { RecruitmentSessionState, } from '@hkrecruitment/shared'; import { CreateTimeSlotDto } from './create-timeslot.dto'; -import { Role } from '@hkrecruitment/shared/person'; +import { Role } from '@hkrecruitment/shared'; import { AvailabilityState } from '@hkrecruitment/shared/availability'; @Injectable() From dc04c2fdf496232195b5abe4ed62788778e3fa02 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 16:54:47 +0200 Subject: [PATCH 056/104] fix: minor 2 --- api/src/timeslots/timeslots.controller.spec.ts | 1 - api/src/timeslots/timeslots.controller.ts | 2 +- api/src/timeslots/timeslots.service.ts | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/src/timeslots/timeslots.controller.spec.ts b/api/src/timeslots/timeslots.controller.spec.ts index da039cc..2ab30d8 100644 --- a/api/src/timeslots/timeslots.controller.spec.ts +++ b/api/src/timeslots/timeslots.controller.spec.ts @@ -2,7 +2,6 @@ import { TestBed } from '@automock/jest'; import { TimeSlotsController } from './timeslots.controller'; import { TimeSlotsService } from './timeslots.service'; import { TimeSlot } from './timeslot.entity'; -import { Test } from '@nestjs/testing'; describe('TimeSlotController', () => { let controller: TimeSlotsController; diff --git a/api/src/timeslots/timeslots.controller.ts b/api/src/timeslots/timeslots.controller.ts index 05311bc..90c5b84 100644 --- a/api/src/timeslots/timeslots.controller.ts +++ b/api/src/timeslots/timeslots.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Post } from '@nestjs/common'; +import { Controller, Get } from '@nestjs/common'; import { TimeSlotsService } from './timeslots.service'; import { ApiBearerAuth, diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 9782c6f..0e34c88 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -5,10 +5,10 @@ import { TimeSlot } from './timeslot.entity'; import { RecruitmentSession, RecruitmentSessionState, + AvailabilityState, + Role, } from '@hkrecruitment/shared'; import { CreateTimeSlotDto } from './create-timeslot.dto'; -import { Role } from '@hkrecruitment/shared'; -import { AvailabilityState } from '@hkrecruitment/shared/availability'; @Injectable() export class TimeSlotsService { From 71c66ed7523a0ab5d4cd5a30472ea5f8b690c89f Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 18:52:38 +0200 Subject: [PATCH 057/104] fix: minor 3 --- api/src/mocks/db.sql | 47 +++++++++++------------- api/src/timeslots/create-timeslot.dto.ts | 2 +- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/api/src/mocks/db.sql b/api/src/mocks/db.sql index d61b0d1..c1cc5f2 100644 --- a/api/src/mocks/db.sql +++ b/api/src/mocks/db.sql @@ -6,8 +6,7 @@ INSERT INTO "recruitment_session" VALUES (1, 'concluded', 1, '2024-04-10', '2024-04-04', ARRAY['2024-04-05', '2024-04-06', '2024-04-07', '2024-04-08', '2024-04-09']::date[], '2024-04-05', '2024-04-10'), (2, 'concluded', 1, '2024-05-10', '2024-05-04', ARRAY['2024-05-05', '2024-05-06', '2024-05-07', '2024-05-08']::date[], '2024-05-05', '2024-05-09'), - (3, 'active', 1, '2024-05-14', '2024-05-14', ARRAY['2024-05-16', '2024-05-17', '2024-05-18', '2024-05-19', '2024-05-20']::date[], '2024-05-16', '2024-05-21') -; + (3, 'active', 1, '2024-05-14', '2024-05-14', ARRAY['2024-05-16', '2024-05-17', '2024-05-18', '2024-05-19', '2024-05-20']::date[], '2024-05-16', '2024-05-21'); @@ -94,33 +93,32 @@ VALUES (77, '2024-05-20 15:00:00', '2024-05-20 16:00:00', 3), (78, '2024-05-20 16:00:00', '2024-05-20 17:00:00', 3), (79, '2024-05-20 17:00:00', '2024-05-20 18:00:00', 3), - (80, '2024-05-20 18:00:00', '2024-05-20 19:00:00', 3) -; + (80, '2024-05-20 18:00:00', '2024-05-20 19:00:00', 3); INSERT INTO "user" ("oauthId","firstName","lastName","sex","email","phone_no","telegramId","role","is_board","is_expert") VALUES - ('1', 'Pasquale', 'Bianco', 'male', 'p.bianco@gmail.com', '3405174444', 'pbianco', 'member', true, true), - ('2', 'John', 'Doe', 'male', 'j.doe@gmail.com', '1234567890', 'jdoe', 'member', false, true), - ('3', 'Jane', 'Smith', 'female', 'j.smith@gmail.com', '9876543210', 'jsmith', 'member', false, false), - ('4', 'Michael', 'Johnson', 'male', 'm.johnson@gmail.com', '5555555555', 'mjohnson', 'member', true, true), - ('5', 'Emily', 'Brown', 'female', 'e.brown@gmail.com', '1111111111', 'ebrown', 'member', false, true), - ('6', 'David', 'Wilson', 'male', 'd.wilson@gmail.com', '2222222222', 'dwilson', 'member', false, false), - ('7', 'Olivia', 'Johnson', 'female', 'o.johnson@gmail.com', '3333333333', 'ojohnson', 'member', true, false), - ('8', 'James', 'Smith', 'male', 'j.smith@gmail.com', '4444444444', 'jsmith', 'member', false, false), - ('9', 'Sophia', 'Miller', 'female', 's.miller@gmail.com', '5555555555', 'smiller', 'member', false, true), - ('10', 'Benjamin', 'Davis', 'male', 'b.davis@gmail.com', '6666666666', 'bdavis', 'member', false, false), - ('11', 'Ava', 'Wilson', 'female', 'a.wilson@gmail.com', '7777777777', 'awilson', 'member', true, true), - ('12', 'William', 'Anderson','male', 'w.anderson@gmail.com', '8888888888', 'wanderson', 'member', false, false), - ('13', 'Mia', 'Thomas', 'female', 'm.thomas@gmail.com', '9999999999', 'mthomas', 'member', false, false), - ('14', 'Alexander', 'Taylor', 'male', 'a.taylor@gmail.com', '1111111111', 'ataylor', 'member', false, true), - ('15', 'Charlotte', 'Clark', 'female', 'c.clark@gmail.com', '2222222222', 'cclark', 'member', false, false), - ('16', 'Daniel', 'Moore', 'male', 'd.moore@gmail.com', '3333333333', 'dmoore', 'member', false, false), - ('17', 'Amelia', 'Walker', 'female', 'a.walker@gmail.com', '4444444444', 'awalker', 'clerk', false, false), - ('18', 'Matthew', 'Lewis', 'male', 'm.lewis@gmail.com', '5555555555', 'mlewis', 'admin', false, true), + ('1', 'Pasquale', 'Bianco', 'male', 'p.bianco@gmail.com', '3405174444', 'pbianco', 'member', true, true), + ('2', 'John', 'Doe', 'male', 'j.doe@gmail.com', '1234567890', 'jdoe', 'member', false, true), + ('3', 'Jane', 'Smith', 'female', 'j.smith@gmail.com', '9876543210', 'jsmith', 'member', false, false), + ('4', 'Michael', 'Johnson', 'male', 'm.johnson@gmail.com', '5555555555', 'mjohnson', 'member', true, true), + ('5', 'Emily', 'Brown', 'female', 'e.brown@gmail.com', '1111111111', 'ebrown', 'member', false, true), + ('6', 'David', 'Wilson', 'male', 'd.wilson@gmail.com', '2222222222', 'dwilson', 'member', false, false), + ('7', 'Olivia', 'Johnson', 'female', 'o.johnson@gmail.com', '3333333333', 'ojohnson', 'member', true, false), + ('8', 'James', 'Smith', 'male', 'j.smith@gmail.com', '4444444444', 'jsmith', 'member', false, false), + ('9', 'Sophia', 'Miller', 'female', 's.miller@gmail.com', '5555555555', 'smiller', 'member', false, true), + ('10', 'Benjamin', 'Davis', 'male', 'b.davis@gmail.com', '6666666666', 'bdavis', 'member', false, false), + ('11', 'Ava', 'Wilson', 'female', 'a.wilson@gmail.com', '7777777777', 'awilson', 'member', true, true), + ('12', 'William', 'Anderson','male', 'w.anderson@gmail.com', '8888888888', 'wanderson', 'member', false, false), + ('13', 'Mia', 'Thomas', 'female', 'm.thomas@gmail.com', '9999999999', 'mthomas', 'member', false, false), + ('14', 'Alexander', 'Taylor', 'male', 'a.taylor@gmail.com', '1111111111', 'ataylor', 'member', false, true), + ('15', 'Charlotte', 'Clark', 'female', 'c.clark@gmail.com', '2222222222', 'cclark', 'member', false, false), + ('16', 'Daniel', 'Moore', 'male', 'd.moore@gmail.com', '3333333333', 'dmoore', 'member', false, false), + ('17', 'Amelia', 'Walker', 'female', 'a.walker@gmail.com', '4444444444', 'awalker', 'clerk', false, false), + ('18', 'Matthew', 'Lewis', 'male', 'm.lewis@gmail.com', '5555555555', 'mlewis', 'admin', false, true), ('19', 'Ella', 'Harris', 'female', 'e.harris@gmail.com', '6666666666', 'eharris', 'supervisor', false, true), - ('20', 'Joseph', 'King', 'male', 'j.king@gmail.com', '7777777777', 'jking', 'supervisor', true, false) + ('20', 'Joseph', 'King', 'male', 'j.king@gmail.com', '7777777777', 'jking', 'supervisor', true, false); # Availability @@ -178,6 +176,5 @@ VALUES (49, 'recovering' ,'2024-05-14 00:00:00', 63, '8' ), (50, 'free' ,'2024-05-14 00:00:00', 62, '14'), (51, 'free' ,'2024-05-14 00:00:00', 61, '4' ), - (52, 'free' ,'2024-05-14 00:00:00', 60, '13') -; + (52, 'free' ,'2024-05-14 00:00:00', 60, '13'); diff --git a/api/src/timeslots/create-timeslot.dto.ts b/api/src/timeslots/create-timeslot.dto.ts index 2e7836d..704d66a 100644 --- a/api/src/timeslots/create-timeslot.dto.ts +++ b/api/src/timeslots/create-timeslot.dto.ts @@ -2,7 +2,7 @@ import { TimeSlot } from '@hkrecruitment/shared'; import { ApiProperty } from '@nestjs/swagger'; export class CreateTimeSlotDto implements Partial { - @ApiProperty() + @ApiProperty() // start: Date; @ApiProperty() From 2050e010047738a401e189e9b2f68a6a1e45ed56 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:04:41 +0200 Subject: [PATCH 058/104] add: test 2e2 --- api/src/timeslots/timeslots.e2e-spec.ts | 230 ++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 api/src/timeslots/timeslots.e2e-spec.ts diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts new file mode 100644 index 0000000..d24138c --- /dev/null +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -0,0 +1,230 @@ +import { INestApplication } from "@nestjs/common"; +import * as request from 'supertest'; +import { TimeSlotsService } from "./timeslots.service"; +import { UsersService } from "../users/users.service"; +import { AvailabilityService } from "../availability/availability.service"; +import { RecruitmentSessionService } from "../recruitment-session/recruitment-session.service"; +import { Person, Role, TimeSlot, AvailabilityState, RecruitmentSessionState } from '@hkrecruitment/shared'; +import { RecruitmentSession } from "src/recruitment-session/recruitment-session.entity"; +import { Availability } from "src/availability/availability.entity"; +import { createApp, getAccessToken, getSub } from "test/app.e2e-spec"; + +describe("TimeslotsController", () => { + let app: INestApplication; + let newMemberToken: string; + let timeSlotsService: TimeSlotsService; + let usersService: UsersService; + let availabilityService: AvailabilityService; + let recruitmentSessionService: RecruitmentSessionService; + let mockUsers: Person[]; + let mockTimeSlots: TimeSlot[]; + let mockRecruitmentSessions: RecruitmentSession[]; + let mockAvailability: Availability[]; + + beforeAll(async () => { + newMemberToken = await getAccessToken('newMember'); + + mockTimeSlots = [ + {id: 1, start: new Date('2024-04-05 10:00:00'), end: new Date('2024-05-21 11:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 2, start: new Date('2024-04-05 11:00:00'), end: new Date('2024-05-21 12:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 3, start: new Date('2024-04-05 15:00:00'), end: new Date('2024-05-21 16:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 4, start: new Date('2024-04-05 16:00:00'), end: new Date('2024-05-21 17:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 5, start: new Date('2024-04-05 17:00:00'), end: new Date('2024-05-21 18:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 6, start: new Date('2024-04-05 18:00:00'), end: new Date('2024-05-21 19:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 7, start: new Date('2024-04-06 10:00:00'), end: new Date('2024-06-21 11:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 8, start: new Date('2024-04-06 11:00:00'), end: new Date('2024-06-21 12:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 9, start: new Date('2024-04-06 15:00:00'), end: new Date('2024-06-21 16:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 10, start: new Date('2024-04-06 16:00:00'), end: new Date('2024-06-21 17:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 11, start: new Date('2024-04-06 17:00:00'), end: new Date('2024-06-21 18:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 12, start: new Date('2024-04-06 18:00:00'), end: new Date('2024-06-21 19:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 13, start: new Date('2024-04-07 10:00:00'), end: new Date('2024-07-21 11:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 14, start: new Date('2024-04-07 11:00:00'), end: new Date('2024-07-21 12:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 15, start: new Date('2024-04-07 15:00:00'), end: new Date('2024-07-21 16:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 16, start: new Date('2024-04-07 16:00:00'), end: new Date('2024-07-21 17:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 17, start: new Date('2024-04-07 17:00:00'), end: new Date('2024-07-21 18:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 18, start: new Date('2024-04-07 18:00:00'), end: new Date('2024-07-21 19:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 19, start: new Date('2024-04-08 10:00:00'), end: new Date('2024-08-21 11:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 20, start: new Date('2024-04-08 11:00:00'), end: new Date('2024-08-21 12:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 21, start: new Date('2024-04-08 15:00:00'), end: new Date('2024-08-21 16:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 22, start: new Date('2024-04-08 16:00:00'), end: new Date('2024-08-21 17:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 23, start: new Date('2024-04-08 17:00:00'), end: new Date('2024-08-21 18:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 24, start: new Date('2024-04-08 18:00:00'), end: new Date('2024-08-21 19:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 25, start: new Date('2024-04-09 10:00:00'), end: new Date('2024-09-21 11:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 26, start: new Date('2024-04-09 11:00:00'), end: new Date('2024-09-21 12:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 27, start: new Date('2024-04-09 15:00:00'), end: new Date('2024-09-21 16:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 28, start: new Date('2024-04-09 16:00:00'), end: new Date('2024-09-21 17:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 29, start: new Date('2024-04-09 17:00:00'), end: new Date('2024-09-21 18:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 30, start: new Date('2024-04-09 18:00:00'), end: new Date('2024-09-21 19:00:00'), recruitmentSession: 1} as TimeSlot, + {id: 31, start: new Date('2024-05-05 10:00:00'), end: new Date('2024-05-05 11:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 32, start: new Date('2024-05-05 11:00:00'), end: new Date('2024-05-05 12:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 33, start: new Date('2024-05-05 15:00:00'), end: new Date('2024-05-05 16:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 34, start: new Date('2024-05-05 16:00:00'), end: new Date('2024-05-05 17:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 35, start: new Date('2024-05-05 17:00:00'), end: new Date('2024-05-05 18:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 36, start: new Date('2024-05-05 18:00:00'), end: new Date('2024-05-05 19:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 37, start: new Date('2024-05-06 10:00:00'), end: new Date('2024-05-06 11:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 38, start: new Date('2024-05-06 11:00:00'), end: new Date('2024-05-06 12:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 39, start: new Date('2024-05-06 15:00:00'), end: new Date('2024-05-06 16:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 40, start: new Date('2024-05-06 16:00:00'), end: new Date('2024-05-06 17:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 41, start: new Date('2024-05-06 17:00:00'), end: new Date('2024-05-06 18:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 42, start: new Date('2024-05-06 18:00:00'), end: new Date('2024-05-06 19:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 43, start: new Date('2024-05-07 10:00:00'), end: new Date('2024-05-07 11:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 44, start: new Date('2024-05-07 11:00:00'), end: new Date('2024-05-07 12:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 45, start: new Date('2024-05-07 15:00:00'), end: new Date('2024-05-07 16:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 46, start: new Date('2024-05-07 16:00:00'), end: new Date('2024-05-07 17:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 47, start: new Date('2024-05-07 17:00:00'), end: new Date('2024-05-07 18:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 48, start: new Date('2024-05-07 18:00:00'), end: new Date('2024-05-07 19:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 49, start: new Date('2024-05-08 10:00:00'), end: new Date('2024-05-08 11:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 50, start: new Date('2024-05-08 11:00:00'), end: new Date('2024-05-08 12:00:00'), recruitmentSession: 2} as TimeSlot, + {id: 51, start: new Date('2024-05-16 10:00:00'), end: new Date('2024-05-16 11:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 52, start: new Date('2024-05-16 11:00:00'), end: new Date('2024-05-16 12:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 53, start: new Date('2024-05-16 15:00:00'), end: new Date('2024-05-16 16:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 54, start: new Date('2024-05-16 16:00:00'), end: new Date('2024-05-16 17:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 55, start: new Date('2024-05-16 17:00:00'), end: new Date('2024-05-16 18:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 56, start: new Date('2024-05-16 18:00:00'), end: new Date('2024-05-16 19:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 57, start: new Date('2024-05-17 10:00:00'), end: new Date('2024-05-17 11:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 58, start: new Date('2024-05-17 11:00:00'), end: new Date('2024-05-17 12:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 59, start: new Date('2024-05-17 15:00:00'), end: new Date('2024-05-17 16:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 60, start: new Date('2024-05-17 16:00:00'), end: new Date('2024-05-17 17:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 61, start: new Date('2024-05-17 17:00:00'), end: new Date('2024-05-17 18:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 62, start: new Date('2024-05-17 18:00:00'), end: new Date('2024-05-17 19:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 63, start: new Date('2024-05-18 10:00:00'), end: new Date('2024-05-18 11:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 64, start: new Date('2024-05-18 11:00:00'), end: new Date('2024-05-18 12:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 65, start: new Date('2024-05-18 15:00:00'), end: new Date('2024-05-18 16:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 66, start: new Date('2024-05-18 16:00:00'), end: new Date('2024-05-18 17:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 67, start: new Date('2024-05-18 17:00:00'), end: new Date('2024-05-18 18:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 68, start: new Date('2024-05-18 18:00:00'), end: new Date('2024-05-18 19:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 69, start: new Date('2024-05-19 10:00:00'), end: new Date('2024-05-19 11:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 70, start: new Date('2024-05-19 11:00:00'), end: new Date('2024-05-19 12:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 71, start: new Date('2024-05-19 15:00:00'), end: new Date('2024-05-19 16:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 72, start: new Date('2024-05-19 16:00:00'), end: new Date('2024-05-19 17:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 73, start: new Date('2024-05-19 17:00:00'), end: new Date('2024-05-19 18:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 74, start: new Date('2024-05-19 18:00:00'), end: new Date('2024-05-19 19:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 75, start: new Date('2024-05-20 10:00:00'), end: new Date('2024-05-20 11:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 76, start: new Date('2024-05-20 11:00:00'), end: new Date('2024-05-20 12:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 77, start: new Date('2024-05-20 15:00:00'), end: new Date('2024-05-20 16:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 78, start: new Date('2024-05-20 16:00:00'), end: new Date('2024-05-20 17:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 79, start: new Date('2024-05-20 17:00:00'), end: new Date('2024-05-20 18:00:00'), recruitmentSession: 3} as TimeSlot, + {id: 80, start: new Date('2024-05-20 18:00:00'), end: new Date('2024-05-20 19:00:00'), recruitmentSession: 3} as TimeSlot + ] + + mockAvailability = [ + {id: 1, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 51, userOauthId: '5' } as unknown as Availability, + {id: 2, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '7' } as unknown as Availability, + {id: 3, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 55, userOauthId: '12'} as unknown as Availability, + {id: 4, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '19'} as unknown as Availability, + {id: 5, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '3' } as unknown as Availability, + {id: 6, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '15'} as unknown as Availability, + {id: 7, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 58, userOauthId: '11'} as unknown as Availability, + {id: 8, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '8' } as unknown as Availability, + {id: 9, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5' } as unknown as Availability, + {id: 10, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '17'} as unknown as Availability, + {id: 11, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '2' } as unknown as Availability, + {id: 12, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '9' } as unknown as Availability, + {id: 13, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '14'} as unknown as Availability, + {id: 14, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '6' } as unknown as Availability, + {id: 15, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '13'} as unknown as Availability, + {id: 16, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '10'} as unknown as Availability, + {id: 17, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '4' } as unknown as Availability, + {id: 18, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '16'} as unknown as Availability, + {id: 19, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 59, userOauthId: '18'} as unknown as Availability, + {id: 20, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '1' } as unknown as Availability, + {id: 21, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '20'} as unknown as Availability, + {id: 22, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '19'} as unknown as Availability, + {id: 23, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '7' } as unknown as Availability, + {id: 24, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '16'} as unknown as Availability, + {id: 25, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '15'} as unknown as Availability, + {id: 26, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '11'} as unknown as Availability, + {id: 27, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '14'} as unknown as Availability, + {id: 28, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '9' } as unknown as Availability, + {id: 29, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '8' } as unknown as Availability, + {id: 30, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '10'} as unknown as Availability, + {id: 31, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '13'} as unknown as Availability, + {id: 32, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 80, userOauthId: '12'} as unknown as Availability, + {id: 33, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '20'} as unknown as Availability, + {id: 34, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '18'} as unknown as Availability, + {id: 35, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '17'} as unknown as Availability, + {id: 36, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '19'} as unknown as Availability, + {id: 37, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '16'} as unknown as Availability, + {id: 38, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '15'} as unknown as Availability, + {id: 39, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '14'} as unknown as Availability, + {id: 40, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '13'} as unknown as Availability, + {id: 41, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '12'} as unknown as Availability, + {id: 42, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '11'} as unknown as Availability, + {id: 43, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '10'} as unknown as Availability, + {id: 44, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '9' } as unknown as Availability, + {id: 45, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 67, userOauthId: '8' } as unknown as Availability, + {id: 46, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '7' } as unknown as Availability, + {id: 47, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 65, userOauthId: '6' } as unknown as Availability, + {id: 48, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5' } as unknown as Availability, + {id: 49, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 63, userOauthId: '8' } as unknown as Availability, + {id: 50, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '14'} as unknown as Availability, + {id: 51, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '4' } as unknown as Availability, + {id: 52, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 60, userOauthId: '13'} as unknown as Availability, + ]; + + mockRecruitmentSessions = [ + {id: 1, state: RecruitmentSessionState.Concluded, slotDuration: 1, lastModified: new Date('2024-04-10'), createdAt: new Date('2024-04-04'), days:[new Date('2024-04-05'), new Date('2024-04-06'), new Date('2024-04-07'), new Date('2024-04-08'), new Date('2024-04-09')], interviewStart: new Date('2024-04-05'), interviewEnd: new Date('2024-04-10')}, + {id: 2, state: RecruitmentSessionState.Concluded, slotDuration: 1, lastModified: new Date('2024-05-10'), createdAt: new Date('2024-05-04'), days:[new Date('2024-05-05'), new Date('2024-05-06'), new Date('2024-05-07'), new Date('2024-05-08')] , interviewStart: new Date('2024-05-05'), interviewEnd: new Date('2024-05-09')}, + {id: 3, state: RecruitmentSessionState.Active, slotDuration: 1, lastModified: new Date('2024-05-14'), createdAt: new Date('2024-05-14'), days:[new Date('2024-05-16'), new Date('2024-05-17'), new Date('2024-05-18'), new Date('2024-05-19'), new Date('2024-05-20')], interviewStart: new Date('2024-05-16'), interviewEnd: new Date('2024-05-21')} + ]; + + mockUsers = [ + {oauthId: '1', firstName: 'Pasquale', lastName: 'Bianco', sex: 'male', email: 'p.bianco@gmail.com', phone_no: '3405174444', telegramId: 'pbianco', role: Role.Member, is_board: true, is_expert: true}, + {oauthId: '2', firstName: 'John', lastName: 'Doe', sex: 'male', email: 'j.doe@gmail.com', phone_no: '1234567890', telegramId: 'jdoe', role: Role.Member, is_board: false, is_expert: true}, + {oauthId: '3', firstName: 'Jane', lastName: 'Smith', sex: 'female', email: 'j.smith@gmail.com', phone_no: '9876543210', telegramId: 'jsmith', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '4', firstName: 'Michael', lastName: 'Johnson', sex: 'male', email: 'm.johnson@gmail.com', phone_no: '5555555555', telegramId: 'mjohnson', role: Role.Member, is_board: true, is_expert: true}, + {oauthId: '5', firstName: 'Emily', lastName: 'Brown', sex: 'female', email: 'e.brown@gmail.com', phone_no: '1111111111', telegramId: 'ebrown', role: Role.Member, is_board: false, is_expert: true}, + {oauthId: '6', firstName: 'David', lastName: 'Wilson', sex: 'male', email: 'd.wilson@gmail.com', phone_no: '2222222222', telegramId: 'dwilson', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '7', firstName: 'Olivia', lastName: 'Johnson', sex: 'female', email: 'o.johnson@gmail.com', phone_no: '3333333333', telegramId: 'ojohnson', role: Role.Member, is_board: true, is_expert: false}, + {oauthId: '8', firstName: 'James', lastName: 'Smith', sex: 'male', email: 'j.smith@gmail.com', phone_no: '4444444444', telegramId: 'jsmith', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '9', firstName: 'Sophia', lastName: 'Miller', sex: 'female', email: 's.miller@gmail.com', phone_no: '5555555555', telegramId: 'smiller', role: Role.Member, is_board: false, is_expert: true}, + {oauthId: '10', firstName: 'Benjamin', lastName: 'Davis', sex: 'male', email: 'b.davis@gmail.com', phone_no: '6666666666', telegramId: 'bdavis', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '11', firstName: 'Ava', lastName: 'Wilson', sex: 'female', email: 'a.wilson@gmail.com', phone_no: '7777777777', telegramId: 'awilson', role: Role.Member, is_board: true, is_expert: true}, + {oauthId: '12', firstName: 'William', lastName: 'Anderson',sex: 'male', email: 'w.anderson@gmail.com', phone_no: '8888888888', telegramId: 'wanderson', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '13', firstName: 'Mia', lastName: 'Thomas', sex: 'female', email: 'm.thomas@gmail.com', phone_no: '9999999999', telegramId: 'mthomas', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '14', firstName: 'Alexander', lastName: 'Taylor', sex: 'male', email: 'a.taylor@gmail.com', phone_no: '1111111111', telegramId: 'ataylor', role: Role.Member, is_board: false, is_expert: true}, + {oauthId: '15', firstName: 'Charlotte', lastName: 'Clark', sex: 'female', email: 'c.clark@gmail.com', phone_no: '2222222222', telegramId: 'cclark', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '16', firstName: 'Daniel', lastName: 'Moore', sex: 'male', email: 'd.moore@gmail.com', phone_no: '3333333333', telegramId: 'dmoore', role: Role.Member, is_board: false, is_expert: false}, + {oauthId: '17', firstName: 'Amelia', lastName: 'Walker', sex: 'female', email: 'a.walker@gmail.com', phone_no: '4444444444', telegramId: 'awalker', role: Role.Clerk, is_board: false, is_expert: false}, + {oauthId: '18', firstName: 'Matthew', lastName: 'Lewis', sex: 'male', email: 'm.lewis@gmail.com', phone_no: '5555555555', telegramId: 'mlewis', role: Role.Admin, is_board: false, is_expert: true}, + {oauthId: '19', firstName: 'Ella', lastName: 'Harris', sex: 'female', email: 'e.harris@gmail.com', phone_no: '6666666666', telegramId: 'eharris', role: Role.Supervisor, is_board: false, is_expert: true}, + {oauthId: '20', firstName: 'Joseph', lastName: 'King', sex: 'male', email: 'j.king@gmail.com', phone_no: '7777777777', telegramId: 'jking', role: Role.Supervisor, is_board: true, is_expert: false} + ]; + + + }); + + beforeEach(async () => { + app = await createApp(); + timeSlotsService = app.get(TimeSlotsService); + availabilityService = app.get(AvailabilityService); + recruitmentSessionService = app.get(RecruitmentSessionService); + usersService = app.get(UsersService); + }); + + afterEach(async () => { + await app.close(); + }); + + describe(" GET /timeslots", () => { + beforeEach(async () => { + await mockUsers.forEach(async (u) => await usersService.create(u)); + await mockTimeSlots.forEach(async (ts) => await timeSlotsService.createTimeSlot(ts)); + await mockAvailability.forEach(async (a) => await availabilityService.createAvailability(a)); + await mockRecruitmentSessions.forEach(async (rs) => await recruitmentSessionService.createRecruitmentSession(rs)); + }); + + it('should return all available timeslots', async () => { + + return await request(app.getHttpServer()) + .get('/timeslots') + .set('Authorization', `Bearer ${newMemberToken}`) + .expect(200) + .expect((res) => { + console.log(res.body); + expect(res.body).toBeInstanceOf(Array); + }); + }); + }); + +}); \ No newline at end of file From 4602a26e1b5b10d6d9feb4ad9eddfc244965b78a Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:05:01 +0200 Subject: [PATCH 059/104] format --- api/src/timeslots/timeslots.e2e-spec.ts | 1417 +++++++++++++++++++---- 1 file changed, 1202 insertions(+), 215 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index d24138c..e8b7512 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1,230 +1,1217 @@ -import { INestApplication } from "@nestjs/common"; +import { INestApplication } from '@nestjs/common'; import * as request from 'supertest'; -import { TimeSlotsService } from "./timeslots.service"; -import { UsersService } from "../users/users.service"; -import { AvailabilityService } from "../availability/availability.service"; -import { RecruitmentSessionService } from "../recruitment-session/recruitment-session.service"; -import { Person, Role, TimeSlot, AvailabilityState, RecruitmentSessionState } from '@hkrecruitment/shared'; -import { RecruitmentSession } from "src/recruitment-session/recruitment-session.entity"; -import { Availability } from "src/availability/availability.entity"; -import { createApp, getAccessToken, getSub } from "test/app.e2e-spec"; +import { TimeSlotsService } from './timeslots.service'; +import { UsersService } from '../users/users.service'; +import { AvailabilityService } from '../availability/availability.service'; +import { RecruitmentSessionService } from '../recruitment-session/recruitment-session.service'; +import { + Person, + Role, + TimeSlot, + AvailabilityState, + RecruitmentSessionState, +} from '@hkrecruitment/shared'; +import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; +import { Availability } from 'src/availability/availability.entity'; +import { createApp, getAccessToken, getSub } from 'test/app.e2e-spec'; -describe("TimeslotsController", () => { - let app: INestApplication; - let newMemberToken: string; - let timeSlotsService: TimeSlotsService; - let usersService: UsersService; - let availabilityService: AvailabilityService; - let recruitmentSessionService: RecruitmentSessionService; - let mockUsers: Person[]; - let mockTimeSlots: TimeSlot[]; - let mockRecruitmentSessions: RecruitmentSession[]; - let mockAvailability: Availability[]; +describe('TimeslotsController', () => { + let app: INestApplication; + let newMemberToken: string; + let timeSlotsService: TimeSlotsService; + let usersService: UsersService; + let availabilityService: AvailabilityService; + let recruitmentSessionService: RecruitmentSessionService; + let mockUsers: Person[]; + let mockTimeSlots: TimeSlot[]; + let mockRecruitmentSessions: RecruitmentSession[]; + let mockAvailability: Availability[]; - beforeAll(async () => { - newMemberToken = await getAccessToken('newMember'); + beforeAll(async () => { + newMemberToken = await getAccessToken('newMember'); - mockTimeSlots = [ - {id: 1, start: new Date('2024-04-05 10:00:00'), end: new Date('2024-05-21 11:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 2, start: new Date('2024-04-05 11:00:00'), end: new Date('2024-05-21 12:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 3, start: new Date('2024-04-05 15:00:00'), end: new Date('2024-05-21 16:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 4, start: new Date('2024-04-05 16:00:00'), end: new Date('2024-05-21 17:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 5, start: new Date('2024-04-05 17:00:00'), end: new Date('2024-05-21 18:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 6, start: new Date('2024-04-05 18:00:00'), end: new Date('2024-05-21 19:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 7, start: new Date('2024-04-06 10:00:00'), end: new Date('2024-06-21 11:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 8, start: new Date('2024-04-06 11:00:00'), end: new Date('2024-06-21 12:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 9, start: new Date('2024-04-06 15:00:00'), end: new Date('2024-06-21 16:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 10, start: new Date('2024-04-06 16:00:00'), end: new Date('2024-06-21 17:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 11, start: new Date('2024-04-06 17:00:00'), end: new Date('2024-06-21 18:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 12, start: new Date('2024-04-06 18:00:00'), end: new Date('2024-06-21 19:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 13, start: new Date('2024-04-07 10:00:00'), end: new Date('2024-07-21 11:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 14, start: new Date('2024-04-07 11:00:00'), end: new Date('2024-07-21 12:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 15, start: new Date('2024-04-07 15:00:00'), end: new Date('2024-07-21 16:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 16, start: new Date('2024-04-07 16:00:00'), end: new Date('2024-07-21 17:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 17, start: new Date('2024-04-07 17:00:00'), end: new Date('2024-07-21 18:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 18, start: new Date('2024-04-07 18:00:00'), end: new Date('2024-07-21 19:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 19, start: new Date('2024-04-08 10:00:00'), end: new Date('2024-08-21 11:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 20, start: new Date('2024-04-08 11:00:00'), end: new Date('2024-08-21 12:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 21, start: new Date('2024-04-08 15:00:00'), end: new Date('2024-08-21 16:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 22, start: new Date('2024-04-08 16:00:00'), end: new Date('2024-08-21 17:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 23, start: new Date('2024-04-08 17:00:00'), end: new Date('2024-08-21 18:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 24, start: new Date('2024-04-08 18:00:00'), end: new Date('2024-08-21 19:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 25, start: new Date('2024-04-09 10:00:00'), end: new Date('2024-09-21 11:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 26, start: new Date('2024-04-09 11:00:00'), end: new Date('2024-09-21 12:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 27, start: new Date('2024-04-09 15:00:00'), end: new Date('2024-09-21 16:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 28, start: new Date('2024-04-09 16:00:00'), end: new Date('2024-09-21 17:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 29, start: new Date('2024-04-09 17:00:00'), end: new Date('2024-09-21 18:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 30, start: new Date('2024-04-09 18:00:00'), end: new Date('2024-09-21 19:00:00'), recruitmentSession: 1} as TimeSlot, - {id: 31, start: new Date('2024-05-05 10:00:00'), end: new Date('2024-05-05 11:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 32, start: new Date('2024-05-05 11:00:00'), end: new Date('2024-05-05 12:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 33, start: new Date('2024-05-05 15:00:00'), end: new Date('2024-05-05 16:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 34, start: new Date('2024-05-05 16:00:00'), end: new Date('2024-05-05 17:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 35, start: new Date('2024-05-05 17:00:00'), end: new Date('2024-05-05 18:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 36, start: new Date('2024-05-05 18:00:00'), end: new Date('2024-05-05 19:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 37, start: new Date('2024-05-06 10:00:00'), end: new Date('2024-05-06 11:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 38, start: new Date('2024-05-06 11:00:00'), end: new Date('2024-05-06 12:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 39, start: new Date('2024-05-06 15:00:00'), end: new Date('2024-05-06 16:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 40, start: new Date('2024-05-06 16:00:00'), end: new Date('2024-05-06 17:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 41, start: new Date('2024-05-06 17:00:00'), end: new Date('2024-05-06 18:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 42, start: new Date('2024-05-06 18:00:00'), end: new Date('2024-05-06 19:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 43, start: new Date('2024-05-07 10:00:00'), end: new Date('2024-05-07 11:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 44, start: new Date('2024-05-07 11:00:00'), end: new Date('2024-05-07 12:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 45, start: new Date('2024-05-07 15:00:00'), end: new Date('2024-05-07 16:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 46, start: new Date('2024-05-07 16:00:00'), end: new Date('2024-05-07 17:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 47, start: new Date('2024-05-07 17:00:00'), end: new Date('2024-05-07 18:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 48, start: new Date('2024-05-07 18:00:00'), end: new Date('2024-05-07 19:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 49, start: new Date('2024-05-08 10:00:00'), end: new Date('2024-05-08 11:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 50, start: new Date('2024-05-08 11:00:00'), end: new Date('2024-05-08 12:00:00'), recruitmentSession: 2} as TimeSlot, - {id: 51, start: new Date('2024-05-16 10:00:00'), end: new Date('2024-05-16 11:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 52, start: new Date('2024-05-16 11:00:00'), end: new Date('2024-05-16 12:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 53, start: new Date('2024-05-16 15:00:00'), end: new Date('2024-05-16 16:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 54, start: new Date('2024-05-16 16:00:00'), end: new Date('2024-05-16 17:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 55, start: new Date('2024-05-16 17:00:00'), end: new Date('2024-05-16 18:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 56, start: new Date('2024-05-16 18:00:00'), end: new Date('2024-05-16 19:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 57, start: new Date('2024-05-17 10:00:00'), end: new Date('2024-05-17 11:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 58, start: new Date('2024-05-17 11:00:00'), end: new Date('2024-05-17 12:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 59, start: new Date('2024-05-17 15:00:00'), end: new Date('2024-05-17 16:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 60, start: new Date('2024-05-17 16:00:00'), end: new Date('2024-05-17 17:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 61, start: new Date('2024-05-17 17:00:00'), end: new Date('2024-05-17 18:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 62, start: new Date('2024-05-17 18:00:00'), end: new Date('2024-05-17 19:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 63, start: new Date('2024-05-18 10:00:00'), end: new Date('2024-05-18 11:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 64, start: new Date('2024-05-18 11:00:00'), end: new Date('2024-05-18 12:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 65, start: new Date('2024-05-18 15:00:00'), end: new Date('2024-05-18 16:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 66, start: new Date('2024-05-18 16:00:00'), end: new Date('2024-05-18 17:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 67, start: new Date('2024-05-18 17:00:00'), end: new Date('2024-05-18 18:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 68, start: new Date('2024-05-18 18:00:00'), end: new Date('2024-05-18 19:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 69, start: new Date('2024-05-19 10:00:00'), end: new Date('2024-05-19 11:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 70, start: new Date('2024-05-19 11:00:00'), end: new Date('2024-05-19 12:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 71, start: new Date('2024-05-19 15:00:00'), end: new Date('2024-05-19 16:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 72, start: new Date('2024-05-19 16:00:00'), end: new Date('2024-05-19 17:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 73, start: new Date('2024-05-19 17:00:00'), end: new Date('2024-05-19 18:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 74, start: new Date('2024-05-19 18:00:00'), end: new Date('2024-05-19 19:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 75, start: new Date('2024-05-20 10:00:00'), end: new Date('2024-05-20 11:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 76, start: new Date('2024-05-20 11:00:00'), end: new Date('2024-05-20 12:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 77, start: new Date('2024-05-20 15:00:00'), end: new Date('2024-05-20 16:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 78, start: new Date('2024-05-20 16:00:00'), end: new Date('2024-05-20 17:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 79, start: new Date('2024-05-20 17:00:00'), end: new Date('2024-05-20 18:00:00'), recruitmentSession: 3} as TimeSlot, - {id: 80, start: new Date('2024-05-20 18:00:00'), end: new Date('2024-05-20 19:00:00'), recruitmentSession: 3} as TimeSlot - ] + mockTimeSlots = [ + { + id: 1, + start: new Date('2024-04-05 10:00:00'), + end: new Date('2024-05-21 11:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 2, + start: new Date('2024-04-05 11:00:00'), + end: new Date('2024-05-21 12:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 3, + start: new Date('2024-04-05 15:00:00'), + end: new Date('2024-05-21 16:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 4, + start: new Date('2024-04-05 16:00:00'), + end: new Date('2024-05-21 17:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 5, + start: new Date('2024-04-05 17:00:00'), + end: new Date('2024-05-21 18:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 6, + start: new Date('2024-04-05 18:00:00'), + end: new Date('2024-05-21 19:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 7, + start: new Date('2024-04-06 10:00:00'), + end: new Date('2024-06-21 11:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 8, + start: new Date('2024-04-06 11:00:00'), + end: new Date('2024-06-21 12:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 9, + start: new Date('2024-04-06 15:00:00'), + end: new Date('2024-06-21 16:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 10, + start: new Date('2024-04-06 16:00:00'), + end: new Date('2024-06-21 17:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 11, + start: new Date('2024-04-06 17:00:00'), + end: new Date('2024-06-21 18:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 12, + start: new Date('2024-04-06 18:00:00'), + end: new Date('2024-06-21 19:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 13, + start: new Date('2024-04-07 10:00:00'), + end: new Date('2024-07-21 11:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 14, + start: new Date('2024-04-07 11:00:00'), + end: new Date('2024-07-21 12:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 15, + start: new Date('2024-04-07 15:00:00'), + end: new Date('2024-07-21 16:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 16, + start: new Date('2024-04-07 16:00:00'), + end: new Date('2024-07-21 17:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 17, + start: new Date('2024-04-07 17:00:00'), + end: new Date('2024-07-21 18:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 18, + start: new Date('2024-04-07 18:00:00'), + end: new Date('2024-07-21 19:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 19, + start: new Date('2024-04-08 10:00:00'), + end: new Date('2024-08-21 11:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 20, + start: new Date('2024-04-08 11:00:00'), + end: new Date('2024-08-21 12:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 21, + start: new Date('2024-04-08 15:00:00'), + end: new Date('2024-08-21 16:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 22, + start: new Date('2024-04-08 16:00:00'), + end: new Date('2024-08-21 17:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 23, + start: new Date('2024-04-08 17:00:00'), + end: new Date('2024-08-21 18:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 24, + start: new Date('2024-04-08 18:00:00'), + end: new Date('2024-08-21 19:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 25, + start: new Date('2024-04-09 10:00:00'), + end: new Date('2024-09-21 11:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 26, + start: new Date('2024-04-09 11:00:00'), + end: new Date('2024-09-21 12:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 27, + start: new Date('2024-04-09 15:00:00'), + end: new Date('2024-09-21 16:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 28, + start: new Date('2024-04-09 16:00:00'), + end: new Date('2024-09-21 17:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 29, + start: new Date('2024-04-09 17:00:00'), + end: new Date('2024-09-21 18:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 30, + start: new Date('2024-04-09 18:00:00'), + end: new Date('2024-09-21 19:00:00'), + recruitmentSession: 1, + } as TimeSlot, + { + id: 31, + start: new Date('2024-05-05 10:00:00'), + end: new Date('2024-05-05 11:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 32, + start: new Date('2024-05-05 11:00:00'), + end: new Date('2024-05-05 12:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 33, + start: new Date('2024-05-05 15:00:00'), + end: new Date('2024-05-05 16:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 34, + start: new Date('2024-05-05 16:00:00'), + end: new Date('2024-05-05 17:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 35, + start: new Date('2024-05-05 17:00:00'), + end: new Date('2024-05-05 18:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 36, + start: new Date('2024-05-05 18:00:00'), + end: new Date('2024-05-05 19:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 37, + start: new Date('2024-05-06 10:00:00'), + end: new Date('2024-05-06 11:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 38, + start: new Date('2024-05-06 11:00:00'), + end: new Date('2024-05-06 12:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 39, + start: new Date('2024-05-06 15:00:00'), + end: new Date('2024-05-06 16:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 40, + start: new Date('2024-05-06 16:00:00'), + end: new Date('2024-05-06 17:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 41, + start: new Date('2024-05-06 17:00:00'), + end: new Date('2024-05-06 18:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 42, + start: new Date('2024-05-06 18:00:00'), + end: new Date('2024-05-06 19:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 43, + start: new Date('2024-05-07 10:00:00'), + end: new Date('2024-05-07 11:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 44, + start: new Date('2024-05-07 11:00:00'), + end: new Date('2024-05-07 12:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 45, + start: new Date('2024-05-07 15:00:00'), + end: new Date('2024-05-07 16:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 46, + start: new Date('2024-05-07 16:00:00'), + end: new Date('2024-05-07 17:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 47, + start: new Date('2024-05-07 17:00:00'), + end: new Date('2024-05-07 18:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 48, + start: new Date('2024-05-07 18:00:00'), + end: new Date('2024-05-07 19:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 49, + start: new Date('2024-05-08 10:00:00'), + end: new Date('2024-05-08 11:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 50, + start: new Date('2024-05-08 11:00:00'), + end: new Date('2024-05-08 12:00:00'), + recruitmentSession: 2, + } as TimeSlot, + { + id: 51, + start: new Date('2024-05-16 10:00:00'), + end: new Date('2024-05-16 11:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 52, + start: new Date('2024-05-16 11:00:00'), + end: new Date('2024-05-16 12:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 53, + start: new Date('2024-05-16 15:00:00'), + end: new Date('2024-05-16 16:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 54, + start: new Date('2024-05-16 16:00:00'), + end: new Date('2024-05-16 17:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 55, + start: new Date('2024-05-16 17:00:00'), + end: new Date('2024-05-16 18:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 56, + start: new Date('2024-05-16 18:00:00'), + end: new Date('2024-05-16 19:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 57, + start: new Date('2024-05-17 10:00:00'), + end: new Date('2024-05-17 11:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 58, + start: new Date('2024-05-17 11:00:00'), + end: new Date('2024-05-17 12:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 59, + start: new Date('2024-05-17 15:00:00'), + end: new Date('2024-05-17 16:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 60, + start: new Date('2024-05-17 16:00:00'), + end: new Date('2024-05-17 17:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 61, + start: new Date('2024-05-17 17:00:00'), + end: new Date('2024-05-17 18:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 62, + start: new Date('2024-05-17 18:00:00'), + end: new Date('2024-05-17 19:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 63, + start: new Date('2024-05-18 10:00:00'), + end: new Date('2024-05-18 11:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 64, + start: new Date('2024-05-18 11:00:00'), + end: new Date('2024-05-18 12:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 65, + start: new Date('2024-05-18 15:00:00'), + end: new Date('2024-05-18 16:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 66, + start: new Date('2024-05-18 16:00:00'), + end: new Date('2024-05-18 17:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 67, + start: new Date('2024-05-18 17:00:00'), + end: new Date('2024-05-18 18:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 68, + start: new Date('2024-05-18 18:00:00'), + end: new Date('2024-05-18 19:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 69, + start: new Date('2024-05-19 10:00:00'), + end: new Date('2024-05-19 11:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 70, + start: new Date('2024-05-19 11:00:00'), + end: new Date('2024-05-19 12:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 71, + start: new Date('2024-05-19 15:00:00'), + end: new Date('2024-05-19 16:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 72, + start: new Date('2024-05-19 16:00:00'), + end: new Date('2024-05-19 17:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 73, + start: new Date('2024-05-19 17:00:00'), + end: new Date('2024-05-19 18:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 74, + start: new Date('2024-05-19 18:00:00'), + end: new Date('2024-05-19 19:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 75, + start: new Date('2024-05-20 10:00:00'), + end: new Date('2024-05-20 11:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 76, + start: new Date('2024-05-20 11:00:00'), + end: new Date('2024-05-20 12:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 77, + start: new Date('2024-05-20 15:00:00'), + end: new Date('2024-05-20 16:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 78, + start: new Date('2024-05-20 16:00:00'), + end: new Date('2024-05-20 17:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 79, + start: new Date('2024-05-20 17:00:00'), + end: new Date('2024-05-20 18:00:00'), + recruitmentSession: 3, + } as TimeSlot, + { + id: 80, + start: new Date('2024-05-20 18:00:00'), + end: new Date('2024-05-20 19:00:00'), + recruitmentSession: 3, + } as TimeSlot, + ]; - mockAvailability = [ - {id: 1, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 51, userOauthId: '5' } as unknown as Availability, - {id: 2, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '7' } as unknown as Availability, - {id: 3, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 55, userOauthId: '12'} as unknown as Availability, - {id: 4, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '19'} as unknown as Availability, - {id: 5, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '3' } as unknown as Availability, - {id: 6, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '15'} as unknown as Availability, - {id: 7, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 58, userOauthId: '11'} as unknown as Availability, - {id: 8, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '8' } as unknown as Availability, - {id: 9, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5' } as unknown as Availability, - {id: 10, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '17'} as unknown as Availability, - {id: 11, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '2' } as unknown as Availability, - {id: 12, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '9' } as unknown as Availability, - {id: 13, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '14'} as unknown as Availability, - {id: 14, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '6' } as unknown as Availability, - {id: 15, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '13'} as unknown as Availability, - {id: 16, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '10'} as unknown as Availability, - {id: 17, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '4' } as unknown as Availability, - {id: 18, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '16'} as unknown as Availability, - {id: 19, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 59, userOauthId: '18'} as unknown as Availability, - {id: 20, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '1' } as unknown as Availability, - {id: 21, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '20'} as unknown as Availability, - {id: 22, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '19'} as unknown as Availability, - {id: 23, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '7' } as unknown as Availability, - {id: 24, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '16'} as unknown as Availability, - {id: 25, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '15'} as unknown as Availability, - {id: 26, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '11'} as unknown as Availability, - {id: 27, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '14'} as unknown as Availability, - {id: 28, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '9' } as unknown as Availability, - {id: 29, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '8' } as unknown as Availability, - {id: 30, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '10'} as unknown as Availability, - {id: 31, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '13'} as unknown as Availability, - {id: 32, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 80, userOauthId: '12'} as unknown as Availability, - {id: 33, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '20'} as unknown as Availability, - {id: 34, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '18'} as unknown as Availability, - {id: 35, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '17'} as unknown as Availability, - {id: 36, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '19'} as unknown as Availability, - {id: 37, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '16'} as unknown as Availability, - {id: 38, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '15'} as unknown as Availability, - {id: 39, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '14'} as unknown as Availability, - {id: 40, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '13'} as unknown as Availability, - {id: 41, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '12'} as unknown as Availability, - {id: 42, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '11'} as unknown as Availability, - {id: 43, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '10'} as unknown as Availability, - {id: 44, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '9' } as unknown as Availability, - {id: 45, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 67, userOauthId: '8' } as unknown as Availability, - {id: 46, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '7' } as unknown as Availability, - {id: 47, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 65, userOauthId: '6' } as unknown as Availability, - {id: 48, state: AvailabilityState.Interviewing , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5' } as unknown as Availability, - {id: 49, state: AvailabilityState.Recovering , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 63, userOauthId: '8' } as unknown as Availability, - {id: 50, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '14'} as unknown as Availability, - {id: 51, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '4' } as unknown as Availability, - {id: 52, state: AvailabilityState.Free , lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 60, userOauthId: '13'} as unknown as Availability, - ]; + mockAvailability = [ + { + id: 1, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 51, + userOauthId: '5', + } as unknown as Availability, + { + id: 2, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 53, + userOauthId: '7', + } as unknown as Availability, + { + id: 3, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 55, + userOauthId: '12', + } as unknown as Availability, + { + id: 4, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 61, + userOauthId: '19', + } as unknown as Availability, + { + id: 5, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '3', + } as unknown as Availability, + { + id: 6, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 78, + userOauthId: '15', + } as unknown as Availability, + { + id: 7, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 58, + userOauthId: '11', + } as unknown as Availability, + { + id: 8, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 57, + userOauthId: '8', + } as unknown as Availability, + { + id: 9, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '5', + } as unknown as Availability, + { + id: 10, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 53, + userOauthId: '17', + } as unknown as Availability, + { + id: 11, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 62, + userOauthId: '2', + } as unknown as Availability, + { + id: 12, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '9', + } as unknown as Availability, + { + id: 13, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '14', + } as unknown as Availability, + { + id: 14, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '6', + } as unknown as Availability, + { + id: 15, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '13', + } as unknown as Availability, + { + id: 16, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '10', + } as unknown as Availability, + { + id: 17, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '4', + } as unknown as Availability, + { + id: 18, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '16', + } as unknown as Availability, + { + id: 19, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 59, + userOauthId: '18', + } as unknown as Availability, + { + id: 20, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 57, + userOauthId: '1', + } as unknown as Availability, + { + id: 21, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 66, + userOauthId: '20', + } as unknown as Availability, + { + id: 22, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 68, + userOauthId: '19', + } as unknown as Availability, + { + id: 23, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 69, + userOauthId: '7', + } as unknown as Availability, + { + id: 24, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 70, + userOauthId: '16', + } as unknown as Availability, + { + id: 25, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '15', + } as unknown as Availability, + { + id: 26, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 73, + userOauthId: '11', + } as unknown as Availability, + { + id: 27, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '14', + } as unknown as Availability, + { + id: 28, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 76, + userOauthId: '9', + } as unknown as Availability, + { + id: 29, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 77, + userOauthId: '8', + } as unknown as Availability, + { + id: 30, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 78, + userOauthId: '10', + } as unknown as Availability, + { + id: 31, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 79, + userOauthId: '13', + } as unknown as Availability, + { + id: 32, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 80, + userOauthId: '12', + } as unknown as Availability, + { + id: 33, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 79, + userOauthId: '20', + } as unknown as Availability, + { + id: 34, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 78, + userOauthId: '18', + } as unknown as Availability, + { + id: 35, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 77, + userOauthId: '17', + } as unknown as Availability, + { + id: 36, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 76, + userOauthId: '19', + } as unknown as Availability, + { + id: 37, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '16', + } as unknown as Availability, + { + id: 38, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '15', + } as unknown as Availability, + { + id: 39, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 73, + userOauthId: '14', + } as unknown as Availability, + { + id: 40, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '13', + } as unknown as Availability, + { + id: 41, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '12', + } as unknown as Availability, + { + id: 42, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 70, + userOauthId: '11', + } as unknown as Availability, + { + id: 43, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 69, + userOauthId: '10', + } as unknown as Availability, + { + id: 44, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 68, + userOauthId: '9', + } as unknown as Availability, + { + id: 45, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 67, + userOauthId: '8', + } as unknown as Availability, + { + id: 46, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 66, + userOauthId: '7', + } as unknown as Availability, + { + id: 47, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 65, + userOauthId: '6', + } as unknown as Availability, + { + id: 48, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '5', + } as unknown as Availability, + { + id: 49, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 63, + userOauthId: '8', + } as unknown as Availability, + { + id: 50, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 62, + userOauthId: '14', + } as unknown as Availability, + { + id: 51, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 61, + userOauthId: '4', + } as unknown as Availability, + { + id: 52, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 60, + userOauthId: '13', + } as unknown as Availability, + ]; - mockRecruitmentSessions = [ - {id: 1, state: RecruitmentSessionState.Concluded, slotDuration: 1, lastModified: new Date('2024-04-10'), createdAt: new Date('2024-04-04'), days:[new Date('2024-04-05'), new Date('2024-04-06'), new Date('2024-04-07'), new Date('2024-04-08'), new Date('2024-04-09')], interviewStart: new Date('2024-04-05'), interviewEnd: new Date('2024-04-10')}, - {id: 2, state: RecruitmentSessionState.Concluded, slotDuration: 1, lastModified: new Date('2024-05-10'), createdAt: new Date('2024-05-04'), days:[new Date('2024-05-05'), new Date('2024-05-06'), new Date('2024-05-07'), new Date('2024-05-08')] , interviewStart: new Date('2024-05-05'), interviewEnd: new Date('2024-05-09')}, - {id: 3, state: RecruitmentSessionState.Active, slotDuration: 1, lastModified: new Date('2024-05-14'), createdAt: new Date('2024-05-14'), days:[new Date('2024-05-16'), new Date('2024-05-17'), new Date('2024-05-18'), new Date('2024-05-19'), new Date('2024-05-20')], interviewStart: new Date('2024-05-16'), interviewEnd: new Date('2024-05-21')} - ]; + mockRecruitmentSessions = [ + { + id: 1, + state: RecruitmentSessionState.Concluded, + slotDuration: 1, + lastModified: new Date('2024-04-10'), + createdAt: new Date('2024-04-04'), + days: [ + new Date('2024-04-05'), + new Date('2024-04-06'), + new Date('2024-04-07'), + new Date('2024-04-08'), + new Date('2024-04-09'), + ], + interviewStart: new Date('2024-04-05'), + interviewEnd: new Date('2024-04-10'), + }, + { + id: 2, + state: RecruitmentSessionState.Concluded, + slotDuration: 1, + lastModified: new Date('2024-05-10'), + createdAt: new Date('2024-05-04'), + days: [ + new Date('2024-05-05'), + new Date('2024-05-06'), + new Date('2024-05-07'), + new Date('2024-05-08'), + ], + interviewStart: new Date('2024-05-05'), + interviewEnd: new Date('2024-05-09'), + }, + { + id: 3, + state: RecruitmentSessionState.Active, + slotDuration: 1, + lastModified: new Date('2024-05-14'), + createdAt: new Date('2024-05-14'), + days: [ + new Date('2024-05-16'), + new Date('2024-05-17'), + new Date('2024-05-18'), + new Date('2024-05-19'), + new Date('2024-05-20'), + ], + interviewStart: new Date('2024-05-16'), + interviewEnd: new Date('2024-05-21'), + }, + ]; - mockUsers = [ - {oauthId: '1', firstName: 'Pasquale', lastName: 'Bianco', sex: 'male', email: 'p.bianco@gmail.com', phone_no: '3405174444', telegramId: 'pbianco', role: Role.Member, is_board: true, is_expert: true}, - {oauthId: '2', firstName: 'John', lastName: 'Doe', sex: 'male', email: 'j.doe@gmail.com', phone_no: '1234567890', telegramId: 'jdoe', role: Role.Member, is_board: false, is_expert: true}, - {oauthId: '3', firstName: 'Jane', lastName: 'Smith', sex: 'female', email: 'j.smith@gmail.com', phone_no: '9876543210', telegramId: 'jsmith', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '4', firstName: 'Michael', lastName: 'Johnson', sex: 'male', email: 'm.johnson@gmail.com', phone_no: '5555555555', telegramId: 'mjohnson', role: Role.Member, is_board: true, is_expert: true}, - {oauthId: '5', firstName: 'Emily', lastName: 'Brown', sex: 'female', email: 'e.brown@gmail.com', phone_no: '1111111111', telegramId: 'ebrown', role: Role.Member, is_board: false, is_expert: true}, - {oauthId: '6', firstName: 'David', lastName: 'Wilson', sex: 'male', email: 'd.wilson@gmail.com', phone_no: '2222222222', telegramId: 'dwilson', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '7', firstName: 'Olivia', lastName: 'Johnson', sex: 'female', email: 'o.johnson@gmail.com', phone_no: '3333333333', telegramId: 'ojohnson', role: Role.Member, is_board: true, is_expert: false}, - {oauthId: '8', firstName: 'James', lastName: 'Smith', sex: 'male', email: 'j.smith@gmail.com', phone_no: '4444444444', telegramId: 'jsmith', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '9', firstName: 'Sophia', lastName: 'Miller', sex: 'female', email: 's.miller@gmail.com', phone_no: '5555555555', telegramId: 'smiller', role: Role.Member, is_board: false, is_expert: true}, - {oauthId: '10', firstName: 'Benjamin', lastName: 'Davis', sex: 'male', email: 'b.davis@gmail.com', phone_no: '6666666666', telegramId: 'bdavis', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '11', firstName: 'Ava', lastName: 'Wilson', sex: 'female', email: 'a.wilson@gmail.com', phone_no: '7777777777', telegramId: 'awilson', role: Role.Member, is_board: true, is_expert: true}, - {oauthId: '12', firstName: 'William', lastName: 'Anderson',sex: 'male', email: 'w.anderson@gmail.com', phone_no: '8888888888', telegramId: 'wanderson', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '13', firstName: 'Mia', lastName: 'Thomas', sex: 'female', email: 'm.thomas@gmail.com', phone_no: '9999999999', telegramId: 'mthomas', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '14', firstName: 'Alexander', lastName: 'Taylor', sex: 'male', email: 'a.taylor@gmail.com', phone_no: '1111111111', telegramId: 'ataylor', role: Role.Member, is_board: false, is_expert: true}, - {oauthId: '15', firstName: 'Charlotte', lastName: 'Clark', sex: 'female', email: 'c.clark@gmail.com', phone_no: '2222222222', telegramId: 'cclark', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '16', firstName: 'Daniel', lastName: 'Moore', sex: 'male', email: 'd.moore@gmail.com', phone_no: '3333333333', telegramId: 'dmoore', role: Role.Member, is_board: false, is_expert: false}, - {oauthId: '17', firstName: 'Amelia', lastName: 'Walker', sex: 'female', email: 'a.walker@gmail.com', phone_no: '4444444444', telegramId: 'awalker', role: Role.Clerk, is_board: false, is_expert: false}, - {oauthId: '18', firstName: 'Matthew', lastName: 'Lewis', sex: 'male', email: 'm.lewis@gmail.com', phone_no: '5555555555', telegramId: 'mlewis', role: Role.Admin, is_board: false, is_expert: true}, - {oauthId: '19', firstName: 'Ella', lastName: 'Harris', sex: 'female', email: 'e.harris@gmail.com', phone_no: '6666666666', telegramId: 'eharris', role: Role.Supervisor, is_board: false, is_expert: true}, - {oauthId: '20', firstName: 'Joseph', lastName: 'King', sex: 'male', email: 'j.king@gmail.com', phone_no: '7777777777', telegramId: 'jking', role: Role.Supervisor, is_board: true, is_expert: false} - ]; + mockUsers = [ + { + oauthId: '1', + firstName: 'Pasquale', + lastName: 'Bianco', + sex: 'male', + email: 'p.bianco@gmail.com', + phone_no: '3405174444', + telegramId: 'pbianco', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '2', + firstName: 'John', + lastName: 'Doe', + sex: 'male', + email: 'j.doe@gmail.com', + phone_no: '1234567890', + telegramId: 'jdoe', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '3', + firstName: 'Jane', + lastName: 'Smith', + sex: 'female', + email: 'j.smith@gmail.com', + phone_no: '9876543210', + telegramId: 'jsmith', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '4', + firstName: 'Michael', + lastName: 'Johnson', + sex: 'male', + email: 'm.johnson@gmail.com', + phone_no: '5555555555', + telegramId: 'mjohnson', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '5', + firstName: 'Emily', + lastName: 'Brown', + sex: 'female', + email: 'e.brown@gmail.com', + phone_no: '1111111111', + telegramId: 'ebrown', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '6', + firstName: 'David', + lastName: 'Wilson', + sex: 'male', + email: 'd.wilson@gmail.com', + phone_no: '2222222222', + telegramId: 'dwilson', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '7', + firstName: 'Olivia', + lastName: 'Johnson', + sex: 'female', + email: 'o.johnson@gmail.com', + phone_no: '3333333333', + telegramId: 'ojohnson', + role: Role.Member, + is_board: true, + is_expert: false, + }, + { + oauthId: '8', + firstName: 'James', + lastName: 'Smith', + sex: 'male', + email: 'j.smith@gmail.com', + phone_no: '4444444444', + telegramId: 'jsmith', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '9', + firstName: 'Sophia', + lastName: 'Miller', + sex: 'female', + email: 's.miller@gmail.com', + phone_no: '5555555555', + telegramId: 'smiller', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '10', + firstName: 'Benjamin', + lastName: 'Davis', + sex: 'male', + email: 'b.davis@gmail.com', + phone_no: '6666666666', + telegramId: 'bdavis', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '11', + firstName: 'Ava', + lastName: 'Wilson', + sex: 'female', + email: 'a.wilson@gmail.com', + phone_no: '7777777777', + telegramId: 'awilson', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '12', + firstName: 'William', + lastName: 'Anderson', + sex: 'male', + email: 'w.anderson@gmail.com', + phone_no: '8888888888', + telegramId: 'wanderson', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '13', + firstName: 'Mia', + lastName: 'Thomas', + sex: 'female', + email: 'm.thomas@gmail.com', + phone_no: '9999999999', + telegramId: 'mthomas', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '14', + firstName: 'Alexander', + lastName: 'Taylor', + sex: 'male', + email: 'a.taylor@gmail.com', + phone_no: '1111111111', + telegramId: 'ataylor', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '15', + firstName: 'Charlotte', + lastName: 'Clark', + sex: 'female', + email: 'c.clark@gmail.com', + phone_no: '2222222222', + telegramId: 'cclark', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '16', + firstName: 'Daniel', + lastName: 'Moore', + sex: 'male', + email: 'd.moore@gmail.com', + phone_no: '3333333333', + telegramId: 'dmoore', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '17', + firstName: 'Amelia', + lastName: 'Walker', + sex: 'female', + email: 'a.walker@gmail.com', + phone_no: '4444444444', + telegramId: 'awalker', + role: Role.Clerk, + is_board: false, + is_expert: false, + }, + { + oauthId: '18', + firstName: 'Matthew', + lastName: 'Lewis', + sex: 'male', + email: 'm.lewis@gmail.com', + phone_no: '5555555555', + telegramId: 'mlewis', + role: Role.Admin, + is_board: false, + is_expert: true, + }, + { + oauthId: '19', + firstName: 'Ella', + lastName: 'Harris', + sex: 'female', + email: 'e.harris@gmail.com', + phone_no: '6666666666', + telegramId: 'eharris', + role: Role.Supervisor, + is_board: false, + is_expert: true, + }, + { + oauthId: '20', + firstName: 'Joseph', + lastName: 'King', + sex: 'male', + email: 'j.king@gmail.com', + phone_no: '7777777777', + telegramId: 'jking', + role: Role.Supervisor, + is_board: true, + is_expert: false, + }, + ]; + }); + beforeEach(async () => { + app = await createApp(); + timeSlotsService = app.get(TimeSlotsService); + availabilityService = app.get(AvailabilityService); + recruitmentSessionService = app.get( + RecruitmentSessionService, + ); + usersService = app.get(UsersService); + }); - }); + afterEach(async () => { + await app.close(); + }); + describe(' GET /timeslots', () => { beforeEach(async () => { - app = await createApp(); - timeSlotsService = app.get(TimeSlotsService); - availabilityService = app.get(AvailabilityService); - recruitmentSessionService = app.get(RecruitmentSessionService); - usersService = app.get(UsersService); - }); - - afterEach(async () => { - await app.close(); + await mockUsers.forEach(async (u) => await usersService.create(u)); + await mockTimeSlots.forEach( + async (ts) => await timeSlotsService.createTimeSlot(ts), + ); + await mockAvailability.forEach( + async (a) => await availabilityService.createAvailability(a), + ); + await mockRecruitmentSessions.forEach( + async (rs) => + await recruitmentSessionService.createRecruitmentSession(rs), + ); }); - describe(" GET /timeslots", () => { - beforeEach(async () => { - await mockUsers.forEach(async (u) => await usersService.create(u)); - await mockTimeSlots.forEach(async (ts) => await timeSlotsService.createTimeSlot(ts)); - await mockAvailability.forEach(async (a) => await availabilityService.createAvailability(a)); - await mockRecruitmentSessions.forEach(async (rs) => await recruitmentSessionService.createRecruitmentSession(rs)); - }); - - it('should return all available timeslots', async () => { - - return await request(app.getHttpServer()) - .get('/timeslots') - .set('Authorization', `Bearer ${newMemberToken}`) - .expect(200) - .expect((res) => { - console.log(res.body); - expect(res.body).toBeInstanceOf(Array); - }); + it('should return all available timeslots', async () => { + return await request(app.getHttpServer()) + .get('/timeslots') + .set('Authorization', `Bearer ${newMemberToken}`) + .expect(200) + .expect((res) => { + console.log(res.body); + expect(res.body).toBeInstanceOf(Array); }); }); - -}); \ No newline at end of file + }); +}); From d034c3315c12cf4e344b224ad3aad0c6b7c1d31d Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:12:59 +0200 Subject: [PATCH 060/104] fix: sql errors --- api/src/timeslots/timeslots.e2e-spec.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index e8b7512..d265175 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1190,6 +1190,10 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { + await mockRecruitmentSessions.forEach( + async (rs) => + await recruitmentSessionService.createRecruitmentSession(rs), + ); await mockUsers.forEach(async (u) => await usersService.create(u)); await mockTimeSlots.forEach( async (ts) => await timeSlotsService.createTimeSlot(ts), @@ -1197,13 +1201,21 @@ describe('TimeslotsController', () => { await mockAvailability.forEach( async (a) => await availabilityService.createAvailability(a), ); - await mockRecruitmentSessions.forEach( - async (rs) => - await recruitmentSessionService.createRecruitmentSession(rs), - ); }); it('should return all available timeslots', async () => { + const expected = [ + { + id: 71, + start: '2024-05-19T13:00:00.000Z', + end: '2024-05-19T14:00:00.000Z', + }, + { + id: 73, + start: '2024-05-19T15:00:00.000Z', + end: '2024-05-19T16:00:00.000Z', + }, + ]; return await request(app.getHttpServer()) .get('/timeslots') .set('Authorization', `Bearer ${newMemberToken}`) @@ -1211,6 +1223,7 @@ describe('TimeslotsController', () => { .expect((res) => { console.log(res.body); expect(res.body).toBeInstanceOf(Array); + expect(res.body).toEqual(expected); }); }); }); From 127c223a9abff5c60c73a9e6af382a6f13223918 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:21:19 +0200 Subject: [PATCH 061/104] fix: sql error 2 --- api/src/timeslots/timeslots.e2e-spec.ts | 35 ++++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index d265175..4bb9b6a 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1190,17 +1190,32 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { - await mockRecruitmentSessions.forEach( - async (rs) => - await recruitmentSessionService.createRecruitmentSession(rs), - ); - await mockUsers.forEach(async (u) => await usersService.create(u)); - await mockTimeSlots.forEach( - async (ts) => await timeSlotsService.createTimeSlot(ts), - ); - await mockAvailability.forEach( - async (a) => await availabilityService.createAvailability(a), + + let promises = []; + mockRecruitmentSessions.forEach(async (rs) => + promises.push(recruitmentSessionService.createRecruitmentSession(rs)), ); + + Promise.all(promises).then(() => { + promises = []; + mockUsers.forEach(async (u) => promises.push(usersService.create(u))); + + Promise.all(promises).then(() => { + promises = []; + mockTimeSlots.forEach(async (ts) => + promises.push(timeSlotsService.createTimeSlot(ts)), + ); + + Promise.all(promises).then(() => { + promises = []; + mockAvailability.forEach(async (a) => + promises.push(availabilityService.createAvailability(a)), + ); + + Promise.all(promises); + }); + }); + }); }); it('should return all available timeslots', async () => { From cd7290964c68e439aa487824ebcabca383eafa75 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:22:17 +0200 Subject: [PATCH 062/104] format --- api/src/timeslots/timeslots.e2e-spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 4bb9b6a..64a547d 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1190,7 +1190,6 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { - let promises = []; mockRecruitmentSessions.forEach(async (rs) => promises.push(recruitmentSessionService.createRecruitmentSession(rs)), From 520a1f4f2ecd932f4a31e74ec9cf12032b67e570 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:29:06 +0200 Subject: [PATCH 063/104] fix: minor --- api/src/timeslots/timeslots.e2e-spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 64a547d..fd96f40 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1220,14 +1220,14 @@ describe('TimeslotsController', () => { it('should return all available timeslots', async () => { const expected = [ { + end: '2024-05-19T14:00:00.000Z', id: 71, start: '2024-05-19T13:00:00.000Z', - end: '2024-05-19T14:00:00.000Z', }, { + end: '2024-05-19T16:00:00.000Z', id: 73, start: '2024-05-19T15:00:00.000Z', - end: '2024-05-19T16:00:00.000Z', }, ]; return await request(app.getHttpServer()) @@ -1235,7 +1235,6 @@ describe('TimeslotsController', () => { .set('Authorization', `Bearer ${newMemberToken}`) .expect(200) .expect((res) => { - console.log(res.body); expect(res.body).toBeInstanceOf(Array); expect(res.body).toEqual(expected); }); From 3a8c0d23f03e007698d25965888ae4a8a050c05c Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:35:46 +0200 Subject: [PATCH 064/104] fix: 2 --- api/src/timeslots/timeslots.e2e-spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index fd96f40..6b63e08 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1195,7 +1195,7 @@ describe('TimeslotsController', () => { promises.push(recruitmentSessionService.createRecruitmentSession(rs)), ); - Promise.all(promises).then(() => { + await Promise.all(promises).then(() => { promises = []; mockUsers.forEach(async (u) => promises.push(usersService.create(u))); @@ -1231,7 +1231,7 @@ describe('TimeslotsController', () => { }, ]; return await request(app.getHttpServer()) - .get('/timeslots') + .get('/v1/timeslots') .set('Authorization', `Bearer ${newMemberToken}`) .expect(200) .expect((res) => { From 50533c5cc6eb7eef7573daed7b32a79e4512cb15 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:38:34 +0200 Subject: [PATCH 065/104] fix: 3 --- api/src/timeslots/timeslots.e2e-spec.ts | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 6b63e08..7259342 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1191,30 +1191,30 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { let promises = []; - mockRecruitmentSessions.forEach(async (rs) => + mockRecruitmentSessions.forEach((rs) => promises.push(recruitmentSessionService.createRecruitmentSession(rs)), ); - await Promise.all(promises).then(() => { - promises = []; - mockUsers.forEach(async (u) => promises.push(usersService.create(u))); + await Promise.all(promises); - Promise.all(promises).then(() => { - promises = []; - mockTimeSlots.forEach(async (ts) => - promises.push(timeSlotsService.createTimeSlot(ts)), - ); + promises = []; + mockUsers.forEach((u) => promises.push(usersService.create(u))); - Promise.all(promises).then(() => { - promises = []; - mockAvailability.forEach(async (a) => - promises.push(availabilityService.createAvailability(a)), - ); + await Promise.all(promises); - Promise.all(promises); - }); - }); - }); + promises = []; + mockTimeSlots.forEach((ts) => + promises.push(timeSlotsService.createTimeSlot(ts)), + ); + + await Promise.all(promises); + + promises = []; + mockAvailability.forEach((a) => + promises.push(availabilityService.createAvailability(a)), + ); + + await Promise.all(promises); }); it('should return all available timeslots', async () => { From 682b5b8c79fe86b1d8ba56025e1695948ae37038 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:42:00 +0200 Subject: [PATCH 066/104] fix 4 --- api/src/timeslots/timeslots.e2e-spec.ts | 36 +++++++++---------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 7259342..dc590b3 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -1190,31 +1190,21 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { - let promises = []; - mockRecruitmentSessions.forEach((rs) => - promises.push(recruitmentSessionService.createRecruitmentSession(rs)), - ); + for (const rs of mockRecruitmentSessions) { + await recruitmentSessionService.createRecruitmentSession(rs); + } - await Promise.all(promises); + for (const u of mockUsers) { + await usersService.create(u); + } - promises = []; - mockUsers.forEach((u) => promises.push(usersService.create(u))); + for (const ts of mockTimeSlots) { + await timeSlotsService.createTimeSlot(ts); + } - await Promise.all(promises); - - promises = []; - mockTimeSlots.forEach((ts) => - promises.push(timeSlotsService.createTimeSlot(ts)), - ); - - await Promise.all(promises); - - promises = []; - mockAvailability.forEach((a) => - promises.push(availabilityService.createAvailability(a)), - ); - - await Promise.all(promises); + for (const a of mockAvailability) { + await availabilityService.createAvailability(a); + } }); it('should return all available timeslots', async () => { @@ -1231,7 +1221,7 @@ describe('TimeslotsController', () => { }, ]; return await request(app.getHttpServer()) - .get('/v1/timeslots') + .get('/timeslots') .set('Authorization', `Bearer ${newMemberToken}`) .expect(200) .expect((res) => { From d7dd6c3be2055fd183994fabab3da9bef881bb18 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:51:02 +0200 Subject: [PATCH 067/104] fix: 6 --- api/src/timeslots/timeslots.e2e-spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index dc590b3..8a27c0e 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -13,7 +13,7 @@ import { } from '@hkrecruitment/shared'; import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; import { Availability } from 'src/availability/availability.entity'; -import { createApp, getAccessToken, getSub } from 'test/app.e2e-spec'; +import { createApp, getAccessToken } from 'test/app.e2e-spec'; describe('TimeslotsController', () => { let app: INestApplication; @@ -1220,14 +1220,17 @@ describe('TimeslotsController', () => { start: '2024-05-19T15:00:00.000Z', }, ]; - return await request(app.getHttpServer()) + await request(app.getHttpServer()) .get('/timeslots') .set('Authorization', `Bearer ${newMemberToken}`) .expect(200) .expect((res) => { expect(res.body).toBeInstanceOf(Array); - expect(res.body).toEqual(expected); }); + + const availableTimeSlots = + await timeSlotsService.findAvailableTimeSlots(); + expect(availableTimeSlots).toEqual(expected); }); }); }); From 2154316e98b8d68ae8ff072822905d3462e22dea Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 19:57:36 +0200 Subject: [PATCH 068/104] fix: 5 --- api/src/timeslots/timeslots.e2e-spec.ts | 78 ++++++------------------- 1 file changed, 19 insertions(+), 59 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 8a27c0e..a0319e7 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -935,10 +935,8 @@ describe('TimeslotsController', () => { oauthId: '1', firstName: 'Pasquale', lastName: 'Bianco', - sex: 'male', + sex: 'M', email: 'p.bianco@gmail.com', - phone_no: '3405174444', - telegramId: 'pbianco', role: Role.Member, is_board: true, is_expert: true, @@ -947,10 +945,8 @@ describe('TimeslotsController', () => { oauthId: '2', firstName: 'John', lastName: 'Doe', - sex: 'male', + sex: 'M', email: 'j.doe@gmail.com', - phone_no: '1234567890', - telegramId: 'jdoe', role: Role.Member, is_board: false, is_expert: true, @@ -959,10 +955,8 @@ describe('TimeslotsController', () => { oauthId: '3', firstName: 'Jane', lastName: 'Smith', - sex: 'female', + sex: 'F', email: 'j.smith@gmail.com', - phone_no: '9876543210', - telegramId: 'jsmith', role: Role.Member, is_board: false, is_expert: false, @@ -971,10 +965,8 @@ describe('TimeslotsController', () => { oauthId: '4', firstName: 'Michael', lastName: 'Johnson', - sex: 'male', + sex: 'M', email: 'm.johnson@gmail.com', - phone_no: '5555555555', - telegramId: 'mjohnson', role: Role.Member, is_board: true, is_expert: true, @@ -983,10 +975,8 @@ describe('TimeslotsController', () => { oauthId: '5', firstName: 'Emily', lastName: 'Brown', - sex: 'female', + sex: 'F', email: 'e.brown@gmail.com', - phone_no: '1111111111', - telegramId: 'ebrown', role: Role.Member, is_board: false, is_expert: true, @@ -995,10 +985,8 @@ describe('TimeslotsController', () => { oauthId: '6', firstName: 'David', lastName: 'Wilson', - sex: 'male', + sex: 'M', email: 'd.wilson@gmail.com', - phone_no: '2222222222', - telegramId: 'dwilson', role: Role.Member, is_board: false, is_expert: false, @@ -1007,10 +995,8 @@ describe('TimeslotsController', () => { oauthId: '7', firstName: 'Olivia', lastName: 'Johnson', - sex: 'female', + sex: 'F', email: 'o.johnson@gmail.com', - phone_no: '3333333333', - telegramId: 'ojohnson', role: Role.Member, is_board: true, is_expert: false, @@ -1019,10 +1005,8 @@ describe('TimeslotsController', () => { oauthId: '8', firstName: 'James', lastName: 'Smith', - sex: 'male', + sex: 'M', email: 'j.smith@gmail.com', - phone_no: '4444444444', - telegramId: 'jsmith', role: Role.Member, is_board: false, is_expert: false, @@ -1031,10 +1015,8 @@ describe('TimeslotsController', () => { oauthId: '9', firstName: 'Sophia', lastName: 'Miller', - sex: 'female', + sex: 'F', email: 's.miller@gmail.com', - phone_no: '5555555555', - telegramId: 'smiller', role: Role.Member, is_board: false, is_expert: true, @@ -1043,10 +1025,8 @@ describe('TimeslotsController', () => { oauthId: '10', firstName: 'Benjamin', lastName: 'Davis', - sex: 'male', + sex: 'M', email: 'b.davis@gmail.com', - phone_no: '6666666666', - telegramId: 'bdavis', role: Role.Member, is_board: false, is_expert: false, @@ -1057,8 +1037,6 @@ describe('TimeslotsController', () => { lastName: 'Wilson', sex: 'female', email: 'a.wilson@gmail.com', - phone_no: '7777777777', - telegramId: 'awilson', role: Role.Member, is_board: true, is_expert: true, @@ -1067,10 +1045,8 @@ describe('TimeslotsController', () => { oauthId: '12', firstName: 'William', lastName: 'Anderson', - sex: 'male', + sex: 'M', email: 'w.anderson@gmail.com', - phone_no: '8888888888', - telegramId: 'wanderson', role: Role.Member, is_board: false, is_expert: false, @@ -1079,10 +1055,8 @@ describe('TimeslotsController', () => { oauthId: '13', firstName: 'Mia', lastName: 'Thomas', - sex: 'female', + sex: 'F', email: 'm.thomas@gmail.com', - phone_no: '9999999999', - telegramId: 'mthomas', role: Role.Member, is_board: false, is_expert: false, @@ -1091,10 +1065,8 @@ describe('TimeslotsController', () => { oauthId: '14', firstName: 'Alexander', lastName: 'Taylor', - sex: 'male', + sex: 'M', email: 'a.taylor@gmail.com', - phone_no: '1111111111', - telegramId: 'ataylor', role: Role.Member, is_board: false, is_expert: true, @@ -1103,10 +1075,8 @@ describe('TimeslotsController', () => { oauthId: '15', firstName: 'Charlotte', lastName: 'Clark', - sex: 'female', + sex: 'F', email: 'c.clark@gmail.com', - phone_no: '2222222222', - telegramId: 'cclark', role: Role.Member, is_board: false, is_expert: false, @@ -1115,10 +1085,8 @@ describe('TimeslotsController', () => { oauthId: '16', firstName: 'Daniel', lastName: 'Moore', - sex: 'male', + sex: 'M', email: 'd.moore@gmail.com', - phone_no: '3333333333', - telegramId: 'dmoore', role: Role.Member, is_board: false, is_expert: false, @@ -1127,10 +1095,8 @@ describe('TimeslotsController', () => { oauthId: '17', firstName: 'Amelia', lastName: 'Walker', - sex: 'female', + sex: 'F', email: 'a.walker@gmail.com', - phone_no: '4444444444', - telegramId: 'awalker', role: Role.Clerk, is_board: false, is_expert: false, @@ -1139,10 +1105,8 @@ describe('TimeslotsController', () => { oauthId: '18', firstName: 'Matthew', lastName: 'Lewis', - sex: 'male', + sex: 'M', email: 'm.lewis@gmail.com', - phone_no: '5555555555', - telegramId: 'mlewis', role: Role.Admin, is_board: false, is_expert: true, @@ -1151,10 +1115,8 @@ describe('TimeslotsController', () => { oauthId: '19', firstName: 'Ella', lastName: 'Harris', - sex: 'female', + sex: 'F', email: 'e.harris@gmail.com', - phone_no: '6666666666', - telegramId: 'eharris', role: Role.Supervisor, is_board: false, is_expert: true, @@ -1163,10 +1125,8 @@ describe('TimeslotsController', () => { oauthId: '20', firstName: 'Joseph', lastName: 'King', - sex: 'male', + sex: 'M', email: 'j.king@gmail.com', - phone_no: '7777777777', - telegramId: 'jking', role: Role.Supervisor, is_board: true, is_expert: false, From fc00fc51b6e88fae5aa927a72826f337067646f7 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 20:11:21 +0200 Subject: [PATCH 069/104] fix: 7 --- api/src/timeslots/create-timeslot.dto.ts | 5 ++ api/src/timeslots/timeslots.e2e-spec.ts | 83 +----------------------- shared/src/timeslot.ts | 4 ++ 3 files changed, 11 insertions(+), 81 deletions(-) diff --git a/api/src/timeslots/create-timeslot.dto.ts b/api/src/timeslots/create-timeslot.dto.ts index 704d66a..0b50071 100644 --- a/api/src/timeslots/create-timeslot.dto.ts +++ b/api/src/timeslots/create-timeslot.dto.ts @@ -1,5 +1,7 @@ import { TimeSlot } from '@hkrecruitment/shared'; import { ApiProperty } from '@nestjs/swagger'; +import { Availability } from 'src/availability/availability.entity'; +import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; export class CreateTimeSlotDto implements Partial { @ApiProperty() // @@ -7,4 +9,7 @@ export class CreateTimeSlotDto implements Partial { @ApiProperty() end: Date; + + @ApiProperty() + recruitmentSession: RecruitmentSession; } diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index a0319e7..41cedb9 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -14,6 +14,7 @@ import { import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; import { Availability } from 'src/availability/availability.entity'; import { createApp, getAccessToken } from 'test/app.e2e-spec'; +import { CreateTimeSlotDto } from './create-timeslot.dto'; describe('TimeslotsController', () => { let app: INestApplication; @@ -23,7 +24,7 @@ describe('TimeslotsController', () => { let availabilityService: AvailabilityService; let recruitmentSessionService: RecruitmentSessionService; let mockUsers: Person[]; - let mockTimeSlots: TimeSlot[]; + let mockTimeSlots: CreateTimeSlotDto[]; let mockRecruitmentSessions: RecruitmentSession[]; let mockAvailability: Availability[]; @@ -32,181 +33,151 @@ describe('TimeslotsController', () => { mockTimeSlots = [ { - id: 1, start: new Date('2024-04-05 10:00:00'), end: new Date('2024-05-21 11:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 2, start: new Date('2024-04-05 11:00:00'), end: new Date('2024-05-21 12:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 3, start: new Date('2024-04-05 15:00:00'), end: new Date('2024-05-21 16:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 4, start: new Date('2024-04-05 16:00:00'), end: new Date('2024-05-21 17:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 5, start: new Date('2024-04-05 17:00:00'), end: new Date('2024-05-21 18:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 6, start: new Date('2024-04-05 18:00:00'), end: new Date('2024-05-21 19:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 7, start: new Date('2024-04-06 10:00:00'), end: new Date('2024-06-21 11:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 8, start: new Date('2024-04-06 11:00:00'), end: new Date('2024-06-21 12:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 9, start: new Date('2024-04-06 15:00:00'), end: new Date('2024-06-21 16:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 10, start: new Date('2024-04-06 16:00:00'), end: new Date('2024-06-21 17:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 11, start: new Date('2024-04-06 17:00:00'), end: new Date('2024-06-21 18:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 12, start: new Date('2024-04-06 18:00:00'), end: new Date('2024-06-21 19:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 13, start: new Date('2024-04-07 10:00:00'), end: new Date('2024-07-21 11:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 14, start: new Date('2024-04-07 11:00:00'), end: new Date('2024-07-21 12:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 15, start: new Date('2024-04-07 15:00:00'), end: new Date('2024-07-21 16:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 16, start: new Date('2024-04-07 16:00:00'), end: new Date('2024-07-21 17:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 17, start: new Date('2024-04-07 17:00:00'), end: new Date('2024-07-21 18:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 18, start: new Date('2024-04-07 18:00:00'), end: new Date('2024-07-21 19:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 19, start: new Date('2024-04-08 10:00:00'), end: new Date('2024-08-21 11:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 20, start: new Date('2024-04-08 11:00:00'), end: new Date('2024-08-21 12:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 21, start: new Date('2024-04-08 15:00:00'), end: new Date('2024-08-21 16:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 22, start: new Date('2024-04-08 16:00:00'), end: new Date('2024-08-21 17:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 23, start: new Date('2024-04-08 17:00:00'), end: new Date('2024-08-21 18:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 24, start: new Date('2024-04-08 18:00:00'), end: new Date('2024-08-21 19:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 25, start: new Date('2024-04-09 10:00:00'), end: new Date('2024-09-21 11:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 26, start: new Date('2024-04-09 11:00:00'), end: new Date('2024-09-21 12:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 27, start: new Date('2024-04-09 15:00:00'), end: new Date('2024-09-21 16:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 28, start: new Date('2024-04-09 16:00:00'), end: new Date('2024-09-21 17:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 29, start: new Date('2024-04-09 17:00:00'), end: new Date('2024-09-21 18:00:00'), recruitmentSession: 1, } as TimeSlot, { - id: 30, start: new Date('2024-04-09 18:00:00'), end: new Date('2024-09-21 19:00:00'), recruitmentSession: 1, @@ -218,295 +189,245 @@ describe('TimeslotsController', () => { recruitmentSession: 2, } as TimeSlot, { - id: 32, start: new Date('2024-05-05 11:00:00'), end: new Date('2024-05-05 12:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 33, start: new Date('2024-05-05 15:00:00'), end: new Date('2024-05-05 16:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 34, start: new Date('2024-05-05 16:00:00'), end: new Date('2024-05-05 17:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 35, start: new Date('2024-05-05 17:00:00'), end: new Date('2024-05-05 18:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 36, start: new Date('2024-05-05 18:00:00'), end: new Date('2024-05-05 19:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 37, start: new Date('2024-05-06 10:00:00'), end: new Date('2024-05-06 11:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 38, start: new Date('2024-05-06 11:00:00'), end: new Date('2024-05-06 12:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 39, start: new Date('2024-05-06 15:00:00'), end: new Date('2024-05-06 16:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 40, - start: new Date('2024-05-06 16:00:00'), end: new Date('2024-05-06 17:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 41, start: new Date('2024-05-06 17:00:00'), end: new Date('2024-05-06 18:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 42, start: new Date('2024-05-06 18:00:00'), end: new Date('2024-05-06 19:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 43, start: new Date('2024-05-07 10:00:00'), end: new Date('2024-05-07 11:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 44, start: new Date('2024-05-07 11:00:00'), end: new Date('2024-05-07 12:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 45, start: new Date('2024-05-07 15:00:00'), end: new Date('2024-05-07 16:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 46, start: new Date('2024-05-07 16:00:00'), end: new Date('2024-05-07 17:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 47, start: new Date('2024-05-07 17:00:00'), end: new Date('2024-05-07 18:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 48, start: new Date('2024-05-07 18:00:00'), end: new Date('2024-05-07 19:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 49, start: new Date('2024-05-08 10:00:00'), end: new Date('2024-05-08 11:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 50, start: new Date('2024-05-08 11:00:00'), end: new Date('2024-05-08 12:00:00'), recruitmentSession: 2, } as TimeSlot, { - id: 51, start: new Date('2024-05-16 10:00:00'), end: new Date('2024-05-16 11:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 52, start: new Date('2024-05-16 11:00:00'), end: new Date('2024-05-16 12:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 53, start: new Date('2024-05-16 15:00:00'), end: new Date('2024-05-16 16:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 54, start: new Date('2024-05-16 16:00:00'), end: new Date('2024-05-16 17:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 55, start: new Date('2024-05-16 17:00:00'), end: new Date('2024-05-16 18:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 56, start: new Date('2024-05-16 18:00:00'), end: new Date('2024-05-16 19:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 57, start: new Date('2024-05-17 10:00:00'), end: new Date('2024-05-17 11:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 58, start: new Date('2024-05-17 11:00:00'), end: new Date('2024-05-17 12:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 59, start: new Date('2024-05-17 15:00:00'), end: new Date('2024-05-17 16:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 60, start: new Date('2024-05-17 16:00:00'), end: new Date('2024-05-17 17:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 61, start: new Date('2024-05-17 17:00:00'), end: new Date('2024-05-17 18:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 62, start: new Date('2024-05-17 18:00:00'), end: new Date('2024-05-17 19:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 63, start: new Date('2024-05-18 10:00:00'), end: new Date('2024-05-18 11:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 64, start: new Date('2024-05-18 11:00:00'), end: new Date('2024-05-18 12:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 65, start: new Date('2024-05-18 15:00:00'), end: new Date('2024-05-18 16:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 66, start: new Date('2024-05-18 16:00:00'), end: new Date('2024-05-18 17:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 67, start: new Date('2024-05-18 17:00:00'), end: new Date('2024-05-18 18:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 68, start: new Date('2024-05-18 18:00:00'), end: new Date('2024-05-18 19:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 69, start: new Date('2024-05-19 10:00:00'), end: new Date('2024-05-19 11:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 70, start: new Date('2024-05-19 11:00:00'), end: new Date('2024-05-19 12:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 71, start: new Date('2024-05-19 15:00:00'), end: new Date('2024-05-19 16:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 72, start: new Date('2024-05-19 16:00:00'), end: new Date('2024-05-19 17:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 73, start: new Date('2024-05-19 17:00:00'), end: new Date('2024-05-19 18:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 74, start: new Date('2024-05-19 18:00:00'), end: new Date('2024-05-19 19:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 75, start: new Date('2024-05-20 10:00:00'), end: new Date('2024-05-20 11:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 76, start: new Date('2024-05-20 11:00:00'), end: new Date('2024-05-20 12:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 77, start: new Date('2024-05-20 15:00:00'), end: new Date('2024-05-20 16:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 78, start: new Date('2024-05-20 16:00:00'), end: new Date('2024-05-20 17:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 79, start: new Date('2024-05-20 17:00:00'), end: new Date('2024-05-20 18:00:00'), recruitmentSession: 3, } as TimeSlot, { - id: 80, start: new Date('2024-05-20 18:00:00'), end: new Date('2024-05-20 19:00:00'), recruitmentSession: 3, diff --git a/shared/src/timeslot.ts b/shared/src/timeslot.ts index 6343754..3665746 100644 --- a/shared/src/timeslot.ts +++ b/shared/src/timeslot.ts @@ -1,10 +1,14 @@ +import { Availability } from "availability"; import { Action, ApplyAbilities } from "./abilities"; import { Role } from "./person"; +import { RecruitmentSession } from "recruitment-session"; export interface TimeSlot { id: number; start: Date; end: Date; + recruitmentSession: RecruitmentSession; + availabilities: Availability[]; } /* Abilities */ From 5aa5400f0969b71cab798e194b381a3b50f20741 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 20:47:12 +0200 Subject: [PATCH 070/104] fix: 8 --- api/src/mocks/data.ts | 15 +- api/src/timeslots/timeslot.entity.ts | 1 - api/src/timeslots/timeslots.e2e-spec.ts | 1138 ++++++++----------- api/src/timeslots/timeslots.service.spec.ts | 8 +- api/src/timeslots/timeslots.service.ts | 8 +- shared/src/timeslot.ts | 4 +- 6 files changed, 474 insertions(+), 700 deletions(-) diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index 64b1c36..c4c0bd3 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -28,13 +28,6 @@ export const testDateTime1Hour = new Date(2023, 0, 1, 11, 30, 0); export const testDateTime3Hours = new Date(2023, 0, 1, 13, 30, 0); export const testDateTimeEnd = new Date(2023, 0, 1, 11, 30, 0); -export const mockTimeSlot = { - start: testDateTimeStart, - end: testDateTimeEnd, - id: 1, - availabilities: [], -} as TimeSlot & { availabilities: any[] }; - export const testInterviewStart = '11:55' as unknown as Date; export const testInterviewEnd = '20:35' as unknown as Date; export const testDay1 = '2024-10-20' as unknown as Date; @@ -54,6 +47,14 @@ export const mockRecruitmentSession = { lastModified: testDateLastModified, }; +export const mockTimeSlot = { + recruitmentSession: mockRecruitmentSession, + start: testDateTimeStart, + end: testDateTimeEnd, + id: 1, + availabilities: [], +} as TimeSlot & { availabilities: any[] }; + export const mockCreateRecruitmentSessionDto = { slotDuration: 50, interviewStart: testInterviewStart, diff --git a/api/src/timeslots/timeslot.entity.ts b/api/src/timeslots/timeslot.entity.ts index de1e72d..4d8f8db 100644 --- a/api/src/timeslots/timeslot.entity.ts +++ b/api/src/timeslots/timeslot.entity.ts @@ -1,7 +1,6 @@ import { Column, Entity, - JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn, diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 41cedb9..a47ed8e 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -14,7 +14,6 @@ import { import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; import { Availability } from 'src/availability/availability.entity'; import { createApp, getAccessToken } from 'test/app.e2e-spec'; -import { CreateTimeSlotDto } from './create-timeslot.dto'; describe('TimeslotsController', () => { let app: INestApplication; @@ -24,414 +23,447 @@ describe('TimeslotsController', () => { let availabilityService: AvailabilityService; let recruitmentSessionService: RecruitmentSessionService; let mockUsers: Person[]; - let mockTimeSlots: CreateTimeSlotDto[]; + let mockTimeSlots: TimeSlot[]; let mockRecruitmentSessions: RecruitmentSession[]; let mockAvailability: Availability[]; beforeAll(async () => { newMemberToken = await getAccessToken('newMember'); - mockTimeSlots = [ + mockRecruitmentSessions = [ { - start: new Date('2024-04-05 10:00:00'), - end: new Date('2024-05-21 11:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-05 11:00:00'), - end: new Date('2024-05-21 12:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-05 15:00:00'), - end: new Date('2024-05-21 16:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-05 16:00:00'), - end: new Date('2024-05-21 17:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-05 17:00:00'), - end: new Date('2024-05-21 18:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-05 18:00:00'), - end: new Date('2024-05-21 19:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-06 10:00:00'), - end: new Date('2024-06-21 11:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-06 11:00:00'), - end: new Date('2024-06-21 12:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-06 15:00:00'), - end: new Date('2024-06-21 16:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-06 16:00:00'), - end: new Date('2024-06-21 17:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-06 17:00:00'), - end: new Date('2024-06-21 18:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-06 18:00:00'), - end: new Date('2024-06-21 19:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-07 10:00:00'), - end: new Date('2024-07-21 11:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-07 11:00:00'), - end: new Date('2024-07-21 12:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-07 15:00:00'), - end: new Date('2024-07-21 16:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-07 16:00:00'), - end: new Date('2024-07-21 17:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-07 17:00:00'), - end: new Date('2024-07-21 18:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-07 18:00:00'), - end: new Date('2024-07-21 19:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-08 10:00:00'), - end: new Date('2024-08-21 11:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-08 11:00:00'), - end: new Date('2024-08-21 12:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-08 15:00:00'), - end: new Date('2024-08-21 16:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-08 16:00:00'), - end: new Date('2024-08-21 17:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-08 17:00:00'), - end: new Date('2024-08-21 18:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-08 18:00:00'), - end: new Date('2024-08-21 19:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-09 10:00:00'), - end: new Date('2024-09-21 11:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-09 11:00:00'), - end: new Date('2024-09-21 12:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-09 15:00:00'), - end: new Date('2024-09-21 16:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-09 16:00:00'), - end: new Date('2024-09-21 17:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-09 17:00:00'), - end: new Date('2024-09-21 18:00:00'), - recruitmentSession: 1, - } as TimeSlot, - { - start: new Date('2024-04-09 18:00:00'), - end: new Date('2024-09-21 19:00:00'), - recruitmentSession: 1, - } as TimeSlot, + id: 1, + state: RecruitmentSessionState.Concluded, + slotDuration: 1, + lastModified: new Date('2024-04-10'), + createdAt: new Date('2024-04-04'), + days: [ + new Date('2024-04-05'), + new Date('2024-04-06'), + new Date('2024-04-07'), + new Date('2024-04-08'), + new Date('2024-04-09'), + ], + interviewStart: new Date('2024-04-05'), + interviewEnd: new Date('2024-04-10'), + }, { - id: 31, - start: new Date('2024-05-05 10:00:00'), - end: new Date('2024-05-05 11:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-05 11:00:00'), - end: new Date('2024-05-05 12:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-05 15:00:00'), - end: new Date('2024-05-05 16:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-05 16:00:00'), - end: new Date('2024-05-05 17:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-05 17:00:00'), - end: new Date('2024-05-05 18:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-05 18:00:00'), - end: new Date('2024-05-05 19:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-06 10:00:00'), - end: new Date('2024-05-06 11:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-06 11:00:00'), - end: new Date('2024-05-06 12:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-06 15:00:00'), - end: new Date('2024-05-06 16:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - end: new Date('2024-05-06 17:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-06 17:00:00'), - end: new Date('2024-05-06 18:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-06 18:00:00'), - end: new Date('2024-05-06 19:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-07 10:00:00'), - end: new Date('2024-05-07 11:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-07 11:00:00'), - end: new Date('2024-05-07 12:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-07 15:00:00'), - end: new Date('2024-05-07 16:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-07 16:00:00'), - end: new Date('2024-05-07 17:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-07 17:00:00'), - end: new Date('2024-05-07 18:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-07 18:00:00'), - end: new Date('2024-05-07 19:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-08 10:00:00'), - end: new Date('2024-05-08 11:00:00'), - recruitmentSession: 2, - } as TimeSlot, - { - start: new Date('2024-05-08 11:00:00'), - end: new Date('2024-05-08 12:00:00'), - recruitmentSession: 2, - } as TimeSlot, + id: 2, + state: RecruitmentSessionState.Concluded, + slotDuration: 1, + lastModified: new Date('2024-05-10'), + createdAt: new Date('2024-05-04'), + days: [ + new Date('2024-05-05'), + new Date('2024-05-06'), + new Date('2024-05-07'), + new Date('2024-05-08'), + ], + interviewStart: new Date('2024-05-05'), + interviewEnd: new Date('2024-05-09'), + }, + { + id: 3, + state: RecruitmentSessionState.Active, + slotDuration: 1, + lastModified: new Date('2024-05-14'), + createdAt: new Date('2024-05-14'), + days: [ + new Date('2024-05-16'), + new Date('2024-05-17'), + new Date('2024-05-18'), + new Date('2024-05-19'), + new Date('2024-05-20'), + ], + interviewStart: new Date('2024-05-16'), + interviewEnd: new Date('2024-05-21'), + }, + ]; + + mockUsers = [ + { + oauthId: '1', + firstName: 'Pasquale', + lastName: 'Bianco', + sex: 'M', + email: 'p.bianco@gmail.com', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '2', + firstName: 'John', + lastName: 'Doe', + sex: 'M', + email: 'j.doe@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '3', + firstName: 'Jane', + lastName: 'Smith', + sex: 'F', + email: 'j.smith@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '4', + firstName: 'Michael', + lastName: 'Johnson', + sex: 'M', + email: 'm.johnson@gmail.com', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '5', + firstName: 'Emily', + lastName: 'Brown', + sex: 'F', + email: 'e.brown@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '6', + firstName: 'David', + lastName: 'Wilson', + sex: 'M', + email: 'd.wilson@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '7', + firstName: 'Olivia', + lastName: 'Johnson', + sex: 'F', + email: 'o.johnson@gmail.com', + role: Role.Member, + is_board: true, + is_expert: false, + }, + { + oauthId: '8', + firstName: 'James', + lastName: 'Smith', + sex: 'M', + email: 'j.smith@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '9', + firstName: 'Sophia', + lastName: 'Miller', + sex: 'F', + email: 's.miller@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '10', + firstName: 'Benjamin', + lastName: 'Davis', + sex: 'M', + email: 'b.davis@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '11', + firstName: 'Ava', + lastName: 'Wilson', + sex: 'female', + email: 'a.wilson@gmail.com', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '12', + firstName: 'William', + lastName: 'Anderson', + sex: 'M', + email: 'w.anderson@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '13', + firstName: 'Mia', + lastName: 'Thomas', + sex: 'F', + email: 'm.thomas@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '14', + firstName: 'Alexander', + lastName: 'Taylor', + sex: 'M', + email: 'a.taylor@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '15', + firstName: 'Charlotte', + lastName: 'Clark', + sex: 'F', + email: 'c.clark@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '16', + firstName: 'Daniel', + lastName: 'Moore', + sex: 'M', + email: 'd.moore@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '17', + firstName: 'Amelia', + lastName: 'Walker', + sex: 'F', + email: 'a.walker@gmail.com', + role: Role.Clerk, + is_board: false, + is_expert: false, + }, { + oauthId: '18', + firstName: 'Matthew', + lastName: 'Lewis', + sex: 'M', + email: 'm.lewis@gmail.com', + role: Role.Admin, + is_board: false, + is_expert: true, + }, + { + oauthId: '19', + firstName: 'Ella', + lastName: 'Harris', + sex: 'F', + email: 'e.harris@gmail.com', + role: Role.Supervisor, + is_board: false, + is_expert: true, + }, + { + oauthId: '20', + firstName: 'Joseph', + lastName: 'King', + sex: 'M', + email: 'j.king@gmail.com', + role: Role.Supervisor, + is_board: true, + is_expert: false, + }, + ]; + + mockTimeSlots = [ + { + id: 51, start: new Date('2024-05-16 10:00:00'), end: new Date('2024-05-16 11:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 52, start: new Date('2024-05-16 11:00:00'), end: new Date('2024-05-16 12:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 53, start: new Date('2024-05-16 15:00:00'), end: new Date('2024-05-16 16:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 54, start: new Date('2024-05-16 16:00:00'), end: new Date('2024-05-16 17:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 55, start: new Date('2024-05-16 17:00:00'), end: new Date('2024-05-16 18:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 56, start: new Date('2024-05-16 18:00:00'), end: new Date('2024-05-16 19:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 57, start: new Date('2024-05-17 10:00:00'), end: new Date('2024-05-17 11:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 58, start: new Date('2024-05-17 11:00:00'), end: new Date('2024-05-17 12:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 59, start: new Date('2024-05-17 15:00:00'), end: new Date('2024-05-17 16:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 60, start: new Date('2024-05-17 16:00:00'), end: new Date('2024-05-17 17:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 61, start: new Date('2024-05-17 17:00:00'), end: new Date('2024-05-17 18:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 62, start: new Date('2024-05-17 18:00:00'), end: new Date('2024-05-17 19:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 63, start: new Date('2024-05-18 10:00:00'), end: new Date('2024-05-18 11:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 64, start: new Date('2024-05-18 11:00:00'), end: new Date('2024-05-18 12:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 65, start: new Date('2024-05-18 15:00:00'), end: new Date('2024-05-18 16:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 66, start: new Date('2024-05-18 16:00:00'), end: new Date('2024-05-18 17:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 67, start: new Date('2024-05-18 17:00:00'), end: new Date('2024-05-18 18:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 68, start: new Date('2024-05-18 18:00:00'), end: new Date('2024-05-18 19:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 69, start: new Date('2024-05-19 10:00:00'), end: new Date('2024-05-19 11:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 70, start: new Date('2024-05-19 11:00:00'), end: new Date('2024-05-19 12:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 71, start: new Date('2024-05-19 15:00:00'), end: new Date('2024-05-19 16:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 72, start: new Date('2024-05-19 16:00:00'), end: new Date('2024-05-19 17:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 73, start: new Date('2024-05-19 17:00:00'), end: new Date('2024-05-19 18:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 74, start: new Date('2024-05-19 18:00:00'), end: new Date('2024-05-19 19:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 75, start: new Date('2024-05-20 10:00:00'), end: new Date('2024-05-20 11:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 76, start: new Date('2024-05-20 11:00:00'), end: new Date('2024-05-20 12:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 77, start: new Date('2024-05-20 15:00:00'), end: new Date('2024-05-20 16:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 78, start: new Date('2024-05-20 16:00:00'), end: new Date('2024-05-20 17:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 79, start: new Date('2024-05-20 17:00:00'), end: new Date('2024-05-20 18:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, { + id: 80, start: new Date('2024-05-20 18:00:00'), end: new Date('2024-05-20 19:00:00'), - recruitmentSession: 3, - } as TimeSlot, + recruitmentSession: mockRecruitmentSessions[2], + }, ]; mockAvailability = [ @@ -675,383 +707,130 @@ describe('TimeslotsController', () => { } as unknown as Availability, { id: 35, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 77, - userOauthId: '17', - } as unknown as Availability, - { - id: 36, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 76, - userOauthId: '19', - } as unknown as Availability, - { - id: 37, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 75, - userOauthId: '16', - } as unknown as Availability, - { - id: 38, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 74, - userOauthId: '15', - } as unknown as Availability, - { - id: 39, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 73, - userOauthId: '14', - } as unknown as Availability, - { - id: 40, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 72, - userOauthId: '13', - } as unknown as Availability, - { - id: 41, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 71, - userOauthId: '12', - } as unknown as Availability, - { - id: 42, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 70, - userOauthId: '11', - } as unknown as Availability, - { - id: 43, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 69, - userOauthId: '10', - } as unknown as Availability, - { - id: 44, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 68, - userOauthId: '9', - } as unknown as Availability, - { - id: 45, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 67, - userOauthId: '8', - } as unknown as Availability, - { - id: 46, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 66, - userOauthId: '7', - } as unknown as Availability, - { - id: 47, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 65, - userOauthId: '6', - } as unknown as Availability, - { - id: 48, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 64, - userOauthId: '5', - } as unknown as Availability, - { - id: 49, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 63, - userOauthId: '8', - } as unknown as Availability, - { - id: 50, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 62, - userOauthId: '14', - } as unknown as Availability, - { - id: 51, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 61, - userOauthId: '4', - } as unknown as Availability, - { - id: 52, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 60, - userOauthId: '13', - } as unknown as Availability, - ]; - - mockRecruitmentSessions = [ - { - id: 1, - state: RecruitmentSessionState.Concluded, - slotDuration: 1, - lastModified: new Date('2024-04-10'), - createdAt: new Date('2024-04-04'), - days: [ - new Date('2024-04-05'), - new Date('2024-04-06'), - new Date('2024-04-07'), - new Date('2024-04-08'), - new Date('2024-04-09'), - ], - interviewStart: new Date('2024-04-05'), - interviewEnd: new Date('2024-04-10'), - }, - { - id: 2, - state: RecruitmentSessionState.Concluded, - slotDuration: 1, - lastModified: new Date('2024-05-10'), - createdAt: new Date('2024-05-04'), - days: [ - new Date('2024-05-05'), - new Date('2024-05-06'), - new Date('2024-05-07'), - new Date('2024-05-08'), - ], - interviewStart: new Date('2024-05-05'), - interviewEnd: new Date('2024-05-09'), - }, - { - id: 3, - state: RecruitmentSessionState.Active, - slotDuration: 1, - lastModified: new Date('2024-05-14'), - createdAt: new Date('2024-05-14'), - days: [ - new Date('2024-05-16'), - new Date('2024-05-17'), - new Date('2024-05-18'), - new Date('2024-05-19'), - new Date('2024-05-20'), - ], - interviewStart: new Date('2024-05-16'), - interviewEnd: new Date('2024-05-21'), - }, - ]; - - mockUsers = [ - { - oauthId: '1', - firstName: 'Pasquale', - lastName: 'Bianco', - sex: 'M', - email: 'p.bianco@gmail.com', - role: Role.Member, - is_board: true, - is_expert: true, - }, - { - oauthId: '2', - firstName: 'John', - lastName: 'Doe', - sex: 'M', - email: 'j.doe@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, - { - oauthId: '3', - firstName: 'Jane', - lastName: 'Smith', - sex: 'F', - email: 'j.smith@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '4', - firstName: 'Michael', - lastName: 'Johnson', - sex: 'M', - email: 'm.johnson@gmail.com', - role: Role.Member, - is_board: true, - is_expert: true, - }, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 77, + userOauthId: '17', + } as unknown as Availability, { - oauthId: '5', - firstName: 'Emily', - lastName: 'Brown', - sex: 'F', - email: 'e.brown@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, + id: 36, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 76, + userOauthId: '19', + } as unknown as Availability, { - oauthId: '6', - firstName: 'David', - lastName: 'Wilson', - sex: 'M', - email: 'd.wilson@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 37, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '16', + } as unknown as Availability, { - oauthId: '7', - firstName: 'Olivia', - lastName: 'Johnson', - sex: 'F', - email: 'o.johnson@gmail.com', - role: Role.Member, - is_board: true, - is_expert: false, - }, + id: 38, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '15', + } as unknown as Availability, { - oauthId: '8', - firstName: 'James', - lastName: 'Smith', - sex: 'M', - email: 'j.smith@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 39, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 73, + userOauthId: '14', + } as unknown as Availability, { - oauthId: '9', - firstName: 'Sophia', - lastName: 'Miller', - sex: 'F', - email: 's.miller@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, + id: 40, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '13', + } as unknown as Availability, { - oauthId: '10', - firstName: 'Benjamin', - lastName: 'Davis', - sex: 'M', - email: 'b.davis@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 41, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '12', + } as unknown as Availability, { - oauthId: '11', - firstName: 'Ava', - lastName: 'Wilson', - sex: 'female', - email: 'a.wilson@gmail.com', - role: Role.Member, - is_board: true, - is_expert: true, - }, + id: 42, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 70, + userOauthId: '11', + } as unknown as Availability, { - oauthId: '12', - firstName: 'William', - lastName: 'Anderson', - sex: 'M', - email: 'w.anderson@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 43, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 69, + userOauthId: '10', + } as unknown as Availability, { - oauthId: '13', - firstName: 'Mia', - lastName: 'Thomas', - sex: 'F', - email: 'm.thomas@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 44, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 68, + userOauthId: '9', + } as unknown as Availability, { - oauthId: '14', - firstName: 'Alexander', - lastName: 'Taylor', - sex: 'M', - email: 'a.taylor@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, + id: 45, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 67, + userOauthId: '8', + } as unknown as Availability, { - oauthId: '15', - firstName: 'Charlotte', - lastName: 'Clark', - sex: 'F', - email: 'c.clark@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 46, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 66, + userOauthId: '7', + } as unknown as Availability, { - oauthId: '16', - firstName: 'Daniel', - lastName: 'Moore', - sex: 'M', - email: 'd.moore@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, + id: 47, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 65, + userOauthId: '6', + } as unknown as Availability, { - oauthId: '17', - firstName: 'Amelia', - lastName: 'Walker', - sex: 'F', - email: 'a.walker@gmail.com', - role: Role.Clerk, - is_board: false, - is_expert: false, - }, + id: 48, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '5', + } as unknown as Availability, { - oauthId: '18', - firstName: 'Matthew', - lastName: 'Lewis', - sex: 'M', - email: 'm.lewis@gmail.com', - role: Role.Admin, - is_board: false, - is_expert: true, - }, + id: 49, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 63, + userOauthId: '8', + } as unknown as Availability, { - oauthId: '19', - firstName: 'Ella', - lastName: 'Harris', - sex: 'F', - email: 'e.harris@gmail.com', - role: Role.Supervisor, - is_board: false, - is_expert: true, - }, + id: 50, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 62, + userOauthId: '14', + } as unknown as Availability, { - oauthId: '20', - firstName: 'Joseph', - lastName: 'King', - sex: 'M', - email: 'j.king@gmail.com', - role: Role.Supervisor, - is_board: true, - is_expert: false, - }, + id: 51, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 61, + userOauthId: '4', + } as unknown as Availability, + { + id: 52, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 60, + userOauthId: '13', + } as unknown as Availability, ]; }); @@ -1071,16 +850,13 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { - for (const rs of mockRecruitmentSessions) { - await recruitmentSessionService.createRecruitmentSession(rs); - } - - for (const u of mockUsers) { - await usersService.create(u); - } - for (const ts of mockTimeSlots) { - await timeSlotsService.createTimeSlot(ts); + const timeSlot = { + ...ts, + recruitmentSession: mockRecruitmentSessions[2], + availabilities: [], + }; + await timeSlotsService.createTimeSlot(timeSlot); } for (const a of mockAvailability) { diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index cb95d62..e47d835 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -200,7 +200,7 @@ describe('TimeSlotsService', () => { it('should correctly call all functions provided for database query', async () => { // Mock the query builder and its methods const mockQueryBuilder = { - leftJoinAndSelect: jest.fn().mockReturnThis(), + innerJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue([]), @@ -217,15 +217,15 @@ describe('TimeSlotsService', () => { const result = await timeSlotService.findAvailableTimeSlots(); // Assert that the query builder methods were called correctly - expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledWith( + expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( 'TimeSlot.availabilities', 'availability', ); - expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledWith( + expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( 'TimeSlot.recruitmentSession', 'recruitmentSession', ); - expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledWith( + expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( 'availability.user', 'user', ); diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 0e34c88..3c2562e 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -79,7 +79,7 @@ export class TimeSlotsService { * @param timeSlot - Time slot to create * @returns {Promise} - Created time slot */ - async createTimeSlot(timeSlot: CreateTimeSlotDto): Promise { + async createTimeSlot(timeSlot: TimeSlot): Promise { return await this.timeSlotRepository.save(timeSlot); } @@ -157,9 +157,9 @@ export class TimeSlotsService { async findAvailableTimeSlots(): Promise { const queryBuilder = this.timeSlotRepository.createQueryBuilder('TimeSlot'); queryBuilder - .leftJoinAndSelect('TimeSlot.availabilities', 'availability') - .leftJoinAndSelect('TimeSlot.recruitmentSession', 'recruitmentSession') - .leftJoinAndSelect('availability.user', 'user') + .innerJoinAndSelect('TimeSlot.availabilities', 'availability') + .innerJoinAndSelect('TimeSlot.recruitmentSession', 'recruitmentSession') + .innerJoinAndSelect('availability.user', 'user') // only active recruitment sessions (the current one) .where('recruitmentSession.state = :recruitmentSessionState', { diff --git a/shared/src/timeslot.ts b/shared/src/timeslot.ts index 3665746..aff04cc 100644 --- a/shared/src/timeslot.ts +++ b/shared/src/timeslot.ts @@ -1,14 +1,12 @@ -import { Availability } from "availability"; import { Action, ApplyAbilities } from "./abilities"; import { Role } from "./person"; -import { RecruitmentSession } from "recruitment-session"; +import { RecruitmentSession } from "./recruitment-session"; export interface TimeSlot { id: number; start: Date; end: Date; recruitmentSession: RecruitmentSession; - availabilities: Availability[]; } /* Abilities */ From 23ffd9efee82f71b50f651f320473c4b542dd151 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:00:14 +0200 Subject: [PATCH 071/104] fix: 8 --- api/src/timeslots/timeslots.e2e-spec.ts | 125 +++++++++++++----------- 1 file changed, 70 insertions(+), 55 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index a47ed8e..843a4e5 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -25,7 +25,15 @@ describe('TimeslotsController', () => { let mockUsers: Person[]; let mockTimeSlots: TimeSlot[]; let mockRecruitmentSessions: RecruitmentSession[]; - let mockAvailability: Availability[]; + + class mockAv { + id: number; + state: AvailabilityState; + lastModified: Date; + timeSlot: number; + userOauthId: string; + } + let mockAvailability: mockAv[]; beforeAll(async () => { newMemberToken = await getAccessToken('newMember'); @@ -473,364 +481,364 @@ describe('TimeslotsController', () => { lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 51, userOauthId: '5', - } as unknown as Availability, + } , { id: 2, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '7', - } as unknown as Availability, + } , { id: 3, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 55, userOauthId: '12', - } as unknown as Availability, + } , { id: 4, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '19', - } as unknown as Availability, + } , { id: 5, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '3', - } as unknown as Availability, + } , { id: 6, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '15', - } as unknown as Availability, + } , { id: 7, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 58, userOauthId: '11', - } as unknown as Availability, + } , { id: 8, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '8', - } as unknown as Availability, + } , { id: 9, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5', - } as unknown as Availability, + } , { id: 10, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '17', - } as unknown as Availability, + } , { id: 11, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '2', - } as unknown as Availability, + } , { id: 12, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '9', - } as unknown as Availability, + } , { id: 13, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '14', - } as unknown as Availability, + } , { id: 14, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '6', - } as unknown as Availability, + } , { id: 15, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '13', - } as unknown as Availability, + } , { id: 16, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '10', - } as unknown as Availability, + } , { id: 17, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '4', - } as unknown as Availability, + } , { id: 18, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '16', - } as unknown as Availability, + } , { id: 19, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 59, userOauthId: '18', - } as unknown as Availability, + } { id: 20, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '1', - } as unknown as Availability, + } , { id: 21, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '20', - } as unknown as Availability, + } , { id: 22, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '19', - } as unknown as Availability, + } , { id: 23, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '7', - } as unknown as Availability, + } , { id: 24, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '16', - } as unknown as Availability, + } , { id: 25, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '15', - } as unknown as Availability, + } , { id: 26, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '11', - } as unknown as Availability, + } , { id: 27, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '14', - } as unknown as Availability, + } , { id: 28, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '9', - } as unknown as Availability, + } , { id: 29, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '8', - } as unknown as Availability, + } , { id: 30, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '10', - } as unknown as Availability, + } , { id: 31, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '13', - } as unknown as Availability, + } , { id: 32, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 80, userOauthId: '12', - } as unknown as Availability, + } , { id: 33, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '20', - } as unknown as Availability, + } , { id: 34, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '18', - } as unknown as Availability, + } , { id: 35, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '17', - } as unknown as Availability, + } , { id: 36, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '19', - } as unknown as Availability, + } , { id: 37, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '16', - } as unknown as Availability, + } , { id: 38, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '15', - } as unknown as Availability, + } , { id: 39, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '14', - } as unknown as Availability, + } , { id: 40, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '13', - } as unknown as Availability, + } , { id: 41, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '12', - } as unknown as Availability, + } , { id: 42, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '11', - } as unknown as Availability, + } , { id: 43, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '10', - } as unknown as Availability, + } , { id: 44, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '9', - } as unknown as Availability, + } , { id: 45, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 67, userOauthId: '8', - } as unknown as Availability, + } , { id: 46, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '7', - } as unknown as Availability, + } , { id: 47, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 65, userOauthId: '6', - } as unknown as Availability, + } , { id: 48, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5', - } as unknown as Availability, + } , { id: 49, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 63, userOauthId: '8', - } as unknown as Availability, + } , { id: 50, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '14', - } as unknown as Availability, + } , { id: 51, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '4', - } as unknown as Availability, + } , { id: 52, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 60, userOauthId: '13', - } as unknown as Availability, + } , ]; }); @@ -854,13 +862,20 @@ describe('TimeslotsController', () => { const timeSlot = { ...ts, recruitmentSession: mockRecruitmentSessions[2], - availabilities: [], + availabilities: [] as Availability[], }; await timeSlotsService.createTimeSlot(timeSlot); } for (const a of mockAvailability) { - await availabilityService.createAvailability(a); + const availability = { + id: a.id, + state: a.state, + lastModified: a.lastModified, + timeSlot: timeSlotsService.findById(a.timeSlot), + user: mockUsers.find((u) => u.oauthId === a.userOauthId), + } as unknown as Availability; + await availabilityService.createAvailability(availability); } }); From 395a3211a6da2688fd8883e53e4a154b518ec199 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:00:46 +0200 Subject: [PATCH 072/104] fix: 9 --- api/src/timeslots/timeslots.e2e-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 843a4e5..f431eda 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -607,7 +607,7 @@ describe('TimeslotsController', () => { lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 59, userOauthId: '18', - } + } , { id: 20, state: AvailabilityState.Free, From dd3f63ab83262be9931424559d6b211e4a39a6e6 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:02:00 +0200 Subject: [PATCH 073/104] format --- api/src/timeslots/timeslots.e2e-spec.ts | 104 ++++++++++++------------ 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index f431eda..409ee93 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -481,364 +481,364 @@ describe('TimeslotsController', () => { lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 51, userOauthId: '5', - } , + }, { id: 2, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '7', - } , + }, { id: 3, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 55, userOauthId: '12', - } , + }, { id: 4, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '19', - } , + }, { id: 5, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '3', - } , + }, { id: 6, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '15', - } , + }, { id: 7, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 58, userOauthId: '11', - } , + }, { id: 8, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '8', - } , + }, { id: 9, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5', - } , + }, { id: 10, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 53, userOauthId: '17', - } , + }, { id: 11, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '2', - } , + }, { id: 12, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '9', - } , + }, { id: 13, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '14', - } , + }, { id: 14, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '6', - } , + }, { id: 15, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '13', - } , + }, { id: 16, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '10', - } , + }, { id: 17, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '4', - } , + }, { id: 18, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '16', - } , + }, { id: 19, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 59, userOauthId: '18', - } , + }, { id: 20, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 57, userOauthId: '1', - } , + }, { id: 21, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '20', - } , + }, { id: 22, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '19', - } , + }, { id: 23, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '7', - } , + }, { id: 24, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '16', - } , + }, { id: 25, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '15', - } , + }, { id: 26, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '11', - } , + }, { id: 27, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '14', - } , + }, { id: 28, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '9', - } , + }, { id: 29, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '8', - } , + }, { id: 30, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '10', - } , + }, { id: 31, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '13', - } , + }, { id: 32, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 80, userOauthId: '12', - } , + }, { id: 33, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 79, userOauthId: '20', - } , + }, { id: 34, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 78, userOauthId: '18', - } , + }, { id: 35, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 77, userOauthId: '17', - } , + }, { id: 36, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 76, userOauthId: '19', - } , + }, { id: 37, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 75, userOauthId: '16', - } , + }, { id: 38, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 74, userOauthId: '15', - } , + }, { id: 39, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 73, userOauthId: '14', - } , + }, { id: 40, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 72, userOauthId: '13', - } , + }, { id: 41, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 71, userOauthId: '12', - } , + }, { id: 42, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 70, userOauthId: '11', - } , + }, { id: 43, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 69, userOauthId: '10', - } , + }, { id: 44, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 68, userOauthId: '9', - } , + }, { id: 45, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 67, userOauthId: '8', - } , + }, { id: 46, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 66, userOauthId: '7', - } , + }, { id: 47, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 65, userOauthId: '6', - } , + }, { id: 48, state: AvailabilityState.Interviewing, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 64, userOauthId: '5', - } , + }, { id: 49, state: AvailabilityState.Recovering, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 63, userOauthId: '8', - } , + }, { id: 50, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 62, userOauthId: '14', - } , + }, { id: 51, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 61, userOauthId: '4', - } , + }, { id: 52, state: AvailabilityState.Free, lastModified: new Date('2024-05-14 00:00:00'), timeSlot: 60, userOauthId: '13', - } , + }, ]; }); From 0c92b3d5f9e44a02c25f7e8d523b9865c2d4a6fe Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:33:07 +0200 Subject: [PATCH 074/104] fix: 10 --- api/src/timeslots/timeslots.e2e-spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 409ee93..90035f7 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -858,6 +858,14 @@ describe('TimeslotsController', () => { describe(' GET /timeslots', () => { beforeEach(async () => { + for (const user of mockUsers) { + await usersService.create(user); + } + + for (const rs of mockRecruitmentSessions) { + await recruitmentSessionService.createRecruitmentSession(rs); + } + for (const ts of mockTimeSlots) { const timeSlot = { ...ts, From aab3bc97dbfe4f438d0a1c7603e4a3c8730f9d66 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:37:08 +0200 Subject: [PATCH 075/104] fix: 11 --- api/src/timeslots/timeslots.e2e-spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 90035f7..6d2fb9e 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -887,6 +887,11 @@ describe('TimeslotsController', () => { } }); + it('there should be users in the db', async () => { + const allUsers = await usersService.findAll(); + expect(allUsers).toHaveLength(mockUsers.length); + }); + it('should return all available timeslots', async () => { const expected = [ { From b82b00d1f731e4f3f0e4bf1ca16ab48c847da432 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:45:05 +0200 Subject: [PATCH 076/104] debubg: tests --- api/src/timeslots/timeslots.e2e-spec.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 6d2fb9e..519df36 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -892,6 +892,30 @@ describe('TimeslotsController', () => { expect(allUsers).toHaveLength(mockUsers.length); }); + it('there should be recruitment sessions in the db', async () => { + const allRecruitmentSessions = + await recruitmentSessionService.findAllRecruitmentSessions(); + expect(allRecruitmentSessions).toHaveLength(mockRecruitmentSessions.length); + }); + + it('there should be timeslots in the db', async () => { + const allTimeSlots = await timeSlotsService.listTimeSlots(); + expect(allTimeSlots).toHaveLength(mockTimeSlots.length); + }); + + it('there should be availabilities in the db', async () => { + const allAvailabilities = await availabilityService.listAvailabilities(); + expect(allAvailabilities).toHaveLength(mockAvailability.length); + }); + + it("DEBUG: check availabilities for timeslot having id 71", async () => { + const allAvailabilities = await availabilityService.listAvailabilities() + const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71) + const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73) + expect(av71).toHaveLength(4); + expect(av73).toHaveLength(2); + }); + it('should return all available timeslots', async () => { const expected = [ { From dff77a4d9c701f8ad32138024b8ef4e08d7a1f02 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:45:21 +0200 Subject: [PATCH 077/104] format --- api/src/timeslots/timeslots.e2e-spec.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 519df36..1ede84b 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -895,7 +895,9 @@ describe('TimeslotsController', () => { it('there should be recruitment sessions in the db', async () => { const allRecruitmentSessions = await recruitmentSessionService.findAllRecruitmentSessions(); - expect(allRecruitmentSessions).toHaveLength(mockRecruitmentSessions.length); + expect(allRecruitmentSessions).toHaveLength( + mockRecruitmentSessions.length, + ); }); it('there should be timeslots in the db', async () => { @@ -908,10 +910,10 @@ describe('TimeslotsController', () => { expect(allAvailabilities).toHaveLength(mockAvailability.length); }); - it("DEBUG: check availabilities for timeslot having id 71", async () => { - const allAvailabilities = await availabilityService.listAvailabilities() - const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71) - const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73) + it('DEBUG: check availabilities for timeslot having id 71', async () => { + const allAvailabilities = await availabilityService.listAvailabilities(); + const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); + const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73); expect(av71).toHaveLength(4); expect(av73).toHaveLength(2); }); From 542e5dc51ca423206a62581067b879760eb16cf7 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:48:55 +0200 Subject: [PATCH 078/104] debubg: tests 2 --- api/src/timeslots/timeslots.e2e-spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 1ede84b..4d94600 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -910,10 +910,10 @@ describe('TimeslotsController', () => { expect(allAvailabilities).toHaveLength(mockAvailability.length); }); - it('DEBUG: check availabilities for timeslot having id 71', async () => { + it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { const allAvailabilities = await availabilityService.listAvailabilities(); - const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); - const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73); + const av71 = allAvailabilities.filter((a) => a.timeSlotId === 71); + const av73 = allAvailabilities.filter((a) => a.timeSlotId === 73); expect(av71).toHaveLength(4); expect(av73).toHaveLength(2); }); From 527c65c6584b742dba51604293534880995b411d Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:57:30 +0200 Subject: [PATCH 079/104] debug: test 3 --- api/src/mocks/requests.http | 4 ++++ api/src/timeslots/timeslots.e2e-spec.ts | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/api/src/mocks/requests.http b/api/src/mocks/requests.http index 74e9e9e..56bdf35 100644 --- a/api/src/mocks/requests.http +++ b/api/src/mocks/requests.http @@ -1,2 +1,6 @@ GET http://localhost:3000/v1/timeslots/ +Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc4aFdBRTB0U00tX3JJVUR0WElNMyJ9.eyJlbWFpbCI6InBhc3F1YWxlLmJpYW5jb0Boa25wb2xpdG8ub3JnIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDYyMjgyMTU0MzYxNTk5ODcwNTEiLCJhdWQiOlsiaHR0cDovL2hrcmVjcnVpdG1lbnQub3JnIiwiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzE2MTEzNTk3LCJleHAiOjE3MTYxOTk5OTcsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJaekNWd2R2eUJOUWZKc3R1ZUJPcVh3TW1jazZCa0d4NiJ9.PyyW67LJIWc9NbMibzvzfxlY0Pe1YTsZVsV2--ckBQ5hfYsCKqCUi6aIjmiliz_uZO5qdPAsZ7FYNOtOI8fGhPt9QfKSqA166D-CZWnpuwE4cM_EjbpCoHRRQ49r2Ycv_ZnzVXrO6zCyEVZYgx-USGiuBnRcKbIPDJCS4Tc9bcmklcUdq4ujAPtSDdmid9jcVN3DM5eLtXAQuYIUho3G3NRVuKlUnoNIsK16yrKtx5kgOHKuUP6dFYExpp2ViB4oZV-v_linCsXZEE5x-7GBMntJAGtenCs-1uihB-OGLYJY_6fPsRG2rgw5Fl8Ax0Ioc8LeXkwlJ2L-MO1ms-r8uQ + +### +GET http://localhost:3000/v1/availabilities/ Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc4aFdBRTB0U00tX3JJVUR0WElNMyJ9.eyJlbWFpbCI6InBhc3F1YWxlLmJpYW5jb0Boa25wb2xpdG8ub3JnIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDYyMjgyMTU0MzYxNTk5ODcwNTEiLCJhdWQiOlsiaHR0cDovL2hrcmVjcnVpdG1lbnQub3JnIiwiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzE2MTEzNTk3LCJleHAiOjE3MTYxOTk5OTcsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJaekNWd2R2eUJOUWZKc3R1ZUJPcVh3TW1jazZCa0d4NiJ9.PyyW67LJIWc9NbMibzvzfxlY0Pe1YTsZVsV2--ckBQ5hfYsCKqCUi6aIjmiliz_uZO5qdPAsZ7FYNOtOI8fGhPt9QfKSqA166D-CZWnpuwE4cM_EjbpCoHRRQ49r2Ycv_ZnzVXrO6zCyEVZYgx-USGiuBnRcKbIPDJCS4Tc9bcmklcUdq4ujAPtSDdmid9jcVN3DM5eLtXAQuYIUho3G3NRVuKlUnoNIsK16yrKtx5kgOHKuUP6dFYExpp2ViB4oZV-v_linCsXZEE5x-7GBMntJAGtenCs-1uihB-OGLYJY_6fPsRG2rgw5Fl8Ax0Ioc8LeXkwlJ2L-MO1ms-r8uQ \ No newline at end of file diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 4d94600..d223cfc 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -910,10 +910,22 @@ describe('TimeslotsController', () => { expect(allAvailabilities).toHaveLength(mockAvailability.length); }); + it('DEBUG: check format of an abailability', async () => { + const availability = await availabilityService.findById(1); + expect(availability).toBeDefined(); + expect(availability).toHaveProperty('id', 1); + expect(availability).toHaveProperty('state'); + expect(availability).toHaveProperty('lastModified'); + expect(availability).toHaveProperty('timeSlot'); + expect(availability).toHaveProperty('user'); + expect(availability).toEqual(mockAvailability[0]); + + }); + it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { const allAvailabilities = await availabilityService.listAvailabilities(); - const av71 = allAvailabilities.filter((a) => a.timeSlotId === 71); - const av73 = allAvailabilities.filter((a) => a.timeSlotId === 73); + const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); + const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73); expect(av71).toHaveLength(4); expect(av73).toHaveLength(2); }); From da50afecaf15a93d27f82cb4434151e658de2821 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 21:59:03 +0200 Subject: [PATCH 080/104] format --- api/src/timeslots/timeslots.e2e-spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index d223cfc..7b8abfa 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -919,7 +919,6 @@ describe('TimeslotsController', () => { expect(availability).toHaveProperty('timeSlot'); expect(availability).toHaveProperty('user'); expect(availability).toEqual(mockAvailability[0]); - }); it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { From 37729bbe4c38362574ac4591a9ca006a3480fbf9 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 22:08:12 +0200 Subject: [PATCH 081/104] debug: test 4 --- api/src/mocks/db.sql | 1 - api/src/mocks/requests.http | 4 ---- api/src/timeslots/timeslots.e2e-spec.ts | 13 ++++++++++++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/api/src/mocks/db.sql b/api/src/mocks/db.sql index c1cc5f2..cd4bb63 100644 --- a/api/src/mocks/db.sql +++ b/api/src/mocks/db.sql @@ -177,4 +177,3 @@ VALUES (50, 'free' ,'2024-05-14 00:00:00', 62, '14'), (51, 'free' ,'2024-05-14 00:00:00', 61, '4' ), (52, 'free' ,'2024-05-14 00:00:00', 60, '13'); - diff --git a/api/src/mocks/requests.http b/api/src/mocks/requests.http index 56bdf35..c0f0e99 100644 --- a/api/src/mocks/requests.http +++ b/api/src/mocks/requests.http @@ -1,6 +1,2 @@ GET http://localhost:3000/v1/timeslots/ Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc4aFdBRTB0U00tX3JJVUR0WElNMyJ9.eyJlbWFpbCI6InBhc3F1YWxlLmJpYW5jb0Boa25wb2xpdG8ub3JnIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDYyMjgyMTU0MzYxNTk5ODcwNTEiLCJhdWQiOlsiaHR0cDovL2hrcmVjcnVpdG1lbnQub3JnIiwiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzE2MTEzNTk3LCJleHAiOjE3MTYxOTk5OTcsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJaekNWd2R2eUJOUWZKc3R1ZUJPcVh3TW1jazZCa0d4NiJ9.PyyW67LJIWc9NbMibzvzfxlY0Pe1YTsZVsV2--ckBQ5hfYsCKqCUi6aIjmiliz_uZO5qdPAsZ7FYNOtOI8fGhPt9QfKSqA166D-CZWnpuwE4cM_EjbpCoHRRQ49r2Ycv_ZnzVXrO6zCyEVZYgx-USGiuBnRcKbIPDJCS4Tc9bcmklcUdq4ujAPtSDdmid9jcVN3DM5eLtXAQuYIUho3G3NRVuKlUnoNIsK16yrKtx5kgOHKuUP6dFYExpp2ViB4oZV-v_linCsXZEE5x-7GBMntJAGtenCs-1uihB-OGLYJY_6fPsRG2rgw5Fl8Ax0Ioc8LeXkwlJ2L-MO1ms-r8uQ - -### -GET http://localhost:3000/v1/availabilities/ -Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc4aFdBRTB0U00tX3JJVUR0WElNMyJ9.eyJlbWFpbCI6InBhc3F1YWxlLmJpYW5jb0Boa25wb2xpdG8ub3JnIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDYyMjgyMTU0MzYxNTk5ODcwNTEiLCJhdWQiOlsiaHR0cDovL2hrcmVjcnVpdG1lbnQub3JnIiwiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzE2MTEzNTk3LCJleHAiOjE3MTYxOTk5OTcsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJaekNWd2R2eUJOUWZKc3R1ZUJPcVh3TW1jazZCa0d4NiJ9.PyyW67LJIWc9NbMibzvzfxlY0Pe1YTsZVsV2--ckBQ5hfYsCKqCUi6aIjmiliz_uZO5qdPAsZ7FYNOtOI8fGhPt9QfKSqA166D-CZWnpuwE4cM_EjbpCoHRRQ49r2Ycv_ZnzVXrO6zCyEVZYgx-USGiuBnRcKbIPDJCS4Tc9bcmklcUdq4ujAPtSDdmid9jcVN3DM5eLtXAQuYIUho3G3NRVuKlUnoNIsK16yrKtx5kgOHKuUP6dFYExpp2ViB4oZV-v_linCsXZEE5x-7GBMntJAGtenCs-1uihB-OGLYJY_6fPsRG2rgw5Fl8Ax0Ioc8LeXkwlJ2L-MO1ms-r8uQ \ No newline at end of file diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 7b8abfa..fc8f76a 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -910,7 +910,7 @@ describe('TimeslotsController', () => { expect(allAvailabilities).toHaveLength(mockAvailability.length); }); - it('DEBUG: check format of an abailability', async () => { + it('DEBUG: check format of an Availabilities table in db', async () => { const availability = await availabilityService.findById(1); expect(availability).toBeDefined(); expect(availability).toHaveProperty('id', 1); @@ -921,6 +921,17 @@ describe('TimeslotsController', () => { expect(availability).toEqual(mockAvailability[0]); }); + it('DEBUG: check format of a TimeSlots table in db', async () => { + const timeSlot = await timeSlotsService.findById(51); + expect(timeSlot).toBeDefined(); + expect(timeSlot).toHaveProperty('id', 51); + expect(timeSlot).toHaveProperty('start'); + expect(timeSlot).toHaveProperty('end'); + expect(timeSlot).toHaveProperty('recruitmentSession'); + expect(timeSlot).toHaveProperty('availabilities'); + expect(timeSlot).toEqual(mockTimeSlots[0]); + }); + it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { const allAvailabilities = await availabilityService.listAvailabilities(); const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); From 04656b02db4d112e6b9b8f2ec462a464b5a14e3f Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 22:19:56 +0200 Subject: [PATCH 082/104] debug: test 5 --- api/src/timeslots/timeslots.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 3c2562e..3ade8fb 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -185,6 +185,7 @@ export class TimeSlotsService { ); const allMatches = await queryBuilder.getMany(); + return allMatches; let goodTimeSlots: TimeSlot[] = []; allMatches.forEach((timeSlot) => { From 0596a937a813225f24fc64c093de457529149eea Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 22:28:37 +0200 Subject: [PATCH 083/104] debug: test 6 --- api/src/timeslots/timeslots.service.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 3ade8fb..f623b9c 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, LessThan, MoreThan, QueryRunner } from 'typeorm'; +import { Repository, LessThan, MoreThan, QueryRunner, Not, In } from 'typeorm'; import { TimeSlot } from './timeslot.entity'; import { RecruitmentSession, @@ -185,6 +185,21 @@ export class TimeSlotsService { ); const allMatches = await queryBuilder.getMany(); + return this.timeSlotRepository.find({ + relations: [ + 'availabilities', + 'availabilities.user', + 'recruitmentSession', + ], + where: { + availabilities: { + state: AvailabilityState.Free, + user: { + role: Not(In([Role.Applicant, Role.None])), + }, + }, + }, + }); return allMatches; let goodTimeSlots: TimeSlot[] = []; From 7351d023e6863c9c92cd521089b12f31643b504d Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 22:34:05 +0200 Subject: [PATCH 084/104] debug: test 7 --- api/src/timeslots/timeslots.service.spec.ts | 108 ++++++++++---------- api/src/timeslots/timeslots.service.ts | 5 +- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index e47d835..d400597 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -196,64 +196,64 @@ describe('TimeSlotsService', () => { }); }); - describe('findAvailableTimeSlots', () => { - it('should correctly call all functions provided for database query', async () => { - // Mock the query builder and its methods - const mockQueryBuilder = { - innerJoinAndSelect: jest.fn().mockReturnThis(), - where: jest.fn().mockReturnThis(), - andWhere: jest.fn().mockReturnThis(), - getMany: jest.fn().mockResolvedValue([]), - }; + // describe('findAvailableTimeSlots', () => { + // it('should correctly call all functions provided for database query', async () => { + // // Mock the query builder and its methods + // const mockQueryBuilder = { + // innerJoinAndSelect: jest.fn().mockReturnThis(), + // where: jest.fn().mockReturnThis(), + // andWhere: jest.fn().mockReturnThis(), + // getMany: jest.fn().mockResolvedValue([]), + // }; - // Mock the timeSlotRepository and its methods - const mockTimeSlotRepository = { - createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), - }; + // // Mock the timeSlotRepository and its methods + // const mockTimeSlotRepository = { + // createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), + // }; - const timeSlotService = new TimeSlotsService( - mockTimeSlotRepository as any, - ); - const result = await timeSlotService.findAvailableTimeSlots(); + // const timeSlotService = new TimeSlotsService( + // mockTimeSlotRepository as any, + // ); + // const result = await timeSlotService.findAvailableTimeSlots(); - // Assert that the query builder methods were called correctly - expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( - 'TimeSlot.availabilities', - 'availability', - ); - expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( - 'TimeSlot.recruitmentSession', - 'recruitmentSession', - ); - expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( - 'availability.user', - 'user', - ); - expect(mockQueryBuilder.where).toHaveBeenCalledWith( - 'recruitmentSession.state = :recruitmentSessionState', - { - recruitmentSessionState: RecruitmentSessionState.Active, - }, - ); - expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( - 'user.role NOT IN (:...roles)', - { - roles: [Role.None, Role.Applicant], - }, - ); - expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( - 'availability.state = :availabilityState AND (user.is_board = true OR user.is_expert = true)', - { - availabilityState: AvailabilityState.Free, - }, - ); - expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( - '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', - ); + // // Assert that the query builder methods were called correctly + // expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( + // 'TimeSlot.availabilities', + // 'availability', + // ); + // expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( + // 'TimeSlot.recruitmentSession', + // 'recruitmentSession', + // ); + // expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( + // 'availability.user', + // 'user', + // ); + // expect(mockQueryBuilder.where).toHaveBeenCalledWith( + // 'recruitmentSession.state = :recruitmentSessionState', + // { + // recruitmentSessionState: RecruitmentSessionState.Active, + // }, + // ); + // expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + // 'user.role NOT IN (:...roles)', + // { + // roles: [Role.None, Role.Applicant], + // }, + // ); + // expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + // 'availability.state = :availabilityState AND (user.is_board = true OR user.is_expert = true)', + // { + // availabilityState: AvailabilityState.Free, + // }, + // ); + // expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + // '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', + // ); - expect(result).toEqual([]); - }); - }); + // expect(result).toEqual([]); + // }); + // }); }); function testTimeSlotsGeneration( diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index f623b9c..98ad638 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -184,8 +184,8 @@ export class TimeSlotsService { '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', ); - const allMatches = await queryBuilder.getMany(); - return this.timeSlotRepository.find({ + queryBuilder.getMany(); + const allMatches = await this.timeSlotRepository.find({ relations: [ 'availabilities', 'availabilities.user', @@ -200,6 +200,7 @@ export class TimeSlotsService { }, }, }); + return allMatches; let goodTimeSlots: TimeSlot[] = []; From 22522fc384d1be29ba7b2b606d4d674dbfc58726 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 22:43:10 +0200 Subject: [PATCH 085/104] debug: test 8 --- api/src/timeslots/timeslots.e2e-spec.ts | 6 ++---- api/src/timeslots/timeslots.service.ts | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index fc8f76a..b175296 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -959,11 +959,9 @@ describe('TimeslotsController', () => { .expect(200) .expect((res) => { expect(res.body).toBeInstanceOf(Array); + expect(res.body).toHaveLength(2); + expect(res.body).toEqual(expected); }); - - const availableTimeSlots = - await timeSlotsService.findAvailableTimeSlots(); - expect(availableTimeSlots).toEqual(expected); }); }); }); diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 98ad638..852ebd3 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -9,6 +9,7 @@ import { Role, } from '@hkrecruitment/shared'; import { CreateTimeSlotDto } from './create-timeslot.dto'; +import { count } from 'console'; @Injectable() export class TimeSlotsService { @@ -201,8 +202,6 @@ export class TimeSlotsService { }, }); - return allMatches; - let goodTimeSlots: TimeSlot[] = []; allMatches.forEach((timeSlot) => { let boardMembers = 0; From a4a43adccbc7ec8a4f815bd6f6744790819072d9 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 22:49:30 +0200 Subject: [PATCH 086/104] debug: test 9 --- api/src/timeslots/timeslots.e2e-spec.ts | 22 ---------------------- api/src/timeslots/timeslots.service.ts | 3 ++- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index b175296..a618636 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -910,28 +910,6 @@ describe('TimeslotsController', () => { expect(allAvailabilities).toHaveLength(mockAvailability.length); }); - it('DEBUG: check format of an Availabilities table in db', async () => { - const availability = await availabilityService.findById(1); - expect(availability).toBeDefined(); - expect(availability).toHaveProperty('id', 1); - expect(availability).toHaveProperty('state'); - expect(availability).toHaveProperty('lastModified'); - expect(availability).toHaveProperty('timeSlot'); - expect(availability).toHaveProperty('user'); - expect(availability).toEqual(mockAvailability[0]); - }); - - it('DEBUG: check format of a TimeSlots table in db', async () => { - const timeSlot = await timeSlotsService.findById(51); - expect(timeSlot).toBeDefined(); - expect(timeSlot).toHaveProperty('id', 51); - expect(timeSlot).toHaveProperty('start'); - expect(timeSlot).toHaveProperty('end'); - expect(timeSlot).toHaveProperty('recruitmentSession'); - expect(timeSlot).toHaveProperty('availabilities'); - expect(timeSlot).toEqual(mockTimeSlots[0]); - }); - it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { const allAvailabilities = await availabilityService.listAvailabilities(); const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 852ebd3..7272b64 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -186,7 +186,7 @@ export class TimeSlotsService { ); queryBuilder.getMany(); - const allMatches = await this.timeSlotRepository.find({ + return await this.timeSlotRepository.find({ relations: [ 'availabilities', 'availabilities.user', @@ -201,6 +201,7 @@ export class TimeSlotsService { }, }, }); + let allMatches = await queryBuilder.getMany(); let goodTimeSlots: TimeSlot[] = []; allMatches.forEach((timeSlot) => { From bbb997f9e0fbae18494067eb73b70f6bfd29d880 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 23:05:01 +0200 Subject: [PATCH 087/104] not def --- api/src/timeslots/timeslots.e2e-spec.ts | 4 +- api/src/timeslots/timeslots.service.spec.ts | 108 ++++++++++---------- api/src/timeslots/timeslots.service.ts | 35 +++---- 3 files changed, 72 insertions(+), 75 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index a618636..5a02cd6 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -856,7 +856,7 @@ describe('TimeslotsController', () => { await app.close(); }); - describe(' GET /timeslots', () => { + describe('GET /timeslots', () => { beforeEach(async () => { for (const user of mockUsers) { await usersService.create(user); @@ -879,7 +879,7 @@ describe('TimeslotsController', () => { const availability = { id: a.id, state: a.state, - lastModified: a.lastModified, + lastModified: a.lastModified , timeSlot: timeSlotsService.findById(a.timeSlot), user: mockUsers.find((u) => u.oauthId === a.userOauthId), } as unknown as Availability; diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index d400597..e47d835 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -196,64 +196,64 @@ describe('TimeSlotsService', () => { }); }); - // describe('findAvailableTimeSlots', () => { - // it('should correctly call all functions provided for database query', async () => { - // // Mock the query builder and its methods - // const mockQueryBuilder = { - // innerJoinAndSelect: jest.fn().mockReturnThis(), - // where: jest.fn().mockReturnThis(), - // andWhere: jest.fn().mockReturnThis(), - // getMany: jest.fn().mockResolvedValue([]), - // }; + describe('findAvailableTimeSlots', () => { + it('should correctly call all functions provided for database query', async () => { + // Mock the query builder and its methods + const mockQueryBuilder = { + innerJoinAndSelect: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + getMany: jest.fn().mockResolvedValue([]), + }; - // // Mock the timeSlotRepository and its methods - // const mockTimeSlotRepository = { - // createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), - // }; + // Mock the timeSlotRepository and its methods + const mockTimeSlotRepository = { + createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), + }; - // const timeSlotService = new TimeSlotsService( - // mockTimeSlotRepository as any, - // ); - // const result = await timeSlotService.findAvailableTimeSlots(); + const timeSlotService = new TimeSlotsService( + mockTimeSlotRepository as any, + ); + const result = await timeSlotService.findAvailableTimeSlots(); - // // Assert that the query builder methods were called correctly - // expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( - // 'TimeSlot.availabilities', - // 'availability', - // ); - // expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( - // 'TimeSlot.recruitmentSession', - // 'recruitmentSession', - // ); - // expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( - // 'availability.user', - // 'user', - // ); - // expect(mockQueryBuilder.where).toHaveBeenCalledWith( - // 'recruitmentSession.state = :recruitmentSessionState', - // { - // recruitmentSessionState: RecruitmentSessionState.Active, - // }, - // ); - // expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( - // 'user.role NOT IN (:...roles)', - // { - // roles: [Role.None, Role.Applicant], - // }, - // ); - // expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( - // 'availability.state = :availabilityState AND (user.is_board = true OR user.is_expert = true)', - // { - // availabilityState: AvailabilityState.Free, - // }, - // ); - // expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( - // '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', - // ); + // Assert that the query builder methods were called correctly + expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( + 'TimeSlot.availabilities', + 'availability', + ); + expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( + 'TimeSlot.recruitmentSession', + 'recruitmentSession', + ); + expect(mockQueryBuilder.innerJoinAndSelect).toHaveBeenCalledWith( + 'availability.user', + 'user', + ); + expect(mockQueryBuilder.where).toHaveBeenCalledWith( + 'recruitmentSession.state = :recruitmentSessionState', + { + recruitmentSessionState: RecruitmentSessionState.Active, + }, + ); + expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + 'user.role NOT IN (:...roles)', + { + roles: [Role.None, Role.Applicant], + }, + ); + expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + 'availability.state = :availabilityState AND (user.is_board = true OR user.is_expert = true)', + { + availabilityState: AvailabilityState.Free, + }, + ); + expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith( + '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', + ); - // expect(result).toEqual([]); - // }); - // }); + expect(result).toEqual([]); + }); + }); }); function testTimeSlotsGeneration( diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 7272b64..b1885ca 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -8,8 +8,6 @@ import { AvailabilityState, Role, } from '@hkrecruitment/shared'; -import { CreateTimeSlotDto } from './create-timeslot.dto'; -import { count } from 'console'; @Injectable() export class TimeSlotsService { @@ -185,23 +183,22 @@ export class TimeSlotsService { '(SELECT COUNT(availability.id) FROM Availability availability WHERE availability.timeSlotId = TimeSlot.id) > 1', ); - queryBuilder.getMany(); - return await this.timeSlotRepository.find({ - relations: [ - 'availabilities', - 'availabilities.user', - 'recruitmentSession', - ], - where: { - availabilities: { - state: AvailabilityState.Free, - user: { - role: Not(In([Role.Applicant, Role.None])), - }, - }, - }, - }); - let allMatches = await queryBuilder.getMany(); + const allMatches = await queryBuilder.getMany(); + // const allMatches = await this.timeSlotRepository.find({ + // relations: [ + // 'availabilities', + // 'availabilities.user', + // 'recruitmentSession', + // ], + // where: { + // availabilities: { + // state: AvailabilityState.Free, + // user: { + // role: Not(In([Role.Applicant, Role.None])), + // }, + // }, + // }, + // }); let goodTimeSlots: TimeSlot[] = []; allMatches.forEach((timeSlot) => { From 1356a787d02569756b42b3dddc20e2ba8979552d Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 23:05:12 +0200 Subject: [PATCH 088/104] not def --- api/src/timeslots/timeslots.e2e-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 5a02cd6..8b2a3bf 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -879,7 +879,7 @@ describe('TimeslotsController', () => { const availability = { id: a.id, state: a.state, - lastModified: a.lastModified , + lastModified: a.lastModified, timeSlot: timeSlotsService.findById(a.timeSlot), user: mockUsers.find((u) => u.oauthId === a.userOauthId), } as unknown as Availability; From 4ed9b9d7d009928b86dd55ded241af66ff74bdc6 Mon Sep 17 00:00:00 2001 From: white Date: Sun, 19 May 2024 23:07:10 +0200 Subject: [PATCH 089/104] not def! --- api/src/timeslots/timeslots.e2e-spec.ts | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 8b2a3bf..261da44 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -910,36 +910,36 @@ describe('TimeslotsController', () => { expect(allAvailabilities).toHaveLength(mockAvailability.length); }); - it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { - const allAvailabilities = await availabilityService.listAvailabilities(); - const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); - const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73); - expect(av71).toHaveLength(4); - expect(av73).toHaveLength(2); - }); + // it('DEBUG: check availabilities for timeslot having id 71 & 73', async () => { + // const allAvailabilities = await availabilityService.listAvailabilities(); + // const av71 = allAvailabilities.filter((a) => a.timeSlot.id === 71); + // const av73 = allAvailabilities.filter((a) => a.timeSlot.id === 73); + // expect(av71).toHaveLength(4); + // expect(av73).toHaveLength(2); + // }); - it('should return all available timeslots', async () => { - const expected = [ - { - end: '2024-05-19T14:00:00.000Z', - id: 71, - start: '2024-05-19T13:00:00.000Z', - }, - { - end: '2024-05-19T16:00:00.000Z', - id: 73, - start: '2024-05-19T15:00:00.000Z', - }, - ]; - await request(app.getHttpServer()) - .get('/timeslots') - .set('Authorization', `Bearer ${newMemberToken}`) - .expect(200) - .expect((res) => { - expect(res.body).toBeInstanceOf(Array); - expect(res.body).toHaveLength(2); - expect(res.body).toEqual(expected); - }); - }); + // it('should return all available timeslots', async () => { + // const expected = [ + // { + // end: '2024-05-19T14:00:00.000Z', + // id: 71, + // start: '2024-05-19T13:00:00.000Z', + // }, + // { + // end: '2024-05-19T16:00:00.000Z', + // id: 73, + // start: '2024-05-19T15:00:00.000Z', + // }, + // ]; + // await request(app.getHttpServer()) + // .get('/timeslots') + // .set('Authorization', `Bearer ${newMemberToken}`) + // .expect(200) + // .expect((res) => { + // expect(res.body).toBeInstanceOf(Array); + // expect(res.body).toHaveLength(2); + // expect(res.body).toEqual(expected); + // }); + // }); }); }); From 7d6690c1b3dc2a66cc04dd7f2890d0aafd767f2c Mon Sep 17 00:00:00 2001 From: white Date: Wed, 22 May 2024 09:26:04 +0200 Subject: [PATCH 090/104] fix: pull requested changes --- api/src/mocks/db-data.ts | 810 ++++++++++++++++++++++ api/src/mocks/requests.http | 2 +- api/src/timeslots/create-timeslot.dto.ts | 2 +- api/src/timeslots/timeslots.e2e-spec.ts | 829 +---------------------- api/src/timeslots/timeslots.service.ts | 15 - 5 files changed, 818 insertions(+), 840 deletions(-) create mode 100644 api/src/mocks/db-data.ts diff --git a/api/src/mocks/db-data.ts b/api/src/mocks/db-data.ts new file mode 100644 index 0000000..e54310e --- /dev/null +++ b/api/src/mocks/db-data.ts @@ -0,0 +1,810 @@ +import { + Person, + Role, + TimeSlot, + AvailabilityState, + RecruitmentSessionState, +} from '@hkrecruitment/shared'; + +export let mockRecruitmentSessions = [ + { + id: 1, + state: RecruitmentSessionState.Concluded, + slotDuration: 1, + lastModified: new Date('2024-04-10'), + createdAt: new Date('2024-04-04'), + days: [ + new Date('2024-04-05'), + new Date('2024-04-06'), + new Date('2024-04-07'), + new Date('2024-04-08'), + new Date('2024-04-09'), + ], + interviewStart: new Date('2024-04-05'), + interviewEnd: new Date('2024-04-10'), + }, + { + id: 2, + state: RecruitmentSessionState.Concluded, + slotDuration: 1, + lastModified: new Date('2024-05-10'), + createdAt: new Date('2024-05-04'), + days: [ + new Date('2024-05-05'), + new Date('2024-05-06'), + new Date('2024-05-07'), + new Date('2024-05-08'), + ], + interviewStart: new Date('2024-05-05'), + interviewEnd: new Date('2024-05-09'), + }, + { + id: 3, + state: RecruitmentSessionState.Active, + slotDuration: 1, + lastModified: new Date('2024-05-14'), + createdAt: new Date('2024-05-14'), + days: [ + new Date('2024-05-16'), + new Date('2024-05-17'), + new Date('2024-05-18'), + new Date('2024-05-19'), + new Date('2024-05-20'), + ], + interviewStart: new Date('2024-05-16'), + interviewEnd: new Date('2024-05-21'), + }, +]; + +export let mockUsers = [ + { + oauthId: '1', + firstName: 'Pasquale', + lastName: 'Bianco', + sex: 'M', + email: 'p.bianco@gmail.com', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '2', + firstName: 'John', + lastName: 'Doe', + sex: 'M', + email: 'j.doe@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '3', + firstName: 'Jane', + lastName: 'Smith', + sex: 'F', + email: 'j.smith@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '4', + firstName: 'Michael', + lastName: 'Johnson', + sex: 'M', + email: 'm.johnson@gmail.com', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '5', + firstName: 'Emily', + lastName: 'Brown', + sex: 'F', + email: 'e.brown@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '6', + firstName: 'David', + lastName: 'Wilson', + sex: 'M', + email: 'd.wilson@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '7', + firstName: 'Olivia', + lastName: 'Johnson', + sex: 'F', + email: 'o.johnson@gmail.com', + role: Role.Member, + is_board: true, + is_expert: false, + }, + { + oauthId: '8', + firstName: 'James', + lastName: 'Smith', + sex: 'M', + email: 'j.smith@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '9', + firstName: 'Sophia', + lastName: 'Miller', + sex: 'F', + email: 's.miller@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '10', + firstName: 'Benjamin', + lastName: 'Davis', + sex: 'M', + email: 'b.davis@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '11', + firstName: 'Ava', + lastName: 'Wilson', + sex: 'female', + email: 'a.wilson@gmail.com', + role: Role.Member, + is_board: true, + is_expert: true, + }, + { + oauthId: '12', + firstName: 'William', + lastName: 'Anderson', + sex: 'M', + email: 'w.anderson@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '13', + firstName: 'Mia', + lastName: 'Thomas', + sex: 'F', + email: 'm.thomas@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '14', + firstName: 'Alexander', + lastName: 'Taylor', + sex: 'M', + email: 'a.taylor@gmail.com', + role: Role.Member, + is_board: false, + is_expert: true, + }, + { + oauthId: '15', + firstName: 'Charlotte', + lastName: 'Clark', + sex: 'F', + email: 'c.clark@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '16', + firstName: 'Daniel', + lastName: 'Moore', + sex: 'M', + email: 'd.moore@gmail.com', + role: Role.Member, + is_board: false, + is_expert: false, + }, + { + oauthId: '17', + firstName: 'Amelia', + lastName: 'Walker', + sex: 'F', + email: 'a.walker@gmail.com', + role: Role.Clerk, + is_board: false, + is_expert: false, + }, + { + oauthId: '18', + firstName: 'Matthew', + lastName: 'Lewis', + sex: 'M', + email: 'm.lewis@gmail.com', + role: Role.Admin, + is_board: false, + is_expert: true, + }, + { + oauthId: '19', + firstName: 'Ella', + lastName: 'Harris', + sex: 'F', + email: 'e.harris@gmail.com', + role: Role.Supervisor, + is_board: false, + is_expert: true, + }, + { + oauthId: '20', + firstName: 'Joseph', + lastName: 'King', + sex: 'M', + email: 'j.king@gmail.com', + role: Role.Supervisor, + is_board: true, + is_expert: false, + }, +]; + +export let mockTimeSlots = [ + { + id: 51, + start: new Date('2024-05-16 10:00:00'), + end: new Date('2024-05-16 11:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 52, + start: new Date('2024-05-16 11:00:00'), + end: new Date('2024-05-16 12:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 53, + start: new Date('2024-05-16 15:00:00'), + end: new Date('2024-05-16 16:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 54, + start: new Date('2024-05-16 16:00:00'), + end: new Date('2024-05-16 17:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 55, + start: new Date('2024-05-16 17:00:00'), + end: new Date('2024-05-16 18:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 56, + start: new Date('2024-05-16 18:00:00'), + end: new Date('2024-05-16 19:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 57, + start: new Date('2024-05-17 10:00:00'), + end: new Date('2024-05-17 11:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 58, + start: new Date('2024-05-17 11:00:00'), + end: new Date('2024-05-17 12:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 59, + start: new Date('2024-05-17 15:00:00'), + end: new Date('2024-05-17 16:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 60, + start: new Date('2024-05-17 16:00:00'), + end: new Date('2024-05-17 17:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 61, + start: new Date('2024-05-17 17:00:00'), + end: new Date('2024-05-17 18:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 62, + start: new Date('2024-05-17 18:00:00'), + end: new Date('2024-05-17 19:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 63, + start: new Date('2024-05-18 10:00:00'), + end: new Date('2024-05-18 11:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 64, + start: new Date('2024-05-18 11:00:00'), + end: new Date('2024-05-18 12:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 65, + start: new Date('2024-05-18 15:00:00'), + end: new Date('2024-05-18 16:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 66, + start: new Date('2024-05-18 16:00:00'), + end: new Date('2024-05-18 17:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 67, + start: new Date('2024-05-18 17:00:00'), + end: new Date('2024-05-18 18:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 68, + start: new Date('2024-05-18 18:00:00'), + end: new Date('2024-05-18 19:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 69, + start: new Date('2024-05-19 10:00:00'), + end: new Date('2024-05-19 11:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 70, + start: new Date('2024-05-19 11:00:00'), + end: new Date('2024-05-19 12:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 71, + start: new Date('2024-05-19 15:00:00'), + end: new Date('2024-05-19 16:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 72, + start: new Date('2024-05-19 16:00:00'), + end: new Date('2024-05-19 17:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 73, + start: new Date('2024-05-19 17:00:00'), + end: new Date('2024-05-19 18:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 74, + start: new Date('2024-05-19 18:00:00'), + end: new Date('2024-05-19 19:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 75, + start: new Date('2024-05-20 10:00:00'), + end: new Date('2024-05-20 11:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 76, + start: new Date('2024-05-20 11:00:00'), + end: new Date('2024-05-20 12:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 77, + start: new Date('2024-05-20 15:00:00'), + end: new Date('2024-05-20 16:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 78, + start: new Date('2024-05-20 16:00:00'), + end: new Date('2024-05-20 17:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 79, + start: new Date('2024-05-20 17:00:00'), + end: new Date('2024-05-20 18:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, + { + id: 80, + start: new Date('2024-05-20 18:00:00'), + end: new Date('2024-05-20 19:00:00'), + recruitmentSession: mockRecruitmentSessions[2], + }, +]; + +export let mockAvailability = [ + { + id: 1, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 51, + userOauthId: '5', + }, + { + id: 2, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 53, + userOauthId: '7', + }, + { + id: 3, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 55, + userOauthId: '12', + }, + { + id: 4, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 61, + userOauthId: '19', + }, + { + id: 5, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '3', + }, + { + id: 6, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 78, + userOauthId: '15', + }, + { + id: 7, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 58, + userOauthId: '11', + }, + { + id: 8, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 57, + userOauthId: '8', + }, + { + id: 9, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '5', + }, + { + id: 10, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 53, + userOauthId: '17', + }, + { + id: 11, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 62, + userOauthId: '2', + }, + { + id: 12, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '9', + }, + { + id: 13, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '14', + }, + { + id: 14, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '6', + }, + { + id: 15, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '13', + }, + { + id: 16, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '10', + }, + { + id: 17, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '4', + }, + { + id: 18, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '16', + }, + { + id: 19, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 59, + userOauthId: '18', + }, + { + id: 20, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 57, + userOauthId: '1', + }, + { + id: 21, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 66, + userOauthId: '20', + }, + { + id: 22, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 68, + userOauthId: '19', + }, + { + id: 23, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 69, + userOauthId: '7', + }, + { + id: 24, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 70, + userOauthId: '16', + }, + { + id: 25, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '15', + }, + { + id: 26, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 73, + userOauthId: '11', + }, + { + id: 27, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '14', + }, + { + id: 28, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 76, + userOauthId: '9', + }, + { + id: 29, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 77, + userOauthId: '8', + }, + { + id: 30, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 78, + userOauthId: '10', + }, + { + id: 31, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 79, + userOauthId: '13', + }, + { + id: 32, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 80, + userOauthId: '12', + }, + { + id: 33, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 79, + userOauthId: '20', + }, + { + id: 34, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 78, + userOauthId: '18', + }, + { + id: 35, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 77, + userOauthId: '17', + }, + { + id: 36, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 76, + userOauthId: '19', + }, + { + id: 37, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 75, + userOauthId: '16', + }, + { + id: 38, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 74, + userOauthId: '15', + }, + { + id: 39, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 73, + userOauthId: '14', + }, + { + id: 40, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 72, + userOauthId: '13', + }, + { + id: 41, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 71, + userOauthId: '12', + }, + { + id: 42, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 70, + userOauthId: '11', + }, + { + id: 43, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 69, + userOauthId: '10', + }, + { + id: 44, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 68, + userOauthId: '9', + }, + { + id: 45, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 67, + userOauthId: '8', + }, + { + id: 46, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 66, + userOauthId: '7', + }, + { + id: 47, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 65, + userOauthId: '6', + }, + { + id: 48, + state: AvailabilityState.Interviewing, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 64, + userOauthId: '5', + }, + { + id: 49, + state: AvailabilityState.Recovering, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 63, + userOauthId: '8', + }, + { + id: 50, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 62, + userOauthId: '14', + }, + { + id: 51, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 61, + userOauthId: '4', + }, + { + id: 52, + state: AvailabilityState.Free, + lastModified: new Date('2024-05-14 00:00:00'), + timeSlot: 60, + userOauthId: '13', + }, +]; diff --git a/api/src/mocks/requests.http b/api/src/mocks/requests.http index c0f0e99..1dcbb14 100644 --- a/api/src/mocks/requests.http +++ b/api/src/mocks/requests.http @@ -1,2 +1,2 @@ GET http://localhost:3000/v1/timeslots/ -Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc4aFdBRTB0U00tX3JJVUR0WElNMyJ9.eyJlbWFpbCI6InBhc3F1YWxlLmJpYW5jb0Boa25wb2xpdG8ub3JnIiwiaXNzIjoiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDYyMjgyMTU0MzYxNTk5ODcwNTEiLCJhdWQiOlsiaHR0cDovL2hrcmVjcnVpdG1lbnQub3JnIiwiaHR0cHM6Ly9kZXYtYzhyb29jZGw3NjNsbDVxZi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNzE2MTEzNTk3LCJleHAiOjE3MTYxOTk5OTcsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJhenAiOiJaekNWd2R2eUJOUWZKc3R1ZUJPcVh3TW1jazZCa0d4NiJ9.PyyW67LJIWc9NbMibzvzfxlY0Pe1YTsZVsV2--ckBQ5hfYsCKqCUi6aIjmiliz_uZO5qdPAsZ7FYNOtOI8fGhPt9QfKSqA166D-CZWnpuwE4cM_EjbpCoHRRQ49r2Ycv_ZnzVXrO6zCyEVZYgx-USGiuBnRcKbIPDJCS4Tc9bcmklcUdq4ujAPtSDdmid9jcVN3DM5eLtXAQuYIUho3G3NRVuKlUnoNIsK16yrKtx5kgOHKuUP6dFYExpp2ViB4oZV-v_linCsXZEE5x-7GBMntJAGtenCs-1uihB-OGLYJY_6fPsRG2rgw5Fl8Ax0Ioc8LeXkwlJ2L-MO1ms-r8uQ +Authorization: Bearer diff --git a/api/src/timeslots/create-timeslot.dto.ts b/api/src/timeslots/create-timeslot.dto.ts index 0b50071..2d40f64 100644 --- a/api/src/timeslots/create-timeslot.dto.ts +++ b/api/src/timeslots/create-timeslot.dto.ts @@ -4,7 +4,7 @@ import { Availability } from 'src/availability/availability.entity'; import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; export class CreateTimeSlotDto implements Partial { - @ApiProperty() // + @ApiProperty() start: Date; @ApiProperty() diff --git a/api/src/timeslots/timeslots.e2e-spec.ts b/api/src/timeslots/timeslots.e2e-spec.ts index 261da44..065e852 100644 --- a/api/src/timeslots/timeslots.e2e-spec.ts +++ b/api/src/timeslots/timeslots.e2e-spec.ts @@ -4,16 +4,14 @@ import { TimeSlotsService } from './timeslots.service'; import { UsersService } from '../users/users.service'; import { AvailabilityService } from '../availability/availability.service'; import { RecruitmentSessionService } from '../recruitment-session/recruitment-session.service'; -import { - Person, - Role, - TimeSlot, - AvailabilityState, - RecruitmentSessionState, -} from '@hkrecruitment/shared'; -import { RecruitmentSession } from 'src/recruitment-session/recruitment-session.entity'; import { Availability } from 'src/availability/availability.entity'; import { createApp, getAccessToken } from 'test/app.e2e-spec'; +import { + mockAvailability, + mockRecruitmentSessions, + mockTimeSlots, + mockUsers, +} from '../mocks/db-data'; describe('TimeslotsController', () => { let app: INestApplication; @@ -22,824 +20,9 @@ describe('TimeslotsController', () => { let usersService: UsersService; let availabilityService: AvailabilityService; let recruitmentSessionService: RecruitmentSessionService; - let mockUsers: Person[]; - let mockTimeSlots: TimeSlot[]; - let mockRecruitmentSessions: RecruitmentSession[]; - - class mockAv { - id: number; - state: AvailabilityState; - lastModified: Date; - timeSlot: number; - userOauthId: string; - } - let mockAvailability: mockAv[]; beforeAll(async () => { newMemberToken = await getAccessToken('newMember'); - - mockRecruitmentSessions = [ - { - id: 1, - state: RecruitmentSessionState.Concluded, - slotDuration: 1, - lastModified: new Date('2024-04-10'), - createdAt: new Date('2024-04-04'), - days: [ - new Date('2024-04-05'), - new Date('2024-04-06'), - new Date('2024-04-07'), - new Date('2024-04-08'), - new Date('2024-04-09'), - ], - interviewStart: new Date('2024-04-05'), - interviewEnd: new Date('2024-04-10'), - }, - { - id: 2, - state: RecruitmentSessionState.Concluded, - slotDuration: 1, - lastModified: new Date('2024-05-10'), - createdAt: new Date('2024-05-04'), - days: [ - new Date('2024-05-05'), - new Date('2024-05-06'), - new Date('2024-05-07'), - new Date('2024-05-08'), - ], - interviewStart: new Date('2024-05-05'), - interviewEnd: new Date('2024-05-09'), - }, - { - id: 3, - state: RecruitmentSessionState.Active, - slotDuration: 1, - lastModified: new Date('2024-05-14'), - createdAt: new Date('2024-05-14'), - days: [ - new Date('2024-05-16'), - new Date('2024-05-17'), - new Date('2024-05-18'), - new Date('2024-05-19'), - new Date('2024-05-20'), - ], - interviewStart: new Date('2024-05-16'), - interviewEnd: new Date('2024-05-21'), - }, - ]; - - mockUsers = [ - { - oauthId: '1', - firstName: 'Pasquale', - lastName: 'Bianco', - sex: 'M', - email: 'p.bianco@gmail.com', - role: Role.Member, - is_board: true, - is_expert: true, - }, - { - oauthId: '2', - firstName: 'John', - lastName: 'Doe', - sex: 'M', - email: 'j.doe@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, - { - oauthId: '3', - firstName: 'Jane', - lastName: 'Smith', - sex: 'F', - email: 'j.smith@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '4', - firstName: 'Michael', - lastName: 'Johnson', - sex: 'M', - email: 'm.johnson@gmail.com', - role: Role.Member, - is_board: true, - is_expert: true, - }, - { - oauthId: '5', - firstName: 'Emily', - lastName: 'Brown', - sex: 'F', - email: 'e.brown@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, - { - oauthId: '6', - firstName: 'David', - lastName: 'Wilson', - sex: 'M', - email: 'd.wilson@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '7', - firstName: 'Olivia', - lastName: 'Johnson', - sex: 'F', - email: 'o.johnson@gmail.com', - role: Role.Member, - is_board: true, - is_expert: false, - }, - { - oauthId: '8', - firstName: 'James', - lastName: 'Smith', - sex: 'M', - email: 'j.smith@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '9', - firstName: 'Sophia', - lastName: 'Miller', - sex: 'F', - email: 's.miller@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, - { - oauthId: '10', - firstName: 'Benjamin', - lastName: 'Davis', - sex: 'M', - email: 'b.davis@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '11', - firstName: 'Ava', - lastName: 'Wilson', - sex: 'female', - email: 'a.wilson@gmail.com', - role: Role.Member, - is_board: true, - is_expert: true, - }, - { - oauthId: '12', - firstName: 'William', - lastName: 'Anderson', - sex: 'M', - email: 'w.anderson@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '13', - firstName: 'Mia', - lastName: 'Thomas', - sex: 'F', - email: 'm.thomas@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '14', - firstName: 'Alexander', - lastName: 'Taylor', - sex: 'M', - email: 'a.taylor@gmail.com', - role: Role.Member, - is_board: false, - is_expert: true, - }, - { - oauthId: '15', - firstName: 'Charlotte', - lastName: 'Clark', - sex: 'F', - email: 'c.clark@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '16', - firstName: 'Daniel', - lastName: 'Moore', - sex: 'M', - email: 'd.moore@gmail.com', - role: Role.Member, - is_board: false, - is_expert: false, - }, - { - oauthId: '17', - firstName: 'Amelia', - lastName: 'Walker', - sex: 'F', - email: 'a.walker@gmail.com', - role: Role.Clerk, - is_board: false, - is_expert: false, - }, - { - oauthId: '18', - firstName: 'Matthew', - lastName: 'Lewis', - sex: 'M', - email: 'm.lewis@gmail.com', - role: Role.Admin, - is_board: false, - is_expert: true, - }, - { - oauthId: '19', - firstName: 'Ella', - lastName: 'Harris', - sex: 'F', - email: 'e.harris@gmail.com', - role: Role.Supervisor, - is_board: false, - is_expert: true, - }, - { - oauthId: '20', - firstName: 'Joseph', - lastName: 'King', - sex: 'M', - email: 'j.king@gmail.com', - role: Role.Supervisor, - is_board: true, - is_expert: false, - }, - ]; - - mockTimeSlots = [ - { - id: 51, - start: new Date('2024-05-16 10:00:00'), - end: new Date('2024-05-16 11:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 52, - start: new Date('2024-05-16 11:00:00'), - end: new Date('2024-05-16 12:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 53, - start: new Date('2024-05-16 15:00:00'), - end: new Date('2024-05-16 16:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 54, - start: new Date('2024-05-16 16:00:00'), - end: new Date('2024-05-16 17:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 55, - start: new Date('2024-05-16 17:00:00'), - end: new Date('2024-05-16 18:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 56, - start: new Date('2024-05-16 18:00:00'), - end: new Date('2024-05-16 19:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 57, - start: new Date('2024-05-17 10:00:00'), - end: new Date('2024-05-17 11:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 58, - start: new Date('2024-05-17 11:00:00'), - end: new Date('2024-05-17 12:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 59, - start: new Date('2024-05-17 15:00:00'), - end: new Date('2024-05-17 16:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 60, - start: new Date('2024-05-17 16:00:00'), - end: new Date('2024-05-17 17:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 61, - start: new Date('2024-05-17 17:00:00'), - end: new Date('2024-05-17 18:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 62, - start: new Date('2024-05-17 18:00:00'), - end: new Date('2024-05-17 19:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 63, - start: new Date('2024-05-18 10:00:00'), - end: new Date('2024-05-18 11:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 64, - start: new Date('2024-05-18 11:00:00'), - end: new Date('2024-05-18 12:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 65, - start: new Date('2024-05-18 15:00:00'), - end: new Date('2024-05-18 16:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 66, - start: new Date('2024-05-18 16:00:00'), - end: new Date('2024-05-18 17:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 67, - start: new Date('2024-05-18 17:00:00'), - end: new Date('2024-05-18 18:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 68, - start: new Date('2024-05-18 18:00:00'), - end: new Date('2024-05-18 19:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 69, - start: new Date('2024-05-19 10:00:00'), - end: new Date('2024-05-19 11:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 70, - start: new Date('2024-05-19 11:00:00'), - end: new Date('2024-05-19 12:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 71, - start: new Date('2024-05-19 15:00:00'), - end: new Date('2024-05-19 16:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 72, - start: new Date('2024-05-19 16:00:00'), - end: new Date('2024-05-19 17:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 73, - start: new Date('2024-05-19 17:00:00'), - end: new Date('2024-05-19 18:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 74, - start: new Date('2024-05-19 18:00:00'), - end: new Date('2024-05-19 19:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 75, - start: new Date('2024-05-20 10:00:00'), - end: new Date('2024-05-20 11:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 76, - start: new Date('2024-05-20 11:00:00'), - end: new Date('2024-05-20 12:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 77, - start: new Date('2024-05-20 15:00:00'), - end: new Date('2024-05-20 16:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 78, - start: new Date('2024-05-20 16:00:00'), - end: new Date('2024-05-20 17:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 79, - start: new Date('2024-05-20 17:00:00'), - end: new Date('2024-05-20 18:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - { - id: 80, - start: new Date('2024-05-20 18:00:00'), - end: new Date('2024-05-20 19:00:00'), - recruitmentSession: mockRecruitmentSessions[2], - }, - ]; - - mockAvailability = [ - { - id: 1, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 51, - userOauthId: '5', - }, - { - id: 2, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 53, - userOauthId: '7', - }, - { - id: 3, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 55, - userOauthId: '12', - }, - { - id: 4, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 61, - userOauthId: '19', - }, - { - id: 5, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 71, - userOauthId: '3', - }, - { - id: 6, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 78, - userOauthId: '15', - }, - { - id: 7, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 58, - userOauthId: '11', - }, - { - id: 8, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 57, - userOauthId: '8', - }, - { - id: 9, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 64, - userOauthId: '5', - }, - { - id: 10, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 53, - userOauthId: '17', - }, - { - id: 11, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 62, - userOauthId: '2', - }, - { - id: 12, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 71, - userOauthId: '9', - }, - { - id: 13, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 72, - userOauthId: '14', - }, - { - id: 14, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 74, - userOauthId: '6', - }, - { - id: 15, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 75, - userOauthId: '13', - }, - { - id: 16, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 74, - userOauthId: '10', - }, - { - id: 17, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 71, - userOauthId: '4', - }, - { - id: 18, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 64, - userOauthId: '16', - }, - { - id: 19, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 59, - userOauthId: '18', - }, - { - id: 20, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 57, - userOauthId: '1', - }, - { - id: 21, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 66, - userOauthId: '20', - }, - { - id: 22, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 68, - userOauthId: '19', - }, - { - id: 23, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 69, - userOauthId: '7', - }, - { - id: 24, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 70, - userOauthId: '16', - }, - { - id: 25, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 72, - userOauthId: '15', - }, - { - id: 26, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 73, - userOauthId: '11', - }, - { - id: 27, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 75, - userOauthId: '14', - }, - { - id: 28, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 76, - userOauthId: '9', - }, - { - id: 29, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 77, - userOauthId: '8', - }, - { - id: 30, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 78, - userOauthId: '10', - }, - { - id: 31, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 79, - userOauthId: '13', - }, - { - id: 32, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 80, - userOauthId: '12', - }, - { - id: 33, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 79, - userOauthId: '20', - }, - { - id: 34, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 78, - userOauthId: '18', - }, - { - id: 35, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 77, - userOauthId: '17', - }, - { - id: 36, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 76, - userOauthId: '19', - }, - { - id: 37, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 75, - userOauthId: '16', - }, - { - id: 38, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 74, - userOauthId: '15', - }, - { - id: 39, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 73, - userOauthId: '14', - }, - { - id: 40, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 72, - userOauthId: '13', - }, - { - id: 41, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 71, - userOauthId: '12', - }, - { - id: 42, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 70, - userOauthId: '11', - }, - { - id: 43, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 69, - userOauthId: '10', - }, - { - id: 44, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 68, - userOauthId: '9', - }, - { - id: 45, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 67, - userOauthId: '8', - }, - { - id: 46, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 66, - userOauthId: '7', - }, - { - id: 47, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 65, - userOauthId: '6', - }, - { - id: 48, - state: AvailabilityState.Interviewing, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 64, - userOauthId: '5', - }, - { - id: 49, - state: AvailabilityState.Recovering, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 63, - userOauthId: '8', - }, - { - id: 50, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 62, - userOauthId: '14', - }, - { - id: 51, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 61, - userOauthId: '4', - }, - { - id: 52, - state: AvailabilityState.Free, - lastModified: new Date('2024-05-14 00:00:00'), - timeSlot: 60, - userOauthId: '13', - }, - ]; }); beforeEach(async () => { diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index b1885ca..075ba9c 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -184,21 +184,6 @@ export class TimeSlotsService { ); const allMatches = await queryBuilder.getMany(); - // const allMatches = await this.timeSlotRepository.find({ - // relations: [ - // 'availabilities', - // 'availabilities.user', - // 'recruitmentSession', - // ], - // where: { - // availabilities: { - // state: AvailabilityState.Free, - // user: { - // role: Not(In([Role.Applicant, Role.None])), - // }, - // }, - // }, - // }); let goodTimeSlots: TimeSlot[] = []; allMatches.forEach((timeSlot) => { From 90e0cb594f36b0706ac8fa126edf3bc87264e9c3 Mon Sep 17 00:00:00 2001 From: white Date: Wed, 22 May 2024 09:29:52 +0200 Subject: [PATCH 091/104] upd: sonar properties --- sonar-project.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/sonar-project.properties b/sonar-project.properties index edb0d90..8ec4d92 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,3 +5,4 @@ sonar.projectName=HKrecruitment sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info sonar.sources=api/, frontend/, shared/ sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md +sonar.cpd.exclusions=**/mocks/**/* \ No newline at end of file From 58dc3f14874bb19078263e6d566cb060b2bce3ff Mon Sep 17 00:00:00 2001 From: white Date: Wed, 22 May 2024 10:12:04 +0200 Subject: [PATCH 092/104] =?UTF-8?q?test:=20find=20available=20time=20slots?= =?UTF-8?q?=C3=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/timeslots/timeslots.service.spec.ts | 76 +++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index e47d835..bdac22f 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -252,6 +252,82 @@ describe('TimeSlotsService', () => { ); expect(result).toEqual([]); + + jest.clearAllMocks(); + jest.resetAllMocks(); + }); + + it("should return only time slots with at least 2 available people, one of which is a board member", async () => { + const mockQueryBuilder = { + innerJoinAndSelect: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + getMany: jest.fn().mockResolvedValue([ + { + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T10:00:00'), + id: 1, + recruitmentSession: 3, + availabilities: [ + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: true, + is_expert: false, + }, + }, + { + state: AvailabilityState.Free, + user: { + role: Role.Applicant, + is_board: false, + is_expert: false, + }, + }, + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: false, + is_expert: true, + }, + }, + { + state: AvailabilityState.Interviewing, + user: { + role: Role.Member, + is_board: false, + is_expert: true, + }, + }, + { + state: AvailabilityState.Interviewing, + user: { + role: Role.Admin, + is_board: false, + is_expert: true, + }, + }, + ]} + ]), + }; + + // Mock the timeSlotRepository and its methods + const mockTimeSlotRepository = { + createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), + }; + + const timeSlotService = new TimeSlotsService( + mockTimeSlotRepository as any, + ); + + const result = await timeSlotService.findAvailableTimeSlots(); + expect(JSON.stringify(result)).toBe(JSON.stringify([{ + id: 1, + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T10:00:00'), + }])); }); }); }); From 72e2679d1bb337d6bc686d3e296a6d9e24dafcf2 Mon Sep 17 00:00:00 2001 From: white Date: Wed, 22 May 2024 10:13:30 +0200 Subject: [PATCH 093/104] fix: format --- api/src/timeslots/timeslots.service.spec.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index bdac22f..24284e3 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -257,7 +257,7 @@ describe('TimeSlotsService', () => { jest.resetAllMocks(); }); - it("should return only time slots with at least 2 available people, one of which is a board member", async () => { + it('should return only time slots with at least 2 available people, one of which is a board member', async () => { const mockQueryBuilder = { innerJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), @@ -309,7 +309,8 @@ describe('TimeSlotsService', () => { is_expert: true, }, }, - ]} + ], + }, ]), }; @@ -323,11 +324,15 @@ describe('TimeSlotsService', () => { ); const result = await timeSlotService.findAvailableTimeSlots(); - expect(JSON.stringify(result)).toBe(JSON.stringify([{ - id: 1, - start: new Date('2022-01-01T09:00:00'), - end: new Date('2022-01-01T10:00:00'), - }])); + expect(JSON.stringify(result)).toBe( + JSON.stringify([ + { + id: 1, + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T10:00:00'), + }, + ]), + ); }); }); }); From d8a4b8e8da7e2ccb2fd0c4372562dcb9283b9f35 Mon Sep 17 00:00:00 2001 From: white Date: Wed, 22 May 2024 10:23:54 +0200 Subject: [PATCH 094/104] test: improvements --- api/src/timeslots/timeslots.service.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index 24284e3..32d6d8d 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -311,6 +311,22 @@ describe('TimeSlotsService', () => { }, ], }, + { + start: new Date('2022-01-01T9:00:00'), + end: new Date('2022-01-01T20:00:00'), + id: 2, + recruitmentSession: 3, + availabilities: [ + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: true, + is_expert: true, + }, + }, + ], + }, ]), }; From 3c82ffaf7b67db42b51ebaba078a6bec09548367 Mon Sep 17 00:00:00 2001 From: white Date: Fri, 24 May 2024 01:38:10 +0200 Subject: [PATCH 095/104] fix: improve coverage --- .../availability/availability.service.spec.ts | 29 +++++++ api/src/mocks/repositories.ts | 3 + .../recruitment-session.service.spec.ts | 83 ++++++++++++++++++- api/src/timeslots/timeslots.service.spec.ts | 75 +++++++++++++++++ 4 files changed, 189 insertions(+), 1 deletion(-) diff --git a/api/src/availability/availability.service.spec.ts b/api/src/availability/availability.service.spec.ts index 9f62ba2..43121e6 100644 --- a/api/src/availability/availability.service.spec.ts +++ b/api/src/availability/availability.service.spec.ts @@ -78,6 +78,28 @@ describe('AvailabilityService', () => { }); }); + describe('updateAvailability', () => { + it('should update the availability with the specified id', async () => { + const mockUpdatedAvailability = { + ...mockAvailability, + state: AvailabilityState.Interviewing, + }; + jest + .spyOn(mockedRepository, 'save') + .mockResolvedValue(mockUpdatedAvailability); + const result = await service.updateAvailability( + mockAvailability.id, + mockUpdatedAvailability, + ); + expect(result).toEqual(mockUpdatedAvailability); + expect(mockedRepository.save).toHaveBeenCalledTimes(1); + expect(mockedRepository.save).toHaveBeenCalledWith({ + ...mockUpdatedAvailability, + id: mockAvailability.id, + }); + }); + }); + describe('deleteAvailability', () => { it('should remove the specified availability from the database', async () => { const mockAvailabilityRepository = { @@ -142,6 +164,13 @@ describe('AvailabilityService', () => { expect(result).toEqual(mockAvailability); expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); }); + + it('should return null if no availability is found', async () => { + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([]); + const result = await service.findById(mockAvailability.id); + expect(result).toBeNull(); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + }); }); describe('findByUserAndTimeSlot', () => { diff --git a/api/src/mocks/repositories.ts b/api/src/mocks/repositories.ts index e418727..7ac0b52 100644 --- a/api/src/mocks/repositories.ts +++ b/api/src/mocks/repositories.ts @@ -1,6 +1,9 @@ +import { count } from 'console'; + export const mockedRepository = { find: jest.fn(), findBy: jest.fn(), remove: jest.fn(), save: jest.fn(), + count: jest.fn(), }; diff --git a/api/src/recruitment-session/recruitment-session.service.spec.ts b/api/src/recruitment-session/recruitment-session.service.spec.ts index 6df16dc..888ba10 100644 --- a/api/src/recruitment-session/recruitment-session.service.spec.ts +++ b/api/src/recruitment-session/recruitment-session.service.spec.ts @@ -5,7 +5,7 @@ import { getRepositoryToken, getDataSourceToken } from '@nestjs/typeorm'; import { RecruitmentSession } from './recruitment-session.entity'; import { RecruitmentSessionService } from './recruitment-session.service'; import { mockedTimeSlotsService as mockedTimeSlotsServiceClass } from '@mocks/services'; -import { mockDataSource } from 'src/mocks/data-sources'; +import { MockedDataSource, mockDataSource } from 'src/mocks/data-sources'; import { TimeSlotsService } from 'src/timeslots/timeslots.service'; import { RecruitmentSessionState } from '@hkrecruitment/shared'; @@ -118,4 +118,85 @@ describe('Recruitment Session Service', () => { ); }); }); + + describe('findAllRecruitmentSessions', () => { + it('should return all recruitment sessions', async () => { + const recruitmentSessions = [mockRecruitmentSession]; + jest + .spyOn(mockedRepository, 'find') + .mockResolvedValue(recruitmentSessions); + const result = + await recruitmentSessionService.findAllRecruitmentSessions(); + expect(result).toEqual(recruitmentSessions); + expect(mockedRepository.find).toHaveBeenCalledTimes(1); + }); + + it('should return an empty array if there are no recruitment sessions', async () => { + jest.spyOn(mockedRepository, 'find').mockResolvedValue([]); + const result = + await recruitmentSessionService.findAllRecruitmentSessions(); + expect(result).toEqual([]); + expect(mockedRepository.find).toHaveBeenCalledTimes(1); + }); + }); + + describe('findRecruitmentSessionById', () => { + it('should return the recruitment session with the given ID', async () => { + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockRecruitmentSession]); + const result = await recruitmentSessionService.findRecruitmentSessionById( + 1, + ); + expect(result).toEqual(mockRecruitmentSession); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ id: 1 }); + }); + + it('should return null if the recruitment session does not exist', async () => { + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([]); + const result = await recruitmentSessionService.findRecruitmentSessionById( + 1, + ); + expect(result).toBeNull(); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ id: 1 }); + }); + }); + + describe('findActiveRecruitmentSession', () => { + it('should return the active recruitment session', async () => { + jest + .spyOn(mockedRepository, 'findBy') + .mockResolvedValue([mockRecruitmentSession]); + const result = + await recruitmentSessionService.findActiveRecruitmentSession(); + expect(result).toEqual(mockRecruitmentSession); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ + state: RecruitmentSessionState.Active, + }); + }); + + it('should return null if there is no active recruitment session', async () => { + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([]); + const result = + await recruitmentSessionService.findActiveRecruitmentSession(); + expect(result).toBeNull(); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + expect(mockedRepository.findBy).toHaveBeenCalledWith({ + state: RecruitmentSessionState.Active, + }); + }); + }); + + describe('sessionHasPendingInterviews', () => { + it('should return error', async () => { + await recruitmentSessionService + .sessionHasPendingInterviews(mockRecruitmentSession) + .catch((error) => { + expect(error.message).toBe('Method not implemented.'); + }); + }); + }); }); diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index 32d6d8d..24af579 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -45,6 +45,20 @@ describe('TimeSlotsService', () => { expect(timeSlotService).toBeDefined(); }); + describe('countOverlappingTimeSlots', () => { + it('should return the number of overlapping time slots', async () => { + jest.spyOn(mockedRepository, 'count').mockResolvedValue(2); + const startDate = new Date('2022-01-01T09:00:00'); + const endDate = new Date('2022-01-01T10:00:00'); + const result = await timeSlotService.countOverlappingTimeSlots( + startDate, + endDate, + ); + expect(result).toBe(2); + expect(mockedRepository.count).toHaveBeenCalledTimes(1); + }); + }); + describe('deleteTimeSlot', () => { it('should remove the specified timeslot from the database', async () => { jest.spyOn(mockedRepository, 'remove').mockResolvedValue(mockTimeSlot); @@ -72,6 +86,13 @@ describe('TimeSlotsService', () => { expect(result).toEqual(mockTimeSlot); expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); }); + + it("should return null if the timeslot doesn't exist", async () => { + jest.spyOn(mockedRepository, 'findBy').mockResolvedValue([]); + const result = await timeSlotService.findById(mockTimeSlot.id); + expect(result).toBeNull(); + expect(mockedRepository.findBy).toHaveBeenCalledTimes(1); + }); }); describe('createTimeSlot', () => { @@ -269,6 +290,14 @@ describe('TimeSlotsService', () => { id: 1, recruitmentSession: 3, availabilities: [ + { + state: AvailabilityState.Interviewing, + user: { + role: Role.None, + is_board: false, + is_expert: true, + }, + }, { state: AvailabilityState.Free, user: { @@ -350,6 +379,52 @@ describe('TimeSlotsService', () => { ]), ); }); + + it("should return an empty array if there aren't any time slots with at least 2 available people", async () => { + const mockQueryBuilder = { + innerJoinAndSelect: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + getMany: jest.fn().mockResolvedValue([ + { + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T10:00:00'), + id: 1, + recruitmentSession: 3, + availabilities: [ + { + state: AvailabilityState.Interviewing, + user: { + role: Role.None, + is_board: false, + is_expert: true, + }, + }, + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: false, + is_expert: true, + }, + }, + ], + }, + ]), + }; + + // Mock the timeSlotRepository and its methods + const mockTimeSlotRepository = { + createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder), + }; + + const timeSlotService = new TimeSlotsService( + mockTimeSlotRepository as any, + ); + + const result = await timeSlotService.findAvailableTimeSlots(); + expect(result).toEqual([]); + }); }); }); From 76bd43c32698c83d50f36509401df6b2ffd4e9ac Mon Sep 17 00:00:00 2001 From: white Date: Fri, 24 May 2024 02:02:23 +0200 Subject: [PATCH 096/104] fix: improve coverage --- api/src/availability/availability.service.ts | 5 +++++ sonar-project.properties | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/api/src/availability/availability.service.ts b/api/src/availability/availability.service.ts index 6caa4aa..c03e6af 100644 --- a/api/src/availability/availability.service.ts +++ b/api/src/availability/availability.service.ts @@ -36,6 +36,11 @@ export class AvailabilityService { return matches.length > 0 ? matches[0] : null; } + /** + * Find all availabilities for a given user + * @param user - User to find availabilities for + * @returns {Promise} - List of availabilities for the user + */ async findByUserAndTimeSlot(user: User, timeSlot: TimeSlot) { const matches = await this.availabilityRepository.findBy({ user: user as any, diff --git a/sonar-project.properties b/sonar-project.properties index 8ec4d92..57da252 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,4 +5,5 @@ sonar.projectName=HKrecruitment sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info sonar.sources=api/, frontend/, shared/ sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md -sonar.cpd.exclusions=**/mocks/**/* \ No newline at end of file +sonar.cpd.exclusions=**/mocks/**/* +sonar.exclusions**/mocks/**/*, =**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md \ No newline at end of file From 0e82629d7128fea767e6eb48933b322fec05a908 Mon Sep 17 00:00:00 2001 From: white Date: Fri, 24 May 2024 02:13:59 +0200 Subject: [PATCH 097/104] fix: improve coverage --- api/src/timeslots/timeslots.controller.spec.ts | 16 ++++++++++++++++ sonar-project.properties | 3 +-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/api/src/timeslots/timeslots.controller.spec.ts b/api/src/timeslots/timeslots.controller.spec.ts index 2ab30d8..f67e9fb 100644 --- a/api/src/timeslots/timeslots.controller.spec.ts +++ b/api/src/timeslots/timeslots.controller.spec.ts @@ -20,6 +20,7 @@ describe('TimeSlotController', () => { expect(service).toBeDefined(); }); + describe('findAvailableTimeSlots', () => { it('should return an array of available time slots', async () => { const expectedTimeSlots: TimeSlot[] = [ @@ -46,5 +47,20 @@ describe('TimeSlotController', () => { expect(result).toEqual(expectedTimeSlots); expect(service.findAvailableTimeSlots).toHaveBeenCalledTimes(1); }); + + it("should return an empty array if there are no available time slots", async () => { + const expectedTimeSlots: TimeSlot[] = []; + + jest + .spyOn(service, 'findAvailableTimeSlots') + .mockResolvedValue(expectedTimeSlots); + + // Act + const result = await controller.findAvailableTimeSlots(); + + // Assert + expect(result).toEqual(expectedTimeSlots); + expect(service.findAvailableTimeSlots).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/sonar-project.properties b/sonar-project.properties index 57da252..8ec4d92 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,5 +5,4 @@ sonar.projectName=HKrecruitment sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info sonar.sources=api/, frontend/, shared/ sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md -sonar.cpd.exclusions=**/mocks/**/* -sonar.exclusions**/mocks/**/*, =**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md \ No newline at end of file +sonar.cpd.exclusions=**/mocks/**/* \ No newline at end of file From defd47b911faa30ab30a0dc6560256b9968cc195 Mon Sep 17 00:00:00 2001 From: white Date: Fri, 24 May 2024 02:16:57 +0200 Subject: [PATCH 098/104] fix: improve coverage - format --- api/src/timeslots/timeslots.controller.spec.ts | 3 +-- sonar-project.properties | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/timeslots/timeslots.controller.spec.ts b/api/src/timeslots/timeslots.controller.spec.ts index f67e9fb..f39ddcd 100644 --- a/api/src/timeslots/timeslots.controller.spec.ts +++ b/api/src/timeslots/timeslots.controller.spec.ts @@ -20,7 +20,6 @@ describe('TimeSlotController', () => { expect(service).toBeDefined(); }); - describe('findAvailableTimeSlots', () => { it('should return an array of available time slots', async () => { const expectedTimeSlots: TimeSlot[] = [ @@ -48,7 +47,7 @@ describe('TimeSlotController', () => { expect(service.findAvailableTimeSlots).toHaveBeenCalledTimes(1); }); - it("should return an empty array if there are no available time slots", async () => { + it('should return an empty array if there are no available time slots', async () => { const expectedTimeSlots: TimeSlot[] = []; jest diff --git a/sonar-project.properties b/sonar-project.properties index 8ec4d92..a2f1d75 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,4 +5,5 @@ sonar.projectName=HKrecruitment sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info sonar.sources=api/, frontend/, shared/ sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md -sonar.cpd.exclusions=**/mocks/**/* \ No newline at end of file +sonar.cpd.exclusions=**/mocks/**/* +sonar.ex \ No newline at end of file From dad499284c554f65f6a2aa631596fa4cd8f90e5b Mon Sep 17 00:00:00 2001 From: white Date: Sat, 25 May 2024 19:56:07 +0200 Subject: [PATCH 099/104] fix: minor --- api/src/mocks/data.ts | 75 +++++++++++++++++++ api/src/timeslots/timeslots.service.spec.ts | 82 ++------------------- api/src/timeslots/timeslots.service.ts | 35 ++++----- 3 files changed, 96 insertions(+), 96 deletions(-) diff --git a/api/src/mocks/data.ts b/api/src/mocks/data.ts index c4c0bd3..9c7ea0a 100644 --- a/api/src/mocks/data.ts +++ b/api/src/mocks/data.ts @@ -229,3 +229,78 @@ export const mockAvailability = { export const mockCreateAvailabilityDto = { timeSlotId: mockTimeSlot.id, } as CreateAvailabilityDto; + +export const mockTimeSlotsJoined = [ + { + start: new Date('2022-01-01T09:00:00'), + end: new Date('2022-01-01T10:00:00'), + id: 1, + recruitmentSession: 3, + availabilities: [ + { + state: AvailabilityState.Interviewing, + user: { + role: Role.None, + is_board: false, + is_expert: true, + }, + }, + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: true, + is_expert: false, + }, + }, + { + state: AvailabilityState.Free, + user: { + role: Role.Applicant, + is_board: false, + is_expert: false, + }, + }, + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: false, + is_expert: true, + }, + }, + { + state: AvailabilityState.Interviewing, + user: { + role: Role.Member, + is_board: false, + is_expert: true, + }, + }, + { + state: AvailabilityState.Interviewing, + user: { + role: Role.Admin, + is_board: false, + is_expert: true, + }, + }, + ], + }, + { + start: new Date('2022-01-01T9:00:00'), + end: new Date('2022-01-01T20:00:00'), + id: 2, + recruitmentSession: 3, + availabilities: [ + { + state: AvailabilityState.Free, + user: { + role: Role.Member, + is_board: true, + is_expert: true, + }, + }, + ], + }, +]; diff --git a/api/src/timeslots/timeslots.service.spec.ts b/api/src/timeslots/timeslots.service.spec.ts index 24af579..1c2e7e1 100644 --- a/api/src/timeslots/timeslots.service.spec.ts +++ b/api/src/timeslots/timeslots.service.spec.ts @@ -1,4 +1,9 @@ -import { mockGenerateTimeSlots, mockTimeSlot, testDate } from 'src/mocks/data'; +import { + mockGenerateTimeSlots, + mockTimeSlot, + mockTimeSlotsJoined, + testDate, +} from 'src/mocks/data'; import { mockedRepository } from 'src/mocks/repositories'; import { TestingModule, Test } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; @@ -283,80 +288,7 @@ describe('TimeSlotsService', () => { innerJoinAndSelect: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), - getMany: jest.fn().mockResolvedValue([ - { - start: new Date('2022-01-01T09:00:00'), - end: new Date('2022-01-01T10:00:00'), - id: 1, - recruitmentSession: 3, - availabilities: [ - { - state: AvailabilityState.Interviewing, - user: { - role: Role.None, - is_board: false, - is_expert: true, - }, - }, - { - state: AvailabilityState.Free, - user: { - role: Role.Member, - is_board: true, - is_expert: false, - }, - }, - { - state: AvailabilityState.Free, - user: { - role: Role.Applicant, - is_board: false, - is_expert: false, - }, - }, - { - state: AvailabilityState.Free, - user: { - role: Role.Member, - is_board: false, - is_expert: true, - }, - }, - { - state: AvailabilityState.Interviewing, - user: { - role: Role.Member, - is_board: false, - is_expert: true, - }, - }, - { - state: AvailabilityState.Interviewing, - user: { - role: Role.Admin, - is_board: false, - is_expert: true, - }, - }, - ], - }, - { - start: new Date('2022-01-01T9:00:00'), - end: new Date('2022-01-01T20:00:00'), - id: 2, - recruitmentSession: 3, - availabilities: [ - { - state: AvailabilityState.Free, - user: { - role: Role.Member, - is_board: true, - is_expert: true, - }, - }, - ], - }, - ]), + getMany: jest.fn().mockResolvedValue(mockTimeSlotsJoined), }; // Mock the timeSlotRepository and its methods diff --git a/api/src/timeslots/timeslots.service.ts b/api/src/timeslots/timeslots.service.ts index 075ba9c..729d8e7 100644 --- a/api/src/timeslots/timeslots.service.ts +++ b/api/src/timeslots/timeslots.service.ts @@ -185,30 +185,23 @@ export class TimeSlotsService { const allMatches = await queryBuilder.getMany(); - let goodTimeSlots: TimeSlot[] = []; - allMatches.forEach((timeSlot) => { - let boardMembers = 0; - let expertMembers = 0; - for (let availability of timeSlot.availabilities) { - // redundant checks - if (availability.state !== AvailabilityState.Free) continue; - if (availability.user.role === Role.None) continue; - if (availability.user.role === Role.Applicant) continue; - - if (availability.user.is_board) ++boardMembers; - else if (availability.user.is_expert) ++expertMembers; - } - - if ((boardMembers && expertMembers) || boardMembers > 1) { - const timeslotToPush = { + return allMatches + .filter((timeSlot) => { + let [boardMembers, expertMembers] = [0, 0]; + for (let av of timeSlot.availabilities) { + if (av.state !== AvailabilityState.Free) continue; + if ([Role.None, Role.Applicant].includes(av.user.role)) continue; + if (av.user.is_board) ++boardMembers; + else if (av.user.is_expert) ++expertMembers; + } + return (boardMembers && expertMembers) || boardMembers > 1; + }) + .map((timeSlot) => { + return { id: timeSlot.id, start: timeSlot.start, end: timeSlot.end, } as TimeSlot; - goodTimeSlots.push(timeslotToPush); - } - }); - - return goodTimeSlots; + }); } } From f2f4a4ada97d359311bcd1dabc3dffd8d3de3e8a Mon Sep 17 00:00:00 2001 From: white Date: Sat, 25 May 2024 20:01:02 +0200 Subject: [PATCH 100/104] fix: formatting comments --- .../recruitment-session.service.ts | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts index 8a982f2..525dbef 100644 --- a/api/src/recruitment-session/recruitment-session.service.ts +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -17,11 +17,6 @@ export class RecruitmentSessionService { private dataSource: DataSource, ) {} - /** - * Create a recruitment session - * @param recruitmentSession - Recruitment session to create - * @returns {Promise} - Created recruitment session - */ async createRecruitmentSession( recruitmentSession: CreateRecruitmentSessionDto, ): Promise { @@ -45,19 +40,10 @@ export class RecruitmentSessionService { }); } - /** - * List all recruitment sessions - * @returns {Promise} - List of recruitment sessions - */ async findAllRecruitmentSessions(): Promise { return await this.recruitmentSessionRepository.find(); } - /** - * Find a recruitment session by its ID - * @param RSid - ID of the recruitment session - * @returns {Promise} - Recruitment session with the given ID - */ async findRecruitmentSessionById( RSid: number, ): Promise { @@ -67,10 +53,6 @@ export class RecruitmentSessionService { return matches.length > 0 ? matches[0] : null; } - /** - * Find an active recruitment session - * @returns {Promise} - Active recruitment session - */ async findActiveRecruitmentSession(): Promise { const matches = await this.recruitmentSessionRepository.findBy({ state: RecruitmentSessionState.Active, @@ -78,11 +60,6 @@ export class RecruitmentSessionService { return matches.length > 0 ? matches[0] : null; } - /** - * Delete a recruitment session - * @param recruitmentSession - Recruitment session to delete - * @returns {Promise} - Deleted recruitment session - */ async deleteRecruitmentSession( recruitmentSession: RecruitmentSession, ): Promise { @@ -100,11 +77,6 @@ export class RecruitmentSessionService { return await this.recruitmentSessionRepository.save(recruitmentSession); } - /** - * Check if a recruitment session has pending interviews - * @param recruitmentSession - Recruitment session to check - * @returns {Promise} - True if the recruitment session has pending interviews, false otherwise - */ async sessionHasPendingInterviews( recruitmentSession: RecruitmentSession, ): Promise { From 87a2eb16605cb76c54a181f1789298c276f81fce Mon Sep 17 00:00:00 2001 From: white Date: Sat, 25 May 2024 20:02:43 +0200 Subject: [PATCH 101/104] fix: formatting comments --- api/src/recruitment-session/recruitment-session.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api/src/recruitment-session/recruitment-session.service.ts b/api/src/recruitment-session/recruitment-session.service.ts index 525dbef..670a9e6 100644 --- a/api/src/recruitment-session/recruitment-session.service.ts +++ b/api/src/recruitment-session/recruitment-session.service.ts @@ -66,11 +66,6 @@ export class RecruitmentSessionService { return await this.recruitmentSessionRepository.remove(recruitmentSession); } - /** - * Update a recruitment session - * @param recruitmentSession - Recruitment session to update - * @returns {Promise} - Updated recruitment session - */ async updateRecruitmentSession( recruitmentSession: RecruitmentSession, ): Promise { From 6dd26b60fc1526cd1b58f2455a4c0e3ed99e733c Mon Sep 17 00:00:00 2001 From: white Date: Sat, 25 May 2024 20:06:12 +0200 Subject: [PATCH 102/104] fix: formatting comments --- sonar-project.properties | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index a2f1d75..a288a22 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -4,6 +4,5 @@ sonar.projectName=HKrecruitment sonar.javascript.lcov.reportPaths=./coverage/shared/lcov.info, ./coverage/api/lcov.info, ./coverage/api-e2e/lcov.info sonar.sources=api/, frontend/, shared/ -sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md -sonar.cpd.exclusions=**/mocks/**/* -sonar.ex \ No newline at end of file +sonar.coverage.exclusions=**/node_modules/**/*, **/*.spec.*, **/*.e2e-spec.*, **/documentation/**/*, **/frontend/**/*, **/test/**/*, **/tests/**/*, **/*.json, **/*.yaml, **/*.yml, **/*.md, **/mocks/**/* +sonar.cpd.exclusions=**/mocks/**/* \ No newline at end of file From 1afe26942d63881dc9529c4ab476b5a523dabfc9 Mon Sep 17 00:00:00 2001 From: white Date: Sat, 25 May 2024 20:06:56 +0200 Subject: [PATCH 103/104] fix: formatting comments --- api/src/users/users.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index 8eb1fd8..e85a453 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -28,11 +28,6 @@ export class UsersService { return this.userRepository.findOne({ where: { oauthId } }); } - /** - * Delete a user - * @param {User} user - User to delete - * @returns {Promise} - Deleted user - */ async delete(user: User): Promise { return this.userRepository.remove(user); } From c9c30fbd83e2bb018da3dd6c6919f8ad1217d029 Mon Sep 17 00:00:00 2001 From: white Date: Thu, 30 May 2024 11:29:30 +0200 Subject: [PATCH 104/104] feat: check ability --- api/src/timeslots/timeslots.controller.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/src/timeslots/timeslots.controller.ts b/api/src/timeslots/timeslots.controller.ts index 90c5b84..6a3af4a 100644 --- a/api/src/timeslots/timeslots.controller.ts +++ b/api/src/timeslots/timeslots.controller.ts @@ -6,6 +6,8 @@ import { ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { TimeSlot } from './timeslot.entity'; +import { CheckPolicies } from 'src/authorization/check-policies.decorator'; +import { Action } from '@hkrecruitment/shared'; @ApiBearerAuth() @ApiTags('timeslots') @@ -14,6 +16,7 @@ export class TimeSlotsController { constructor(private readonly timeSlotsService: TimeSlotsService) {} @ApiUnauthorizedResponse() + @CheckPolicies((ability) => ability.can(Action.Read, 'TimeSlot')) @Get() async findAvailableTimeSlots(): Promise { return await this.timeSlotsService.findAvailableTimeSlots();