From 2028a73c9661b1589e2e4f15faab6427200791f9 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Tue, 6 Sep 2022 17:36:17 -0400 Subject: [PATCH] refactor(playlists): address review comments Signed-off-by: Phil Kuang --- .changeset/calm-snakes-tap.md | 2 +- .github/CODEOWNERS | 4 + microsite/static/img/playlist-logo.png | Bin 14734 -> 14844 bytes packages/backend/src/plugins/playlist.ts | 22 ++--- packages/core-components/api-report.md | 24 +++-- .../src/components/Table/Table.tsx | 2 +- .../HeaderActionMenu.test.tsx | 6 +- .../HeaderActionMenu/HeaderActionMenu.tsx | 16 ++-- .../src/layout/HeaderActionMenu/index.ts | 2 +- plugins/playlist-backend/README.md | 21 ++--- plugins/playlist-backend/api-report.md | 7 +- .../migrations/20220701011329_init.js | 8 +- plugins/playlist-backend/package.json | 18 ++-- .../src/service/ListPlaylistsFilter.ts | 13 ++- .../src/service/router.test.ts | 71 +++++++++++++-- .../playlist-backend/src/service/router.ts | 55 ++++++++++-- .../src/service/standaloneServer.ts | 5 +- plugins/playlist-common/package.json | 4 +- plugins/playlist/api-report.md | 12 +-- plugins/playlist/package.json | 23 +++-- plugins/playlist/src/api/PlaylistApi.ts | 3 +- .../playlist/src/api/PlaylistClient.test.ts | 24 ++++- plugins/playlist/src/api/PlaylistClient.ts | 3 +- .../EntityPlaylistDialog.tsx | 7 +- .../PlaylistEntitiesTable.test.tsx | 48 ++-------- .../PlaylistPage/PlaylistEntitiesTable.tsx | 48 ++-------- yarn.lock | 83 +++++++++++++++++- 27 files changed, 330 insertions(+), 201 deletions(-) diff --git a/.changeset/calm-snakes-tap.md b/.changeset/calm-snakes-tap.md index 6b853da53d..6fdf092ede 100644 --- a/.changeset/calm-snakes-tap.md +++ b/.changeset/calm-snakes-tap.md @@ -2,4 +2,4 @@ '@backstage/core-components': patch --- -Export `HeaderActionMenu` and expose default `Table` icons via `Table.tableIcons` +Export `HeaderActionMenu` and expose default `Table` icons via `Table.icons` diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f00771d29d..b0f1e828cd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,6 +16,8 @@ yarn.lock @backstage/reviewers @backst /docs/plugins/integrating-search-into-plugins.md @backstage/reviewers @backstage/techdocs-core /packages/techdocs-cli @backstage/reviewers @backstage/techdocs-core /packages/techdocs-cli-embedded-app @backstage/reviewers @backstage/techdocs-core +/plugins/adr @backstage/reviewers @kuangp +/plugins/adr-* @backstage/reviewers @kuangp /plugins/allure @backstage/reviewers @deepak-bhardwaj-ps /plugins/apache-airflow @backstage/reviewers @cmpadden /plugins/api-docs @backstage/reviewers @backstage/sda-se-reviewers @@ -47,6 +49,8 @@ yarn.lock @backstage/reviewers @backst /plugins/kubernetes @backstage/reviewers @backstage/warpspeed /plugins/kubernetes-* @backstage/reviewers @backstage/warpspeed /plugins/newrelic-dashboard @backstage/reviewers @mufaddal7 +/plugins/playlist @backstage/reviewers @kuangp +/plugins/playlist-* @backstage/reviewers @kuangp /plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski /plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka /plugins/search @backstage/reviewers @backstage/techdocs-core diff --git a/microsite/static/img/playlist-logo.png b/microsite/static/img/playlist-logo.png index b5c8d9940175d6221c1bf1cdf9ab473e5793a5fb..333a9f832a48f430f415c94cc9e1346d5a04ce20 100644 GIT binary patch literal 14844 zcmeHudpuNY`?nb*L&dDxItj~6B}o)fIn0uYP_%iTgdEyQwR1j%oMyJlv~!pqHrYaL zp_4;|ipEs-CPar_D5q^3D#sbyDPz3Xa(JHi^V{#=@B4W_k3ag%$Gz@#-S>T6-|KrF z?$xD3*5=D)*U3sqNG!M5&#;w{Aad}3-^i_eA^D9Aze}(Q_U6(qcp6acYXp8n?;+NgGLN&;1+-{y;X7Y=fuH?aQt1V>? z+S--BVmq$PIZxUoD_J9Ba`(!%oHZNWPo$`4dwySgDb;l(W?VaMWpCTixv_H@C!LMM zdg@IgGDh!>PHK)CO!XHB%h1Ub6k|#dh$Qkq$Voq*G_sug14dEOO{@7w6!g$W6utY< z@W8@T6m7%bnSGhHg+Orlm49sEZvx@7+<&CXkZZPVlV7>83VDCvGnylE{i5S0)xGu$*Mi9- z6N&!k)m#3whF`Vj)5!{)x3O0$u&rhdx8O^ zRq1ZI4u>&o9ZP9d?D~Bs({tJPkFJnN-TU9}IbJ{%E(_||fVq)i#b-(Verg25qm})- zb+R?5%|4?fvhFGhC(cAu|6fez`qPdl`~Qh$+!#zFkz-L<@#?>f24UskAJRP>2)kcy zRyr@i`VI22?YH=5kb2P$_QgJtt#Hh5}Xe2;m}BS~&A6FW{Iw5<8vp9ZS9k`wfs z%c2n$jj-qm|4%(ZPsf%1^4R+Gz?#dYEY*YckLIUk?-v+5TuV%%`fb1l7 z&AR4*`(d*Mqi;N~o3MMJ@mhL-Q_s{L<~5%jYX)8-N(0qNaw2|l0yiHx% z0iGh629oe;yIe3#*=O+bG%OYAaZC*_HnlmxPp6KQemDyJrB;ICb>uxDMftyzB6YWp1U>GPBHdKnJ6o;m!6}!Ya!EDA=Hm@aqrtQ*AGq(* zn9P1J>X<1$(G=#X{^p)I7oIe&@UZ~k^+&yZflW)v`}J{TkjS1u>58u^cEmB~xg18# zWyv+Pl8C$dVD68M{uq4XMVMzA`(o1^^*bixV?AKgfX1fJ`Ku`$Z-|s&vt}@l{`Jrk z>p-ou!3sv>0Tt%oPaXDwuvK=cSstIbOch0A)+8RBi)|%a@v(YM6z$C@c(x;-;rFq;c4#2@th}#+Ph7JX!{oMaN}CJbXB_act40C=XjH0FOT@buQ5@ZfS^+a?2WdH*10Cza>!dG+oXsXNv#C?^fj zlV;i0^XP9`8`1yHnJ?Nu5$(iULD?pW)l%*&%`StsZ-BP_Zw05=4zBg@)r(+5{#3OU zRWxzDD8c#+rsB{PIHvIf4-YP_F?&xeLT;V_>so`wciR$ncQ?m>e8BW{*IayIQA>-^ zSoDYgzgfeu;X}%7sG_p!D>SGs^w(FsjecZa3oOiw--tR{IL=1nWGKL_;yxZ5f~3M( zW-(Oa8e7cK7IU=!)*Q_}OVNKcYX7=CkF`Y7pw0CpfBEzvsU0KJv@ZR9RnpGW)7+U! zA$_J}e6nxmsJPFzmS5S?AH(!CJAH!y{jIDUCp`2VYj=>yt(80@$ma+zHGLH>Wv)Ro z14o4YC8sXISxuonUB<^Yzs;QIeh@U+p z+=CJB&oCUmd7P_G-4->S#h%E`PKpX>GbVLg*a=&BMJ5 zaO-ZR@twD(l{=~J;R;Cwn%Ncn_Ytx+*0~-|F;LQ&)z7U(u2CPtJX|8r3hpC(`}eFn?1lvhc#`L3m-WU`1R?W#o*mblA8_yq~v zlUb3Pm`iTG!Hl}pBS#oNXQUH#l3_UTrJ&<}Q}Dg8t5AZ*o~xIasWA&!$%#WXO?i!& zT#)pR5uwvSt75#0-tRqLV|$?Rj6A9VO-W}8p@sZr2VMpSqdF>Qt#*lfRQ0?PcS7A{ zcIN3xDKHacz_aQq=SrqdUT23i{L|1y{QGKp57bxz4J9qNN$xbuc`@!~Af|FFHA4}J zj5}Ut_kHPhLQAZF!v)a$BiRK(!3b#ZiSd7>gh=F6+LnGtl1O~^`ZG? z`7tAah}(h{-h$bThqfr&bX;D34a#YoER{c>gOqCvQgWf+8y>-v zSOh+n2PxVMQtZH%V!_=I(JMQ>&aAoG?M+K0oQ$=ADETLFGN*!P0%7ip36oA2(DSzc zq32_8uBc#!=q3B1s7;<&CQIHwZh|SN+}(mnNUXhse+%FzP3lH!xhCkKdUor}KN`TP zgg*8&>FkLK1J{?bE1}qtiTNPhAGxIHO3oZ=BA57&18g#t5ufI1Q)BHSC~rc~N%uQr zdV5#R;s83#=Ku!9w62}ykyTwffYY@(5C2>XDmc1;0dLqF_@7qGfaIzSXv8+?zEobEEoI;&Ucd9<#g7e^%ou3$J*Qv!v{b?^V=X6O)60mURu~6IfUhF^9^=oF)Ji1 zoLm*=x1fjX=L8~XJGi=}Sr)KQ@cr zjlKm?`>xJcfPVEhb|!1qN8sQul!mqHb&nklMF@|CfBE>HC4@@eOf#gSKEHIYB?fQp zJc;^XS5q?)guV_@36HKe+S?f+1G;|Jh$$~9n3u^2C^g%>0Iw-?h0?;9J z)mu&M=#*up0a)+sZuQlxbW za~md1+~lES7W0L126poNn|VC6kAY^FQoCBpB3?<7N}*LWaMr3q9aW5T!1R82unan2 zdEmNRsFAh%R15JI-rA7A_&`cB3yAbt%_(5Bs~>JAodwS~kWAoK0E?I`AbMKXy{Ff_ zDxrJ>NeoTHB@%ti#Hl=D0Sw8td6uh3Od#e+8Ui%%MZ>%B$f03yrdh{{qEWfO|BuJD_2ug{Pe8=aM>c1-784J7 zL>^&?zGH|=6&m_t+n}K}X!fS*BFWu#q)-uxgbFKEN2}7SkzY0>h~`0f6D}KNan(`j zb;)@v(874kgKJ1tE^oM${y+!9dJA^$WBhzWktZakPHsm8Ln$#5zB#Z(yC|m5(i*}= z_sh_e-N;F`iDjqU3;I5H-~fi5)#J36(&ihbg%y}bWEdlyFQiB`K$JT9MSj?eu$#y6 zyduFW0j&znSG$PGrgZW(_^_$*UnLV$ z4?@Fp3V)K%Py?(dn*8;6IiaP`z5lWVYa?Lf0wRdrC_nVd2+p68^ZE}Ll6#El{f$J@ zdxe_%vVB$61sa{*=|H*GOJ4Wsk=zRs?`siB$t!E>=VnC$(MkqrjN0XArS#!g7!c|c>zu?08Q9Y? zMd&ux)MqBJV>sw57VN~0Y~*8tjTtJ1(J(i4R=uba5s3ef@7>d!CC{Ado99 z&#KNN*C^+HXG0=yRfCwMfE3K05PI_cK|9j>t>7%=kI)&#PZ-V+)qIe(?zjREPpvWX z-iM9tQV64BZo3JU(%t^Fp=lnmXj~?}f=&n(N`z-X4|p|f#|{xF>5>DfC~B~lQScEz z%WqsV<%lJKdOKCzZyR&AIRlc+#>YEr|iU?@&(dv(%4#DGH-#CGajX?iqj0)c;i3WbTM3rwXCrhly zJ)D#Pr*aqFmd;{A&Adwf3i|32&qqC5)cE%eWGW&Bjzitnt+#p7Nw)1RpW8j}N`fQ* zTXV126RS6n1JU6#*+~hX+*C?m!CiHK$gWZ|CT|aDmW2ou`>r)x7_6H3Y+Fk+XwS?G zf{;3|!cHnD)hB^1{R;Ih;*F=+a=W{YCc=BcLL-D0WdgB}?B2s~!dxOXZ>o?QzM1%7 zTU&PR5uszy6^ucr8Nd}d0&uBA1pLeEo3>9WgoLZ}heped4EmtWKc+1ehWFIVXOJ;| z!j1~jm5Y&-U3NT`;~_a>v||+~dReWW4CSgTq@o5?7o8c4^g_r`F4*)+emNFKEQ(QY zmIPR}PwQ}06p~tw}Kf}G5LOux&4Pa`PoU; zK@YW@zRc1V<>S1sTQCg$z}M;Dc4}CucZ7D0N1UU;jYiFNPq=2^3D{kW2%hb_BMzJ)Gpn}PG9_4(B+;IlNsm3eOOf}J{3l1t zA}J5St1FPi7=W_xJ@ByYROQfq`v|XNtf9@>qR$r^hd%uTrd(0~Ctz*YUhv&5RB;#5 zAzV&`YEGT6!a;rQAhg-3HzLq%!N8JL?1^95A(fK!KD#rrqUW}?xp|?1x$Rp}w5&EX z0esb9DNb_FR5Z$pi}`<^0aZR@VQ|xQ@3j!W@sazcrd}(at+H!4(YPds7j31Uk~uII z&4Vv4y5nve?gs`v4uoz>?Q%7%GFP5D;p()%b121Dx%gG6QVE=AHUTA(`&BuqDfHD- z!rtRkklV0wQ*2!xR6l!vZTe}#{|7l zkkrXP5y7N3e3CKIV_U1Q>+;tBGI-#=B?DLCdkhT-b8L1>d>GMS=uoE;A{e%<)e3&7 z@3aowTuys~(+1qlx!b792YR@Sv=ct;N@w%%i@a;tw!+Az;-cc8DOvpzKdM zDRI-gMq_Tt^CvBDP{_aMf*ybivRCp7(%_-aa!YkrznC*25q^-bO-T8M6G^`zHTAh> zRpW8R#|;J!b*9_XrzQfF245yG0lt@9UUwL7H94vDFd2yzBt6-R!X#mR+|ao=UKk|G zb$Co-!2*}5UDDDYit@&2zLOCu^sFSI)wJgeYcq&LS=J3)8Oo8vz?ZwI84tEHqkR-Y z4y*ISO1b?;a}^TK!qE`EU>khbTa(PErQYSeG8IKhLg~h-3av&c>Q1KJ8iCup^AP{E zs2Sx|$CQNDQrl*_>iH_pGxh1)x+93=P;3R`q9h?Lj5#{JwL4n6m8+?uG29C{3F%jh z@Hz`FH+4QeL4tK!N>pn6HolbqR4f3UiOuTvRRJLVT zeAi)fBw`=vtn2smRCpVXOh5!**c0bZ%kj}Xl6bE=zt2(AL+O7z5J2@f^;zSN<;K;& zeT+J&&X>onPin_ItVFS8(#j( z>`bBG{w<+ID#O7t^p6iD>q=-!wK_F_#ocN@kk^+lO{T_wUAOsqAd0B9CEM^xrB};q zPmof2X%F0uTERy6O;4P~+3V=@)tO;1MDNn+ z$0xa@c4Z_%EBh!;1?7;ow_S?BFMF#&&p?mDNFT~4&7FHBHl$Yy&8dE z|Kd|F)4J5FqgqzWuX-rj-9!%>){pT158QfI0m~dQ{C?1GTg2l!pQe!y*;4Dp-vAHD zmM7q6-UW_Th6wzsBVMP-tq8FNMpynzGgEaK^z!BcPUu8pYU#yM z=o#amApjB#(J4L-Gg_}eT#aCazg5!g;_Hwy94#kVI>!BJq(P9;hIhudZsw#m{>UD3 z9R5U8o@}CZSxqn1p~ZW*8!fE~J=y0D>U;=juiQly#UAb$@6Lnt(Y99cYkN*x9w6C1 z?coT1&Q+|tbb~3aeO0DU1J#CqWQUwp=X8AX;p;mSfv5 zStC&a>U_mL9}@B?AzDx!?w!3dE(a_=LIj5Eb7e)A48z@Myd>e{Hm=(-q5*(F>5!%c5P}2j z2q;#lb3CQe*ey9A$Tz;O(jj8fp}MmDsf#BID}iM-kdq|RPo_uYoQOgVd+M~u5 z37J-g;@%z&C(c|=b}AFU5ne`24V}!3%uqt)GygD@&iuo?K41KSxGfp23P@(?=y+Xbc)(Z!RPkpD#4BP?+B_dGhjPj}{?{CsT9^Y< zJsIJ)jHBZF&^yu7=& zM4zDlwUsa3(bz%XOpTmQt`wUyMB`JU@yJJzJrv)XhPhA^wUT>xf=A7s_zn=(yB7xf ya3*|U@$sTs7U8hy28-;l$cq2hqrr^4=*S(lcUyh(;J#KfIyUz6jcV_&%nQ3*iYbVNbqA1`1S#)EFlb0 zHHm)={$pVZkTR2#gP;b}un^E6K0!eJg+M@pFKGXwKY;0P|Ct_Y2?6~N3s`{~&asDIPDe}UHo^)X<$gQY6K86YRiZESB#Z)jp~WJ>RD>+lxVpD~pQ%iyi!rkHo^+*@2sZ!OhK$-i?Lc-pQPSiHnPifsvVknVAlZpaXi?IUBmu z*#SxaDda!oh?)Y8oh%)kE$!`y{>n8pvUhRjBO&>#=-;1z+Uaa*_Me*Ufd9%CID-s- zpD-}dGcx?I*ua@pwlueJ2L9X1|HkWX=cxg3()j$(m%3kV`9bt z*6>g5|8UPgt>*n}6Ss)Ht%H*(5D3m4KNIgi?EG(O|C!5w;+0K-_BJkmouOuD>CDgk zFP8t2{6D!AoGeYj-u=t=Z!P~d?SJAGE$p4`!6ofvX)I;uZ0ZCy@n78kq40l?_%E{l z?O1LNQzI3y1XEkE8T@~%0`OmT!Pw9a=xlFn?eJG4{okUpu{1Wd1DY~$F|ji-ae?ax z?_c-*vw;4)od2TfueZ5n|G{bRq^2zUf6!s;0<<)ybFeY=u(1R>)47@&JKHaaZ>H~)MGoLE-ofJ#sc%Wev3S|0F#^JA<$2S+d>^=2;^+M|8vR`dh zww$dlQ+$2g?;G!$*3}*ND=~7J?E86QsEW{0g58!uNz?@-KVSi{-wYdxzd?Q?{t?7h zQm3jaCJ0wYdD7i478d4fy3l-rf1O_6O(PF5fD)w68DMBL%3&L3|nwFMC z_@Sw<`jHr*fPsP3GGc1bNi{2dl(;M^Iz@^c0S$;4pP2I!-aVR52FM>gx90 zqtfxikKTRJG`cyl8_vUTEt;Y*k$-qMBhg$toBcFBzjJt`QD`FHiO1_JMn*5bKw079 z)}EG9);yGk*8U=a@#T*+8H5WRgCVd|;#EjEl+=0Skx7{PZJS(EANk(i(Kfa}*jdHT ziCNhbD)69&u)t25>|!v1b7=gYZod25_Z98rD^-Qwe8AfV|KqD3JdIMF`ny|>mG)Oq zdUI0xI?R+g^YF)B&i51RX_ZEl>hsfqQ<$eqVRQU9MEk+EuInzxlDdy=UNB0UI86;@ zl4nKfXWM38>E;hqW^ccxm(qv4H#U`Ub?HT78QH8Ru3b3O9EkQ2dI+rqdvOL&`K0zz zvqECMk5{oP?0RKjR^we-q(G3)Mrc!u`9ay$Yyx9b*$U=y3h^yegmspL*Ow%Tu0%K! zJ?Ec)HVTCIM2NMVMQ#e)oiB6FFCce~1+wBhJigw5J`@IJ?}zYQ*ymzB#6!t958(zP z{P_br>rng6|$}6hmzq4td1qJhVsHKZKZ}b)gWjCt{v?0q|V|!4B+GeTN z$I#Zbet~@B75cm-pd4LYUaM&3M{T})5MDDP>>HT>%p8)?vA+PsqFa7t< zn*;luL;W@!9)0gG#q?H!`m~moM&_Ux9#=p{rhjxO-&o-us|)@jpqHCmIK(75q`~Le z6#)@3xh$3Gfy!_wsAvxvZIQ+>@b2!eq_mW4o1vX{U4Bvr_roWd$7{zkkLwR>iSj$W zr*^`Ik@E3&1RYU8LnHV<#|>VKj)znxYd9UlwX+S(pN#RBa^GI&Ic zo-~Vvis7v>>AK4mc*Bab?AmuTPOofyk(e|fVA{M;Y|46J-jCZ_UJ0uuhVpCydg#JY z=fq>Fk2)8txH15b-G{q7!+{8lme>1L;)5%b2sBq_iy0I0_!oS6uERB;!*(#1e0WXD zZhwz% z_R6C0v1(_=gHO5-v7&-c70h?GyMxhyHdi{M+NEKrqDQ%L+fww$|df)R)29v{=wDEFFW z^2A^ZL&r+6LOR;k1iFSUQSO?2QpOdASeoQd=z31gtO+S{lyf0R3x{6qMXke(G(ulg?Cb^ferCeM#l2iI zj@<@n+YhK|=$fLcAE=F*B{sFxMZ6Qljt>kSZ+?W{+h1B)iO96?Tz0ybgP|wor1*el zc_L!9QN!V<;v@8OywSFOv$!4b;Da#bhCp%-zr9dJoaNFZwsiTM&i(G8OL%xn391uf z$63!br1${Cgs(!oSmYL`tq5Eyz2PIPYim*V-)}M>C*3-Y(^XwlAc@c2MUcx3DHjK! z7bVF83>YmuA}HPN_hOMkdnLuuh80&wdB1Xq;AntUcS`kNu;n5#f9_YvEn&!A!VtbS zR3rMn^>m5z1XADgK9hxdVb<7w`Avn(HrBGwYrBVv{z;lU6S4T>?ZB1P+E>2LWt96VSkUhD$k7l@Kmo`Ps-C@z~ z?W6C5eX((THQIV90$LV!P4rB}#p7P|I+?d>E=Wes%WDMc7*@{t&MWmzQ`^kDQk111I84TWt;q7=* z8J+7=NH8Ad4$GAGy$YR=mO=IMs#i>e4z%GYF-V%nLlW<8FN!NzGz)W5xj zyPJ}-$f{($tM3m`th_L~o}Lgbsd8Vxzc z)K=rw<{P+D{mWchdJ%o5#)}Wirl?_IEyd=3t<%e+I2g$RF+RE4_9VK!!lVShN+LP+ z3cBzi4Q6rPoTnNO;BAC`&Grc!Oq47{{|MZ`J!8UCAg@@yuUME8^*@)LE`(?lqdH6w?1)tBn+-@B5`9!m0nzJ zmm3ua;!1>f1TijRzumgA00Q3eWxn+L^O8Fgl*s#^sm3kYInwRh9U6vMjTMx#%yUfS z9R_oeXrNo%tk7#7@+}e(NT;l(7$J-*3p|4N?dBTV&7s?pD5`rH2MZKV+quFLO&46( z+gswkFJ+jPg~-+t7T96gXrOLsmG}?2L)O6z6e>y;+Kt%R416Jcv74d6&dg#z9S?0CP1`qo{I%o^bw&aqVE1dIAkM>@K;p7MQ zBXsbwJThn$>9%XO(aTjf2s>{)zOZiIDA5R+w+_E_tshWM6u6<}tf%@FeF;yzS;K)& zKIeUtg-r!hM$nL4WB&bQ3gaqgq9Udry^pa8+MWew&URV(Vg@K%yQa2>P7n`C(V|H( z@Qmia?4|(mi#b@+0BjYg(JH~bxF47t)1O#R58bU%vif4oKD{;bzcLE*c`2a|lb*X8 z_zhKf^--8<-D>6~>(RsS6he(kQvq+q#=gg*HJz19exwcerhlqsw^-`G8BSC*d(gUERp4262Gfx< z4fsGIVsNexhU!3XNXFip>yni4zCzI>H6L9wtBqE|KjsAF>Ntd66VR1>1U zrh}y&W>G9a_eT5Oj*H@SM;2eRFR;LcWmo_k_D6j^x~=UwXgW5|J#6llI<-Qus*HdM z>8v3AOs>k(h$*GUH7z6jN9H$R>o%Q9a;3qFQ&KXED;d@o%De7?DM<~OvZVo+_~8V9 zlA0el%Lc)X^AaJi02O$`k#Ul_!?P?ov@d#gh@-NqGBUPP{>fSW$|eML9o4rul;1DH zMh^Q@Q}b}N6+RoL*FzAPMj4hTngB!`gx&0F#d9N8h zqHFSt%v5HE;|wXZC5)8?;NbbyN0r;Zh>~ce~dg(sYd}(IK5y@)PCzLqRosNzU(3sM-g&d8h1YbxRc&P3? zKLygzCXl&F+zJOIp~`8};2B3)Uz)T!`3Ybfg?vuPwUt22%UDWeOQ~t%0xc{AGx+`) zbANx{w7;_q9-eJ(Y!ogXUGPbi5QL=}%kzG_$sGzr<_}9rNg2^MVK$dE2x1%L21ylq z<#{Bq3^IyZLd}Yk@kJT8K+$g2FC)xN4C4tp@uq7;kyi+=&&>EYcbwaTn7?}6pVdlg z*;Fmt>)wQwAj8cNTgzo;zH=f}VLqezV$WRE%Y{u*BxA(E=Jo&XsV zHXS)Uvy$?IU-7hf#LFL381(H>)j(eHZxwQYi41q6)m((=un@l8D3%!O#%^>PxoxX_ z&dScp!stkrSC&WpItnKW+h5by=;4KV+I2>mO`lVXMe2_KzB3!M<&k+W zA?(_(#BDgOp`NLN$##q|%cZ=H@S@xC%gf7aT~5`**jUafu0#h2*|RI9j-6FoD{D2; zSOI+lwUGF!(LI#U%j~lWLh(g4B4IWqt0REkh)>C;$g8WXjw9r$TPNk!ITjeFjzj)_ z00uOn+d#sXZj#@QIz|b_igZZU@?5n5!Wf;^y~bycCBZ(^ATF*4;RUrwJvf?@av&!FeFE&LOanp(8u zAcX5Mm9cgsGQ)jD>P%K#;!4&6^bWmJ4>hh1H26W`m7boSK7ys=4&&qU!|Z%(a$2Q2 zr8wt4iW)MU)bL|I*+f9stw~TBHP<^^2m)Q8!E|U~Y*!eB20AP|h$NfCYc!F06(608YdYH`xSzoj!NRFClu_V0vib zE<;`bKK+-O)hg?d{*48ZkXy$kZ%2O?%jrQFZFVw6*H$$78ColRx<`|dRbI%b-R3sTtg=`XM;8X ziF>fcW+(uckf7uB&_Y}Q5+NT&WohirXh6=?is?bkn0kl$0ZtG+ydP&Irm$m=)wplo zUK^_p30z@IaOxfNsFjoL1$EwWoq7K@Wc`FwH|nSrYGHctof>JF^F#0W5qVM#%jf0| zn_gJJiW=+LOeSaM8h)%x4a+J{q~Y?iefO_s{Se)7+D~;-Aw*5a=^O#(7p+i#9w9_f zOI2`Ww`&q&So#6<1{O-axLS@kx;Bq>Y z&f@w8gwmtY@w)k%eX(SuHazLss73}&`M%1VH(#Tj)=cWy^FDc-1`FoAF7nn#@1@}m zqu&Ch3loj2a8#N0YW)4*d>@z3FB$Dx5?Fv)XkaTetD}oiNNR^mf z0Yj1vYDefH-QCkfWt@{MzfN7e-{Et6r0hLuesNiszita`%^z@E>B=LG9B?Mf8|8Rt zBdv@EeMrBwtC<=Z-+D(^KlRLG7Wea6B$4AJfy=}Y85heQX-^rYmd`%zW7&MB-wg@0 z*b_w&P^*?zuc=dFtfVTHuDhNiImI?^GmR3hwJ0-;oSE*L5cCtJ|0Y+3D#OezwKj)) zl75w|M~Sp88K0NcF3QFP$X7g@+O7`y4qgPTnB`DF}Aw{e79qnlB=@3W?Duk;{X zI&-7j#YU?#rRg7hW-RqOPt~x8))nxY2PntRtnCjYQD;dtKLRE_%nLH{axMEa@Gd zT8El97x(F5c|LkqwMHOKn(PUWKD93cqc2hKM5l# zY|Szuh9nayN6xieQjJQ8BEt{ZaePSd!d;)%g|jefn#~*?d*C0pnYbA^4C@RAGaEwJ zvX-To;!-_{sa$&KK@M$r>;4#t6{7&8F$=F4MDlXpfp+pUXEv4&I~-C2MkQO-AuqAz zp`&8(s&^6`STb`dEQSbb8Z4w0ngTRApIHhDwoGo)QSM|y5R1=hElRp2*X{QRKN7|d z(5ij%5M+ENO(-UvYz6?93W6!Hdj^GiIJXxK#K`Yh%#J(la&0}O?PIhDa)BD^W!d) z2o#mZOyvHzarkios|TIUyrAqpHd?I-ki$>HIjP<-^(12*dJb>Bgz=sRp7Es3DHAi@ zuRad$KEqBkrGxv$2FJ6>4w4CW^o$q|UfXUhD1`XJ_7@OFBB*C8tx)0qA6kHp^C5L5 z)m46rSUYub{A5N-3CU%FM(f z2K@AZ!3m1#6+$tFmKE)F;VjREeL%x+)*xKW9u>{NFR7UcYFmOg%Ofvc1XUk3de*w+ zs(!FdIPytx&98@i)z0bb#Ci`4thN;*#VWD7#C~%D-rK>M5@vnSF%A7Waq+Lh;lsA57+4;1)jI98)R5Zp<2^YGb2@+fI88k+5_{26Iy{)VBy2!1p0 zDREM;ojHg-qrc4o*j=GN=Y{8yu6FNdIng962qk2X)XUy`#N5#)Y=|lMjTR^RD9u#hbYkn-UWhv^ctDIDMuFDsyKRQ)6T24bR@_d9y?@VS+Dydd>L%Qls{n(Kh|GMV_$;naz*(v?Y zS9y~Zlw7jJRCt_EFwdp0IyzjVg>tKd1Ttt+lx1aBimQr|g1UV2{O=PbRsaN6IuY{~ z|G*jAk)u8$0b~;$3(jXFowYj5hmEJHrM7pMs+iyVk?JY)KMoFMgd^G&3sx&V7Xp6n z{dIGW__*Q7RbBO;bLDK1Cw5&(+^7 zu3(Qw=R$MUli3-f?|X`Q=Ig?;Grwl%neQ_{UE9Z%Ur?_6eLXqd4%v8ps@8I@$nd9S zg~%wdD=jub()(&>pygr1Yq>gs!v(}lX`AT8J3?tK9NP&}9bcGPOt?s>Qyb;kxI#Kx zZAOO2q)jstXqr9(_KUUG87A0zT@WFGD!V1#j`6}iX4X_&hs|H+M`SdnZ>;^gg=kuT z0P+Gib+@`{sRQ)pgZp5A;isg_W-cX+crMpgQ$;E+_n=CBCzLLnL6T8DO}&h?gt}N)D)$lvehmNkA&BKgGHqY!6-K6^3Xmf*s9)NQrYz=C{-oR^$(24nvyar?+ zb{hbIBZ}T=Zt{%zawhQ}u{U|>aJ|D#HWxrDcO*M|jU-c@@nV9C+~j>tSgWtuzlSuN zgXpeX;`SUDg4{;XinXfe{mIvHVsp0vp1#*V23yBp+IavcY5QjPd{HkJi+ktD?47NF zD9YUk1<5nsbT_{`_!mBV3=l1RK-o?`o_rqR-I&&J06(RWdI z@>O9jB{L-Xt>ueH&Xpc3x-DV8esG1J)d2L6f;Nl1_a%$I;13USS62jLGQCZ21Q}f_ zeD4=j-rGKVLqnp_UoPdSIA?{WC#KUPc^sDL18=nCfiDycOg)=@dHE`-)j}{D;=b1) z29l;5)2y4kt|8)4!DnnfA5{Db+io6#J2BA?w)p~KaV-h_=44+@AimQMg?-XC^G3(H z@j+kl-=%amcJ`gO=Zm4(@3*8nK6eYD6NZjk_~o){BJ#P*MP z-+4YlWxFY3otTdzciN!&*rjqdiiD*8~hSZDgyuuJmNeXv$Okvx{i`eMT2$is)s z;2s9k;BwOLiICwQqM%!j<}ycFjytq#;O|f7=QWpt77HL~cLTxeB?i>EstMY@cjfv6 zw$1CV*k+cT_pa`TB?3h&5JjGE-yADUj*Je*WF+-CReXrUQe2f+5v6ly_5I=BPoUgA zhynpI#vM`U{B4CFwBI(f-5-ZG2XP{;E&7W}mZ{X3qZzZMJq)KD(ueC;3ix-{53uQX z!*0B5n^@Tm9(Es$fSi87_XD%`d0dfc6){BWe%TqwgUEp82qzGBMJ}j4)gI0=L&Aj9 z##fse%lyf zbr^V_Zu9j^zacUOVHXkbw=8%JkElfrvfEN-aV1Sv@WLw}ERt?1&C#b#)se|Ey+%uz z_#U%kOmWi;eHV&-$kTtN`0$Gbh4%)seGOC0kHuc63Odw$_Hp9t<{-5%E@E<{B-t#2 zk%0?yQF!8W*zp<8=7`H&IW(LpC)p;iQ~H%LLt`mV~Ahd57Z$*Byc^lNGq zq$c0SQ0v{O0r(FrNyp)X=<2MA7y}}Hw)rc7$OByK`dF$^g?R0!%BFMywe%%T!3P7h z?LkahoeqL*d~T^(ZG6Emjq7c-`@i4215R+sj-s6ATFG1;Hl*?$Xi&tCtj}|5zL7d$ z0zn(JDvPEQc`h|wzZ&vT+j6`Jpm<#KQ!?24P^3KUJVu7i(r1N1`4gS$SDkS1>_bSb zDK)@9l{5@Q#NAgZw1MQGu;R%UuVe`0G`|MUBf=5M*CIn&pPWh4x)1&x8GFM%EHu(sebfvQ0cS%endAbim^IVpKDqdf3W@EwX?U!J`)DY~JXf&FTA&OE?HK+sK3|UZZO1$2Rm<8~ zAqzTHZZF<(B|vA6baOt}!K~^>P&Q7M`7OO>0@Om|Js^AcEm)}X#xY(7DraANXetKmuGS=Qj8 zERZNiQ!2P6VX6nC1N|}Y@@y?%Qq{{O=&o7&;kqBc7daezLMEd5VciB_zC_& z19RFZ$&d{Zm@VTLgV7Pqp0Hv{81C|?T)7xtftWZzR*05af zD~I|(DvHp-HC`<^9<0;kR{{H)fQ&Kw^iBfDPP5&k3+uo|g=X4`-DP?L2IH)sB9cIY z3bs!)MRlfliZ@d=_qn#T6hL2N(rk*GxH-qOnT>_xma!{cmEO-CS%OHDz`J_=qhhzE z9t1Rc4)oI2qf*l-$Cc4yGN{~Ok`)gMnVg3)r<|(u*W^6%m2reOb4D?xcP$6{Tl3gq zt+*Qi#p_vDQW4Zt>mSuQ|0Ki3D2g3cFH1dTYyX%c-sdR07%ww2HBp=|Jd*tbFA5&% z0Ez2}DXAj4Um^Mg#)*vx1O_RhioKI)mvbK4;p8zAo~5OdT1Dt72(p(BXUQ=oHd*}> zk|=%7(u{A7m0raxm$S?LTo;lm<;>23^?=dGFU%cukz~&mI0p(&Ei@IfZ}Nj*ro;zU*I^yq$+^4}%@OK3N0cqix(k9c%_4F+ zpBoXfYx48Yb`K3OzCMsnh3eqFU+YOikTwG+m*&XIND>%JPcDwpk=M6Y{)BRFw^(oLKXnh};RA-eI zgUE|*w|JMKZ{%AQh5kIPYR$~GVhJ6ji<9o+7p22u;=@NEF>GUru*gGxJi!V?cy{591Sn{~fdID<|5F{U23p#TI4C#KSplQLS?EBR z6{EV(?|Z6AssTte-1_&)$zb6{X9nD`h$IZ~vcS0H!Q}Fi6nR#Oq9+_J)wD}=(DTjs zl^^6`cEOQSBP?z?4}*l?eYdC|i|lhGlG0Q|86X2iZ`Z%CCzp^aT_^DU^;ExEk|v!p zrg>QO^67;7Y&)_7xXR92-36gb#j)b~NpZCS-~4_5`SLpZJ)|C!gGescDFUuLn;^&e z@s&a~o13^|8*eOYKL~1a`ihD0%KF*Wct8Enc=_0TdwG&orN+rcrfN6QYBSiMPrEkY zSb>GCrXC@`HU{hcn0E@$D@|Yg`uq?`gSDlS`;=5@DkhUI4H$WO5AS-02Jdc!KmTr< z(wfHkpG@O>ro{j;sYTBJpL=*ohEoOEn! zGJG1rzF~e8s7jqZ3=US+J1hHoz)*~poGETHe+;izU+)UlnTb?91~b8gM6a%fVD;h= z&U+n3v9%~z27vjS|Lwf!O&I6>Y5z|-cr1L56FE@hdaebpscAgK9=^}fCZLg1Lr9lQ zd(h~(D_)`BwV~y?cSK528!AFvYJvoBeNwx>aOmi8BLbW2M36eYh06g4ldas?@>u;2 z^R`RFVW8QV*_!eq87FJhJId+^GnKl`jMl=~*em-+R-S|p?mbp~QGD;?Pq(!@kzjtc zW|8-irn0ILFLz>1tv~Qol)wWq9{5`<@{x1d8&{`#>Z5hFB_x-@Tbc(4X1tGEf$Ea0 zCMU&fPtDi7`KAmVx5_eOpvA>lcl<`e|7OC`_~ zIlXT4qMVIdas!RiG0rl@Lw;TD*uS9{>v&__D+Zf&Brk0&#@^Yt;^+17^FG!44vmbZ zYSo+8olQvtwp1_UQzcTA)HMfb2e{C;8$n34#W-)E*f&1cf_C*;2TIZ!EvsJ6+aZ{m z_fnXetkW0iI<|Ca4wngj+}PHebpRrbkJ>8f8u;we+uNJe!l)|lUV$BJPvHJXvKUIu zI*}~PpZheRABFT>*KQekWaT=fQ%sXzO5H7pqG{|NKkry`Y$yNbgq$x?dqcon+^c=f zYrDEd?urhMVzja;sXzEpbj-)U0U>@O5fnh*0Hk#{*M}VA=s?GMTQ{$Do9=P(G65|3 zuMnkHjXUpRap*^80I;aj7e`2y6ueze%-=N97Oe%!t(G9s#L?8L)rP5aR2iV4_Ux&4 zoHHzNB&1NDI9bL8wMQpf|7){!yN^&G_YgS(Q-sg zTKj3eC`f%@3kYRZTot^qNH&Njy%wKqudb1I|Gcq2CE9rgiRf%20ltiGHQ@=wJpKO4 zS;`*d4`^ph5(DxJ*nGx9MqPg3A(?o28I$atoGQb8FO7twkvQNSq#h7DjPKv|To$lB zB^0FA3G8~!-Ry(^^XUwG&jH@p-RBRqmK9V9aPYEwsw))hq62=SRH#AKfsk+Fn)!;+ zmnoSZtrtIL``57Y2=!<}lwd4O$LW5Li~fzU}On?ah= z@aJZv$|+>aJ5Tdg_kqAxL6PSqQ3OE5;b(4-C&a5cG)gkTK)Y+KOR%qC*R<<86 z+s>k=&F&BXuRi#zr#Av<>yxxb#=eo?a9PSzYLF`rTgz?VBlxEHqDAcqg`Ek4kEL80 z>A5ZnVRC|!{V>YA#$JZb=t+ecfN?V4t9XHYpsWY>AoUK^z)qt3Tk%pRE{CK&c)yH> z^s%6q-Y;(#0NkpII6E&ib6~fz(IFCBfE@fMF;A=XI2?+B393!g}<~RI!$p@xsh94xQZ{TzX6X4LAPg3k6%VaPr)QMz5b6K4dvsa$X(F|ir z40`mB&;~Mn{UyHe_^UW zG*;@JjMpNpt{o$dnQpOp-C1aa#9ns8n0VB3!yTJ!gDuwe#=+I7+`S+`5KRQBa;EE& z;sn+UQ*)=2DdD#CW@Y+l5!N%-Mal>y-Nx6$PM4}mT^D&h)&6t-LpCro@8a)eTzZ*#Ac$@So5(s z=RuuT#|yVCwNfxdMh&T;Rh~PwGD$4$n37~0JFN#*4>?;>DCWNkHN>CcR)od4AaP-VXi)(nTMM+%p>5^$z#|72#=dm8A78fBcO~(nHGvFhcNqr zElkoEYc{ID%InT_&x;%N00re$1{Ma}O6o9qY=ECBkRwZnkvnJ0Nu!~-BMM_udEoHK zf?FM2_M(z(XOg^8udcMc1g{3{niklJ18A6lqxFdU!cThz52}+RY$nSRrUFgoaeXYT zk>^Lny|Val8F5Le7e1D zzMtXR(>3=j`1}<|vBJI`7DHK&b4!37F8{X*{{Hw+x%|?D){MtthH&2+F@W_@D!kYc$mLAg;{2iqn8oe1>z`1YDD3eGx zN#T1b_Y_@&%!cHbLc@AQC#;`~Qj4O0ilKh<7mO*YO6=nl1_h|orq$o)4S;O?OUjsR zZHjJG;CQl+aJ_A@>Mvw!8xPzuD+>LO@4i-l%N`Oj-|3<Nc;=c*GFe0GHQN4zCk z=D7u8qH^wdq=^yVg8pvtG5y`*Ln!{^r5az{>@A3G&spqUkcA|bR5Ry)LaQ_P+gkn= z)O2j^o6?!jC#&+b2zQokLhEqMfBC|Ka=3}E@PqD{I6|-$9P2{2n4#4A#UBkE)gt4U z-}c4d2^> { +export default async function createPlugin( + env: PluginEnvironment, +): Promise { return await createRouter({ - database, - identity: IdentityClient.create({ - discovery, - issuer: await discovery.getExternalBaseUrl('auth'), - }), - logger, - permissions, + database: env.database, + discovery: env.discovery, + identity: env.identity, + logger: env.logger, + permissions: env.permissions, }); } diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 20e14312bb..99d18b9b12 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -16,7 +16,6 @@ import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; -import { ComponentType } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; import { ElementType } from 'react'; @@ -53,16 +52,6 @@ import { Theme } from '@material-ui/core/styles'; import { TooltipProps } from '@material-ui/core/Tooltip'; import { WithStyles } from '@material-ui/core/styles'; -// @public (undocumented) -export type ActionItemProps = { - label?: ListItemTextProps['primary']; - secondaryLabel?: ListItemTextProps['secondary']; - icon?: ReactElement; - disabled?: boolean; - onClick?: (event: React_2.MouseEvent) => void; - WrapperComponent?: ComponentType; -}; - // @public (undocumented) export function AlertDisplay(props: AlertDisplayProps): JSX.Element | null; @@ -448,9 +437,18 @@ export function Header(props: PropsWithChildren): JSX.Element; // @public (undocumented) export function HeaderActionMenu(props: HeaderActionMenuProps): JSX.Element; +// @public (undocumented) +export type HeaderActionMenuItem = { + label?: ListItemTextProps['primary']; + secondaryLabel?: ListItemTextProps['secondary']; + icon?: ReactElement; + disabled?: boolean; + onClick?: (event: React_2.MouseEvent) => void; +}; + // @public (undocumented) export type HeaderActionMenuProps = { - actionItems: ActionItemProps[]; + actionItems: HeaderActionMenuItem[]; }; // @public (undocumented) @@ -1357,7 +1355,7 @@ export function Table(props: TableProps): JSX.Element; // @public (undocumented) export namespace Table { var // (undocumented) - tableIcons: Readonly; + icons: Readonly; } // Warning: (ae-missing-release-tag) "TableClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index b04c247b2f..49538b680a 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -523,4 +523,4 @@ export function Table(props: TableProps) { ); } -Table.tableIcons = Object.freeze(tableIcons); +Table.icons = Object.freeze(tableIcons); diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx index 3337571d4e..92ce241aa8 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx @@ -60,7 +60,7 @@ describe('', () => { ); }); - it('Test wrapper, and secondary label', async () => { + it('Secondary label', async () => { const onClickFunction = jest.fn(); const rendered = await renderInTestApp( ', () => { { label: 'Some label', secondaryLabel: 'Secondary label', - WrapperComponent: ({ children }) => ( - - ), + onClick: onClickFunction, }, ]} />, diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx index 125363fe0a..e164244ea7 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { Fragment, ReactElement, ComponentType } from 'react'; +import React, { Fragment, ReactElement } from 'react'; import IconButton from '@material-ui/core/IconButton'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; @@ -28,13 +28,12 @@ import MoreVert from '@material-ui/icons/MoreVert'; /** * @public */ -export type ActionItemProps = { +export type HeaderActionMenuItem = { label?: ListItemTextProps['primary']; secondaryLabel?: ListItemTextProps['secondary']; icon?: ReactElement; disabled?: boolean; - onClick?: (event: React.MouseEvent) => void; - WrapperComponent?: ComponentType; + onClick?: (event: React.MouseEvent) => void; }; const ActionItem = ({ @@ -43,10 +42,9 @@ const ActionItem = ({ icon, disabled = false, onClick, - WrapperComponent = React.Fragment, -}: ActionItemProps) => { +}: HeaderActionMenuItem) => { return ( - + {icon}} - + ); }; @@ -68,7 +66,7 @@ const ActionItem = ({ * @public */ export type HeaderActionMenuProps = { - actionItems: ActionItemProps[]; + actionItems: HeaderActionMenuItem[]; }; /** diff --git a/packages/core-components/src/layout/HeaderActionMenu/index.ts b/packages/core-components/src/layout/HeaderActionMenu/index.ts index 6fa74d9d09..4fd9552fa7 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/index.ts +++ b/packages/core-components/src/layout/HeaderActionMenu/index.ts @@ -16,6 +16,6 @@ export { HeaderActionMenu } from './HeaderActionMenu'; export type { - ActionItemProps, + HeaderActionMenuItem, HeaderActionMenuProps, } from './HeaderActionMenu'; diff --git a/plugins/playlist-backend/README.md b/plugins/playlist-backend/README.md index 75e0281a08..14a39ca741 100644 --- a/plugins/playlist-backend/README.md +++ b/plugins/playlist-backend/README.md @@ -21,20 +21,15 @@ import { createRouter } from '@backstage/plugin-playlist-backend'; import { Router } from 'express'; import { PluginEnvironment } from '../types'; -export default async function createPlugin({ - database, - discovery, - logger, - permissions, -}: PluginEnvironment): Promise { +export default async function createPlugin( + env: PluginEnvironment, +): Promise { return await createRouter({ - database, - identity: IdentityClient.create({ - discovery, - issuer: await discovery.getExternalBaseUrl('auth'), - }), - logger, - permissions, + database: env.database, + discovery: env.discovery, + identity: env.identity, + logger: env.logger, + permissions: env.permissions, }); } ``` diff --git a/plugins/playlist-backend/api-report.md b/plugins/playlist-backend/api-report.md index 5c86b6ae45..c7109efbfb 100644 --- a/plugins/playlist-backend/api-report.md +++ b/plugins/playlist-backend/api-report.md @@ -7,7 +7,7 @@ import { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; import { ConditionalPolicyDecision } from '@backstage/plugin-permission-common'; import { Conditions } from '@backstage/plugin-permission-node'; import express from 'express'; -import { IdentityClient } from '@backstage/plugin-auth-node'; +import { IdentityApi } from '@backstage/plugin-auth-node'; import { Logger } from 'winston'; import { Permission } from '@backstage/plugin-permission-common'; import { PermissionCondition } from '@backstage/plugin-permission-common'; @@ -17,6 +17,7 @@ import { PermissionPolicy } from '@backstage/plugin-permission-node'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; import { PluginDatabaseManager } from '@backstage/backend-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PolicyDecision } from '@backstage/plugin-permission-common'; import { PolicyQuery } from '@backstage/plugin-permission-node'; import { ResourcePermission } from '@backstage/plugin-permission-common'; @@ -84,7 +85,9 @@ export interface RouterOptions { // (undocumented) database: PluginDatabaseManager; // (undocumented) - identity: IdentityClient; + discovery: PluginEndpointDiscovery; + // (undocumented) + identity: IdentityApi; // (undocumented) logger: Logger; // (undocumented) diff --git a/plugins/playlist-backend/migrations/20220701011329_init.js b/plugins/playlist-backend/migrations/20220701011329_init.js index 17a71e8438..be08363dec 100644 --- a/plugins/playlist-backend/migrations/20220701011329_init.js +++ b/plugins/playlist-backend/migrations/20220701011329_init.js @@ -37,7 +37,9 @@ exports.up = async function up(knex) { .onDelete('CASCADE') .comment('The id of the playlist this entity belongs to'); table.string('entity_ref').notNullable().comment('A entity ref'); - table.unique(['playlist_id', 'entity_ref']); + table.unique(['playlist_id', 'entity_ref'], { + indexName: 'playlist_entity_composite_index', + }); }); await knex.schema.createTable('followers', table => { @@ -49,7 +51,9 @@ exports.up = async function up(knex) { .onDelete('CASCADE') .comment('The id of the playlist being followed'); table.string('user_ref').notNullable().comment('A user entity ref'); - table.unique(['playlist_id', 'user_ref']); + table.unique(['playlist_id', 'user_ref'], { + indexName: 'playlist_follower_composite_index', + }); }); }; diff --git a/plugins/playlist-backend/package.json b/plugins/playlist-backend/package.json index bbea8163fb..b760611548 100644 --- a/plugins/playlist-backend/package.json +++ b/plugins/playlist-backend/package.json @@ -22,13 +22,15 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.2", - "@backstage/backend-test-utils": "^0.1.28-next.2", - "@backstage/config": "^1.0.1", - "@backstage/errors": "^1.1.0", - "@backstage/plugin-auth-node": "^0.2.5-next.2", - "@backstage/plugin-permission-common": "^0.6.4-next.1", - "@backstage/plugin-permission-node": "^0.6.5-next.2", + "@backstage/backend-common": "^0.15.1-next.3", + "@backstage/backend-test-utils": "^0.1.28-next.3", + "@backstage/catalog-client": "1.1.0-next.2", + "@backstage/catalog-model": "^1.1.1-next.0", + "@backstage/config": "^1.0.2-next.0", + "@backstage/errors": "^1.1.1-next.0", + "@backstage/plugin-auth-node": "^0.2.5-next.3", + "@backstage/plugin-permission-common": "^0.6.4-next.2", + "@backstage/plugin-permission-node": "^0.6.5-next.3", "@backstage/plugin-playlist-common": "^0.0.0", "@types/express": "*", "express": "^4.17.1", @@ -40,7 +42,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.2", + "@backstage/cli": "^0.19.0-next.3", "@types/supertest": "^2.0.8", "msw": "^0.47.0", "supertest": "^6.1.3" diff --git a/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts b/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts index bff14b2a58..a746286136 100644 --- a/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts +++ b/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts @@ -73,7 +73,7 @@ export function parseListPlaylistsFilterString( return undefined; } - const filtersByKey: Record = {}; + const filtersByKey = new Map(); for (const statement of statements) { const equalsIndex = statement.indexOf('='); @@ -89,13 +89,12 @@ export function parseListPlaylistsFilterString( ); } - const f = - key in filtersByKey - ? filtersByKey[key] - : (filtersByKey[key] = { key, values: [] }); + const f = filtersByKey.has(key) + ? filtersByKey.get(key) + : filtersByKey.set(key, { key, values: [] }).get(key); - f.values.push(value); + f!.values.push(value); } - return Object.values(filtersByKey); + return [...filtersByKey.values()]; } diff --git a/plugins/playlist-backend/src/service/router.test.ts b/plugins/playlist-backend/src/service/router.test.ts index 06185acae8..3f90a8b8af 100644 --- a/plugins/playlist-backend/src/service/router.test.ts +++ b/plugins/playlist-backend/src/service/router.test.ts @@ -14,9 +14,13 @@ * limitations under the License. */ -import { DatabaseManager, getVoidLogger } from '@backstage/backend-common'; +import { + DatabaseManager, + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; -import { IdentityClient } from '@backstage/plugin-auth-node'; +import { IdentityApi } from '@backstage/plugin-auth-node'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { permissions } from '@backstage/plugin-playlist-common'; import express from 'express'; @@ -24,8 +28,39 @@ import request from 'supertest'; import { createRouter } from './router'; +const sampleEntities = [ + { + kind: 'system', + metadata: { + namespace: 'default', + name: 'test-ent-system', + title: 'Test Ent', + description: 'test ent description', + }, + }, + { + kind: 'component', + metadata: { + namespace: 'default', + name: 'test-ent', + title: 'Test Ent 2', + description: 'test ent description 2', + }, + spec: { + type: 'library', + }, + }, +]; +const mockGetEntties = jest + .fn() + .mockImplementation(async () => ({ items: sampleEntities })); +jest.mock('@backstage/catalog-client', () => ({ + CatalogClient: jest + .fn() + .mockImplementation(() => ({ getEntities: mockGetEntties })), +})); + jest.mock('@backstage/plugin-auth-node', () => ({ - ...jest.requireActual('@backstage/plugin-auth-node'), getBearerTokenFromAuthorizationHeader: () => 'token', })); @@ -99,14 +134,20 @@ describe('createRouter', () => { userEntityRef: 'user:default/me', }; const mockIdentityClient = { - authenticate: jest + getIdentity: jest .fn() .mockImplementation(async () => ({ identity: mockUser })), - } as unknown as IdentityClient; + } as unknown as IdentityApi; + + const discovery: jest.Mocked = { + getBaseUrl: jest.fn(), + getExternalBaseUrl: jest.fn(), + }; beforeEach(async () => { const router = await createRouter({ database: createDatabase(), + discovery, identity: mockIdentityClient, logger: getVoidLogger(), permissions: mockPermissionEvaluator, @@ -398,6 +439,7 @@ describe('createRouter', () => { { token: 'token' }, ); expect(mockDbHandler.getPlaylistEntities).not.toHaveBeenCalled(); + expect(mockGetEntties).not.toHaveBeenCalled(); expect(response.status).toEqual(403); }); @@ -406,8 +448,25 @@ describe('createRouter', () => { expect(mockDbHandler.getPlaylistEntities).toHaveBeenCalledWith( 'playlist-id', ); + expect(mockGetEntties).toHaveBeenCalledWith( + { + filter: [ + { + kind: 'component', + 'metadata.namespace': 'default', + 'metadata.name': 'test-ent', + }, + { + kind: 'system', + 'metadata.namespace': 'default', + 'metadata.name': 'test-ent-system', + }, + ], + }, + { token: 'token' }, + ); expect(response.status).toEqual(200); - expect(response.body).toEqual(mockEntities); + expect(response.body).toEqual(sampleEntities); }); }); diff --git a/plugins/playlist-backend/src/service/router.ts b/plugins/playlist-backend/src/service/router.ts index 7ca41c0ac1..bf0b789b7a 100644 --- a/plugins/playlist-backend/src/service/router.ts +++ b/plugins/playlist-backend/src/service/router.ts @@ -14,11 +14,17 @@ * limitations under the License. */ -import { errorHandler, PluginDatabaseManager } from '@backstage/backend-common'; +import { + errorHandler, + PluginDatabaseManager, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { CatalogClient } from '@backstage/catalog-client'; +import { parseEntityRef } from '@backstage/catalog-model'; import { NotAllowedError } from '@backstage/errors'; import { getBearerTokenFromAuthorizationHeader, - IdentityClient, + IdentityApi, } from '@backstage/plugin-auth-node'; import { AuthorizePermissionRequest, @@ -44,7 +50,8 @@ import { parseListPlaylistsFilterParams } from './ListPlaylistsFilter'; */ export interface RouterOptions { database: PluginDatabaseManager; - identity: IdentityClient; + discovery: PluginEndpointDiscovery; + identity: IdentityApi; logger: Logger; permissions: PermissionEvaluator; } @@ -57,6 +64,7 @@ export async function createRouter( ): Promise { const { database, + discovery, identity, logger, permissions: permissionEvaluator, @@ -64,19 +72,23 @@ export async function createRouter( logger.info('Initializing Playlist backend'); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); const db = await database.getClient(); const dbHandler = await DatabaseHandler.create({ database: db }); const evaluateRequestPermission = async ( - req: express.Request, + request: express.Request, permission: AuthorizePermissionRequest | QueryPermissionRequest, conditional: boolean = false, ) => { const token = getBearerTokenFromAuthorizationHeader( - req.header('authorization'), + request.header('authorization'), ); - const user = await identity.authenticate(token); + const user = await identity.getIdentity({ request }); + if (!user) { + throw new NotAllowedError('Unauthorized'); + } const decision = conditional ? ( @@ -196,7 +208,36 @@ export async function createRouter( permission: permissions.playlistListRead, resourceRef: req.params.playlistId, }); - const entities = await dbHandler.getPlaylistEntities(req.params.playlistId); + + const entityRefs = await dbHandler.getPlaylistEntities( + req.params.playlistId, + ); + if (!entityRefs.length) { + res.json([]); + return; + } + + const filter = entityRefs.map(ref => { + const compoundRef = parseEntityRef(ref); + return { + kind: compoundRef.kind, + 'metadata.namespace': compoundRef.namespace, + 'metadata.name': compoundRef.name, + }; + }); + + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + + // TODO(kuanpg): entities in this playlist that no longer exist in the catalog will be + // excluded from this response, we need a way to clean up these orphaned refs potentially + // via catalog events (https://github.com/backstage/backstage/issues/8219) + // + // Note: This will also enforce catalog permissions and will only return entities for which the current user has access to + const entities = (await catalogClient.getEntities({ filter }, { token })) + .items; + res.json(entities); }); diff --git a/plugins/playlist-backend/src/service/standaloneServer.ts b/plugins/playlist-backend/src/service/standaloneServer.ts index 46d6f7ad8d..3b6acfd09b 100644 --- a/plugins/playlist-backend/src/service/standaloneServer.ts +++ b/plugins/playlist-backend/src/service/standaloneServer.ts @@ -23,7 +23,7 @@ import { useHotMemoize, } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; -import { IdentityClient } from '@backstage/plugin-auth-node'; +import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; import { ServerPermissionClient } from '@backstage/plugin-permission-node'; import { Server } from 'http'; import { Logger } from 'winston'; @@ -53,7 +53,7 @@ export async function startStandaloneServer( return manager.forPlugin('playlist'); }); - const identity = IdentityClient.create({ + const identity = DefaultIdentityClient.create({ discovery, issuer: await discovery.getExternalBaseUrl('auth'), }); @@ -69,6 +69,7 @@ export async function startStandaloneServer( logger.debug('Starting application server...'); const router = await createRouter({ database, + discovery, identity, logger, permissions, diff --git a/plugins/playlist-common/package.json b/plugins/playlist-common/package.json index 67a97b1237..b9e8637f17 100644 --- a/plugins/playlist-common/package.json +++ b/plugins/playlist-common/package.json @@ -23,10 +23,10 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/plugin-permission-common": "^0.6.4-next.1" + "@backstage/plugin-permission-common": "^0.6.4-next.2" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.2" + "@backstage/cli": "^0.19.0-next.3" }, "files": [ "dist" diff --git a/plugins/playlist/api-report.md b/plugins/playlist/api-report.md index ddfec2948d..7a732a4a53 100644 --- a/plugins/playlist/api-report.md +++ b/plugins/playlist/api-report.md @@ -8,16 +8,16 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; import { FetchApi } from '@backstage/core-plugin-api'; import { Playlist } from '@backstage/plugin-playlist-common'; import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; import { RouteRef } from '@backstage/core-plugin-api'; // @public (undocumented) -export const EntityPlaylistDialog: ({ - open, - onClose, -}: EntityPlaylistDialogProps) => JSX.Element; +export const EntityPlaylistDialog: ( + props: EntityPlaylistDialogProps, +) => JSX.Element; // @public (undocumented) export type EntityPlaylistDialogProps = { @@ -49,7 +49,7 @@ export interface PlaylistApi { // (undocumented) getPlaylist(playlistId: string): Promise; // (undocumented) - getPlaylistEntities(playlistId: string): Promise; + getPlaylistEntities(playlistId: string): Promise; // (undocumented) removePlaylistEntities( playlistId: string, @@ -80,7 +80,7 @@ export class PlaylistClient implements PlaylistApi { // (undocumented) getPlaylist(playlistId: string): Promise; // (undocumented) - getPlaylistEntities(playlistId: string): Promise; + getPlaylistEntities(playlistId: string): Promise; // (undocumented) removePlaylistEntities( playlistId: string, diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index 51cffcca9f..f59fce18db 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -22,14 +22,14 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.0", - "@backstage/core-components": "^0.11.1-next.2", - "@backstage/core-plugin-api": "^1.0.6-next.2", - "@backstage/errors": "^1.1.0", + "@backstage/catalog-model": "^1.1.1-next.0", + "@backstage/core-components": "^0.11.1-next.3", + "@backstage/core-plugin-api": "^1.0.6-next.3", + "@backstage/errors": "^1.1.1-next.0", "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.1", - "@backstage/plugin-permission-common": "^0.6.4-next.1", - "@backstage/plugin-permission-react": "^0.4.5-next.1", + "@backstage/plugin-catalog-react": "^1.1.4-next.2", + "@backstage/plugin-permission-common": "^0.6.4-next.2", + "@backstage/plugin-permission-react": "^0.4.5-next.2", "@backstage/plugin-playlist-common": "^0.0.0", "@backstage/plugin-search-react": "^1.1.0-next.2", "@backstage/theme": "^0.2.16", @@ -47,15 +47,14 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.2", - "@backstage/core-app-api": "^1.1.0-next.2", - "@backstage/dev-utils": "^1.0.6-next.1", - "@backstage/test-utils": "^1.2.0-next.2", + "@backstage/cli": "^0.19.0-next.3", + "@backstage/core-app-api": "^1.1.0-next.3", + "@backstage/dev-utils": "^1.0.6-next.2", + "@backstage/test-utils": "^1.2.0-next.3", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", "@testing-library/user-event": "^14.0.0", - "@types/jest": "*", "@types/node": "*", "cross-fetch": "^3.1.5", "msw": "^0.47.0", diff --git a/plugins/playlist/src/api/PlaylistApi.ts b/plugins/playlist/src/api/PlaylistApi.ts index d0b87ec28a..9a9da1badb 100644 --- a/plugins/playlist/src/api/PlaylistApi.ts +++ b/plugins/playlist/src/api/PlaylistApi.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { createApiRef } from '@backstage/core-plugin-api'; import { Playlist, PlaylistMetadata } from '@backstage/plugin-playlist-common'; @@ -62,7 +63,7 @@ export interface PlaylistApi { addPlaylistEntities(playlistId: string, entityRefs: string[]): Promise; - getPlaylistEntities(playlistId: string): Promise; + getPlaylistEntities(playlistId: string): Promise; removePlaylistEntities( playlistId: string, diff --git a/plugins/playlist/src/api/PlaylistClient.test.ts b/plugins/playlist/src/api/PlaylistClient.test.ts index f3b62c19ac..20d4a0bcc8 100644 --- a/plugins/playlist/src/api/PlaylistClient.test.ts +++ b/plugins/playlist/src/api/PlaylistClient.test.ts @@ -215,7 +215,29 @@ describe('PlaylistClient', () => { }); it('getPlaylistEntities', async () => { - const entities = ['component:default/ent1', 'component:default/ent2']; + const entities = [ + { + kind: 'system', + metadata: { + namespace: 'default', + name: 'test-ent', + title: 'Test Ent', + description: 'test ent description', + }, + }, + { + kind: 'component', + metadata: { + namespace: 'foo', + name: 'test-ent2', + title: 'Test Ent 2', + description: 'test ent description 2', + }, + spec: { + type: 'library', + }, + }, + ]; server.use( rest.get(`${mockBaseUrl}/id/entities`, (_, res, ctx) => diff --git a/plugins/playlist/src/api/PlaylistClient.ts b/plugins/playlist/src/api/PlaylistClient.ts index b77aa79733..425b185de8 100644 --- a/plugins/playlist/src/api/PlaylistClient.ts +++ b/plugins/playlist/src/api/PlaylistClient.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; import { Playlist, PlaylistMetadata } from '@backstage/plugin-playlist-common'; @@ -142,7 +143,7 @@ export class PlaylistClient implements PlaylistApi { } } - async getPlaylistEntities(playlistId: string): Promise { + async getPlaylistEntities(playlistId: string): Promise { const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); const resp = await this.fetchApi.fetch( `${baseUrl}/${playlistId}/entities`, diff --git a/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.tsx b/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.tsx index 474a9abb1f..247f737f3f 100644 --- a/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.tsx +++ b/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.tsx @@ -78,10 +78,9 @@ export type EntityPlaylistDialogProps = { onClose: () => void; }; -export const EntityPlaylistDialog = ({ - open, - onClose, -}: EntityPlaylistDialogProps) => { +export const EntityPlaylistDialog = (props: EntityPlaylistDialogProps) => { + const { open, onClose } = props; + const classes = useStyles(); const navigate = useNavigate(); const { entity } = useAsyncEntity(); diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx index 3dfd89fda9..f7720b25c1 100644 --- a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx @@ -15,11 +15,7 @@ */ import { ErrorApi, errorApiRef } from '@backstage/core-plugin-api'; -import { - CatalogApi, - catalogApiRef, - entityRouteRef, -} from '@backstage/plugin-catalog-react'; +import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { PermissionApi, @@ -47,17 +43,6 @@ jest.mock('./AddEntitiesDrawer', () => ({ describe('PlaylistEntitiesTable', () => { const errorApi: Partial = { post: jest.fn() }; - const playlistApi: Partial = { - getPlaylistEntities: jest - .fn() - .mockImplementation(async () => [ - 'system:default/test-ent', - 'component:foo/test-ent2', - ]), - addPlaylistEntities: jest.fn().mockImplementation(async () => {}), - removePlaylistEntities: jest.fn().mockImplementation(async () => {}), - }; - const sampleEntities = [ { kind: 'system', @@ -81,10 +66,12 @@ describe('PlaylistEntitiesTable', () => { }, }, ]; - const catalogApi: Partial = { - getEntities: jest + const playlistApi: Partial = { + getPlaylistEntities: jest .fn() - .mockImplementation(async () => ({ items: sampleEntities })), + .mockImplementation(async () => sampleEntities), + addPlaylistEntities: jest.fn().mockImplementation(async () => {}), + removePlaylistEntities: jest.fn().mockImplementation(async () => {}), }; const mockAuthorize = jest @@ -97,7 +84,6 @@ describe('PlaylistEntitiesTable', () => { new Map() }}> { const rendered = await render(); expect(playlistApi.getPlaylistEntities).toHaveBeenCalledWith('playlist-id'); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: [ - { - kind: 'system', - 'metadata.namespace': 'default', - 'metadata.name': 'test-ent', - }, - { - kind: 'component', - 'metadata.namespace': 'foo', - 'metadata.name': 'test-ent2', - }, - ], - fields: [ - 'kind', - 'metadata.namespace', - 'metadata.name', - 'metadata.title', - 'metadata.description', - 'spec.type', - ], - }); expect(rendered.getByText('Test Ent')).toBeInTheDocument(); expect(rendered.getByText('system')).toBeInTheDocument(); diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx index 0fa78b19bd..f499f59a61 100644 --- a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - Entity, - parseEntityRef, - stringifyEntityRef, -} from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { ErrorPanel, SubvalueCell, @@ -26,7 +22,7 @@ import { TableFilter, } from '@backstage/core-components'; import { errorApiRef, useApi } from '@backstage/core-plugin-api'; -import { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react'; +import { EntityRefLink } from '@backstage/plugin-catalog-react'; import { usePermission } from '@backstage/plugin-permission-react'; import { permissions } from '@backstage/plugin-playlist-common'; import AddBoxIcon from '@material-ui/icons/AddBox'; @@ -43,7 +39,6 @@ export const PlaylistEntitiesTable = ({ }: { playlistId: string; }) => { - const catalogApi = useApi(catalogApiRef); const errorApi = useApi(errorApiRef); const playlistApi = useApi(playlistApiRef); const [openAddEntitiesDrawer, setOpenAddEntitiesDrawer] = useState(false); @@ -53,39 +48,10 @@ export const PlaylistEntitiesTable = ({ resourceRef: playlistId, }); - const [{ value: entities, loading, error }, loadEntities] = - useAsyncFn(async () => { - const entityRefs = await playlistApi.getPlaylistEntities(playlistId); - if (!entityRefs.length) { - return []; - } - - const filter = entityRefs.map(ref => { - const compoundRef = parseEntityRef(ref); - return { - kind: compoundRef.kind, - 'metadata.namespace': compoundRef.namespace, - 'metadata.name': compoundRef.name, - }; - }); - - // TODO(kuanpg): entities in this playlist that no longer exist in the catalog will be - // excluded from this response, we need a way to clean up these orphaned refs potentially - // via catalog events (https://github.com/backstage/backstage/issues/8219) - return ( - await catalogApi.getEntities({ - filter, - fields: [ - 'kind', - 'metadata.namespace', - 'metadata.name', - 'metadata.title', - 'metadata.description', - 'spec.type', - ], - }) - ).items; - }, [catalogApi, playlistApi]); + const [{ value: entities, loading, error }, loadEntities] = useAsyncFn( + () => playlistApi.getPlaylistEntities(playlistId), + [playlistApi], + ); useEffect(() => { loadEntities(); @@ -190,7 +156,7 @@ export const PlaylistEntitiesTable = ({ data={entities ?? []} filters={filters} icons={{ - ...Table.tableIcons, + ...Table.icons, Search: forwardRef((props, ref) => ( )), diff --git a/yarn.lock b/yarn.lock index d55434a22a..02659263e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3010,7 +3010,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-client@^1.1.0-next.2, @backstage/catalog-client@workspace:packages/catalog-client": +"@backstage/catalog-client@1.1.0-next.2, @backstage/catalog-client@^1.1.0-next.2, @backstage/catalog-client@workspace:packages/catalog-client": version: 0.0.0-use.local resolution: "@backstage/catalog-client@workspace:packages/catalog-client" dependencies: @@ -6222,6 +6222,85 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-playlist-backend@^0.0.0, @backstage/plugin-playlist-backend@workspace:plugins/playlist-backend": + version: 0.0.0-use.local + resolution: "@backstage/plugin-playlist-backend@workspace:plugins/playlist-backend" + dependencies: + "@backstage/backend-common": ^0.15.1-next.3 + "@backstage/backend-test-utils": ^0.1.28-next.3 + "@backstage/catalog-client": 1.1.0-next.2 + "@backstage/catalog-model": ^1.1.1-next.0 + "@backstage/cli": ^0.19.0-next.3 + "@backstage/config": ^1.0.2-next.0 + "@backstage/errors": ^1.1.1-next.0 + "@backstage/plugin-auth-node": ^0.2.5-next.3 + "@backstage/plugin-permission-common": ^0.6.4-next.2 + "@backstage/plugin-permission-node": ^0.6.5-next.3 + "@backstage/plugin-playlist-common": ^0.0.0 + "@types/express": "*" + "@types/supertest": ^2.0.8 + express: ^4.17.1 + express-promise-router: ^4.1.0 + knex: ^2.0.0 + msw: ^0.47.0 + node-fetch: ^2.6.7 + supertest: ^6.1.3 + uuid: ^8.2.0 + winston: ^3.2.1 + yn: ^4.0.0 + languageName: unknown + linkType: soft + +"@backstage/plugin-playlist-common@^0.0.0, @backstage/plugin-playlist-common@workspace:plugins/playlist-common": + version: 0.0.0-use.local + resolution: "@backstage/plugin-playlist-common@workspace:plugins/playlist-common" + dependencies: + "@backstage/cli": ^0.19.0-next.3 + "@backstage/plugin-permission-common": ^0.6.4-next.2 + languageName: unknown + linkType: soft + +"@backstage/plugin-playlist@^0.0.0, @backstage/plugin-playlist@workspace:plugins/playlist": + version: 0.0.0-use.local + resolution: "@backstage/plugin-playlist@workspace:plugins/playlist" + dependencies: + "@backstage/catalog-model": ^1.1.1-next.0 + "@backstage/cli": ^0.19.0-next.3 + "@backstage/core-app-api": ^1.1.0-next.3 + "@backstage/core-components": ^0.11.1-next.3 + "@backstage/core-plugin-api": ^1.0.6-next.3 + "@backstage/dev-utils": ^1.0.6-next.2 + "@backstage/errors": ^1.1.1-next.0 + "@backstage/plugin-catalog-common": ^1.0.6-next.0 + "@backstage/plugin-catalog-react": ^1.1.4-next.2 + "@backstage/plugin-permission-common": ^0.6.4-next.2 + "@backstage/plugin-permission-react": ^0.4.5-next.2 + "@backstage/plugin-playlist-common": ^0.0.0 + "@backstage/plugin-search-react": ^1.1.0-next.2 + "@backstage/test-utils": ^1.2.0-next.3 + "@backstage/theme": ^0.2.16 + "@material-ui/core": ^4.9.13 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": ^4.0.0-alpha.57 + "@testing-library/jest-dom": ^5.10.1 + "@testing-library/react": ^12.1.3 + "@testing-library/react-hooks": ^8.0.0 + "@testing-library/user-event": ^14.0.0 + "@types/node": "*" + cross-fetch: ^3.1.5 + lodash: ^4.17.21 + msw: ^0.47.0 + qs: ^6.9.4 + react-hook-form: ^7.13.0 + react-use: ^17.2.4 + swr: ^1.1.2 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-router: 6.0.0-beta.0 || ^6.3.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + languageName: unknown + linkType: soft + "@backstage/plugin-proxy-backend@^0.2.30-next.2, @backstage/plugin-proxy-backend@workspace:plugins/proxy-backend": version: 0.0.0-use.local resolution: "@backstage/plugin-proxy-backend@workspace:plugins/proxy-backend" @@ -22470,6 +22549,7 @@ __metadata: "@backstage/plugin-org": ^0.5.9-next.3 "@backstage/plugin-pagerduty": 0.5.2-next.3 "@backstage/plugin-permission-react": ^0.4.5-next.2 + "@backstage/plugin-playlist": ^0.0.0 "@backstage/plugin-rollbar": ^0.4.9-next.3 "@backstage/plugin-scaffolder": ^1.6.0-next.3 "@backstage/plugin-search": ^1.0.2-next.3 @@ -22557,6 +22637,7 @@ __metadata: "@backstage/plugin-permission-backend": ^0.5.11-next.2 "@backstage/plugin-permission-common": ^0.6.4-next.2 "@backstage/plugin-permission-node": ^0.6.5-next.3 + "@backstage/plugin-playlist-backend": ^0.0.0 "@backstage/plugin-proxy-backend": ^0.2.30-next.2 "@backstage/plugin-rollbar-backend": ^0.1.33-next.3 "@backstage/plugin-scaffolder-backend": ^1.6.0-next.3