@metabase/cli 0.4.1-alpha.fix-transform-test-ee-path.b41b46c → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-collection-CcjydN8e.mjs → add-collection-COtKcy6Y.mjs} +4 -4
- package/dist/{alert-DBTU8iHe.mjs → alert-DMlSjPEv.mjs} +7 -7
- package/dist/{alerts-DsgnumqY.mjs → alerts-s3al16NZ.mjs} +6 -6
- package/dist/{append-DUFn3_Og.mjs → append-CbGmdeLw.mjs} +4 -4
- package/dist/{archive-CWs273rN.mjs → archive-5EY9UJU_.mjs} +3 -3
- package/dist/{archive-Bxl1oeJr.mjs → archive-BFmx4B91.mjs} +3 -3
- package/dist/{archive-CcPTctIo.mjs → archive-BZCYIBhQ.mjs} +3 -3
- package/dist/{archive-sfqadV_a.mjs → archive-C_UHeanE.mjs} +3 -3
- package/dist/{archive-CC6THE3k.mjs → archive-Ce4eArrs.mjs} +3 -3
- package/dist/{archive-CI-WE7dA.mjs → archive-CnDriekB.mjs} +5 -5
- package/dist/{archive-DVgglC0F.mjs → archive-DDO2XX0E.mjs} +4 -4
- package/dist/{archive-DjQkD1WI.mjs → archive-DW7x0IFB.mjs} +4 -4
- package/dist/{archive-BEzfnrwM.mjs → archive-DvbaSglr.mjs} +3 -3
- package/dist/{archive-BTfOiOnx.mjs → archive-DzSdJVqE.mjs} +5 -5
- package/dist/{archive-jwyopr47.mjs → archive-a05W75XH.mjs} +3 -3
- package/dist/{auth-IQKgyb_x.mjs → auth-DOz7KW5E.mjs} +5 -5
- package/dist/{body-CT1cQrhp.mjs → body-Di7940x_.mjs} +2 -3
- package/dist/{branch-flag-Czh8JT1G.mjs → branch-flag-CfJ8vwgu.mjs} +1 -1
- package/dist/{branches-ByUeNOBP.mjs → branches-BC-vlk6i.mjs} +3 -3
- package/dist/{breaking-BKR88YVq.mjs → breaking-ByrjsFjq.mjs} +6 -6
- package/dist/{broken-DeS3_Cg2.mjs → broken-DLSk1T88.mjs} +8 -8
- package/dist/{bulk-discard-values-DY7svs0d.mjs → bulk-discard-values-VKGj8c7z.mjs} +5 -5
- package/dist/{bulk-edit-3EfcsqCu.mjs → bulk-edit-CpuleeCQ.mjs} +5 -5
- package/dist/{bulk-rescan-values-BpwnC_WY.mjs → bulk-rescan-values-DeGwiqLS.mjs} +5 -5
- package/dist/{bulk-sync-schema-Dwbs0TsG.mjs → bulk-sync-schema-BKVhauW1.mjs} +5 -5
- package/dist/{cancel-Nz4jJ-Ey.mjs → cancel-qKj45001.mjs} +3 -3
- package/dist/{cancel-task-CX_qYiJ0.mjs → cancel-task-nmBBd1Ky.mjs} +3 -3
- package/dist/card-Buc-X4Mh.mjs +25 -0
- package/dist/{cards-FUW8-5PO.mjs → cards-Bq73s48q.mjs} +4 -4
- package/dist/cli.mjs +628 -229
- package/dist/{client-5y-BaV3d.mjs → client-BmURNkkW.mjs} +997 -365
- package/dist/{collection-DPiztU_l.mjs → collection-C03LXnCT.mjs} +1 -1
- package/dist/collection-D-sS0AFw.mjs +21 -0
- package/dist/{collection-namespace-Q_up_tSg.mjs → collection-namespace-DaDzK31i.mjs} +1 -2
- package/dist/{collection-BgPYl2c5.mjs → collection-zrZoJfgR.mjs} +1 -1
- package/dist/{content-translation-CSkaKlqa.mjs → content-translation-BlG41YrA.mjs} +3 -3
- package/dist/{copy-WlNBviDZ.mjs → copy-DOKlVa7H.mjs} +6 -6
- package/dist/{copy-fikpWJO4.mjs → copy-DTiYs_a2.mjs} +4 -4
- package/dist/{copy-flags-j38p0var.mjs → copy-flags-BhGWC5kS.mjs} +2 -2
- package/dist/{create-C4dDHOzR.mjs → create-1rLk8b5q.mjs} +3 -3
- package/dist/{create-BmCz5lro.mjs → create-B1QDNU1x.mjs} +6 -6
- package/dist/{create-BI73JzZN.mjs → create-B1bX6ngX.mjs} +4 -4
- package/dist/{create-9sPJd45D.mjs → create-B5BwdPwT.mjs} +6 -6
- package/dist/{create-CC89lXeE.mjs → create-BKKNIe93.mjs} +3 -3
- package/dist/{create-COv2mYmk.mjs → create-BXX_n64u.mjs} +4 -4
- package/dist/{create-CEezFRTJ.mjs → create-BpwZJZxb.mjs} +5 -5
- package/dist/{create-DqGen4jU.mjs → create-C0kz0oYc.mjs} +3 -3
- package/dist/{create-CLLcjbzq.mjs → create-Cob0gLsA.mjs} +3 -3
- package/dist/{create-yLNBBrwG.mjs → create-DPggIXfn.mjs} +4 -4
- package/dist/{create-DdnC7PkI.mjs → create-DUytP2Y1.mjs} +4 -4
- package/dist/{create-CO6gBV7I.mjs → create-DraZzrLO.mjs} +4 -4
- package/dist/{create-Dp1597uC.mjs → create-Duj_2YIF.mjs} +4 -4
- package/dist/{create-C0MNevKK.mjs → create-JaFhqysM.mjs} +5 -5
- package/dist/{create-branch-Cflee_NY.mjs → create-branch-B5jB4tKk.mjs} +3 -3
- package/dist/{create-CYqqBKhE.mjs → create-d0trLAzB.mjs} +4 -4
- package/dist/{create-Ci7o6-Fk.mjs → create-vOw8gmBU.mjs} +3 -3
- package/dist/{csv-CX1wws5-.mjs → csv-DfExp9H_.mjs} +4 -4
- package/dist/{current-task-BTnORR7t.mjs → current-task-CbXIH_RA.mjs} +4 -4
- package/dist/dashboard-DH_soSQp.mjs +35 -0
- package/dist/{database-D-CRyRIv.mjs → database-BdKkae1m.mjs} +1 -1
- package/dist/{database-MlUPpitM.mjs → database-DoNoZ8Yc.mjs} +1 -1
- package/dist/{dataset-p5oNPQgL.mjs → dataset-DZgJm5hP.mjs} +3 -14
- package/dist/{db-DNevs1XU.mjs → db-C82M3Gpg.mjs} +7 -7
- package/dist/{delete-BJ18ifIG.mjs → delete-9xHRd2CG.mjs} +4 -4
- package/dist/{delete-dfrM_M6T.mjs → delete-B4BfGfiJ.mjs} +4 -4
- package/dist/{delete-D5Jy83V-.mjs → delete-D0xFMRRq.mjs} +4 -4
- package/dist/{delete-CCUYbNae.mjs → delete-DJ7SNXiH.mjs} +4 -4
- package/dist/{delete-ChmqxYa7.mjs → delete-hcw_4pBG.mjs} +4 -4
- package/dist/{delete-Ch797MmM.mjs → delete-hwPqKXeY.mjs} +4 -4
- package/dist/{delete-runtime-Cc7TASG7.mjs → delete-runtime-DkgYUQ_n.mjs} +1 -1
- package/dist/{delete-table-0xxoSknN.mjs → delete-table-DhTMft7u.mjs} +4 -4
- package/dist/{dependencies-GvzMLOS2.mjs → dependencies-B8H2gA04.mjs} +5 -5
- package/dist/dependency-CLznxtLE.mjs +15 -0
- package/dist/{dependency-BZxbh7qe.mjs → dependency-Dc33mjgd.mjs} +2 -2
- package/dist/{dependency-DaGeVAHF.mjs → dependency-tQBxYjFX.mjs} +1 -1
- package/dist/{dependents-BRqKb0Pu.mjs → dependents-B2GcS5kB.mjs} +9 -9
- package/dist/{dirty-Bq2cf5AG.mjs → dirty-Cz2rUd6S.mjs} +4 -4
- package/dist/{discard-values-k5-ePxWl.mjs → discard-values-QWuWGaVK.mjs} +5 -5
- package/dist/{document-COQzVLjW.mjs → document-BDobDiap.mjs} +1 -1
- package/dist/document-WhSEyI-3.mjs +20 -0
- package/dist/{document-BsaKF8UR.mjs → document-wfWbV0B_.mjs} +1 -1
- package/dist/{download-Dpbarf8I.mjs → download-Dw15fUMy.mjs} +2 -2
- package/dist/{eid-BEc1p_um.mjs → eid-Ex4G-ZlN.mjs} +4 -4
- package/dist/{entity-c_Br4SFc.mjs → entity-DBKPaMr5.mjs} +2 -2
- package/dist/{entry-flags-z8cZTfaQ.mjs → entry-flags-DgY3IXFe.mjs} +4 -4
- package/dist/{events-DBpOpy2y.mjs → events-BibJ4ya0.mjs} +4 -4
- package/dist/{export-F-R_vF48.mjs → export-U8MdaatW.mjs} +7 -7
- package/dist/{export-flags-QbLI0sKz.mjs → export-flags-BrfeyE8D.mjs} +2 -2
- package/dist/{export-preflight-0BvDa8Oz.mjs → export-preflight-A28L45au.mjs} +5 -5
- package/dist/field-qF1KRJYB.mjs +21 -0
- package/dist/{fields-DOdrazv2.mjs → fields-i1TFa_th.mjs} +4 -4
- package/dist/{filter-flags-DiUgZ6vZ.mjs → filter-flags-Bm3vuSM-.mjs} +3 -3
- package/dist/{fks-41Ig_ZQ2.mjs → fks-Dcfn3-73.mjs} +6 -6
- package/dist/{get-Dwp_BfIp.mjs → get-B4_3DID6.mjs} +5 -5
- package/dist/{get-TbBkN-jS.mjs → get-BCdtCwaG.mjs} +3 -3
- package/dist/{get-BVaN-tgY.mjs → get-BKDTRMNU.mjs} +3 -3
- package/dist/{get-CVJFF8Lb.mjs → get-BiPSePQc.mjs} +3 -3
- package/dist/{get-DaxwG0AP.mjs → get-C3ojRVJ_.mjs} +4 -4
- package/dist/{get-B8hREA2A.mjs → get-CCLWWqTD.mjs} +4 -4
- package/dist/{get-aC5jUA1y.mjs → get-CVpIQb8v.mjs} +5 -5
- package/dist/{get-BjJdy_Iw.mjs → get-CcwRn8Y2.mjs} +5 -5
- package/dist/{get-BAV-KpLU.mjs → get-CobILQ_7.mjs} +3 -3
- package/dist/{get-OKOOd2yb.mjs → get-Cwj1Q-at.mjs} +3 -3
- package/dist/{get-DwUB0K2G.mjs → get-D0NZ0ZO1.mjs} +3 -3
- package/dist/{get-2l2CbHc6.mjs → get-DPZUIPjv.mjs} +3 -3
- package/dist/{get-D1c-C-_Q2.mjs → get-DUwX-RU82.mjs} +3 -3
- package/dist/{get-Bby33eun.mjs → get-DqYn9mez.mjs} +4 -4
- package/dist/{get-CPhMYZDP.mjs → get-Dz3dNirC.mjs} +4 -4
- package/dist/{get-CfeNeyGE.mjs → get-EKBYludD.mjs} +3 -3
- package/dist/{get-YinpF0y5.mjs → get-HFQSttF3.mjs} +5 -5
- package/dist/{get-CjK2GzFn.mjs → get-IjoHBExI.mjs} +5 -5
- package/dist/{get-BlLUy1G0.mjs → get-hAoJtVCK.mjs} +3 -3
- package/dist/{get-run-PeXiUJwf.mjs → get-run-B2SnsZSY.mjs} +4 -4
- package/dist/git-sync-BlgB_-8-.mjs +29 -0
- package/dist/{git-sync-DspFahwV.mjs → git-sync-Ch1V1fEK.mjs} +1 -1
- package/dist/{glossary-CdW6RFId.mjs → glossary-6LLpItof.mjs} +1 -1
- package/dist/glossary-BvVxqjxJ.mjs +14 -0
- package/dist/{glossary-Del97Jqg.mjs → glossary-wnuxoE_I.mjs} +1 -1
- package/dist/{graph-M7V_js_w.mjs → graph-CHlfxVy1.mjs} +6 -6
- package/dist/{group-D4w87fXL.mjs → group-B3X3LTcU.mjs} +1 -1
- package/dist/{has-remote-changes-D7uABGR0.mjs → has-remote-changes-DCMpqGgE.mjs} +3 -3
- package/dist/{import-CABYSyWS.mjs → import-C7MZhKo6.mjs} +7 -7
- package/dist/{input-DjHsV-kD.mjs → input-sRdxNc3T.mjs} +1 -1
- package/dist/{is-dirty-DpQBXuVa.mjs → is-dirty-DpLyHebS.mjs} +2 -2
- package/dist/{items-BqUCPf-J.mjs → items-1ME_eg27.mjs} +6 -6
- package/dist/{key-BTaxFn_7.mjs → key-DMtgTOwN.mjs} +1 -1
- package/dist/{library-DmkpcZPb.mjs → library-CP-9yepy.mjs} +1 -1
- package/dist/{library-DDrpsgMv.mjs → library-D2ogEIfI.mjs} +5 -5
- package/dist/{library-C4M8TF2N.mjs → library-fWEv38OB.mjs} +2 -2
- package/dist/{list-Dd9o6Tut.mjs → list-B0h-j1E7.mjs} +3 -3
- package/dist/{list-ISpYiawJ.mjs → list-BUZ91ctZ.mjs} +5 -5
- package/dist/{list-CLRezReA.mjs → list-BUwDGyIu.mjs} +5 -5
- package/dist/{list-C9H24xr5.mjs → list-BXcDPU5d.mjs} +6 -6
- package/dist/{list-BQXq0A65.mjs → list-BoVeF8QA.mjs} +6 -6
- package/dist/{list-CHUwj_Qh.mjs → list-C1oEpUZG.mjs} +3 -3
- package/dist/{list-Hiq5T4Bt.mjs → list-C9996vbS.mjs} +3 -3
- package/dist/{list-DO767sO1.mjs → list-CJ5FyCnL.mjs} +4 -4
- package/dist/{list-BFAc460G.mjs → list-CQi3AgS5.mjs} +4 -4
- package/dist/{list-yDJq3wNs.mjs → list-CVh8DHOO.mjs} +5 -5
- package/dist/{list-D217irjK.mjs → list-DBAEy3Rx.mjs} +3 -3
- package/dist/{list-glgnTw7w.mjs → list-DkCadZ2E.mjs} +3 -3
- package/dist/{list-CEU9UXaZ.mjs → list-DzP7YXdZ.mjs} +3 -3
- package/dist/{list-aHFBopQd.mjs → list-LMlJ4uAY.mjs} +4 -4
- package/dist/{list-BXRUiZuH.mjs → list-NINy7ybI.mjs} +4 -4
- package/dist/{list-CONZkA9i.mjs → list-OXiXtdiM.mjs} +5 -5
- package/dist/{list-BygxQkq7.mjs → list-STy8UHs2.mjs} +6 -6
- package/dist/{list-CZF_gQnI.mjs → list-Ud7F0XdU.mjs} +3 -3
- package/dist/{list-D0MlsdSX.mjs → list-dCi6OaIE.mjs} +4 -4
- package/dist/{login-DCWam2ec.mjs → login-BMON3Fft.mjs} +6 -6
- package/dist/{logout-CmrJqhkc.mjs → logout-Cx934GzW.mjs} +2 -2
- package/dist/{measure-c_VUyXFN.mjs → measure-CUqJtDNm.mjs} +6 -6
- package/dist/{notification-Cuuwoksm.mjs → notification-CCZ9mrOw.mjs} +1 -1
- package/dist/{parameter-values-CnwWZrJ_.mjs → parameter-values-DCrU02Az.mjs} +3 -3
- package/dist/{parse-enum-BPKULOSw.mjs → parse-enum-Do5Cptbv.mjs} +1 -1
- package/dist/{parse-id-aMXePsUP.mjs → parse-id-oCbmouCn.mjs} +1 -1
- package/dist/{parse-ref-8bVX_9IN.mjs → parse-ref-Df8gusl4.mjs} +1 -1
- package/dist/{parse-text-Bm6eUa-p.mjs → parse-text-CVrP7GmZ.mjs} +1 -1
- package/dist/{path-CGbz-QSr.mjs → path-DTuSDGd6.mjs} +4 -4
- package/dist/{poll-DFZRtTA_.mjs → poll-BKo5dK0l.mjs} +2 -2
- package/dist/{preflight-ChJ0pemy.mjs → preflight-UeYuGDPr.mjs} +2 -3
- package/dist/{process-B3Di_HtV.mjs → process-BpmZXbcw.mjs} +1 -1
- package/dist/{publish-7ke00CtK.mjs → publish-D3GwLC-K.mjs} +4 -4
- package/dist/{pulse-CIza18rO.mjs → pulse-DzVPfb5J.mjs} +1 -1
- package/dist/{query-8M7ZN2Ca.mjs → query-B1Cp9EO6.mjs} +4 -4
- package/dist/{query-DNa-fGS7.mjs → query-Ct697cY-.mjs} +11 -12
- package/dist/{remapping-Cy1CEown.mjs → remapping-RsoPWE7c.mjs} +4 -4
- package/dist/{remove-collection-B2CL66No.mjs → remove-collection-BJo3Wd9K.mjs} +4 -4
- package/dist/{render-ColPG5G7.mjs → render-C_ev6qoW.mjs} +2 -2
- package/dist/{replace-COC3ggyx.mjs → replace-BSAGp3cR.mjs} +4 -4
- package/dist/requirement-check-T3bjQ0Mr.mjs +60 -0
- package/dist/{rescan-values-DUX58myu.mjs → rescan-values-CE62_2dc.mjs} +5 -5
- package/dist/{rescan-values-DXzXTw9a.mjs → rescan-values-rpro4Kw2.mjs} +5 -5
- package/dist/{run-C5wf-lug.mjs → run-B3BRodEp.mjs} +3 -3
- package/dist/{run-T2a4m1x9.mjs → run-BI1QO9Kn.mjs} +7 -7
- package/dist/{runs-Datc76Wu.mjs → runs-CUseZezf.mjs} +5 -5
- package/dist/{runtime-C3AL15Dp.mjs → runtime-C5Ha6Ou8.mjs} +248 -534
- package/dist/{schema-tables-CR2S5mNc.mjs → schema-tables-C7YkyRh0.mjs} +6 -6
- package/dist/{schemas-OrGjEuVc.mjs → schemas-Bj5sJ9uB.mjs} +4 -4
- package/dist/{search-BEXBlfak.mjs → search-BsAwK7Rk.mjs} +5 -5
- package/dist/{search-CJYSB5eP.mjs → search-CB1A4HWx.mjs} +6 -6
- package/dist/{segment-Tt8GaRnc.mjs → segment-BEjaeLc5.mjs} +6 -6
- package/dist/{send-uNLODakO.mjs → send-Dfh8d1gG.mjs} +3 -3
- package/dist/{set-DRp3F4wz.mjs → set-DA8dKyzi.mjs} +4 -4
- package/dist/{set-active-CAt6ieXS.mjs → set-active-AyjKtBD-.mjs} +3 -3
- package/dist/{set-sensitivity--Qq2RXVE.mjs → set-sensitivity-D0613dHA.mjs} +4 -4
- package/dist/{setting-DMqvipRp.mjs → setting-B-bxCkC0.mjs} +4 -4
- package/dist/{setup-B2PC1NVz.mjs → setup-Bvvqlz9d.mjs} +3 -3
- package/dist/{signal-0Yt8JTMn.mjs → signal-Dm4N0Bcc.mjs} +1 -1
- package/dist/{skill-list-Cyx1935Q.mjs → skill-list-DkuCf0k4.mjs} +9 -3
- package/dist/{skills-Dlp0MK6g.mjs → skills-DlDCTGVE.mjs} +2 -2
- package/dist/{skills-D7wu2iff.mjs → skills-DxrO7HUz.mjs} +3 -3
- package/dist/snippet-B7Kau5U8.mjs +19 -0
- package/dist/{stash-CHeQPI8Z.mjs → stash-j9JPv3ns.mjs} +5 -5
- package/dist/{status-Bdt5XWrQ.mjs → status-DcbvbRc5.mjs} +3 -3
- package/dist/{status-gTN3G-rs.mjs → status-DxNcrfnI.mjs} +16 -7
- package/dist/{subscription-C9alueg1.mjs → subscription-CSR2t-xK.mjs} +6 -6
- package/dist/{subscriptions-DJKbcxDy.mjs → subscriptions--0YvW40u.mjs} +6 -6
- package/dist/{summary-kaC0aINr.mjs → summary-Dslv78-r.mjs} +3 -3
- package/dist/{sync-schema-CyKovAb9.mjs → sync-schema-BSJiokQf.mjs} +6 -6
- package/dist/{sync-schema-C29bGCtt.mjs → sync-schema-Dgij5ICd.mjs} +5 -5
- package/dist/{sync-task-BAMOf6_t.mjs → sync-task-a1zyF1Qb.mjs} +4 -5
- package/dist/{table-BFIOH6Pb.mjs → table-Uvu1roSx.mjs} +2 -2
- package/dist/table-qjgz2eek.mjs +26 -0
- package/dist/{table-selector-flags-CSoedTgm.mjs → table-selector-flags-B9OiA3Z1.mjs} +3 -3
- package/dist/{table-Ci6bhZkb.mjs → table-zAdW5_3H.mjs} +5 -5
- package/dist/{text-CdtEI6l6.mjs → text-821Rn8M9.mjs} +1 -1
- package/dist/timeline-CNkrY3e3.mjs +21 -0
- package/dist/{timeline-event-DN3y8vwW.mjs → timeline-event-CSzx8EDP.mjs} +6 -6
- package/dist/{transform-D4z70PIN.mjs → transform-BKX6tl3i.mjs} +1 -1
- package/dist/transform-Y86R7_2F.mjs +28 -0
- package/dist/transform-job-Cg_1jzWI.mjs +22 -0
- package/dist/{transform-tag-CIKnNaQH.mjs → transform-tag-DHg7j15d.mjs} +5 -5
- package/dist/{transforms-BQjwZKTN.mjs → transforms-DwxPfMiG.mjs} +5 -5
- package/dist/{tree-C21CG5rm.mjs → tree-COFmA20J.mjs} +3 -3
- package/dist/{unpublish-C7MRZTmS.mjs → unpublish-BQClh4U7.mjs} +3 -3
- package/dist/{unreferenced-BJUIC_Ct.mjs → unreferenced-DKc0zZDl.mjs} +6 -6
- package/dist/{update-CuYvtXQY.mjs → update-B-wcIl8T.mjs} +4 -4
- package/dist/{update-DDH6FTe6.mjs → update-B8jQTspb.mjs} +8 -8
- package/dist/{update-9b4JHJWR.mjs → update-BBLbEPEP.mjs} +4 -4
- package/dist/{update-C0QcHFIP.mjs → update-BNwjjMk6.mjs} +5 -5
- package/dist/{update-BXFGGYT8.mjs → update-BVFfoopT.mjs} +5 -5
- package/dist/{update-CVO8Srng.mjs → update-BVMgrMy7.mjs} +5 -5
- package/dist/{update-CCHTQjsP.mjs → update-Bgy2amR0.mjs} +6 -6
- package/dist/{update-CYy-r_Jq.mjs → update-C9HK3i8s.mjs} +5 -5
- package/dist/{update-IEVAFvpi.mjs → update-CFfr1ZdL.mjs} +4 -4
- package/dist/{update-B2F22ryV.mjs → update-CMRWXoZ2.mjs} +4 -4
- package/dist/{update-D6oHLn1g.mjs → update-CPmPnEvm.mjs} +6 -6
- package/dist/{update-Duxk_-dC.mjs → update-Ca8LSZer.mjs} +5 -5
- package/dist/{update-CBcJTZo6.mjs → update-DJwu8UT2.mjs} +4 -4
- package/dist/{update-DX1s6KnO.mjs → update-Dwr3n7_n.mjs} +7 -7
- package/dist/{update-DEnsF5zT.mjs → update-MxvqmZ3z.mjs} +6 -6
- package/dist/{update-dashcard-C4ZndfvP.mjs → update-dashcard-C8QN2Qcu.mjs} +4 -4
- package/dist/{update-EpHReUGk.mjs → update-kM3TDlEp.mjs} +5 -5
- package/dist/{update-CZ2eSAV5.mjs → update-myfBF5qV.mjs} +4 -4
- package/dist/{upgrade-o2DpB66j.mjs → upgrade-BBOEVIRa.mjs} +4 -4
- package/dist/{upload-CEtKrueq.mjs → upload-CoBd4qBp.mjs} +3 -3
- package/dist/{upload-DJB7DMA1.mjs → upload-CxpXpjCf.mjs} +2 -2
- package/dist/upload-fcim1xtK.mjs +13 -0
- package/dist/{uuid-D9oozo8p.mjs → uuid-DW30GY7B.mjs} +2 -2
- package/dist/{validate-D_iikPkc.mjs → validate-n85yMraN.mjs} +1 -1
- package/dist/{validate-query-BEYjmSv2.mjs → validate-query-BwXvFFYi.mjs} +2 -2
- package/dist/{values-De8aGDiJ.mjs → values-B6eFE4K5.mjs} +3 -3
- package/dist/{verify-DVjaammv.mjs → verify-BSq7x66g.mjs} +3 -3
- package/dist/{verify-ob8JSLxv.mjs → verify-DnU-wXIs.mjs} +4 -4
- package/dist/{verify-DKmz4xJs.mjs → verify-Dp8tUbP3.mjs} +3 -3
- package/dist/{wait-BN_MzTng.mjs → wait-D6Tw4ZxO.mjs} +6 -6
- package/dist/{wait-flags-DQlX2yM_.mjs → wait-flags-BRUzMI6S.mjs} +2 -2
- package/dist/{window-2ybHqui2.mjs → window-BqMFpWMh.mjs} +1 -1
- package/package.json +1 -1
- package/skill-data/core/SKILL.md +5 -8
- package/skill-data/dashboard/SKILL.md +6 -0
- package/skill-data/git-sync/SKILL.md +64 -14
- package/skill-data/transform/SKILL.md +1 -63
- package/dist/card-DmQO85ej.mjs +0 -25
- package/dist/collection-BPVrTfTv.mjs +0 -21
- package/dist/create-DQM_AB3I.mjs +0 -34
- package/dist/dashboard-c_wOMtS9.mjs +0 -35
- package/dist/delete-CZTG4wI2.mjs +0 -44
- package/dist/dependency-D6DMbPVm.mjs +0 -15
- package/dist/document-DCqe5MO9.mjs +0 -20
- package/dist/field-CcCQ0goz.mjs +0 -21
- package/dist/get-DxpDAUz4.mjs +0 -32
- package/dist/git-sync-CO199xCu.mjs +0 -29
- package/dist/glossary-LdAlftZW.mjs +0 -14
- package/dist/list-CQJABPGk.mjs +0 -35
- package/dist/run-C_nqwWmC.mjs +0 -63
- package/dist/snippet-D-5bKT_U.mjs +0 -19
- package/dist/table-8c-V5afO.mjs +0 -26
- package/dist/timeline-BetOmLYk.mjs +0 -21
- package/dist/transform-job-DALBzYVV.mjs +0 -22
- package/dist/transform-p-6_thIh.mjs +0 -28
- package/dist/transform-test-BjjCQ2xm.mjs +0 -169
- package/dist/transform-test-C5Havg4o.mjs +0 -16
- package/dist/transform-test-DHGKeuHa.mjs +0 -32
- package/dist/transport-DuU3fLui.mjs +0 -313
- package/dist/update-BQben_Xm.mjs +0 -41
- package/dist/upload-BqNErDXE.mjs +0 -13
- package/skill-data/transform-test-plan/SKILL.md +0 -82
- package/skill-data/transform-test-plan/references/checklist.md +0 -32
- package/skill-data/transform-test-plan/references/checks.md +0 -156
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as isNonInterruptFailure, Ct as ResponseShapeError, Dt as errorMessage, Et as ValidationError, G as isRetryableStatus, J as isPlainObject, N as CapabilityError, Ot as formatZodIssue, St as PartialWriteError, U as HttpError, X as parseJson, Y as JSON_CONTENT_TYPE, Z as parseJsonOrPlain, n as isMethodKey, r as methodRequirements, vt as ConfigError, wt as TimeoutError, xt as NetworkError, yt as InternalError } from "./cli.mjs";
|
|
2
2
|
import { n as Notification, r as NotificationCardPayload, t as CARD_PAYLOAD_TYPE } from "./notification-DaA4bBHW.mjs";
|
|
3
|
-
import { P as
|
|
4
|
-
import { n as
|
|
3
|
+
import { B as credentialAuthHeader, F as probeBudget, J as createServerProfile, L as profileFromProperties, M as buildNetworkError, N as isConnectionClosed, P as PROBE_PATH, R as serverInfoFromProperties, V as credentialSecrets, W as normalizeUrl, Z as SessionProperties, z as assertCredentialHeaderSafe } from "./runtime-C5Ha6Ou8.mjs";
|
|
4
|
+
import { n as throwIfAborted, r as untilAborted, t as combineAborts } from "./signal-Dm4N0Bcc.mjs";
|
|
5
|
+
import { n as featureFailures, r as supersedingFeature, t as checkFeatures } from "./requirement-check-T3bjQ0Mr.mjs";
|
|
5
6
|
import { f as FieldWithDataSensitivity, h as fieldRemappedValueSchema, m as fieldDetailSchema, n as FieldBaseType, o as FieldSearchMatches, s as FieldSemanticType, u as FieldValues } from "./field-5nivEfd6.mjs";
|
|
6
7
|
import { a as TemporalUnit, r as ParameterValues } from "./parameter-Celg0wZa.mjs";
|
|
7
8
|
import { a as CardQueryResult, d as PivotExportFormat, l as DatasetQuery, t as Card } from "./card-Bv4ZN0oV.mjs";
|
|
8
|
-
import { b as TableVisibilityType, p as TableQueryMetadata, s as TableDataLayerTier, t as Table, u as TableForeignKey, v as TableSelectors } from "./table-
|
|
9
|
-
import { t as Database } from "./database-
|
|
9
|
+
import { b as TableVisibilityType, p as TableQueryMetadata, s as TableDataLayerTier, t as Table, u as TableForeignKey, v as TableSelectors } from "./table-zAdW5_3H.mjs";
|
|
10
|
+
import { t as Database } from "./database-BdKkae1m.mjs";
|
|
10
11
|
import { t as Snippet } from "./snippet-CyKAgIZl.mjs";
|
|
11
|
-
import {
|
|
12
|
-
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-
|
|
12
|
+
import { r as QueryMetadata, t as CompiledQuery } from "./dataset-DZgJm5hP.mjs";
|
|
13
|
+
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-zrZoJfgR.mjs";
|
|
13
14
|
import { t as ContentTranslationUploadResult } from "./content-translation-BrHOrmj9.mjs";
|
|
14
15
|
import { l as Dashcard, o as DashboardDetail, r as DashboardCopy, t as Dashboard } from "./dashboard-Phz3XpRM.mjs";
|
|
15
|
-
import { r as pollUntil } from "./poll-
|
|
16
|
-
import { i as DependencyEntity, s as DependencyGraph, t as BreakingSource, u as DependencyNode } from "./dependency-
|
|
17
|
-
import { t as Document } from "./document-
|
|
16
|
+
import { r as pollUntil } from "./poll-BKo5dK0l.mjs";
|
|
17
|
+
import { i as DependencyEntity, s as DependencyGraph, t as BreakingSource, u as DependencyNode } from "./dependency-tQBxYjFX.mjs";
|
|
18
|
+
import { t as Document } from "./document-BDobDiap.mjs";
|
|
18
19
|
import { i as EidTranslateResult } from "./eid-translation-zEHtr5Oc.mjs";
|
|
19
20
|
import { c as SyncRemoteChanges, d as SyncTask, i as SyncExportPreflight, l as SyncSettingsUpdateResult, m as isSyncTaskTerminal, n as SyncDirtyItem, t as SyncBranchCreated } from "./git-sync-COokpYgc.mjs";
|
|
20
|
-
import { t as Glossary } from "./glossary-
|
|
21
|
-
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-
|
|
21
|
+
import { t as Glossary } from "./glossary-6LLpItof.mjs";
|
|
22
|
+
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-fWEv38OB.mjs";
|
|
22
23
|
import { t as Measure } from "./measure-CtfjqJFP.mjs";
|
|
23
24
|
import { r as ModerationReviewCreateInput, t as ModerationReview } from "./moderation-review-BdqEH6N1.mjs";
|
|
24
25
|
import { t as Pulse } from "./pulse-Dz0ocv0l.mjs";
|
|
@@ -30,9 +31,9 @@ import { n as SetupResult } from "./setup-CO7QV-ao.mjs";
|
|
|
30
31
|
import { i as TimelineEvent, t as Timeline } from "./timeline-CCSU2qOS.mjs";
|
|
31
32
|
import { c as transformJobSchema, n as TransformJobActiveResult, s as transformJobRunResultSchema } from "./transform-job-CCguL6LP.mjs";
|
|
32
33
|
import { t as TransformTag } from "./transform-tag-CcElGp3C.mjs";
|
|
33
|
-
import { i as TransformTestRunResult, t as TransformTest } from "./transform-test-BjjCQ2xm.mjs";
|
|
34
34
|
import { t as UploadResult } from "./upload-DAtxBBms.mjs";
|
|
35
35
|
import { z } from "zod";
|
|
36
|
+
import { setTimeout } from "node:timers/promises";
|
|
36
37
|
//#region \0rolldown/runtime.js
|
|
37
38
|
var __defProp = Object.defineProperty;
|
|
38
39
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -45,15 +46,425 @@ var __exportAll = (all, no_symbols) => {
|
|
|
45
46
|
return target;
|
|
46
47
|
};
|
|
47
48
|
//#endregion
|
|
49
|
+
//#region ../client/src/http/response-shape.ts
|
|
50
|
+
async function parseJsonResponse(text, schema, context) {
|
|
51
|
+
try {
|
|
52
|
+
return parseJson(text, schema, { source: context.url });
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error instanceof ValidationError) throw ResponseShapeError.fromZodIssues({
|
|
55
|
+
kind: "zod",
|
|
56
|
+
method: context.method,
|
|
57
|
+
url: context.url,
|
|
58
|
+
status: context.status,
|
|
59
|
+
zodIssues: error.developerDetail.zodIssues,
|
|
60
|
+
serverTag: await context.getServerTag(),
|
|
61
|
+
serverSkew: context.serverSkew
|
|
62
|
+
});
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region ../client/src/http/retry.ts
|
|
68
|
+
const BASE_DELAY_MS = 250;
|
|
69
|
+
const MAX_DELAY_MS = 8e3;
|
|
70
|
+
const MS_PER_SECOND = 1e3;
|
|
71
|
+
function backoffDelay({ attempt, retryAfterHeader }) {
|
|
72
|
+
const fromHeader = parseRetryAfter(retryAfterHeader);
|
|
73
|
+
if (fromHeader !== null) return fromHeader;
|
|
74
|
+
return Math.min(MAX_DELAY_MS, BASE_DELAY_MS * 2 ** attempt);
|
|
75
|
+
}
|
|
76
|
+
function parseRetryAfter(header) {
|
|
77
|
+
if (!header) return null;
|
|
78
|
+
const numeric = Number(header);
|
|
79
|
+
if (Number.isFinite(numeric) && numeric >= 0) return Math.round(numeric * MS_PER_SECOND);
|
|
80
|
+
const dateMs = Date.parse(header);
|
|
81
|
+
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function sleep(ms, signal) {
|
|
85
|
+
return setTimeout(ms, void 0, { signal });
|
|
86
|
+
}
|
|
87
|
+
async function runWithRetries(attempt, signal) {
|
|
88
|
+
let attemptIndex = 0;
|
|
89
|
+
while (true) {
|
|
90
|
+
const outcome = await attempt(attemptIndex);
|
|
91
|
+
if (outcome.kind === "success") return outcome.response;
|
|
92
|
+
await sleep(outcome.delayMs, signal);
|
|
93
|
+
attemptIndex += 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
97
|
+
const OCTET_STREAM_CONTENT_TYPE = "application/octet-stream";
|
|
98
|
+
const TEXT_CONTENT_TYPE_PREFIX = "text/";
|
|
99
|
+
const ERROR_BODY_BYTE_CAP = 64 * 1024;
|
|
100
|
+
const IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
|
|
101
|
+
"GET",
|
|
102
|
+
"HEAD",
|
|
103
|
+
"OPTIONS"
|
|
104
|
+
]);
|
|
105
|
+
function createTransport(config, options) {
|
|
106
|
+
const baseUrl = normalizeUrl(config.url);
|
|
107
|
+
assertCredentialHeaderSafe(config.credential);
|
|
108
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
109
|
+
let inForce = options.server ?? null;
|
|
110
|
+
let newest = null;
|
|
111
|
+
let probesStarted = 0;
|
|
112
|
+
let applied = null;
|
|
113
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
114
|
+
const getServerTag = options.getServerTag ?? (async () => tagOf(inForce));
|
|
115
|
+
const refreshCredential = options.refreshCredential;
|
|
116
|
+
let credential = config.credential;
|
|
117
|
+
const knownSecrets = new Set(credentialSecrets(credential));
|
|
118
|
+
const redactionContext = { knownSecrets };
|
|
119
|
+
let refreshInFlight = null;
|
|
120
|
+
function tryRefreshCredential() {
|
|
121
|
+
const refresh = refreshCredential;
|
|
122
|
+
if (credential.kind !== "oauth" || refresh === void 0) return Promise.resolve(false);
|
|
123
|
+
refreshInFlight ??= refreshOnce(refresh).finally(() => {
|
|
124
|
+
refreshInFlight = null;
|
|
125
|
+
});
|
|
126
|
+
return refreshInFlight;
|
|
127
|
+
}
|
|
128
|
+
async function refreshOnce(refresh) {
|
|
129
|
+
const next = await refresh();
|
|
130
|
+
if (next === null) return false;
|
|
131
|
+
credential = next;
|
|
132
|
+
for (const secret of credentialSecrets(next)) knownSecrets.add(secret);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async function attemptOnce(prepared, attempt) {
|
|
136
|
+
const hasRetriesLeft = attempt < prepared.retries;
|
|
137
|
+
const timeoutSignal = AbortSignal.timeout(prepared.timeoutMs);
|
|
138
|
+
const combined = combineAborts(timeoutSignal, prepared.cancelSignal);
|
|
139
|
+
let response;
|
|
140
|
+
try {
|
|
141
|
+
response = await fetchImpl(prepared.url, {
|
|
142
|
+
method: prepared.method,
|
|
143
|
+
headers: prepared.headers,
|
|
144
|
+
body: prepared.body,
|
|
145
|
+
signal: combined
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throwIfAborted(prepared.cancelSignal);
|
|
149
|
+
const isStaleSocketFirstTry = attempt === 0 && isConnectionClosed(error);
|
|
150
|
+
if (prepared.idempotent && (hasRetriesLeft || isStaleSocketFirstTry)) return {
|
|
151
|
+
kind: "retry",
|
|
152
|
+
delayMs: backoffDelay({ attempt })
|
|
153
|
+
};
|
|
154
|
+
if (timeoutSignal.aborted) throw new TimeoutError(`Request timed out after ${prepared.timeoutMs}ms`, {
|
|
155
|
+
kind: "http",
|
|
156
|
+
method: prepared.method,
|
|
157
|
+
url: prepared.url,
|
|
158
|
+
timeoutMs: prepared.timeoutMs
|
|
159
|
+
});
|
|
160
|
+
throw buildNetworkError(error, prepared.method, prepared.url);
|
|
161
|
+
}
|
|
162
|
+
const canRetryStatus = hasRetriesLeft && prepared.idempotent;
|
|
163
|
+
if (!response.ok && isRetryableStatus(response.status) && canRetryStatus) {
|
|
164
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
165
|
+
response.body?.cancel().catch(() => void 0);
|
|
166
|
+
return {
|
|
167
|
+
kind: "retry",
|
|
168
|
+
delayMs: backoffDelay({
|
|
169
|
+
attempt,
|
|
170
|
+
retryAfterHeader: retryAfter
|
|
171
|
+
})
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
const rawBody = await readBodyForError(response);
|
|
176
|
+
const serverTag = await getServerTag();
|
|
177
|
+
throw new HttpError({
|
|
178
|
+
status: response.status,
|
|
179
|
+
statusText: response.statusText,
|
|
180
|
+
method: prepared.method,
|
|
181
|
+
url: prepared.url,
|
|
182
|
+
responseHeaders: response.headers,
|
|
183
|
+
rawBody,
|
|
184
|
+
serverTag,
|
|
185
|
+
redactionContext
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
assertContentType(response, prepared);
|
|
189
|
+
return {
|
|
190
|
+
kind: "success",
|
|
191
|
+
response
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async function executeRaw(prepared) {
|
|
195
|
+
try {
|
|
196
|
+
return await runWithRetries((attempt) => attemptOnce(prepared, attempt), prepared.cancelSignal);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throwIfAborted(prepared.cancelSignal);
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function executeWithAuthRefresh(path, opts) {
|
|
203
|
+
const prepared = prepare(path, opts);
|
|
204
|
+
try {
|
|
205
|
+
return {
|
|
206
|
+
response: await executeRaw(prepared),
|
|
207
|
+
prepared
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof HttpError && error.status === 401) {
|
|
211
|
+
if (await tryRefreshCredential()) {
|
|
212
|
+
const retried = prepare(path, opts);
|
|
213
|
+
return {
|
|
214
|
+
response: await executeRaw(retried),
|
|
215
|
+
prepared: retried
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function prepare(path, opts = {}) {
|
|
223
|
+
const method = opts.method ?? "GET";
|
|
224
|
+
const expectContentType = opts.expectContentType ?? "json";
|
|
225
|
+
const url = buildUrl(baseUrl, path, opts.query);
|
|
226
|
+
const headers = new Headers();
|
|
227
|
+
const auth = credentialAuthHeader(credential);
|
|
228
|
+
headers.set(auth.name, auth.value);
|
|
229
|
+
headers.set("accept", acceptHeader(expectContentType));
|
|
230
|
+
headers.set("user-agent", options.userAgent);
|
|
231
|
+
let body = null;
|
|
232
|
+
if (opts.body !== void 0 && opts.body !== null) if (typeof opts.body === "string" || opts.body instanceof URLSearchParams) body = opts.body;
|
|
233
|
+
else if (opts.body instanceof FormData || opts.body instanceof ReadableStream) body = opts.body;
|
|
234
|
+
else if (opts.body instanceof Uint8Array) {
|
|
235
|
+
body = opts.body;
|
|
236
|
+
headers.set("content-type", OCTET_STREAM_CONTENT_TYPE);
|
|
237
|
+
} else {
|
|
238
|
+
body = JSON.stringify(opts.body);
|
|
239
|
+
headers.set("content-type", JSON_CONTENT_TYPE);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
url,
|
|
243
|
+
method,
|
|
244
|
+
headers,
|
|
245
|
+
body,
|
|
246
|
+
expectContentType,
|
|
247
|
+
retries: opts.retries ?? 3,
|
|
248
|
+
idempotent: opts.idempotent ?? IDEMPOTENT_METHODS.has(method),
|
|
249
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
250
|
+
cancelSignal: combineAborts(opts.signal, options.signal)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async function readJson(path, opts = {}) {
|
|
254
|
+
const chosenFor = inForce;
|
|
255
|
+
const { response, prepared } = await executeWithAuthRefresh(path, {
|
|
256
|
+
...opts,
|
|
257
|
+
expectContentType: "json"
|
|
258
|
+
});
|
|
259
|
+
return {
|
|
260
|
+
text: await response.text(),
|
|
261
|
+
context: {
|
|
262
|
+
method: prepared.method,
|
|
263
|
+
url: prepared.url,
|
|
264
|
+
status: response.status,
|
|
265
|
+
...namingOf(chosenFor)
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function namingOf(profile) {
|
|
270
|
+
if (profile === null) return {
|
|
271
|
+
getServerTag,
|
|
272
|
+
serverSkew: null
|
|
273
|
+
};
|
|
274
|
+
return {
|
|
275
|
+
getServerTag: async () => profile.version.tag,
|
|
276
|
+
serverSkew: profile.skew
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
async function runProbe(order, budget) {
|
|
280
|
+
const read = await readJson(PROBE_PATH, probeBudget(budget));
|
|
281
|
+
const properties = await parseJsonResponse(read.text, SessionProperties, read.context);
|
|
282
|
+
const info = serverInfoFromProperties(properties);
|
|
283
|
+
const profile = createServerProfile(info);
|
|
284
|
+
if (applied === null || applied.order < order) {
|
|
285
|
+
options.onServerProbed?.(info, profile);
|
|
286
|
+
applied = {
|
|
287
|
+
order,
|
|
288
|
+
profile
|
|
289
|
+
};
|
|
290
|
+
inForce = profile;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
properties,
|
|
294
|
+
profile,
|
|
295
|
+
read
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function startProbe(budget) {
|
|
299
|
+
probesStarted += 1;
|
|
300
|
+
const probed = runProbe(probesStarted, budget);
|
|
301
|
+
newest = probed;
|
|
302
|
+
inFlight.add(probed);
|
|
303
|
+
probed.then(() => {
|
|
304
|
+
inFlight.delete(probed);
|
|
305
|
+
}, () => {
|
|
306
|
+
inFlight.delete(probed);
|
|
307
|
+
if (newest === probed) newest = null;
|
|
308
|
+
});
|
|
309
|
+
return probed;
|
|
310
|
+
}
|
|
311
|
+
function appliedProfile() {
|
|
312
|
+
return applied === null ? null : applied.profile;
|
|
313
|
+
}
|
|
314
|
+
function verifying() {
|
|
315
|
+
const settled = appliedProfile();
|
|
316
|
+
if (newest === null && settled !== null) return Promise.resolve(settled);
|
|
317
|
+
return (newest ?? startProbe({})).then((probed) => probed.profile, fallBackTo(appliedProfile));
|
|
318
|
+
}
|
|
319
|
+
function profileInForce() {
|
|
320
|
+
const current = inForce;
|
|
321
|
+
if (current === null) return verifying();
|
|
322
|
+
if (newest === null) return Promise.resolve(current);
|
|
323
|
+
return newest.then((probed) => probed.profile, fallBackTo(() => inForce));
|
|
324
|
+
}
|
|
325
|
+
async function server(wait = {}) {
|
|
326
|
+
throwIfAborted(wait.signal);
|
|
327
|
+
return untilAborted(profileInForce(), wait.signal);
|
|
328
|
+
}
|
|
329
|
+
async function verifiedServer(wait = {}) {
|
|
330
|
+
throwIfAborted(wait.signal);
|
|
331
|
+
return untilAborted(verifying(), wait.signal);
|
|
332
|
+
}
|
|
333
|
+
async function probe(reader, read = {}) {
|
|
334
|
+
const { signal, ...budget } = read;
|
|
335
|
+
throwIfAborted(signal);
|
|
336
|
+
const probed = await untilAborted(startProbe(budget), signal);
|
|
337
|
+
const context = {
|
|
338
|
+
...probed.read.context,
|
|
339
|
+
...namingOf(probed.profile)
|
|
340
|
+
};
|
|
341
|
+
return parseJsonResponse(probed.read.text, reader, context);
|
|
342
|
+
}
|
|
343
|
+
async function probesSettled() {
|
|
344
|
+
while (inFlight.size > 0) await Promise.allSettled(inFlight);
|
|
345
|
+
}
|
|
346
|
+
async function requireFeatures(features, wait = {}) {
|
|
347
|
+
if (features.length === 0) return;
|
|
348
|
+
const profile = await preflightServer(wait);
|
|
349
|
+
if (profile === null) return;
|
|
350
|
+
for (const failure of featureFailures(features, profile)) refuseBeforeSending(new CapabilityError(failure));
|
|
351
|
+
}
|
|
352
|
+
async function preflightServer(wait = {}) {
|
|
353
|
+
try {
|
|
354
|
+
return await verifiedServer(wait);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if (!isNonInterruptFailure(error)) throw error;
|
|
357
|
+
skipOrThrow({
|
|
358
|
+
kind: "unverified",
|
|
359
|
+
failure: error
|
|
360
|
+
});
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function refuseBeforeSending(refusal) {
|
|
365
|
+
skipOrThrow({
|
|
366
|
+
kind: "refused",
|
|
367
|
+
refusal
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function skipOrThrow(skipped) {
|
|
371
|
+
const hook = options.onPreflightSkipped;
|
|
372
|
+
if (hook === void 0) throw skippedError(skipped);
|
|
373
|
+
hook(skipped);
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
server,
|
|
377
|
+
verifiedServer,
|
|
378
|
+
probe,
|
|
379
|
+
requireFeatures,
|
|
380
|
+
preflightServer,
|
|
381
|
+
refuseBeforeSending,
|
|
382
|
+
probesSettled,
|
|
383
|
+
async requestRaw(path, opts) {
|
|
384
|
+
return (await executeWithAuthRefresh(path, opts ?? {})).response;
|
|
385
|
+
},
|
|
386
|
+
async requestParsed(schema, path, opts) {
|
|
387
|
+
const read = await readJson(path, opts);
|
|
388
|
+
return parseJsonResponse(read.text, schema, read.context);
|
|
389
|
+
},
|
|
390
|
+
async requestStream(path, opts) {
|
|
391
|
+
const { response, prepared } = await executeWithAuthRefresh(path, {
|
|
392
|
+
...opts,
|
|
393
|
+
expectContentType: opts?.expectContentType ?? "binary"
|
|
394
|
+
});
|
|
395
|
+
if (!response.body) throw new NetworkError("Response had no body to stream", {
|
|
396
|
+
method: prepared.method,
|
|
397
|
+
url: prepared.url,
|
|
398
|
+
cause: "missing body"
|
|
399
|
+
});
|
|
400
|
+
return response.body;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function skippedError(skipped) {
|
|
405
|
+
return skipped.kind === "refused" ? skipped.refusal : skipped.failure;
|
|
406
|
+
}
|
|
407
|
+
function fallBackTo(fallback) {
|
|
408
|
+
return (error) => {
|
|
409
|
+
const profile = fallback();
|
|
410
|
+
if (profile !== null && isNonInterruptFailure(error)) return profile;
|
|
411
|
+
throw error;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function tagOf(profile) {
|
|
415
|
+
return profile === null ? null : profile.version.tag;
|
|
416
|
+
}
|
|
417
|
+
function buildUrl(baseUrl, path, query) {
|
|
418
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
419
|
+
const target = new URL(baseUrl + normalizedPath);
|
|
420
|
+
if (query) for (const [key, value] of Object.entries(query)) {
|
|
421
|
+
if (value === void 0) continue;
|
|
422
|
+
if (Array.isArray(value)) for (const entry of value) target.searchParams.append(key, String(entry));
|
|
423
|
+
else target.searchParams.append(key, String(value));
|
|
424
|
+
}
|
|
425
|
+
return target.toString();
|
|
426
|
+
}
|
|
427
|
+
function acceptHeader(expected) {
|
|
428
|
+
if (expected === "json") return JSON_CONTENT_TYPE;
|
|
429
|
+
if (expected === "text") return "text/*";
|
|
430
|
+
return "*/*";
|
|
431
|
+
}
|
|
432
|
+
function assertContentType(response, prepared) {
|
|
433
|
+
if (prepared.expectContentType === "binary") return;
|
|
434
|
+
const contentType = response.headers.get("content-type");
|
|
435
|
+
if (contentType === null) throwContentTypeMismatch(response, prepared, prepared.expectContentType);
|
|
436
|
+
if (prepared.expectContentType === "json" && !contentType.includes("application/json")) throwContentTypeMismatch(response, prepared, "json");
|
|
437
|
+
if (prepared.expectContentType === "text" && !contentType.startsWith(TEXT_CONTENT_TYPE_PREFIX)) throwContentTypeMismatch(response, prepared, "text");
|
|
438
|
+
}
|
|
439
|
+
function throwContentTypeMismatch(response, prepared, expected) {
|
|
440
|
+
const actual = response.headers.get("content-type") ?? "no content-type";
|
|
441
|
+
throw new HttpError({
|
|
442
|
+
status: response.status,
|
|
443
|
+
statusText: response.statusText,
|
|
444
|
+
method: prepared.method,
|
|
445
|
+
url: prepared.url,
|
|
446
|
+
responseHeaders: response.headers,
|
|
447
|
+
rawBody: null,
|
|
448
|
+
overrideUserMessage: `Expected ${expected} response but got ${actual}`
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
async function readBodyForError(response) {
|
|
452
|
+
try {
|
|
453
|
+
return Buffer.from(await response.arrayBuffer()).subarray(0, ERROR_BODY_BYTE_CAP).toString("utf8");
|
|
454
|
+
} catch (error) {
|
|
455
|
+
return `[body read failed: ${errorMessage(error)}]`;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
//#endregion
|
|
48
459
|
//#region ../client/src/resources/pivot-export.ts
|
|
49
|
-
const
|
|
460
|
+
const PivotedExportsProperty = SessionProperties.extend({ "enable-pivoted-exports": z.boolean() });
|
|
50
461
|
/**
|
|
51
462
|
* Refuse a pivoted export the server would answer with plain rows: a JSON export keeps only the
|
|
52
463
|
* ungrouped rows, and an admin can turn pivoted CSV and XLSX exports off.
|
|
53
464
|
*/
|
|
54
465
|
async function assertPivotedExport(transport, format, options) {
|
|
55
466
|
if (!PivotExportFormat.safeParse(format).success) throw new ConfigError(`a ${format} export cannot be pivoted; only ${PivotExportFormat.options.join(" and ")} can`);
|
|
56
|
-
if (!(await transport.
|
|
467
|
+
if (!(await transport.probe(PivotedExportsProperty, options))["enable-pivoted-exports"]) throw new ConfigError("the server has pivoted exports turned off (the enable-pivoted-exports setting), so it would export the plain rows");
|
|
57
468
|
}
|
|
58
469
|
//#endregion
|
|
59
470
|
//#region ../client/src/resources/card.ts
|
|
@@ -61,7 +472,6 @@ const CardApiList = z.array(Card);
|
|
|
61
472
|
function cardResource(transport) {
|
|
62
473
|
/** List cards. `f` picks a server-side preset; `model_id` scopes the presets that need an id. */
|
|
63
474
|
async function list(params = {}, options = {}) {
|
|
64
|
-
await transport.require("card.list", options);
|
|
65
475
|
return {
|
|
66
476
|
data: await transport.requestParsed(CardApiList, "/api/card", {
|
|
67
477
|
...options,
|
|
@@ -75,12 +485,10 @@ function cardResource(transport) {
|
|
|
75
485
|
}
|
|
76
486
|
/** Get one card by id. */
|
|
77
487
|
async function get(id, options = {}) {
|
|
78
|
-
await transport.require("card.get", options);
|
|
79
488
|
return transport.requestParsed(Card, `/api/card/${id}`, { ...options });
|
|
80
489
|
}
|
|
81
490
|
/** Create a card — a question, a model, or a metric — from a full card body. */
|
|
82
491
|
async function create(params, options = {}) {
|
|
83
|
-
await transport.require("card.create", options);
|
|
84
492
|
return transport.requestParsed(Card, "/api/card", {
|
|
85
493
|
...options,
|
|
86
494
|
method: "POST",
|
|
@@ -89,7 +497,6 @@ function cardResource(transport) {
|
|
|
89
497
|
}
|
|
90
498
|
/** Update a card by id, patching only the fields the body carries. */
|
|
91
499
|
async function update(id, params, options = {}) {
|
|
92
|
-
await transport.require("card.update", options);
|
|
93
500
|
return transport.requestParsed(Card, `/api/card/${id}`, {
|
|
94
501
|
...options,
|
|
95
502
|
method: "PUT",
|
|
@@ -98,12 +505,10 @@ function cardResource(transport) {
|
|
|
98
505
|
}
|
|
99
506
|
/** Archive (soft-delete) a card by id. Metabase models this as an update, not its own endpoint. */
|
|
100
507
|
async function archive(id, options = {}) {
|
|
101
|
-
await transport.require("card.archive", options);
|
|
102
508
|
return update(id, { archived: true }, options);
|
|
103
509
|
}
|
|
104
510
|
/** Run a saved card and return the query result envelope. */
|
|
105
511
|
async function query(id, params, options = {}) {
|
|
106
|
-
await transport.require("card.query", options);
|
|
107
512
|
return transport.requestParsed(CardQueryResult, `/api/card/${id}/query`, {
|
|
108
513
|
...options,
|
|
109
514
|
method: "POST",
|
|
@@ -119,9 +524,8 @@ function cardResource(transport) {
|
|
|
119
524
|
* there before the wire.
|
|
120
525
|
*/
|
|
121
526
|
async function exportQuery(id, format, params, options = {}) {
|
|
122
|
-
await transport.require("card.exportQuery", options);
|
|
123
|
-
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
124
527
|
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
528
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
125
529
|
const body = new URLSearchParams({
|
|
126
530
|
parameters: JSON.stringify(params.parameters),
|
|
127
531
|
format_rows: String(params.format_rows),
|
|
@@ -139,7 +543,6 @@ function cardResource(transport) {
|
|
|
139
543
|
* snippets its query references, plus the card's own metadata for a model or a native query.
|
|
140
544
|
*/
|
|
141
545
|
async function queryMetadata(id, options = {}) {
|
|
142
|
-
await transport.require("card.queryMetadata", options);
|
|
143
546
|
return transport.requestParsed(QueryMetadata, `/api/card/${id}/query_metadata`, { ...options });
|
|
144
547
|
}
|
|
145
548
|
return {
|
|
@@ -225,7 +628,6 @@ function collectionResource(transport) {
|
|
|
225
628
|
* preset includes the Library collections that match it.
|
|
226
629
|
*/
|
|
227
630
|
async function list(params = {}, options = {}) {
|
|
228
|
-
await transport.require("collection.list", options);
|
|
229
631
|
return {
|
|
230
632
|
data: await transport.requestParsed(CollectionApiList, "/api/collection", {
|
|
231
633
|
...options,
|
|
@@ -236,12 +638,10 @@ function collectionResource(transport) {
|
|
|
236
638
|
}
|
|
237
639
|
/** Get one collection by id, entity id, or the `root`/`trash` alias. */
|
|
238
640
|
async function get(ref, options = {}) {
|
|
239
|
-
await transport.require("collection.get", options);
|
|
240
641
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, { ...options });
|
|
241
642
|
}
|
|
242
643
|
/** Create a new collection. */
|
|
243
644
|
async function create(params, options = {}) {
|
|
244
|
-
await transport.require("collection.create", options);
|
|
245
645
|
return transport.requestParsed(Collection, "/api/collection", {
|
|
246
646
|
...options,
|
|
247
647
|
method: "POST",
|
|
@@ -254,7 +654,6 @@ function collectionResource(transport) {
|
|
|
254
654
|
* moves a collection into the trash ignores `parent_id`.
|
|
255
655
|
*/
|
|
256
656
|
async function update(ref, params, options = {}) {
|
|
257
|
-
await transport.require("collection.update", options);
|
|
258
657
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, {
|
|
259
658
|
...options,
|
|
260
659
|
method: "PUT",
|
|
@@ -263,7 +662,6 @@ function collectionResource(transport) {
|
|
|
263
662
|
}
|
|
264
663
|
/** Archive (soft-delete) a collection. Metabase models this as an update, not its own endpoint. */
|
|
265
664
|
async function archive(ref, options = {}) {
|
|
266
|
-
await transport.require("collection.archive", options);
|
|
267
665
|
return update(ref, { archived: true }, options);
|
|
268
666
|
}
|
|
269
667
|
/**
|
|
@@ -271,7 +669,6 @@ function collectionResource(transport) {
|
|
|
271
669
|
* the caller consumes pages rather than a single list and decides how far to pull.
|
|
272
670
|
*/
|
|
273
671
|
async function* itemPages(ref, params = {}, options = {}) {
|
|
274
|
-
await transport.require("collection.itemPages", options);
|
|
275
672
|
yield* paginatePages(transport, `/api/collection/${refPath(ref)}/items`, CollectionItem, {
|
|
276
673
|
query: {
|
|
277
674
|
models: params.models,
|
|
@@ -286,7 +683,6 @@ function collectionResource(transport) {
|
|
|
286
683
|
}
|
|
287
684
|
/** Fetch the collection hierarchy as a forest of nested nodes. */
|
|
288
685
|
async function tree(params = {}, options = {}) {
|
|
289
|
-
await transport.require("collection.tree", options);
|
|
290
686
|
return {
|
|
291
687
|
data: await transport.requestParsed(CollectionTreeApiList, "/api/collection/tree", {
|
|
292
688
|
...options,
|
|
@@ -306,6 +702,128 @@ function collectionResource(transport) {
|
|
|
306
702
|
};
|
|
307
703
|
}
|
|
308
704
|
//#endregion
|
|
705
|
+
//#region ../client/src/version/refusal.ts
|
|
706
|
+
const REFUSAL_STATUSES = /* @__PURE__ */ new Set([
|
|
707
|
+
400,
|
|
708
|
+
402,
|
|
709
|
+
404
|
|
710
|
+
]);
|
|
711
|
+
function explainer(transport, namespace) {
|
|
712
|
+
function explain(name, method, parameters) {
|
|
713
|
+
const key = methodKey(namespace, name);
|
|
714
|
+
return async (...args) => {
|
|
715
|
+
try {
|
|
716
|
+
return await method(...args);
|
|
717
|
+
} catch (error) {
|
|
718
|
+
throw await explainRefusal(transport, callRequirement(key, parameters, args), error);
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function explainWalk(name, method, parameters) {
|
|
723
|
+
const key = methodKey(namespace, name);
|
|
724
|
+
return async function* (...args) {
|
|
725
|
+
try {
|
|
726
|
+
yield* method(...args);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
throw await explainRefusal(transport, callRequirement(key, parameters, args), error);
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function refuse(name, method) {
|
|
733
|
+
const key = methodKey(namespace, name);
|
|
734
|
+
return async (...args) => {
|
|
735
|
+
await transport.requireFeatures(methodRequirements(key), trailingWait(args));
|
|
736
|
+
return method(...args);
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function refuseAfterReading(name, reader, method) {
|
|
740
|
+
const key = methodKey(namespace, name);
|
|
741
|
+
return async (options = {}) => {
|
|
742
|
+
const properties = await transport.probe(reader, options);
|
|
743
|
+
const profile = profileFromProperties(properties);
|
|
744
|
+
for (const failure of featureFailures(methodRequirements(key), profile)) transport.refuseBeforeSending(new CapabilityError(failure));
|
|
745
|
+
return method(properties);
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
explain,
|
|
750
|
+
explainWalk,
|
|
751
|
+
refuse,
|
|
752
|
+
refuseAfterReading
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
function trailingWait(args) {
|
|
756
|
+
const last = args.at(-1);
|
|
757
|
+
if (isPlainObject(last) && last["signal"] instanceof AbortSignal) return { signal: last["signal"] };
|
|
758
|
+
return {};
|
|
759
|
+
}
|
|
760
|
+
function methodKey(namespace, name) {
|
|
761
|
+
const key = `${namespace}.${name}`;
|
|
762
|
+
if (!isMethodKey(key)) throw new InternalError(`no requirements entry for ${key}`);
|
|
763
|
+
return key;
|
|
764
|
+
}
|
|
765
|
+
function callRequirement(key, parameters, args) {
|
|
766
|
+
return {
|
|
767
|
+
parameters: parameters === void 0 ? [] : parameters(...args),
|
|
768
|
+
method: methodRequirements(key)
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* The error a call failed with, as a `CapabilityError` carrying the server's answer when the server
|
|
773
|
+
* refused it for lacking one of the features `call` needs, and unchanged otherwise. The server's own
|
|
774
|
+
* refusal is the verdict; the profile only names what was missing. The explaining probe is bounded
|
|
775
|
+
* by its own timeout and the client's signal; the error stands when the probe fails, and an
|
|
776
|
+
* interrupt surfaces as one.
|
|
777
|
+
*/
|
|
778
|
+
async function explainRefusal(transport, call, error) {
|
|
779
|
+
if (!mayBeRefusal(call, error)) return error;
|
|
780
|
+
let profile;
|
|
781
|
+
try {
|
|
782
|
+
profile = await transport.verifiedServer();
|
|
783
|
+
} catch (probeError) {
|
|
784
|
+
if (isNonInterruptFailure(probeError)) return error;
|
|
785
|
+
throw probeError;
|
|
786
|
+
}
|
|
787
|
+
const failure = explainedFailure(call, profile, error);
|
|
788
|
+
return failure === null ? error : new CapabilityError(failure, error);
|
|
789
|
+
}
|
|
790
|
+
/** Every feature `call` needs, parameters first, since a parameter's floor sits above its method's. */
|
|
791
|
+
function callFeatures(call) {
|
|
792
|
+
return [...call.parameters.map((parameter) => parameter.feature), ...call.method];
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Whether `error` may be the server refusing `call` for a feature it lacks. A call that needs no
|
|
796
|
+
* feature cannot be, and neither can a 404 for a missing row, which comes from a route the server
|
|
797
|
+
* serves.
|
|
798
|
+
*/
|
|
799
|
+
function mayBeRefusal(call, error) {
|
|
800
|
+
if (callFeatures(call).length === 0) return false;
|
|
801
|
+
if (!(error instanceof HttpError) || error.kind === "resource-missing") return false;
|
|
802
|
+
return REFUSAL_STATUSES.has(error.status);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* The feature `profile` lacks that explains `refusal` of `call`, or `null` when the server has every
|
|
806
|
+
* feature the call needs, so it failed for some other reason, or when the refusal points elsewhere.
|
|
807
|
+
*/
|
|
808
|
+
function explainedFailure(call, profile, refusal) {
|
|
809
|
+
const failure = checkFeatures(callFeatures(call), profile);
|
|
810
|
+
if (failure === null || pointsElsewhere(call, profile, refusal)) return null;
|
|
811
|
+
return failure;
|
|
812
|
+
}
|
|
813
|
+
function pointsElsewhere(call, profile, refusal) {
|
|
814
|
+
if (refusal.status !== 400 || checkFeatures(call.method, profile) !== null) return false;
|
|
815
|
+
const named = rejectedFields(refusal);
|
|
816
|
+
const gated = new Set(call.parameters.filter((parameter) => checkFeatures([parameter.feature], profile) !== null).flatMap((parameter) => parameter.fields));
|
|
817
|
+
return named.some((field) => !gated.has(field));
|
|
818
|
+
}
|
|
819
|
+
function rejectedFields(refusal) {
|
|
820
|
+
return [refusal.fieldErrors, refusal.specificFieldErrors].flatMap((errors) => errors === null ? [] : Object.keys(errors).map(topLevelField));
|
|
821
|
+
}
|
|
822
|
+
function topLevelField(path) {
|
|
823
|
+
const end = path.indexOf(".");
|
|
824
|
+
return end === -1 ? path : path.slice(0, end);
|
|
825
|
+
}
|
|
826
|
+
//#endregion
|
|
309
827
|
//#region ../client/src/resources/csv-upload.ts
|
|
310
828
|
const CSV_CONTENT_TYPE = "text/csv";
|
|
311
829
|
function buildCsvFormData(file) {
|
|
@@ -317,14 +835,13 @@ function buildCsvFormData(file) {
|
|
|
317
835
|
//#region ../client/src/resources/content-translation.ts
|
|
318
836
|
const CONTENT_TRANSLATION_PATH = "/api/ee/content-translation";
|
|
319
837
|
function contentTranslationResource(transport) {
|
|
838
|
+
const { explain } = explainer(transport, "contentTranslation");
|
|
320
839
|
/** Download the complete content translation dictionary as CSV. */
|
|
321
840
|
async function download(options = {}) {
|
|
322
|
-
await transport.require("contentTranslation.download", options);
|
|
323
841
|
return transport.requestStream(`${CONTENT_TRANSLATION_PATH}/csv`, options);
|
|
324
842
|
}
|
|
325
843
|
/** Replace the complete content translation dictionary with the attached CSV. */
|
|
326
844
|
async function upload(file, options = {}) {
|
|
327
|
-
await transport.require("contentTranslation.upload", options);
|
|
328
845
|
const form = buildCsvFormData(file);
|
|
329
846
|
return transport.requestParsed(ContentTranslationUploadResult, `${CONTENT_TRANSLATION_PATH}/upload-dictionary`, {
|
|
330
847
|
...options,
|
|
@@ -333,8 +850,8 @@ function contentTranslationResource(transport) {
|
|
|
333
850
|
});
|
|
334
851
|
}
|
|
335
852
|
return {
|
|
336
|
-
download,
|
|
337
|
-
upload
|
|
853
|
+
download: explain("download", download),
|
|
854
|
+
upload: explain("upload", upload)
|
|
338
855
|
};
|
|
339
856
|
}
|
|
340
857
|
//#endregion
|
|
@@ -345,7 +862,6 @@ function dashboardResource(transport) {
|
|
|
345
862
|
const card = cardResource(transport);
|
|
346
863
|
/** List dashboards. `f` picks a server-side preset. */
|
|
347
864
|
async function list(params = {}, options = {}) {
|
|
348
|
-
await transport.require("dashboard.list", options);
|
|
349
865
|
return {
|
|
350
866
|
data: await transport.requestParsed(DashboardApiList, "/api/dashboard", {
|
|
351
867
|
...options,
|
|
@@ -356,7 +872,6 @@ function dashboardResource(transport) {
|
|
|
356
872
|
}
|
|
357
873
|
/** Get one dashboard by id, with its dashcards, tabs and parameters. */
|
|
358
874
|
async function get(id, options = {}) {
|
|
359
|
-
await transport.require("dashboard.get", options);
|
|
360
875
|
return transport.requestParsed(DashboardDetail, `/api/dashboard/${id}`, { ...options });
|
|
361
876
|
}
|
|
362
877
|
/**
|
|
@@ -364,7 +879,6 @@ function dashboardResource(transport) {
|
|
|
364
879
|
* puts content on a fresh dashboard.
|
|
365
880
|
*/
|
|
366
881
|
async function create(params, options = {}) {
|
|
367
|
-
await transport.require("dashboard.create", options);
|
|
368
882
|
return transport.requestParsed(Dashboard, "/api/dashboard", {
|
|
369
883
|
...options,
|
|
370
884
|
method: "POST",
|
|
@@ -376,7 +890,6 @@ function dashboardResource(transport) {
|
|
|
376
890
|
* full replacements: whatever the body omits is deleted from the dashboard.
|
|
377
891
|
*/
|
|
378
892
|
async function update(id, params, options = {}) {
|
|
379
|
-
await transport.require("dashboard.update", options);
|
|
380
893
|
return transport.requestParsed(DashboardDetail, `/api/dashboard/${id}`, {
|
|
381
894
|
...options,
|
|
382
895
|
method: "PUT",
|
|
@@ -394,7 +907,6 @@ function dashboardResource(transport) {
|
|
|
394
907
|
* is duplicated. Action, link and placeholder dashcards are not copied.
|
|
395
908
|
*/
|
|
396
909
|
async function copy(id, params = {}, options = {}) {
|
|
397
|
-
await transport.require("dashboard.copy", options);
|
|
398
910
|
return transport.requestParsed(DashboardCopy, `/api/dashboard/${id}/copy`, {
|
|
399
911
|
...options,
|
|
400
912
|
method: "POST",
|
|
@@ -407,7 +919,6 @@ function dashboardResource(transport) {
|
|
|
407
919
|
* an archive is a state change on the dashboard itself, and its dashcards are not part of it.
|
|
408
920
|
*/
|
|
409
921
|
async function archive(id, options = {}) {
|
|
410
|
-
await transport.require("dashboard.archive", options);
|
|
411
922
|
return transport.requestParsed(Dashboard, `/api/dashboard/${id}`, {
|
|
412
923
|
...options,
|
|
413
924
|
method: "PUT",
|
|
@@ -420,7 +931,6 @@ function dashboardResource(transport) {
|
|
|
420
931
|
* currently stands.
|
|
421
932
|
*/
|
|
422
933
|
async function updateDashcard(dashboardId, dashcardId, params, options = {}) {
|
|
423
|
-
await transport.require("dashboard.updateDashcard", options);
|
|
424
934
|
const dashboard = await get(dashboardId, options);
|
|
425
935
|
const target = dashboard.dashcards.find((dashcard) => dashcard.id === dashcardId);
|
|
426
936
|
if (target === void 0) throw new ConfigError(`dashcard ${dashcardId} not found on dashboard ${dashboardId}`);
|
|
@@ -439,7 +949,6 @@ function dashboardResource(transport) {
|
|
|
439
949
|
* any other way propagates as the failure it was.
|
|
440
950
|
*/
|
|
441
951
|
async function checkCardReferences(dashcards, options = {}) {
|
|
442
|
-
await transport.require("dashboard.checkCardReferences", options);
|
|
443
952
|
const grouped = groupByCardId(collectCardReferences(dashcards));
|
|
444
953
|
return (await Promise.all(Array.from(grouped.entries()).map(async ([cardId, references]) => ({
|
|
445
954
|
references,
|
|
@@ -459,7 +968,7 @@ function dashboardResource(transport) {
|
|
|
459
968
|
} catch (error) {
|
|
460
969
|
if (!(error instanceof HttpError)) throw error;
|
|
461
970
|
if (error.kind === "resource-missing") return { reason: "missing" };
|
|
462
|
-
if (error.kind === "auth") return {
|
|
971
|
+
if (error.kind === "auth" || error.kind === "forbidden") return {
|
|
463
972
|
reason: "unreadable",
|
|
464
973
|
detail: error.userMessage
|
|
465
974
|
};
|
|
@@ -470,7 +979,6 @@ function dashboardResource(transport) {
|
|
|
470
979
|
* Fetch the selectable values for a dashboard parameter, through the chain-filter value endpoint.
|
|
471
980
|
*/
|
|
472
981
|
async function parameterValues(dashboardId, parameterId, options = {}) {
|
|
473
|
-
await transport.require("dashboard.parameterValues", options);
|
|
474
982
|
return transport.requestParsed(ParameterValues, `/api/dashboard/${dashboardId}/params/${encodeURIComponent(parameterId)}/values`, { ...options });
|
|
475
983
|
}
|
|
476
984
|
/**
|
|
@@ -478,7 +986,6 @@ function dashboardResource(transport) {
|
|
|
478
986
|
* substring, capped at the first 1000 matches.
|
|
479
987
|
*/
|
|
480
988
|
async function searchParameterValues(dashboardId, parameterId, query, options = {}) {
|
|
481
|
-
await transport.require("dashboard.searchParameterValues", options);
|
|
482
989
|
const parameter = encodeURIComponent(parameterId);
|
|
483
990
|
return transport.requestParsed(ParameterValues, `/api/dashboard/${dashboardId}/params/${parameter}/search/${encodeURIComponent(query)}`, { ...options });
|
|
484
991
|
}
|
|
@@ -542,7 +1049,6 @@ const SYNC_COMPLETE = "complete";
|
|
|
542
1049
|
function databaseResource(transport) {
|
|
543
1050
|
/** List databases. `include` hydrates each database's tables; `saved` adds the Saved Questions virtual database. */
|
|
544
1051
|
async function list(params = {}, options = {}) {
|
|
545
|
-
await transport.require("database.list", options);
|
|
546
1052
|
const response = await transport.requestParsed(DatabaseApiList, "/api/database", {
|
|
547
1053
|
...options,
|
|
548
1054
|
query: {
|
|
@@ -557,7 +1063,6 @@ function databaseResource(transport) {
|
|
|
557
1063
|
}
|
|
558
1064
|
/** Get one database by id. `include` hydrates its tables, and `tables.fields` their fields too. */
|
|
559
1065
|
async function get(id, params = {}, options = {}) {
|
|
560
|
-
await transport.require("database.get", options);
|
|
561
1066
|
return transport.requestParsed(Database, `/api/database/${id}`, {
|
|
562
1067
|
...options,
|
|
563
1068
|
query: { include: params.include }
|
|
@@ -565,7 +1070,6 @@ function databaseResource(transport) {
|
|
|
565
1070
|
}
|
|
566
1071
|
/** List the schema names in a database. */
|
|
567
1072
|
async function schemas(id, options = {}) {
|
|
568
|
-
await transport.require("database.schemas", options);
|
|
569
1073
|
return {
|
|
570
1074
|
data: await transport.requestParsed(DatabaseApiSchemaList, `/api/database/${id}/schemas`, { ...options }),
|
|
571
1075
|
total: null
|
|
@@ -573,7 +1077,6 @@ function databaseResource(transport) {
|
|
|
573
1077
|
}
|
|
574
1078
|
/** List the tables one schema of a database holds. */
|
|
575
1079
|
async function schemaTables(id, schema, options = {}) {
|
|
576
|
-
await transport.require("database.schemaTables", options);
|
|
577
1080
|
return {
|
|
578
1081
|
data: await transport.requestParsed(DatabaseApiSchemaTableList, `/api/database/${id}/schema/${encodeURIComponent(schema)}`, { ...options }),
|
|
579
1082
|
total: null
|
|
@@ -584,7 +1087,6 @@ function databaseResource(transport) {
|
|
|
584
1087
|
* pass `wait` to poll the database until it reports its initial sync complete.
|
|
585
1088
|
*/
|
|
586
1089
|
async function syncSchema(id, params = {}, options = {}) {
|
|
587
|
-
await transport.require("database.syncSchema", options);
|
|
588
1090
|
const ack = await transport.requestParsed(DatabaseTaskAck, `/api/database/${id}/sync_schema`, {
|
|
589
1091
|
...options,
|
|
590
1092
|
method: "POST"
|
|
@@ -606,7 +1108,6 @@ function databaseResource(transport) {
|
|
|
606
1108
|
}
|
|
607
1109
|
/** Trigger a rescan of the cached field values across a database. */
|
|
608
1110
|
async function rescanValues(id, options = {}) {
|
|
609
|
-
await transport.require("database.rescanValues", options);
|
|
610
1111
|
return {
|
|
611
1112
|
id,
|
|
612
1113
|
status: (await transport.requestParsed(DatabaseTaskAck, `/api/database/${id}/rescan_values`, {
|
|
@@ -632,7 +1133,6 @@ function datasetResource(transport) {
|
|
|
632
1133
|
* MBQL, or native — rather than a reference to a saved one, so nothing here is a card.
|
|
633
1134
|
*/
|
|
634
1135
|
async function query(body, options = {}) {
|
|
635
|
-
await transport.require("dataset.query", options);
|
|
636
1136
|
return transport.requestParsed(CardQueryResult, "/api/dataset", {
|
|
637
1137
|
...options,
|
|
638
1138
|
method: "POST",
|
|
@@ -644,9 +1144,7 @@ function datasetResource(transport) {
|
|
|
644
1144
|
* defaults server-side to true.
|
|
645
1145
|
*/
|
|
646
1146
|
async function native(datasetQuery, params = {}, options = {}) {
|
|
647
|
-
|
|
648
|
-
const { features } = await transport.server(options);
|
|
649
|
-
return transport.requestParsed(compiledQuerySchema(features), "/api/dataset/native", {
|
|
1147
|
+
return transport.requestParsed(CompiledQuery, "/api/dataset/native", {
|
|
650
1148
|
...options,
|
|
651
1149
|
method: "POST",
|
|
652
1150
|
body: {
|
|
@@ -661,7 +1159,6 @@ function datasetResource(transport) {
|
|
|
661
1159
|
* widens the field filter.
|
|
662
1160
|
*/
|
|
663
1161
|
async function queryMetadata(datasetQuery, options = {}) {
|
|
664
|
-
await transport.require("dataset.queryMetadata", options);
|
|
665
1162
|
return transport.requestParsed(QueryMetadata, "/api/dataset/query_metadata", {
|
|
666
1163
|
...options,
|
|
667
1164
|
method: "POST",
|
|
@@ -680,9 +1177,8 @@ function datasetResource(transport) {
|
|
|
680
1177
|
* there before the wire.
|
|
681
1178
|
*/
|
|
682
1179
|
async function exportQuery(format, params, options = {}) {
|
|
683
|
-
await transport.require("dataset.exportQuery", options);
|
|
684
|
-
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
685
1180
|
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
1181
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
686
1182
|
const exported = params.pivot_results ? {
|
|
687
1183
|
...params.query,
|
|
688
1184
|
"was-pivot": true
|
|
@@ -710,15 +1206,20 @@ function datasetResource(transport) {
|
|
|
710
1206
|
//#region ../client/src/resources/dependency.ts
|
|
711
1207
|
const DependencyNodeApiList = z.array(DependencyNode);
|
|
712
1208
|
const DependencyEntityApiList = z.array(DependencyEntity);
|
|
1209
|
+
function graphTypeFeatures(type) {
|
|
1210
|
+
return type === "measure" ? [{
|
|
1211
|
+
feature: "measureDependencyGraph",
|
|
1212
|
+
fields: ["type"]
|
|
1213
|
+
}] : [];
|
|
1214
|
+
}
|
|
713
1215
|
function dependencyResource(transport) {
|
|
1216
|
+
const { explain, explainWalk } = explainer(transport, "dependency");
|
|
714
1217
|
/**
|
|
715
1218
|
* The upstream dependency graph of one entity. `nodes` holds the starting entity plus every
|
|
716
1219
|
* entity it depends on, directly or transitively; each edge runs from the dependent to what it
|
|
717
1220
|
* depends on. A measure starts a graph only on a server with `measureDependencyGraph`.
|
|
718
1221
|
*/
|
|
719
1222
|
async function graph(type, id, options = {}) {
|
|
720
|
-
await transport.require("dependency.graph", options);
|
|
721
|
-
if (type === "measure") await transport.requireFeatures(["measureDependencyGraph"], options);
|
|
722
1223
|
return transport.requestParsed(DependencyGraph, "/api/ee/dependencies/graph", {
|
|
723
1224
|
...options,
|
|
724
1225
|
query: {
|
|
@@ -734,7 +1235,6 @@ function dependencyResource(transport) {
|
|
|
734
1235
|
* personal collections, and `sort-column` / `sort-direction` order the result.
|
|
735
1236
|
*/
|
|
736
1237
|
async function dependents(type, id, params = {}, options = {}) {
|
|
737
|
-
await transport.require("dependency.dependents", options);
|
|
738
1238
|
return {
|
|
739
1239
|
data: await transport.requestParsed(DependencyNodeApiList, "/api/ee/dependencies/graph/dependents", {
|
|
740
1240
|
...options,
|
|
@@ -760,7 +1260,6 @@ function dependencyResource(transport) {
|
|
|
760
1260
|
* `sort-direction` order the result.
|
|
761
1261
|
*/
|
|
762
1262
|
async function broken(type, id, params = {}, options = {}) {
|
|
763
|
-
await transport.require("dependency.broken", options);
|
|
764
1263
|
return {
|
|
765
1264
|
data: await transport.requestParsed(DependencyEntityApiList, "/api/ee/dependencies/graph/broken", {
|
|
766
1265
|
...options,
|
|
@@ -787,7 +1286,6 @@ function dependencyResource(transport) {
|
|
|
787
1286
|
* This endpoint pages on the server, so the caller consumes pages and decides how far to pull.
|
|
788
1287
|
*/
|
|
789
1288
|
async function* unreferencedPages(params = {}, options = {}) {
|
|
790
|
-
await transport.require("dependency.unreferencedPages", options);
|
|
791
1289
|
if (queryLeavesNoKind(params)) {
|
|
792
1290
|
yield {
|
|
793
1291
|
items: [],
|
|
@@ -811,7 +1309,6 @@ function dependencyResource(transport) {
|
|
|
811
1309
|
* caller consumes pages and decides how far to pull.
|
|
812
1310
|
*/
|
|
813
1311
|
async function* breakingPages(params = {}, options = {}) {
|
|
814
|
-
await transport.require("dependency.breakingPages", options);
|
|
815
1312
|
if (queryLeavesNoKind(params)) {
|
|
816
1313
|
yield {
|
|
817
1314
|
items: [],
|
|
@@ -828,11 +1325,11 @@ function dependencyResource(transport) {
|
|
|
828
1325
|
});
|
|
829
1326
|
}
|
|
830
1327
|
return {
|
|
831
|
-
graph,
|
|
832
|
-
dependents,
|
|
833
|
-
broken,
|
|
834
|
-
unreferencedPages,
|
|
835
|
-
breakingPages
|
|
1328
|
+
graph: explain("graph", graph, graphTypeFeatures),
|
|
1329
|
+
dependents: explain("dependents", dependents),
|
|
1330
|
+
broken: explain("broken", broken),
|
|
1331
|
+
unreferencedPages: explainWalk("unreferencedPages", unreferencedPages),
|
|
1332
|
+
breakingPages: explainWalk("breakingPages", breakingPages)
|
|
836
1333
|
};
|
|
837
1334
|
}
|
|
838
1335
|
function queryLeavesNoKind(params) {
|
|
@@ -854,9 +1351,9 @@ function itemListQuery(params) {
|
|
|
854
1351
|
//#region ../client/src/resources/document.ts
|
|
855
1352
|
const DocumentApiList = z.object({ items: z.array(Document) }).loose();
|
|
856
1353
|
function documentResource(transport) {
|
|
1354
|
+
const { explain } = explainer(transport, "document");
|
|
857
1355
|
/** Get existing documents. */
|
|
858
1356
|
async function list(options = {}) {
|
|
859
|
-
await transport.require("document.list", options);
|
|
860
1357
|
return {
|
|
861
1358
|
data: (await transport.requestParsed(DocumentApiList, "/api/document", { ...options })).items,
|
|
862
1359
|
total: null
|
|
@@ -864,12 +1361,10 @@ function documentResource(transport) {
|
|
|
864
1361
|
}
|
|
865
1362
|
/** Return an existing document by id. */
|
|
866
1363
|
async function get(id, options = {}) {
|
|
867
|
-
await transport.require("document.get", options);
|
|
868
1364
|
return transport.requestParsed(Document, `/api/document/${id}`, { ...options });
|
|
869
1365
|
}
|
|
870
1366
|
/** Create a new document. */
|
|
871
1367
|
async function create(params, options = {}) {
|
|
872
|
-
await transport.require("document.create", options);
|
|
873
1368
|
return transport.requestParsed(Document, "/api/document", {
|
|
874
1369
|
...options,
|
|
875
1370
|
method: "POST",
|
|
@@ -878,7 +1373,6 @@ function documentResource(transport) {
|
|
|
878
1373
|
}
|
|
879
1374
|
/** Update an existing document by id. */
|
|
880
1375
|
async function update(id, params, options = {}) {
|
|
881
|
-
await transport.require("document.update", options);
|
|
882
1376
|
return transport.requestParsed(Document, `/api/document/${id}`, {
|
|
883
1377
|
...options,
|
|
884
1378
|
method: "PUT",
|
|
@@ -887,7 +1381,6 @@ function documentResource(transport) {
|
|
|
887
1381
|
}
|
|
888
1382
|
/** Archive (soft-delete) a document by id. Metabase models this as an update, not its own endpoint. */
|
|
889
1383
|
async function archive(id, options = {}) {
|
|
890
|
-
await transport.require("document.archive", options);
|
|
891
1384
|
return update(id, { archived: true }, options);
|
|
892
1385
|
}
|
|
893
1386
|
/**
|
|
@@ -895,7 +1388,6 @@ function documentResource(transport) {
|
|
|
895
1388
|
* it. An archived source is not found.
|
|
896
1389
|
*/
|
|
897
1390
|
async function copy(id, params = {}, options = {}) {
|
|
898
|
-
await transport.require("document.copy", options);
|
|
899
1391
|
return transport.requestParsed(Document, `/api/document/${id}/copy`, {
|
|
900
1392
|
...options,
|
|
901
1393
|
method: "POST",
|
|
@@ -908,7 +1400,7 @@ function documentResource(transport) {
|
|
|
908
1400
|
create,
|
|
909
1401
|
update,
|
|
910
1402
|
archive,
|
|
911
|
-
copy
|
|
1403
|
+
copy: explain("copy", copy)
|
|
912
1404
|
};
|
|
913
1405
|
}
|
|
914
1406
|
//#endregion
|
|
@@ -920,7 +1412,6 @@ function eidTranslationResource(transport) {
|
|
|
920
1412
|
* lookup succeeded — the numeric id.
|
|
921
1413
|
*/
|
|
922
1414
|
async function translate(params, options = {}) {
|
|
923
|
-
await transport.require("eidTranslation.translate", options);
|
|
924
1415
|
return transport.requestParsed(EidTranslateResult, "/api/eid-translation/translate", {
|
|
925
1416
|
...options,
|
|
926
1417
|
method: "POST",
|
|
@@ -974,6 +1465,7 @@ const Erd = z.object({
|
|
|
974
1465
|
//#region ../client/src/resources/erd.ts
|
|
975
1466
|
const ERD_PATH = "/api/ee/erd";
|
|
976
1467
|
function erdResource(transport) {
|
|
1468
|
+
const { explain } = explainer(transport, "erd");
|
|
977
1469
|
/**
|
|
978
1470
|
* Get the entity relationship diagram of a database: the readable tables as nodes, each with its
|
|
979
1471
|
* fields, and the foreign keys between them as edges. `table-ids` makes those tables the focal
|
|
@@ -981,7 +1473,6 @@ function erdResource(transport) {
|
|
|
981
1473
|
* none.
|
|
982
1474
|
*/
|
|
983
1475
|
async function get(params, options = {}) {
|
|
984
|
-
await transport.require("erd.get", options);
|
|
985
1476
|
return transport.requestParsed(Erd, ERD_PATH, {
|
|
986
1477
|
...options,
|
|
987
1478
|
query: {
|
|
@@ -991,7 +1482,7 @@ function erdResource(transport) {
|
|
|
991
1482
|
}
|
|
992
1483
|
});
|
|
993
1484
|
}
|
|
994
|
-
return { get };
|
|
1485
|
+
return { get: explain("get", get) };
|
|
995
1486
|
}
|
|
996
1487
|
//#endregion
|
|
997
1488
|
//#region ../client/src/resources/optional-parsed.ts
|
|
@@ -1009,12 +1500,12 @@ async function fetchOptionalParsed(client, path, schema, options = {}) {
|
|
|
1009
1500
|
//#region ../client/src/resources/field.ts
|
|
1010
1501
|
const FieldApiSummary = z.tuple([z.tuple([z.literal("count"), z.number().int()]), z.tuple([z.literal("distincts"), z.number().int()])]);
|
|
1011
1502
|
function fieldResource(transport) {
|
|
1503
|
+
const { refuse } = explainer(transport, "field");
|
|
1012
1504
|
/**
|
|
1013
1505
|
* Get one field by id. A server that labels data sensitivity answers `data_sensitivity`, `null`
|
|
1014
1506
|
* for an unlabelled field; any other answers the field without the key (`hasDataSensitivity`).
|
|
1015
1507
|
*/
|
|
1016
1508
|
async function get(id, options = {}) {
|
|
1017
|
-
await transport.require("field.get", options);
|
|
1018
1509
|
const { features } = await transport.server(options);
|
|
1019
1510
|
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, { ...options });
|
|
1020
1511
|
}
|
|
@@ -1025,7 +1516,6 @@ function fieldResource(transport) {
|
|
|
1025
1516
|
* the label is refused there before the wire. Answers the field the way `get` does.
|
|
1026
1517
|
*/
|
|
1027
1518
|
async function update(id, params, options = {}) {
|
|
1028
|
-
await transport.require("field.update", options);
|
|
1029
1519
|
await transport.requireFeatures(params.data_sensitivity === void 0 ? [] : ["fieldDataSensitivity"], options);
|
|
1030
1520
|
const { features } = await transport.server(options);
|
|
1031
1521
|
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, {
|
|
@@ -1036,11 +1526,11 @@ function fieldResource(transport) {
|
|
|
1036
1526
|
}
|
|
1037
1527
|
/**
|
|
1038
1528
|
* Label a field's data sensitivity by hand. A person's label is never overwritten by the server's
|
|
1039
|
-
* classifier; `null` withdraws it, so whatever label the classifier wrote shows again.
|
|
1040
|
-
* the
|
|
1529
|
+
* classifier; `null` withdraws it, so whatever label the classifier wrote shows again. A server
|
|
1530
|
+
* without the column drops the key silently, so the label is refused there before the wire.
|
|
1531
|
+
* Answers the field with the label it now carries.
|
|
1041
1532
|
*/
|
|
1042
1533
|
async function setDataSensitivity(id, params, options = {}) {
|
|
1043
|
-
await transport.require("field.setDataSensitivity", options);
|
|
1044
1534
|
return transport.requestParsed(FieldWithDataSensitivity, `/api/field/${id}`, {
|
|
1045
1535
|
...options,
|
|
1046
1536
|
method: "PUT",
|
|
@@ -1056,7 +1546,6 @@ function fieldResource(transport) {
|
|
|
1056
1546
|
* `value` against the display value, ignoring both `searchId` and `limit`.
|
|
1057
1547
|
*/
|
|
1058
1548
|
async function search(id, searchId, params, options = {}) {
|
|
1059
|
-
await transport.require("field.search", options);
|
|
1060
1549
|
return transport.requestParsed(FieldSearchMatches, `/api/field/${id}/search/${searchId}`, {
|
|
1061
1550
|
...options,
|
|
1062
1551
|
query: {
|
|
@@ -1071,7 +1560,6 @@ function fieldResource(transport) {
|
|
|
1071
1560
|
* `value` and ignores any text after it, refusing only a `value` that has none.
|
|
1072
1561
|
*/
|
|
1073
1562
|
async function remapping(id, remappedId, params, options = {}) {
|
|
1074
|
-
await transport.require("field.remapping", options);
|
|
1075
1563
|
const { features } = await transport.server(options);
|
|
1076
1564
|
return fetchOptionalParsed(transport, `/api/field/${id}/remapping/${remappedId}`, fieldRemappedValueSchema(features, id === remappedId), {
|
|
1077
1565
|
...options,
|
|
@@ -1080,7 +1568,6 @@ function fieldResource(transport) {
|
|
|
1080
1568
|
}
|
|
1081
1569
|
/** Get the row count and the distinct-value count for a field. */
|
|
1082
1570
|
async function summary(id, options = {}) {
|
|
1083
|
-
await transport.require("field.summary", options);
|
|
1084
1571
|
const [[, count], [, distincts]] = await transport.requestParsed(FieldApiSummary, `/api/field/${id}/summary`, { ...options });
|
|
1085
1572
|
return {
|
|
1086
1573
|
field_id: id,
|
|
@@ -1090,13 +1577,12 @@ function fieldResource(transport) {
|
|
|
1090
1577
|
}
|
|
1091
1578
|
/** Get the cached distinct values Metabase holds for a field. */
|
|
1092
1579
|
async function values(id, options = {}) {
|
|
1093
|
-
await transport.require("field.values", options);
|
|
1094
1580
|
return transport.requestParsed(FieldValues, `/api/field/${id}/values`, { ...options });
|
|
1095
1581
|
}
|
|
1096
1582
|
return {
|
|
1097
1583
|
get,
|
|
1098
1584
|
update,
|
|
1099
|
-
setDataSensitivity,
|
|
1585
|
+
setDataSensitivity: refuse("setDataSensitivity", setDataSensitivity),
|
|
1100
1586
|
search,
|
|
1101
1587
|
remapping,
|
|
1102
1588
|
summary,
|
|
@@ -1128,22 +1614,22 @@ const SyncStashStarted = z.object({
|
|
|
1128
1614
|
task_id: z.number().int().positive()
|
|
1129
1615
|
});
|
|
1130
1616
|
const RemoteSyncSetting = z.string().nullable();
|
|
1131
|
-
const RemoteSyncBranchProperty =
|
|
1132
|
-
const
|
|
1133
|
-
const
|
|
1617
|
+
const RemoteSyncBranchProperty = SessionProperties.extend({ "remote-sync-branch": z.string().nullable().optional() });
|
|
1618
|
+
const RemoteSyncUrlProperty = SessionProperties.extend({ "remote-sync-url": z.string().nullable().optional() });
|
|
1619
|
+
const BRANCH_UNREADABLE_MESSAGE = "the remote-sync-branch setting is not readable: only admins see it, and a server without remote sync has none";
|
|
1620
|
+
const BRANCH_UNSET_MESSAGE = "git-sync tracks no branch: the remote-sync-branch setting is unset";
|
|
1134
1621
|
function isRemoteUnreadable(error) {
|
|
1135
1622
|
if (!(error instanceof HttpError)) return false;
|
|
1136
|
-
return error.status ===
|
|
1623
|
+
return error.status === 403 || error.status === 404;
|
|
1137
1624
|
}
|
|
1138
1625
|
function gitSyncResource(transport) {
|
|
1626
|
+
const { explain, refuse, refuseAfterReading } = explainer(transport, "gitSync");
|
|
1139
1627
|
/** Get the running or most recently finished sync task, or null when the server has none. */
|
|
1140
1628
|
async function currentTask(options = {}) {
|
|
1141
|
-
await transport.require("gitSync.currentTask", options);
|
|
1142
1629
|
return fetchOptionalParsed(transport, "/api/ee/remote-sync/current-task", SyncTask, options);
|
|
1143
1630
|
}
|
|
1144
1631
|
/** Request cancellation of the running sync task, and answer it in the state that left it. */
|
|
1145
1632
|
async function cancelTask(options = {}) {
|
|
1146
|
-
await transport.require("gitSync.cancelTask", options);
|
|
1147
1633
|
return transport.requestParsed(SyncTask, "/api/ee/remote-sync/current-task/cancel", {
|
|
1148
1634
|
...options,
|
|
1149
1635
|
method: "POST"
|
|
@@ -1151,12 +1637,10 @@ function gitSyncResource(transport) {
|
|
|
1151
1637
|
}
|
|
1152
1638
|
/** Whether Metabase holds content changes the remote has not been told about. */
|
|
1153
1639
|
async function isDirty(options = {}) {
|
|
1154
|
-
await transport.require("gitSync.isDirty", options);
|
|
1155
1640
|
return (await transport.requestParsed(SyncDirtyFlag, "/api/ee/remote-sync/is-dirty", { ...options })).is_dirty;
|
|
1156
1641
|
}
|
|
1157
1642
|
/** List the objects whose local state differs from the remote. */
|
|
1158
1643
|
async function dirty(options = {}) {
|
|
1159
|
-
await transport.require("gitSync.dirty", options);
|
|
1160
1644
|
return {
|
|
1161
1645
|
data: (await transport.requestParsed(SyncDirtyList, "/api/ee/remote-sync/dirty", { ...options })).dirty,
|
|
1162
1646
|
total: null
|
|
@@ -1167,7 +1651,6 @@ function gitSyncResource(transport) {
|
|
|
1167
1651
|
* comparison; `force-refresh` re-reads the remote instead.
|
|
1168
1652
|
*/
|
|
1169
1653
|
async function hasRemoteChanges(params = {}, options = {}) {
|
|
1170
|
-
await transport.require("gitSync.hasRemoteChanges", options);
|
|
1171
1654
|
return transport.requestParsed(SyncRemoteChanges, "/api/ee/remote-sync/has-remote-changes", {
|
|
1172
1655
|
...options,
|
|
1173
1656
|
query: { "force-refresh": params["force-refresh"] }
|
|
@@ -1178,14 +1661,16 @@ function gitSyncResource(transport) {
|
|
|
1178
1661
|
* pass `wait` to poll that task until it reaches a terminal status. A server already up to date
|
|
1179
1662
|
* answers no task id, and there is then nothing to poll. A server that guards the tracked branch
|
|
1180
1663
|
* refuses with 409 when `expected_branch` disagrees with the setting; left out, it is the tracked
|
|
1181
|
-
* branch as read just before the request
|
|
1182
|
-
*
|
|
1664
|
+
* branch as read just before the request, on every server with remote sync. A server that guards
|
|
1665
|
+
* the branch also requires it, so there the request is refused before the wire when the setting
|
|
1666
|
+
* is unset or hidden from the caller; a server without the guard ignores the assertion, and gets
|
|
1667
|
+
* none when there is none to read. `merge` folds the remote's changes into local content by a
|
|
1668
|
+
* three-way merge and keeps un-pushed local changes, where a plain import refuses on a dirty
|
|
1183
1669
|
* instance.
|
|
1184
1670
|
*/
|
|
1185
1671
|
async function importFromRemote(params = {}, options = {}) {
|
|
1186
|
-
await
|
|
1672
|
+
const expectedBranch = await expectedBranchFor(params.expected_branch, options);
|
|
1187
1673
|
await requireMerge(params.merge, options);
|
|
1188
|
-
const expectedBranch = await guardedBranch(params.expected_branch, options);
|
|
1189
1674
|
const started = await transport.requestParsed(SyncImportStarted, "/api/ee/remote-sync/import", {
|
|
1190
1675
|
...options,
|
|
1191
1676
|
method: "POST",
|
|
@@ -1207,28 +1692,38 @@ function gitSyncResource(transport) {
|
|
|
1207
1692
|
final: await settle(params.wait, options)
|
|
1208
1693
|
};
|
|
1209
1694
|
}
|
|
1210
|
-
async function
|
|
1211
|
-
if (
|
|
1695
|
+
async function expectedBranchFor(given, options) {
|
|
1696
|
+
if (given !== void 0) return given;
|
|
1697
|
+
return importAssertion(await readTrackedBranch(options));
|
|
1212
1698
|
}
|
|
1213
|
-
async function
|
|
1699
|
+
async function exportBranchFor(given, options) {
|
|
1214
1700
|
if (given !== void 0) return given;
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1701
|
+
return exportBranch(await readTrackedBranch(options));
|
|
1702
|
+
}
|
|
1703
|
+
async function readTrackedBranch(options) {
|
|
1704
|
+
const properties = await transport.probe(RemoteSyncBranchProperty, options);
|
|
1705
|
+
return {
|
|
1706
|
+
tracked: properties["remote-sync-branch"],
|
|
1707
|
+
features: profileFromProperties(properties).features
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
async function requireMerge(merge, options) {
|
|
1711
|
+
if (merge === true) await transport.requireFeatures(["remoteSyncMerge"], options);
|
|
1218
1712
|
}
|
|
1219
1713
|
/**
|
|
1220
1714
|
* Export Metabase's content to the remote. The endpoint queues a task and returns at once; pass
|
|
1221
1715
|
* `wait` to poll that task until it reaches a terminal status. A server that guards the tracked
|
|
1222
|
-
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked
|
|
1223
|
-
* as read just before the request
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1226
|
-
*
|
|
1716
|
+
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked
|
|
1717
|
+
* branch as read just before the request, and the request is refused before the wire when the
|
|
1718
|
+
* setting is unset or hidden from the caller. A server without the guard exports to a named
|
|
1719
|
+
* `branch` and tracks it from then on; left out, it gets none and exports to its own. When the
|
|
1720
|
+
* remote has moved past the last sync, a plain export ends in a `conflict` task where `merge` is
|
|
1721
|
+
* available, and is refused with 400 before any task elsewhere; `merge` folds the remote's
|
|
1722
|
+
* changes in by a three-way merge instead, and `force` overwrites them.
|
|
1227
1723
|
*/
|
|
1228
1724
|
async function exportToRemote(params = {}, options = {}) {
|
|
1229
|
-
await
|
|
1725
|
+
const branchName = await exportBranchFor(params.branch, options);
|
|
1230
1726
|
await requireMerge(params.merge, options);
|
|
1231
|
-
const branchName = await guardedBranch(params.branch, options);
|
|
1232
1727
|
const started = await transport.requestParsed(SyncExportStarted, "/api/ee/remote-sync/export", {
|
|
1233
1728
|
...options,
|
|
1234
1729
|
method: "POST",
|
|
@@ -1256,7 +1751,6 @@ function gitSyncResource(transport) {
|
|
|
1256
1751
|
* otherwise.
|
|
1257
1752
|
*/
|
|
1258
1753
|
async function exportPreflight(params, options = {}) {
|
|
1259
|
-
await transport.require("gitSync.exportPreflight", options);
|
|
1260
1754
|
return transport.requestParsed(SyncExportPreflight, "/api/ee/remote-sync/export-preflight", {
|
|
1261
1755
|
...options,
|
|
1262
1756
|
query: { branch: params.branch }
|
|
@@ -1267,7 +1761,6 @@ function gitSyncResource(transport) {
|
|
|
1267
1761
|
* queues a task and returns at once; pass `wait` to poll that task to a terminal status.
|
|
1268
1762
|
*/
|
|
1269
1763
|
async function stash(params, options = {}) {
|
|
1270
|
-
await transport.require("gitSync.stash", options);
|
|
1271
1764
|
const started = await transport.requestParsed(SyncStashStarted, "/api/ee/remote-sync/stash", {
|
|
1272
1765
|
...options,
|
|
1273
1766
|
method: "POST",
|
|
@@ -1290,7 +1783,6 @@ function gitSyncResource(transport) {
|
|
|
1290
1783
|
}
|
|
1291
1784
|
/** List the branches the configured remote carries. */
|
|
1292
1785
|
async function branches(options = {}) {
|
|
1293
|
-
await transport.require("gitSync.branches", options);
|
|
1294
1786
|
return {
|
|
1295
1787
|
data: (await transport.requestParsed(SyncBranchList, "/api/ee/remote-sync/branches", { ...options })).items,
|
|
1296
1788
|
total: null
|
|
@@ -1298,7 +1790,6 @@ function gitSyncResource(transport) {
|
|
|
1298
1790
|
}
|
|
1299
1791
|
/** Create a branch on the remote and point git-sync at it. */
|
|
1300
1792
|
async function createBranch(params, options = {}) {
|
|
1301
|
-
await transport.require("gitSync.createBranch", options);
|
|
1302
1793
|
return transport.requestParsed(SyncBranchCreated, "/api/ee/remote-sync/create-branch", {
|
|
1303
1794
|
...options,
|
|
1304
1795
|
method: "POST",
|
|
@@ -1307,19 +1798,42 @@ function gitSyncResource(transport) {
|
|
|
1307
1798
|
}
|
|
1308
1799
|
/**
|
|
1309
1800
|
* Mark one collection as git-synced, or unmark it. The server cascades the flag to descendants
|
|
1310
|
-
* by location prefix, and may queue an
|
|
1801
|
+
* by location prefix, and may queue an import task to bring the instance in line with the
|
|
1802
|
+
* remote. A server without `remoteSyncCollectionsOnlyUpdate` validates every settings update as
|
|
1803
|
+
* a remote configuration and fails one naming no remote, after it has applied the flag, so it is
|
|
1804
|
+
* sent the remote's URL and its token as the server shows it, which it checks against the remote
|
|
1805
|
+
* and keeps as they are, or a blank URL when no remote is configured, which clears the remote
|
|
1806
|
+
* settings that are already empty.
|
|
1311
1807
|
*/
|
|
1312
1808
|
async function setCollectionSynced(collectionId, synced, options = {}) {
|
|
1313
|
-
await transport.require("gitSync.setCollectionSynced", options);
|
|
1314
1809
|
return transport.requestParsed(SyncSettingsUpdateResult, "/api/ee/remote-sync/settings", {
|
|
1315
1810
|
...options,
|
|
1316
1811
|
method: "PUT",
|
|
1317
|
-
body:
|
|
1812
|
+
body: await settingsUpdateBody({ [collectionId]: synced }, options)
|
|
1318
1813
|
});
|
|
1319
1814
|
}
|
|
1815
|
+
async function settingsUpdateBody(collections, options) {
|
|
1816
|
+
const { features } = await transport.server(options);
|
|
1817
|
+
if (features.remoteSyncCollectionsOnlyUpdate) return { collections };
|
|
1818
|
+
const url = (await transport.probe(RemoteSyncUrlProperty, options))["remote-sync-url"];
|
|
1819
|
+
if (url === void 0) return { collections };
|
|
1820
|
+
if (url === null) return {
|
|
1821
|
+
collections,
|
|
1822
|
+
"remote-sync-url": ""
|
|
1823
|
+
};
|
|
1824
|
+
const token = await fetchOptionalParsed(transport, "/api/setting/remote-sync-token", RemoteSyncSetting, options);
|
|
1825
|
+
if (token === null) return {
|
|
1826
|
+
collections,
|
|
1827
|
+
"remote-sync-url": url
|
|
1828
|
+
};
|
|
1829
|
+
return {
|
|
1830
|
+
collections,
|
|
1831
|
+
"remote-sync-url": url,
|
|
1832
|
+
"remote-sync-token": token
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1320
1835
|
/** The collections currently in git-sync's scope. */
|
|
1321
1836
|
async function syncedCollections(options = {}) {
|
|
1322
|
-
await transport.require("gitSync.syncedCollections", options);
|
|
1323
1837
|
return {
|
|
1324
1838
|
data: (await listCollectionsAs(transport, SyncScopeCollection, options)).filter((entry) => entry.is_remote_synced === true),
|
|
1325
1839
|
total: null
|
|
@@ -1327,7 +1841,6 @@ function gitSyncResource(transport) {
|
|
|
1327
1841
|
}
|
|
1328
1842
|
/** The remote's URL, or null when none is configured or the caller may not read it. */
|
|
1329
1843
|
async function remoteUrl(options = {}) {
|
|
1330
|
-
await transport.require("gitSync.remoteUrl", options);
|
|
1331
1844
|
try {
|
|
1332
1845
|
const url = await fetchOptionalParsed(transport, "/api/setting/remote-sync-url", RemoteSyncSetting, options);
|
|
1333
1846
|
return url === "" ? null : url;
|
|
@@ -1337,21 +1850,22 @@ function gitSyncResource(transport) {
|
|
|
1337
1850
|
}
|
|
1338
1851
|
}
|
|
1339
1852
|
/**
|
|
1340
|
-
* The branch git-sync tracks, or null when none is configured. Refuses when the
|
|
1341
|
-
* read the setting (it is admin-only)
|
|
1342
|
-
*
|
|
1853
|
+
* The branch git-sync tracks, or null when none is configured. Refuses when the server has no
|
|
1854
|
+
* remote sync or the caller may not read the setting (it is admin-only), rather than reading
|
|
1855
|
+
* either as unset.
|
|
1343
1856
|
*/
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
if (tracked === void 0) throw new ConfigError("the remote-sync-branch setting is not readable: it is visible to admins only, and absent on a server without the remote-sync module");
|
|
1857
|
+
function branch(properties) {
|
|
1858
|
+
const tracked = properties["remote-sync-branch"];
|
|
1859
|
+
if (tracked === void 0) throw new ConfigError(BRANCH_UNREADABLE_MESSAGE);
|
|
1348
1860
|
return tracked;
|
|
1349
1861
|
}
|
|
1350
|
-
/**
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1862
|
+
/**
|
|
1863
|
+
* The branch git-sync tracks, refused when none is configured, the server has no remote sync, or
|
|
1864
|
+
* the caller may not read it.
|
|
1865
|
+
*/
|
|
1866
|
+
function trackedBranch(properties) {
|
|
1867
|
+
const tracked = branch(properties);
|
|
1868
|
+
if (tracked === null) throw new ConfigError(BRANCH_UNSET_MESSAGE);
|
|
1355
1869
|
return tracked;
|
|
1356
1870
|
}
|
|
1357
1871
|
/**
|
|
@@ -1359,7 +1873,6 @@ function gitSyncResource(transport) {
|
|
|
1359
1873
|
* answers null, which is terminal in the same sense: nothing further will happen.
|
|
1360
1874
|
*/
|
|
1361
1875
|
async function waitForTask(wait, options = {}) {
|
|
1362
|
-
await transport.require("gitSync.waitForTask", options);
|
|
1363
1876
|
return settle(wait, options);
|
|
1364
1877
|
}
|
|
1365
1878
|
async function settle(wait, options) {
|
|
@@ -1369,25 +1882,38 @@ function gitSyncResource(transport) {
|
|
|
1369
1882
|
}), (task) => task === null || isSyncTaskTerminal(task.status), wait);
|
|
1370
1883
|
}
|
|
1371
1884
|
return {
|
|
1372
|
-
currentTask,
|
|
1373
|
-
cancelTask,
|
|
1374
|
-
isDirty,
|
|
1375
|
-
dirty,
|
|
1376
|
-
hasRemoteChanges,
|
|
1377
|
-
import: importFromRemote,
|
|
1378
|
-
export: exportToRemote,
|
|
1379
|
-
exportPreflight,
|
|
1380
|
-
stash,
|
|
1381
|
-
branches,
|
|
1382
|
-
createBranch,
|
|
1383
|
-
setCollectionSynced,
|
|
1384
|
-
syncedCollections,
|
|
1885
|
+
currentTask: explain("currentTask", currentTask),
|
|
1886
|
+
cancelTask: explain("cancelTask", cancelTask),
|
|
1887
|
+
isDirty: explain("isDirty", isDirty),
|
|
1888
|
+
dirty: explain("dirty", dirty),
|
|
1889
|
+
hasRemoteChanges: explain("hasRemoteChanges", hasRemoteChanges),
|
|
1890
|
+
import: explain("import", importFromRemote),
|
|
1891
|
+
export: explain("export", exportToRemote),
|
|
1892
|
+
exportPreflight: explain("exportPreflight", exportPreflight),
|
|
1893
|
+
stash: explain("stash", stash),
|
|
1894
|
+
branches: explain("branches", branches),
|
|
1895
|
+
createBranch: explain("createBranch", createBranch),
|
|
1896
|
+
setCollectionSynced: explain("setCollectionSynced", setCollectionSynced),
|
|
1897
|
+
syncedCollections: refuse("syncedCollections", syncedCollections),
|
|
1385
1898
|
remoteUrl,
|
|
1386
|
-
branch,
|
|
1387
|
-
trackedBranch,
|
|
1388
|
-
waitForTask
|
|
1899
|
+
branch: refuseAfterReading("branch", RemoteSyncBranchProperty, branch),
|
|
1900
|
+
trackedBranch: refuseAfterReading("trackedBranch", RemoteSyncBranchProperty, trackedBranch),
|
|
1901
|
+
waitForTask: explain("waitForTask", waitForTask)
|
|
1389
1902
|
};
|
|
1390
1903
|
}
|
|
1904
|
+
function importAssertion(read) {
|
|
1905
|
+
if (!read.features.remoteSync) return;
|
|
1906
|
+
if (read.features.remoteSyncBranchGuard) return requiredBranch(read.tracked);
|
|
1907
|
+
return typeof read.tracked === "string" ? read.tracked : void 0;
|
|
1908
|
+
}
|
|
1909
|
+
function exportBranch(read) {
|
|
1910
|
+
if (!read.features.remoteSync || !read.features.remoteSyncBranchGuard) return;
|
|
1911
|
+
return requiredBranch(read.tracked);
|
|
1912
|
+
}
|
|
1913
|
+
function requiredBranch(tracked) {
|
|
1914
|
+
if (typeof tracked === "string") return tracked;
|
|
1915
|
+
throw new ConfigError(tracked === null ? BRANCH_UNSET_MESSAGE : BRANCH_UNREADABLE_MESSAGE);
|
|
1916
|
+
}
|
|
1391
1917
|
//#endregion
|
|
1392
1918
|
//#region ../client/src/resources/glossary.ts
|
|
1393
1919
|
const GlossaryApiList = z.object({ data: z.array(Glossary) }).loose();
|
|
@@ -1397,7 +1923,6 @@ function glossaryResource(transport) {
|
|
|
1397
1923
|
* contains it, case-insensitively.
|
|
1398
1924
|
*/
|
|
1399
1925
|
async function list(params = {}, options = {}) {
|
|
1400
|
-
await transport.require("glossary.list", options);
|
|
1401
1926
|
return {
|
|
1402
1927
|
data: (await transport.requestParsed(GlossaryApiList, "/api/glossary", {
|
|
1403
1928
|
...options,
|
|
@@ -1408,7 +1933,6 @@ function glossaryResource(transport) {
|
|
|
1408
1933
|
}
|
|
1409
1934
|
/** Create a glossary entry, a term and its definition. Terms are unique. */
|
|
1410
1935
|
async function create(params, options = {}) {
|
|
1411
|
-
await transport.require("glossary.create", options);
|
|
1412
1936
|
return transport.requestParsed(Glossary, "/api/glossary", {
|
|
1413
1937
|
...options,
|
|
1414
1938
|
method: "POST",
|
|
@@ -1417,7 +1941,6 @@ function glossaryResource(transport) {
|
|
|
1417
1941
|
}
|
|
1418
1942
|
/** Replace the term and definition of a glossary entry by id. */
|
|
1419
1943
|
async function update(id, params, options = {}) {
|
|
1420
|
-
await transport.require("glossary.update", options);
|
|
1421
1944
|
return transport.requestParsed(Glossary, `/api/glossary/${id}`, {
|
|
1422
1945
|
...options,
|
|
1423
1946
|
method: "PUT",
|
|
@@ -1426,7 +1949,6 @@ function glossaryResource(transport) {
|
|
|
1426
1949
|
}
|
|
1427
1950
|
/** Delete a glossary entry by id. */
|
|
1428
1951
|
async function remove(id, options = {}) {
|
|
1429
|
-
await transport.require("glossary.delete", options);
|
|
1430
1952
|
await transport.requestRaw(`/api/glossary/${id}`, {
|
|
1431
1953
|
...options,
|
|
1432
1954
|
method: "DELETE",
|
|
@@ -1459,9 +1981,9 @@ const UNPUBLISH_TABLES_PATH = "/api/ee/data-studio/table/unpublish-tables";
|
|
|
1459
1981
|
const LIBRARY_DATA_TYPE = "library-data";
|
|
1460
1982
|
const PublishTablesResponse = z.object({ target_collection: Collection.nullable() });
|
|
1461
1983
|
function libraryResource(transport) {
|
|
1984
|
+
const { explain } = explainer(transport, "library");
|
|
1462
1985
|
/** Get the Library root and its child collections, or `null` on an instance that has none. */
|
|
1463
1986
|
async function get(options = {}) {
|
|
1464
|
-
await transport.require("library.get", options);
|
|
1465
1987
|
const { features } = await transport.server(options);
|
|
1466
1988
|
const wire = await transport.requestParsed(libraryWireSchema(features), LIBRARY_ROOT_PATH, { ...options });
|
|
1467
1989
|
if (wire === null) return null;
|
|
@@ -1480,7 +2002,6 @@ function libraryResource(transport) {
|
|
|
1480
2002
|
* back from a refetch, which together make this idempotent.
|
|
1481
2003
|
*/
|
|
1482
2004
|
async function create(options = {}) {
|
|
1483
|
-
await transport.require("library.create", options);
|
|
1484
2005
|
const existing = await get(options);
|
|
1485
2006
|
if (existing !== null) return existing;
|
|
1486
2007
|
await transport.requestRaw(LIBRARY_ROOT_PATH, {
|
|
@@ -1493,7 +2014,6 @@ function libraryResource(transport) {
|
|
|
1493
2014
|
}
|
|
1494
2015
|
/** The id of the Library's Data collection, creating the Library first when it does not exist. */
|
|
1495
2016
|
async function ensureDataCollectionId(options = {}) {
|
|
1496
|
-
await transport.require("library.ensureDataCollectionId", options);
|
|
1497
2017
|
const data = (await create(options)).effective_children.find((child) => child.type === LIBRARY_DATA_TYPE);
|
|
1498
2018
|
if (data === void 0) throw new Error("Library has no Data collection to publish into");
|
|
1499
2019
|
if (typeof data.id !== "number") throw new Error(`Library Data collection has a non-numeric id ${String(data.id)}`);
|
|
@@ -1506,7 +2026,6 @@ function libraryResource(transport) {
|
|
|
1506
2026
|
* `LibraryPublishTablesInput` is refused before any request.
|
|
1507
2027
|
*/
|
|
1508
2028
|
async function publishTables(params, options = {}) {
|
|
1509
|
-
await transport.require("library.publishTables", options);
|
|
1510
2029
|
const body = parseRequestBody(LibraryPublishTablesInput, params, "tables to publish");
|
|
1511
2030
|
return (await transport.requestParsed(PublishTablesResponse, PUBLISH_TABLES_PATH, {
|
|
1512
2031
|
...options,
|
|
@@ -1520,7 +2039,6 @@ function libraryResource(transport) {
|
|
|
1520
2039
|
* table, so selectors that do not match `TableSelectors` are refused before any request.
|
|
1521
2040
|
*/
|
|
1522
2041
|
async function unpublishTables(params, options = {}) {
|
|
1523
|
-
await transport.require("library.unpublishTables", options);
|
|
1524
2042
|
const body = parseRequestBody(TableSelectors, params, "table selectors");
|
|
1525
2043
|
await transport.requestRaw(UNPUBLISH_TABLES_PATH, {
|
|
1526
2044
|
...options,
|
|
@@ -1530,20 +2048,20 @@ function libraryResource(transport) {
|
|
|
1530
2048
|
});
|
|
1531
2049
|
}
|
|
1532
2050
|
return {
|
|
1533
|
-
get,
|
|
1534
|
-
create,
|
|
1535
|
-
ensureDataCollectionId,
|
|
1536
|
-
publishTables,
|
|
1537
|
-
unpublishTables
|
|
2051
|
+
get: explain("get", get),
|
|
2052
|
+
create: explain("create", create),
|
|
2053
|
+
ensureDataCollectionId: explain("ensureDataCollectionId", ensureDataCollectionId),
|
|
2054
|
+
publishTables: explain("publishTables", publishTables),
|
|
2055
|
+
unpublishTables: explain("unpublishTables", unpublishTables)
|
|
1538
2056
|
};
|
|
1539
2057
|
}
|
|
1540
2058
|
//#endregion
|
|
1541
2059
|
//#region ../client/src/resources/measure.ts
|
|
1542
2060
|
const MeasureApiList = z.array(Measure);
|
|
1543
2061
|
function measureResource(transport) {
|
|
2062
|
+
const { explain } = explainer(transport, "measure");
|
|
1544
2063
|
/** List measures. */
|
|
1545
2064
|
async function list(options = {}) {
|
|
1546
|
-
await transport.require("measure.list", options);
|
|
1547
2065
|
return {
|
|
1548
2066
|
data: await transport.requestParsed(MeasureApiList, "/api/measure", { ...options }),
|
|
1549
2067
|
total: null
|
|
@@ -1551,12 +2069,10 @@ function measureResource(transport) {
|
|
|
1551
2069
|
}
|
|
1552
2070
|
/** Get one measure by id. */
|
|
1553
2071
|
async function get(id, options = {}) {
|
|
1554
|
-
await transport.require("measure.get", options);
|
|
1555
2072
|
return transport.requestParsed(Measure, `/api/measure/${id}`, { ...options });
|
|
1556
2073
|
}
|
|
1557
2074
|
/** Create a measure — a saved aggregation tied to a table — from a full measure body. */
|
|
1558
2075
|
async function create(params, options = {}) {
|
|
1559
|
-
await transport.require("measure.create", options);
|
|
1560
2076
|
return transport.requestParsed(Measure, "/api/measure", {
|
|
1561
2077
|
...options,
|
|
1562
2078
|
method: "POST",
|
|
@@ -1568,7 +2084,6 @@ function measureResource(transport) {
|
|
|
1568
2084
|
* required and lands in the audit log.
|
|
1569
2085
|
*/
|
|
1570
2086
|
async function update(id, params, options = {}) {
|
|
1571
|
-
await transport.require("measure.update", options);
|
|
1572
2087
|
return transport.requestParsed(Measure, `/api/measure/${id}`, {
|
|
1573
2088
|
...options,
|
|
1574
2089
|
method: "PUT",
|
|
@@ -1580,18 +2095,17 @@ function measureResource(transport) {
|
|
|
1580
2095
|
* so it carries the same required `revision_message`.
|
|
1581
2096
|
*/
|
|
1582
2097
|
async function archive(id, params, options = {}) {
|
|
1583
|
-
await transport.require("measure.archive", options);
|
|
1584
2098
|
return update(id, {
|
|
1585
2099
|
archived: true,
|
|
1586
2100
|
revision_message: params.revision_message
|
|
1587
2101
|
}, options);
|
|
1588
2102
|
}
|
|
1589
2103
|
return {
|
|
1590
|
-
list,
|
|
1591
|
-
get,
|
|
1592
|
-
create,
|
|
1593
|
-
update,
|
|
1594
|
-
archive
|
|
2104
|
+
list: explain("list", list),
|
|
2105
|
+
get: explain("get", get),
|
|
2106
|
+
create: explain("create", create),
|
|
2107
|
+
update: explain("update", update),
|
|
2108
|
+
archive: explain("archive", archive)
|
|
1595
2109
|
};
|
|
1596
2110
|
}
|
|
1597
2111
|
//#endregion
|
|
@@ -1732,12 +2246,12 @@ const MetricDimensionListing = z.object({
|
|
|
1732
2246
|
//#endregion
|
|
1733
2247
|
//#region ../client/src/resources/metric.ts
|
|
1734
2248
|
function metricResource(transport) {
|
|
2249
|
+
const { explain } = explainer(transport, "metric");
|
|
1735
2250
|
/**
|
|
1736
2251
|
* Run a metric definition and return the query result envelope: one metric or measure, or an
|
|
1737
2252
|
* arithmetic expression over several, each leaf filtered and projected on its own `lib/uuid`.
|
|
1738
2253
|
*/
|
|
1739
2254
|
async function query(definition, options = {}) {
|
|
1740
|
-
await transport.require("metric.query", options);
|
|
1741
2255
|
return transport.requestParsed(CardQueryResult, "/api/metric/dataset", {
|
|
1742
2256
|
...options,
|
|
1743
2257
|
method: "POST",
|
|
@@ -1749,7 +2263,6 @@ function metricResource(transport) {
|
|
|
1749
2263
|
* Takes the same definition `query` does.
|
|
1750
2264
|
*/
|
|
1751
2265
|
async function breakoutValues(definition, options = {}) {
|
|
1752
|
-
await transport.require("metric.breakoutValues", options);
|
|
1753
2266
|
return transport.requestParsed(MetricBreakoutValues, "/api/metric/breakout-values", {
|
|
1754
2267
|
...options,
|
|
1755
2268
|
method: "POST",
|
|
@@ -1761,7 +2274,6 @@ function metricResource(transport) {
|
|
|
1761
2274
|
* add, grouped by source table. `query` filters both by a name substring.
|
|
1762
2275
|
*/
|
|
1763
2276
|
async function dimensions(id, params = {}, options = {}) {
|
|
1764
|
-
await transport.require("metric.dimensions", options);
|
|
1765
2277
|
return transport.requestParsed(MetricDimensionListing, `/api/metric/${id}/dimension`, {
|
|
1766
2278
|
...options,
|
|
1767
2279
|
query: {
|
|
@@ -1772,14 +2284,15 @@ function metricResource(transport) {
|
|
|
1772
2284
|
});
|
|
1773
2285
|
}
|
|
1774
2286
|
return {
|
|
1775
|
-
query,
|
|
1776
|
-
breakoutValues,
|
|
1777
|
-
dimensions
|
|
2287
|
+
query: explain("query", query),
|
|
2288
|
+
breakoutValues: explain("breakoutValues", breakoutValues),
|
|
2289
|
+
dimensions: explain("dimensions", dimensions)
|
|
1778
2290
|
};
|
|
1779
2291
|
}
|
|
1780
2292
|
//#endregion
|
|
1781
2293
|
//#region ../client/src/resources/moderation-review.ts
|
|
1782
2294
|
function moderationReviewResource(transport) {
|
|
2295
|
+
const { explain } = explainer(transport, "moderationReview");
|
|
1783
2296
|
/**
|
|
1784
2297
|
* Create a moderation review on a card or dashboard. `status: "verified"` marks the item
|
|
1785
2298
|
* verified, `null` or an absent status records a note that leaves it unverified. The new review
|
|
@@ -1787,7 +2300,6 @@ function moderationReviewResource(transport) {
|
|
|
1787
2300
|
* A body that does not match `ModerationReviewCreateInput` is refused before any request.
|
|
1788
2301
|
*/
|
|
1789
2302
|
async function create(params, options = {}) {
|
|
1790
|
-
await transport.require("moderationReview.create", options);
|
|
1791
2303
|
const body = parseRequestBody(ModerationReviewCreateInput, params, "moderation review");
|
|
1792
2304
|
return transport.requestParsed(ModerationReview, "/api/moderation-review", {
|
|
1793
2305
|
...options,
|
|
@@ -1795,7 +2307,7 @@ function moderationReviewResource(transport) {
|
|
|
1795
2307
|
body
|
|
1796
2308
|
});
|
|
1797
2309
|
}
|
|
1798
|
-
return { create };
|
|
2310
|
+
return { create: explain("create", create) };
|
|
1799
2311
|
}
|
|
1800
2312
|
//#endregion
|
|
1801
2313
|
//#region ../client/src/resources/notification.ts
|
|
@@ -1826,7 +2338,6 @@ function notificationResource(transport) {
|
|
|
1826
2338
|
* the request pins the card payload type.
|
|
1827
2339
|
*/
|
|
1828
2340
|
async function list(params = {}, options = {}) {
|
|
1829
|
-
await transport.require("notification.list", options);
|
|
1830
2341
|
return {
|
|
1831
2342
|
data: await transport.requestParsed(NotificationApiList, "/api/notification", {
|
|
1832
2343
|
...options,
|
|
@@ -1843,12 +2354,10 @@ function notificationResource(transport) {
|
|
|
1843
2354
|
}
|
|
1844
2355
|
/** Get one question alert by id, refusing an id that names a system-event notification. */
|
|
1845
2356
|
async function get(id, options = {}) {
|
|
1846
|
-
await transport.require("notification.get", options);
|
|
1847
2357
|
return assertCardAlert(await transport.requestParsed(Notification, `/api/notification/${id}`, { ...options }));
|
|
1848
2358
|
}
|
|
1849
2359
|
/** Create a question alert — its card payload, schedules and handlers — from a full body. */
|
|
1850
2360
|
async function create(params, options = {}) {
|
|
1851
|
-
await transport.require("notification.create", options);
|
|
1852
2361
|
return transport.requestParsed(Notification, "/api/notification", {
|
|
1853
2362
|
...options,
|
|
1854
2363
|
method: "POST",
|
|
@@ -1857,7 +2366,6 @@ function notificationResource(transport) {
|
|
|
1857
2366
|
}
|
|
1858
2367
|
/** Update a question alert by id, merging the patch over the stored notification. */
|
|
1859
2368
|
async function update(id, params, options = {}) {
|
|
1860
|
-
await transport.require("notification.update", options);
|
|
1861
2369
|
const current = await get(id, options);
|
|
1862
2370
|
return transport.requestParsed(Notification, `/api/notification/${id}`, {
|
|
1863
2371
|
...options,
|
|
@@ -1867,7 +2375,6 @@ function notificationResource(transport) {
|
|
|
1867
2375
|
}
|
|
1868
2376
|
/** Archive a question alert by id, stopping every delivery. Metabase models this as an update. */
|
|
1869
2377
|
async function archive(id, options = {}) {
|
|
1870
|
-
await transport.require("notification.archive", options);
|
|
1871
2378
|
return update(id, { active: false }, options);
|
|
1872
2379
|
}
|
|
1873
2380
|
/**
|
|
@@ -1875,7 +2382,6 @@ function notificationResource(transport) {
|
|
|
1875
2382
|
* body.
|
|
1876
2383
|
*/
|
|
1877
2384
|
async function send(id, options = {}) {
|
|
1878
|
-
await transport.require("notification.send", options);
|
|
1879
2385
|
await get(id, options);
|
|
1880
2386
|
await transport.requestRaw(`/api/notification/${id}/send`, {
|
|
1881
2387
|
...options,
|
|
@@ -1935,7 +2441,6 @@ function permissionResource(transport) {
|
|
|
1935
2441
|
* one database is the question.
|
|
1936
2442
|
*/
|
|
1937
2443
|
async function graph(options = {}) {
|
|
1938
|
-
await transport.require("permission.graph", options);
|
|
1939
2444
|
return transport.requestParsed(PermissionsGraph, "/api/permissions/graph", { ...options });
|
|
1940
2445
|
}
|
|
1941
2446
|
/**
|
|
@@ -1944,12 +2449,10 @@ function permissionResource(transport) {
|
|
|
1944
2449
|
* group up under the database and read `view-data` and `create-queries` down to the table id.
|
|
1945
2450
|
*/
|
|
1946
2451
|
async function databaseGraph(id, options = {}) {
|
|
1947
|
-
await transport.require("permission.databaseGraph", options);
|
|
1948
2452
|
return transport.requestParsed(PermissionsGraph, `/api/permissions/graph/db/${id}`, { ...options });
|
|
1949
2453
|
}
|
|
1950
2454
|
/** Get the data permissions of one group on every database. Admin only. */
|
|
1951
2455
|
async function groupGraph(id, options = {}) {
|
|
1952
|
-
await transport.require("permission.groupGraph", options);
|
|
1953
2456
|
return transport.requestParsed(PermissionsGraph, `/api/permissions/graph/group/${id}`, { ...options });
|
|
1954
2457
|
}
|
|
1955
2458
|
return {
|
|
@@ -1971,7 +2474,6 @@ function mergePulseUpdate(current, patch) {
|
|
|
1971
2474
|
function pulseResource(transport) {
|
|
1972
2475
|
/** List dashboard subscriptions, optionally narrowed to one dashboard or to the archived ones. */
|
|
1973
2476
|
async function list(params = {}, options = {}) {
|
|
1974
|
-
await transport.require("pulse.list", options);
|
|
1975
2477
|
return {
|
|
1976
2478
|
data: await transport.requestParsed(PulseApiList, "/api/pulse", {
|
|
1977
2479
|
...options,
|
|
@@ -1985,21 +2487,27 @@ function pulseResource(transport) {
|
|
|
1985
2487
|
}
|
|
1986
2488
|
/** Get one dashboard subscription by id. */
|
|
1987
2489
|
async function get(id, options = {}) {
|
|
1988
|
-
await transport.require("pulse.get", options);
|
|
1989
2490
|
return transport.requestParsed(Pulse, `/api/pulse/${id}`, { ...options });
|
|
1990
2491
|
}
|
|
1991
|
-
/**
|
|
2492
|
+
/**
|
|
2493
|
+
* Create a dashboard subscription — its cards and its delivery channels — from a full body.
|
|
2494
|
+
* Non-empty `parameters` need the `dashboard_subscription_filters` premium feature, and are
|
|
2495
|
+
* refused before the wire without it.
|
|
2496
|
+
*/
|
|
1992
2497
|
async function create(params, options = {}) {
|
|
1993
|
-
await
|
|
2498
|
+
await requireSubscriptionFilters(params.parameters, options);
|
|
1994
2499
|
return transport.requestParsed(Pulse, "/api/pulse", {
|
|
1995
2500
|
...options,
|
|
1996
2501
|
method: "POST",
|
|
1997
2502
|
body: params
|
|
1998
2503
|
});
|
|
1999
2504
|
}
|
|
2000
|
-
/**
|
|
2505
|
+
/**
|
|
2506
|
+
* Update a dashboard subscription by id, carrying the server-defaulted flags forward. Non-empty
|
|
2507
|
+
* `parameters` are refused as on `create`.
|
|
2508
|
+
*/
|
|
2001
2509
|
async function update(id, params, options = {}) {
|
|
2002
|
-
await
|
|
2510
|
+
await requireSubscriptionFilters(params.parameters, options);
|
|
2003
2511
|
const current = await get(id, options);
|
|
2004
2512
|
return transport.requestParsed(Pulse, `/api/pulse/${id}`, {
|
|
2005
2513
|
...options,
|
|
@@ -2012,9 +2520,11 @@ function pulseResource(transport) {
|
|
|
2012
2520
|
* update, and disables each of the subscription's channels as a side effect.
|
|
2013
2521
|
*/
|
|
2014
2522
|
async function archive(id, options = {}) {
|
|
2015
|
-
await transport.require("pulse.archive", options);
|
|
2016
2523
|
return update(id, { archived: true }, options);
|
|
2017
2524
|
}
|
|
2525
|
+
async function requireSubscriptionFilters(parameters, options) {
|
|
2526
|
+
if (parameters !== void 0 && parameters.length > 0) await transport.requireFeatures(["dashboardSubscriptionFilters"], options);
|
|
2527
|
+
}
|
|
2018
2528
|
return {
|
|
2019
2529
|
list,
|
|
2020
2530
|
get,
|
|
@@ -2117,6 +2627,7 @@ const ReplacementRunStarted = z.object({ run_id: z.number().int().positive() });
|
|
|
2117
2627
|
const ReplacementApiRunList = z.array(ReplacementRun);
|
|
2118
2628
|
const CancelRunResponse = z.object({ success: z.literal(true) });
|
|
2119
2629
|
function replacementResource(transport) {
|
|
2630
|
+
const { explain } = explainer(transport, "replacement");
|
|
2120
2631
|
/**
|
|
2121
2632
|
* Check whether every usage of a source entity could be rewritten to read a target entity
|
|
2122
2633
|
* instead: the two must share a database, the target must not depend on the source, every source
|
|
@@ -2124,7 +2635,6 @@ function replacementResource(transport) {
|
|
|
2124
2635
|
* implicitly or sandboxed. Admins only.
|
|
2125
2636
|
*/
|
|
2126
2637
|
async function checkReplaceSource(params, options = {}) {
|
|
2127
|
-
await transport.require("replacement.checkReplaceSource", options);
|
|
2128
2638
|
return transport.requestParsed(ReplacementCheck, "/api/ee/replacement/check-replace-source", {
|
|
2129
2639
|
...options,
|
|
2130
2640
|
method: "POST",
|
|
@@ -2137,7 +2647,6 @@ function replacementResource(transport) {
|
|
|
2137
2647
|
* while another replacement is running. Admins only.
|
|
2138
2648
|
*/
|
|
2139
2649
|
async function replaceSource(params, options = {}) {
|
|
2140
|
-
await transport.require("replacement.replaceSource", options);
|
|
2141
2650
|
return transport.requestParsed(ReplacementRunStarted, "/api/ee/replacement/replace-source", {
|
|
2142
2651
|
...options,
|
|
2143
2652
|
method: "POST",
|
|
@@ -2152,7 +2661,6 @@ function replacementResource(transport) {
|
|
|
2152
2661
|
* replacement is running. Admins only.
|
|
2153
2662
|
*/
|
|
2154
2663
|
async function replaceModelWithTransform(params, options = {}) {
|
|
2155
|
-
await transport.require("replacement.replaceModelWithTransform", options);
|
|
2156
2664
|
return transport.requestParsed(ReplacementRunStarted, "/api/ee/replacement/replace-model-with-transform", {
|
|
2157
2665
|
...options,
|
|
2158
2666
|
method: "POST",
|
|
@@ -2164,7 +2672,6 @@ function replacementResource(transport) {
|
|
|
2164
2672
|
* `false` only the ended ones. Admins only.
|
|
2165
2673
|
*/
|
|
2166
2674
|
async function listRuns(params = {}, options = {}) {
|
|
2167
|
-
await transport.require("replacement.listRuns", options);
|
|
2168
2675
|
return {
|
|
2169
2676
|
data: await transport.requestParsed(ReplacementApiRunList, "/api/ee/replacement/runs", {
|
|
2170
2677
|
...options,
|
|
@@ -2175,24 +2682,22 @@ function replacementResource(transport) {
|
|
|
2175
2682
|
}
|
|
2176
2683
|
/** Get one replacement run by id, with its status and progress. Admins only. */
|
|
2177
2684
|
async function getRun(id, options = {}) {
|
|
2178
|
-
await transport.require("replacement.getRun", options);
|
|
2179
2685
|
return transport.requestParsed(ReplacementRun, `/api/ee/replacement/runs/${id}`, { ...options });
|
|
2180
2686
|
}
|
|
2181
2687
|
/** Cancel the replacement in progress. Refuses with a 409 once the run has ended. Admins only. */
|
|
2182
2688
|
async function cancelRun(id, options = {}) {
|
|
2183
|
-
await transport.require("replacement.cancelRun", options);
|
|
2184
2689
|
await transport.requestParsed(CancelRunResponse, `/api/ee/replacement/runs/${id}/cancel`, {
|
|
2185
2690
|
...options,
|
|
2186
2691
|
method: "POST"
|
|
2187
2692
|
});
|
|
2188
2693
|
}
|
|
2189
2694
|
return {
|
|
2190
|
-
checkReplaceSource,
|
|
2191
|
-
replaceSource,
|
|
2192
|
-
replaceModelWithTransform,
|
|
2193
|
-
listRuns,
|
|
2194
|
-
getRun,
|
|
2195
|
-
cancelRun
|
|
2695
|
+
checkReplaceSource: explain("checkReplaceSource", checkReplaceSource),
|
|
2696
|
+
replaceSource: explain("replaceSource", replaceSource),
|
|
2697
|
+
replaceModelWithTransform: explain("replaceModelWithTransform", replaceModelWithTransform),
|
|
2698
|
+
listRuns: explain("listRuns", listRuns),
|
|
2699
|
+
getRun: explain("getRun", getRun),
|
|
2700
|
+
cancelRun: explain("cancelRun", cancelRun)
|
|
2196
2701
|
};
|
|
2197
2702
|
}
|
|
2198
2703
|
//#endregion
|
|
@@ -2271,13 +2776,15 @@ const ENTITY_FEATURES = {
|
|
|
2271
2776
|
};
|
|
2272
2777
|
function entityFeatures(entity) {
|
|
2273
2778
|
const feature = ENTITY_FEATURES[entity];
|
|
2274
|
-
return feature === void 0 ? [] : [
|
|
2779
|
+
return feature === void 0 ? [] : [{
|
|
2780
|
+
feature,
|
|
2781
|
+
fields: ["entity"]
|
|
2782
|
+
}];
|
|
2275
2783
|
}
|
|
2276
2784
|
function revisionResource(transport) {
|
|
2785
|
+
const { explain } = explainer(transport, "revision");
|
|
2277
2786
|
/** List the revisions of an entity the caller may read, newest first, each with its diff. */
|
|
2278
2787
|
async function list(entity, id, options = {}) {
|
|
2279
|
-
await transport.require("revision.list", options);
|
|
2280
|
-
await transport.requireFeatures(entityFeatures(entity), options);
|
|
2281
2788
|
return {
|
|
2282
2789
|
data: await transport.requestParsed(RevisionApiList, `/api/revision/${entity}/${id}`, { ...options }),
|
|
2283
2790
|
total: null
|
|
@@ -2288,8 +2795,6 @@ function revisionResource(transport) {
|
|
|
2288
2795
|
* card the permission to run the query being restored.
|
|
2289
2796
|
*/
|
|
2290
2797
|
async function revert(params, options = {}) {
|
|
2291
|
-
await transport.require("revision.revert", options);
|
|
2292
|
-
await transport.requireFeatures(entityFeatures(params.entity), options);
|
|
2293
2798
|
return transport.requestParsed(RevisionApiRevert, "/api/revision/revert", {
|
|
2294
2799
|
...options,
|
|
2295
2800
|
method: "POST",
|
|
@@ -2297,8 +2802,8 @@ function revisionResource(transport) {
|
|
|
2297
2802
|
});
|
|
2298
2803
|
}
|
|
2299
2804
|
return {
|
|
2300
|
-
list,
|
|
2301
|
-
revert
|
|
2805
|
+
list: explain("list", list, entityFeatures),
|
|
2806
|
+
revert: explain("revert", revert, (params) => entityFeatures(params.entity))
|
|
2302
2807
|
};
|
|
2303
2808
|
}
|
|
2304
2809
|
//#endregion
|
|
@@ -2307,7 +2812,14 @@ const SearchApiResponse = z.object({
|
|
|
2307
2812
|
data: z.array(SearchResult),
|
|
2308
2813
|
total: z.number().int().nonnegative()
|
|
2309
2814
|
}).loose();
|
|
2815
|
+
function verifiedFeatures(params = {}) {
|
|
2816
|
+
return params.verified === true ? [{
|
|
2817
|
+
feature: "contentVerification",
|
|
2818
|
+
fields: ["verified"]
|
|
2819
|
+
}] : [];
|
|
2820
|
+
}
|
|
2310
2821
|
function searchResource(transport) {
|
|
2822
|
+
const { explain } = explainer(transport, "search");
|
|
2311
2823
|
/**
|
|
2312
2824
|
* Search over the instance's content, ranked against `q`. `models` narrows which kinds of entity
|
|
2313
2825
|
* may match, `archived` swaps the active set for the archived one, `table_db_id` restricts to
|
|
@@ -2317,9 +2829,12 @@ function searchResource(transport) {
|
|
|
2317
2829
|
* actions, transforms), `include_metadata` attaches each card's `result_metadata`,
|
|
2318
2830
|
* `include_dashboard_questions` also matches questions saved into a dashboard (excluded by
|
|
2319
2831
|
* default), and `limit`/`offset` are the window the server applies before ranking hydration.
|
|
2832
|
+
* `verified` needs the `content_verification` premium feature, and is refused before the wire
|
|
2833
|
+
* without it: a server granting neither it nor `official_collections` refuses the filter, and one
|
|
2834
|
+
* granting `official_collections` alone takes it without applying it.
|
|
2320
2835
|
*/
|
|
2321
2836
|
async function query(params = {}, options = {}) {
|
|
2322
|
-
await transport.
|
|
2837
|
+
await transport.requireFeatures(verifiedFeatures(params).map((requirement) => requirement.feature), options);
|
|
2323
2838
|
const response = await transport.requestParsed(SearchApiResponse, "/api/search", {
|
|
2324
2839
|
...options,
|
|
2325
2840
|
query: {
|
|
@@ -2342,7 +2857,7 @@ function searchResource(transport) {
|
|
|
2342
2857
|
total: response.total
|
|
2343
2858
|
};
|
|
2344
2859
|
}
|
|
2345
|
-
return { query };
|
|
2860
|
+
return { query: explain("query", query, verifiedFeatures) };
|
|
2346
2861
|
}
|
|
2347
2862
|
//#endregion
|
|
2348
2863
|
//#region ../client/src/resources/segment.ts
|
|
@@ -2350,7 +2865,6 @@ const SegmentApiList = z.array(Segment);
|
|
|
2350
2865
|
function segmentResource(transport) {
|
|
2351
2866
|
/** List every segment the caller can see. */
|
|
2352
2867
|
async function list(options = {}) {
|
|
2353
|
-
await transport.require("segment.list", options);
|
|
2354
2868
|
return {
|
|
2355
2869
|
data: await transport.requestParsed(SegmentApiList, "/api/segment", { ...options }),
|
|
2356
2870
|
total: null
|
|
@@ -2358,12 +2872,10 @@ function segmentResource(transport) {
|
|
|
2358
2872
|
}
|
|
2359
2873
|
/** Get one segment by id. */
|
|
2360
2874
|
async function get(id, options = {}) {
|
|
2361
|
-
await transport.require("segment.get", options);
|
|
2362
2875
|
return transport.requestParsed(Segment, `/api/segment/${id}`, { ...options });
|
|
2363
2876
|
}
|
|
2364
2877
|
/** Create a segment — a reusable row filter over one table — from a full segment body. */
|
|
2365
2878
|
async function create(params, options = {}) {
|
|
2366
|
-
await transport.require("segment.create", options);
|
|
2367
2879
|
return transport.requestParsed(Segment, "/api/segment", {
|
|
2368
2880
|
...options,
|
|
2369
2881
|
method: "POST",
|
|
@@ -2375,7 +2887,6 @@ function segmentResource(transport) {
|
|
|
2375
2887
|
* required: Metabase records every segment change in its revision history.
|
|
2376
2888
|
*/
|
|
2377
2889
|
async function update(id, params, options = {}) {
|
|
2378
|
-
await transport.require("segment.update", options);
|
|
2379
2890
|
return transport.requestParsed(Segment, `/api/segment/${id}`, {
|
|
2380
2891
|
...options,
|
|
2381
2892
|
method: "PUT",
|
|
@@ -2387,7 +2898,6 @@ function segmentResource(transport) {
|
|
|
2387
2898
|
* same required `revision_message`.
|
|
2388
2899
|
*/
|
|
2389
2900
|
async function archive(id, params, options = {}) {
|
|
2390
|
-
await transport.require("segment.archive", options);
|
|
2391
2901
|
return update(id, {
|
|
2392
2902
|
archived: true,
|
|
2393
2903
|
revision_message: params.revision_message
|
|
@@ -2412,7 +2922,6 @@ function rethrowSettingError(error, key) {
|
|
|
2412
2922
|
function settingResource(transport) {
|
|
2413
2923
|
/** Get all settings and their values, with descriptions and environment-variable names. */
|
|
2414
2924
|
async function list(options = {}) {
|
|
2415
|
-
await transport.require("setting.list", options);
|
|
2416
2925
|
return {
|
|
2417
2926
|
data: await transport.requestParsed(SettingApiList, "/api/setting", { ...options }),
|
|
2418
2927
|
total: null
|
|
@@ -2423,12 +2932,10 @@ function settingResource(transport) {
|
|
|
2423
2932
|
* reaches the caller as `null`; a string-typed setting answers bare text rather than JSON.
|
|
2424
2933
|
*/
|
|
2425
2934
|
async function get(key, options = {}) {
|
|
2426
|
-
await transport.require("setting.get", options);
|
|
2427
2935
|
return fetchOptionalParsed(transport, `/api/setting/${encodeURIComponent(key)}`, z.unknown(), options).catch((error) => rethrowSettingError(error, key));
|
|
2428
2936
|
}
|
|
2429
2937
|
/** Set a single setting's value by key. The endpoint answers 204 with no body. */
|
|
2430
2938
|
async function set(key, value, options = {}) {
|
|
2431
|
-
await transport.require("setting.set", options);
|
|
2432
2939
|
await transport.requestRaw(`/api/setting/${encodeURIComponent(key)}`, {
|
|
2433
2940
|
...options,
|
|
2434
2941
|
method: "PUT",
|
|
@@ -2450,7 +2957,6 @@ function setupResource(transport) {
|
|
|
2450
2957
|
* applying the instance preferences. The setup token is single-use.
|
|
2451
2958
|
*/
|
|
2452
2959
|
async function create(params, options = {}) {
|
|
2453
|
-
await transport.require("setup.create", options);
|
|
2454
2960
|
return transport.requestParsed(SetupResult, "/api/setup", {
|
|
2455
2961
|
...options,
|
|
2456
2962
|
method: "POST",
|
|
@@ -2465,7 +2971,6 @@ const SnippetApiList = z.array(Snippet);
|
|
|
2465
2971
|
function snippetResource(transport) {
|
|
2466
2972
|
/** List native query snippets. `archived` swaps the listing to archived snippets. */
|
|
2467
2973
|
async function list(params = {}, options = {}) {
|
|
2468
|
-
await transport.require("snippet.list", options);
|
|
2469
2974
|
return {
|
|
2470
2975
|
data: await transport.requestParsed(SnippetApiList, "/api/native-query-snippet", {
|
|
2471
2976
|
...options,
|
|
@@ -2476,12 +2981,10 @@ function snippetResource(transport) {
|
|
|
2476
2981
|
}
|
|
2477
2982
|
/** Get one native query snippet by id. */
|
|
2478
2983
|
async function get(id, options = {}) {
|
|
2479
|
-
await transport.require("snippet.get", options);
|
|
2480
2984
|
return transport.requestParsed(Snippet, `/api/native-query-snippet/${id}`, { ...options });
|
|
2481
2985
|
}
|
|
2482
2986
|
/** Create a native query snippet from a name, its SQL fragment, and an optional collection. */
|
|
2483
2987
|
async function create(params, options = {}) {
|
|
2484
|
-
await transport.require("snippet.create", options);
|
|
2485
2988
|
return transport.requestParsed(Snippet, "/api/native-query-snippet", {
|
|
2486
2989
|
...options,
|
|
2487
2990
|
method: "POST",
|
|
@@ -2490,7 +2993,6 @@ function snippetResource(transport) {
|
|
|
2490
2993
|
}
|
|
2491
2994
|
/** Update a native query snippet by id, patching only the fields the body carries. */
|
|
2492
2995
|
async function update(id, params, options = {}) {
|
|
2493
|
-
await transport.require("snippet.update", options);
|
|
2494
2996
|
return transport.requestParsed(Snippet, `/api/native-query-snippet/${id}`, {
|
|
2495
2997
|
...options,
|
|
2496
2998
|
method: "PUT",
|
|
@@ -2502,7 +3004,6 @@ function snippetResource(transport) {
|
|
|
2502
3004
|
* own endpoint.
|
|
2503
3005
|
*/
|
|
2504
3006
|
async function archive(id, options = {}) {
|
|
2505
|
-
await transport.require("snippet.archive", options);
|
|
2506
3007
|
return update(id, { archived: true }, options);
|
|
2507
3008
|
}
|
|
2508
3009
|
return {
|
|
@@ -2532,13 +3033,23 @@ const LIST_PARAM_FEATURES = [
|
|
|
2532
3033
|
["published-only", "tableListPublishedFilter"]
|
|
2533
3034
|
];
|
|
2534
3035
|
function listParamFeatures(params) {
|
|
2535
|
-
return LIST_PARAM_FEATURES.filter(([param]) => params[param]
|
|
3036
|
+
return LIST_PARAM_FEATURES.filter(([param]) => params[param] === true).map(([, feature]) => feature);
|
|
3037
|
+
}
|
|
3038
|
+
function dataLayerFeatures(value, field) {
|
|
3039
|
+
return isTierName(value) ? [{
|
|
3040
|
+
feature: "tableDataLayerTiers",
|
|
3041
|
+
fields: [field]
|
|
3042
|
+
}] : [];
|
|
3043
|
+
}
|
|
3044
|
+
function isTierName(value) {
|
|
3045
|
+
return value !== null && value !== void 0 && TableDataLayerTier.safeParse(value).success;
|
|
2536
3046
|
}
|
|
2537
3047
|
const UPLOAD_UPDATE_PATHS = {
|
|
2538
3048
|
append: "append-csv",
|
|
2539
3049
|
replace: "replace-csv"
|
|
2540
3050
|
};
|
|
2541
3051
|
function tableResource(transport) {
|
|
3052
|
+
const { explain } = explainer(transport, "table");
|
|
2542
3053
|
/**
|
|
2543
3054
|
* List every table the caller can see, across all databases. `term` matches names and display
|
|
2544
3055
|
* names; `can-query` and `can-write` keep only the tables the caller may query or edit the
|
|
@@ -2546,9 +3057,7 @@ function tableResource(transport) {
|
|
|
2546
3057
|
* tables nothing depends on; `published-only` keeps tables published to the library.
|
|
2547
3058
|
*/
|
|
2548
3059
|
async function list(params = {}, options = {}) {
|
|
2549
|
-
await transport.require("table.list", options);
|
|
2550
3060
|
await transport.requireFeatures(listParamFeatures(params), options);
|
|
2551
|
-
await requireDataLayer(params["data-layer"], options);
|
|
2552
3061
|
return {
|
|
2553
3062
|
data: await transport.requestParsed(TableApiList, "/api/table", {
|
|
2554
3063
|
...options,
|
|
@@ -2572,31 +3081,30 @@ function tableResource(transport) {
|
|
|
2572
3081
|
}
|
|
2573
3082
|
/** Get one table by id, without its fields. */
|
|
2574
3083
|
async function get(id, options = {}) {
|
|
2575
|
-
await transport.require("table.get", options);
|
|
2576
3084
|
return transport.requestParsed(Table, `/api/table/${id}`, { ...options });
|
|
2577
3085
|
}
|
|
2578
3086
|
/** Update a table by id, patching only the fields the body carries. */
|
|
2579
3087
|
async function update(id, params, options = {}) {
|
|
2580
|
-
await
|
|
2581
|
-
await
|
|
2582
|
-
|
|
3088
|
+
await refuseRewrittenDataLayer(params.data_layer, options);
|
|
3089
|
+
await requireDataAuthorityNull(params.data_authority, options);
|
|
3090
|
+
const path = `/api/table/${id}`;
|
|
3091
|
+
const updated = await transport.requestParsed(Table, path, {
|
|
2583
3092
|
...options,
|
|
2584
3093
|
method: "PUT",
|
|
2585
3094
|
body: params
|
|
2586
3095
|
});
|
|
3096
|
+
assertCollectionWritten(path, params.collection_id, updated);
|
|
3097
|
+
return updated;
|
|
2587
3098
|
}
|
|
2588
|
-
async function
|
|
2589
|
-
if (value === null || value === void 0) return;
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
const { features } = await transport.server(options);
|
|
2595
|
-
if (features.tableDataLayerTiers) throw new ConfigError(`data_layer "${value}" is a medallion name; this server names a table's layer ${TableDataLayerTier.options.join(", ")}`);
|
|
3099
|
+
async function refuseRewrittenDataLayer(value, options) {
|
|
3100
|
+
if (value === null || value === void 0 || isTierName(value)) return;
|
|
3101
|
+
const profile = await transport.preflightServer(options);
|
|
3102
|
+
if (profile === null) return;
|
|
3103
|
+
const failure = supersedingFeature("tableDataLayerTiers", profile, `data_layer "${value}" is a medallion name; this server names a table's layer ${TableDataLayerTier.options.join(", ")} and would map it onto one of those without a word`);
|
|
3104
|
+
if (failure !== null) transport.refuseBeforeSending(new CapabilityError(failure));
|
|
2596
3105
|
}
|
|
2597
3106
|
/** Get a table by id with its fields hydrated — the metadata the query builder runs on. */
|
|
2598
3107
|
async function queryMetadata(id, options = {}) {
|
|
2599
|
-
await transport.require("table.queryMetadata", options);
|
|
2600
3108
|
return transport.requestParsed(TableQueryMetadata, `/api/table/${id}/query_metadata`, { ...options });
|
|
2601
3109
|
}
|
|
2602
3110
|
/**
|
|
@@ -2604,7 +3112,6 @@ function tableResource(transport) {
|
|
|
2604
3112
|
* fields answers no content, which is the same empty list.
|
|
2605
3113
|
*/
|
|
2606
3114
|
async function fks(id, options = {}) {
|
|
2607
|
-
await transport.require("table.fks", options);
|
|
2608
3115
|
return {
|
|
2609
3116
|
data: await fetchOptionalParsed(transport, `/api/table/${id}/fks`, TableApiFks, { ...options }) ?? [],
|
|
2610
3117
|
total: null
|
|
@@ -2616,7 +3123,6 @@ function tableResource(transport) {
|
|
|
2616
3123
|
* connect to is a 422.
|
|
2617
3124
|
*/
|
|
2618
3125
|
async function syncSchema(id, options = {}) {
|
|
2619
|
-
await transport.require("table.syncSchema", options);
|
|
2620
3126
|
return {
|
|
2621
3127
|
id,
|
|
2622
3128
|
status: (await transport.requestParsed(SyncSchemaResponse, `/api/table/${id}/sync_schema`, {
|
|
@@ -2630,7 +3136,6 @@ function tableResource(transport) {
|
|
|
2630
3136
|
* cached set. The scan runs after the call returns.
|
|
2631
3137
|
*/
|
|
2632
3138
|
async function rescanValues(id, options = {}) {
|
|
2633
|
-
await transport.require("table.rescanValues", options);
|
|
2634
3139
|
return {
|
|
2635
3140
|
id,
|
|
2636
3141
|
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/rescan_values`, {
|
|
@@ -2645,7 +3150,6 @@ function tableResource(transport) {
|
|
|
2645
3150
|
* time the field's values are read.
|
|
2646
3151
|
*/
|
|
2647
3152
|
async function discardValues(id, options = {}) {
|
|
2648
|
-
await transport.require("table.discardValues", options);
|
|
2649
3153
|
return {
|
|
2650
3154
|
id,
|
|
2651
3155
|
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/discard_values`, {
|
|
@@ -2660,7 +3164,6 @@ function tableResource(transport) {
|
|
|
2660
3164
|
* selectors matched a table.
|
|
2661
3165
|
*/
|
|
2662
3166
|
async function bulkEdit(params, options = {}) {
|
|
2663
|
-
await transport.require("table.bulkEdit", options);
|
|
2664
3167
|
await requireDataAuthorityNull(params.data_authority, options);
|
|
2665
3168
|
await transport.requestParsed(BulkEditResponse, BULK_EDIT_PATH, {
|
|
2666
3169
|
...options,
|
|
@@ -2694,7 +3197,6 @@ function tableResource(transport) {
|
|
|
2694
3197
|
* call is a 422; the syncs run after it returns.
|
|
2695
3198
|
*/
|
|
2696
3199
|
async function bulkSyncSchema(selectors, options = {}) {
|
|
2697
|
-
await transport.require("table.bulkSyncSchema", options);
|
|
2698
3200
|
return postSelection(BULK_SYNC_SCHEMA_PATH, selectors, options);
|
|
2699
3201
|
}
|
|
2700
3202
|
/**
|
|
@@ -2702,7 +3204,6 @@ function tableResource(transport) {
|
|
|
2702
3204
|
* are already cached. The scans run after the call returns.
|
|
2703
3205
|
*/
|
|
2704
3206
|
async function bulkRescanValues(selectors, options = {}) {
|
|
2705
|
-
await transport.require("table.bulkRescanValues", options);
|
|
2706
3207
|
return postSelection(BULK_RESCAN_VALUES_PATH, selectors, options);
|
|
2707
3208
|
}
|
|
2708
3209
|
/**
|
|
@@ -2710,7 +3211,6 @@ function tableResource(transport) {
|
|
|
2710
3211
|
* included. No scan recreates a discarded set.
|
|
2711
3212
|
*/
|
|
2712
3213
|
async function bulkDiscardValues(selectors, options = {}) {
|
|
2713
|
-
await transport.require("table.bulkDiscardValues", options);
|
|
2714
3214
|
return postSelection(BULK_DISCARD_VALUES_PATH, selectors, options);
|
|
2715
3215
|
}
|
|
2716
3216
|
async function updateFromCsv(id, action, file, options) {
|
|
@@ -2732,7 +3232,6 @@ function tableResource(transport) {
|
|
|
2732
3232
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
2733
3233
|
*/
|
|
2734
3234
|
async function appendCsv(id, file, options = {}) {
|
|
2735
|
-
await transport.require("table.appendCsv", options);
|
|
2736
3235
|
return updateFromCsv(id, "append", file, options);
|
|
2737
3236
|
}
|
|
2738
3237
|
/**
|
|
@@ -2742,37 +3241,53 @@ function tableResource(transport) {
|
|
|
2742
3241
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
2743
3242
|
*/
|
|
2744
3243
|
async function replaceCsv(id, file, options = {}) {
|
|
2745
|
-
await transport.require("table.replaceCsv", options);
|
|
2746
3244
|
return updateFromCsv(id, "replace", file, options);
|
|
2747
3245
|
}
|
|
2748
3246
|
return {
|
|
2749
|
-
list,
|
|
3247
|
+
list: explain("list", list, (params) => dataLayerFeatures(params?.["data-layer"], "data-layer")),
|
|
2750
3248
|
get,
|
|
2751
|
-
update,
|
|
3249
|
+
update: explain("update", update, (_id, params) => dataLayerFeatures(params.data_layer, "data_layer")),
|
|
2752
3250
|
queryMetadata,
|
|
2753
3251
|
fks,
|
|
2754
3252
|
syncSchema,
|
|
2755
3253
|
rescanValues,
|
|
2756
3254
|
discardValues,
|
|
2757
|
-
bulkEdit,
|
|
2758
|
-
bulkSyncSchema,
|
|
2759
|
-
bulkRescanValues,
|
|
2760
|
-
bulkDiscardValues,
|
|
3255
|
+
bulkEdit: explain("bulkEdit", bulkEdit),
|
|
3256
|
+
bulkSyncSchema: explain("bulkSyncSchema", bulkSyncSchema),
|
|
3257
|
+
bulkRescanValues: explain("bulkRescanValues", bulkRescanValues),
|
|
3258
|
+
bulkDiscardValues: explain("bulkDiscardValues", bulkDiscardValues),
|
|
2761
3259
|
appendCsv,
|
|
2762
3260
|
replaceCsv
|
|
2763
3261
|
};
|
|
2764
3262
|
}
|
|
3263
|
+
function assertCollectionWritten(path, requested, table) {
|
|
3264
|
+
if (requested === void 0 || table.collection_id === requested) return;
|
|
3265
|
+
const answered = table.collection_id === void 0 ? { kind: "unreported" } : {
|
|
3266
|
+
kind: "reported",
|
|
3267
|
+
value: table.collection_id
|
|
3268
|
+
};
|
|
3269
|
+
throw new PartialWriteError(`the server applied the rest of the update to table ${table.id} but not collection_id: ${describeCollectionAnswer(answered)}, because this server does not move a table to a collection through an update`, {
|
|
3270
|
+
method: "PUT",
|
|
3271
|
+
path,
|
|
3272
|
+
field: "collection_id",
|
|
3273
|
+
requested,
|
|
3274
|
+
answered
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
function describeCollectionAnswer(answered) {
|
|
3278
|
+
if (answered.kind === "unreported") return "it answered the table without reporting a collection";
|
|
3279
|
+
if (answered.value === null) return "the table stays in no collection";
|
|
3280
|
+
return `the table stays in collection ${answered.value}`;
|
|
3281
|
+
}
|
|
2765
3282
|
//#endregion
|
|
2766
3283
|
//#region ../client/src/resources/timeline-event.ts
|
|
2767
3284
|
function timelineEventResource(transport) {
|
|
2768
3285
|
/** Get one timeline event by id. */
|
|
2769
3286
|
async function get(id, options = {}) {
|
|
2770
|
-
await transport.require("timelineEvent.get", options);
|
|
2771
3287
|
return transport.requestParsed(TimelineEvent, `/api/timeline-event/${id}`, { ...options });
|
|
2772
3288
|
}
|
|
2773
3289
|
/** Create a timeline event on an existing timeline. */
|
|
2774
3290
|
async function create(params, options = {}) {
|
|
2775
|
-
await transport.require("timelineEvent.create", options);
|
|
2776
3291
|
return transport.requestParsed(TimelineEvent, "/api/timeline-event", {
|
|
2777
3292
|
...options,
|
|
2778
3293
|
method: "POST",
|
|
@@ -2784,7 +3299,6 @@ function timelineEventResource(transport) {
|
|
|
2784
3299
|
* the event to another timeline.
|
|
2785
3300
|
*/
|
|
2786
3301
|
async function update(id, params, options = {}) {
|
|
2787
|
-
await transport.require("timelineEvent.update", options);
|
|
2788
3302
|
return transport.requestParsed(TimelineEvent, `/api/timeline-event/${id}`, {
|
|
2789
3303
|
...options,
|
|
2790
3304
|
method: "PUT",
|
|
@@ -2796,12 +3310,10 @@ function timelineEventResource(transport) {
|
|
|
2796
3310
|
* endpoint.
|
|
2797
3311
|
*/
|
|
2798
3312
|
async function archive(id, options = {}) {
|
|
2799
|
-
await transport.require("timelineEvent.archive", options);
|
|
2800
3313
|
return update(id, { archived: true }, options);
|
|
2801
3314
|
}
|
|
2802
3315
|
/** Permanently delete a timeline event by id. The server answers with no body. */
|
|
2803
3316
|
async function remove(id, options = {}) {
|
|
2804
|
-
await transport.require("timelineEvent.delete", options);
|
|
2805
3317
|
await transport.requestRaw(`/api/timeline-event/${id}`, {
|
|
2806
3318
|
...options,
|
|
2807
3319
|
method: "DELETE",
|
|
@@ -2823,7 +3335,6 @@ const TimelineWithEvents = Timeline.required({ events: true });
|
|
|
2823
3335
|
function timelineResource(transport) {
|
|
2824
3336
|
/** List timelines. `archived` swaps the active listing for the archived one. */
|
|
2825
3337
|
async function list(params = {}, options = {}) {
|
|
2826
|
-
await transport.require("timeline.list", options);
|
|
2827
3338
|
return {
|
|
2828
3339
|
data: await transport.requestParsed(TimelineApiList, "/api/timeline", {
|
|
2829
3340
|
...options,
|
|
@@ -2834,7 +3345,6 @@ function timelineResource(transport) {
|
|
|
2834
3345
|
}
|
|
2835
3346
|
/** Get one timeline by id, without its events. */
|
|
2836
3347
|
async function get(id, options = {}) {
|
|
2837
|
-
await transport.require("timeline.get", options);
|
|
2838
3348
|
return transport.requestParsed(Timeline, `/api/timeline/${id}`, { ...options });
|
|
2839
3349
|
}
|
|
2840
3350
|
/**
|
|
@@ -2842,7 +3352,6 @@ function timelineResource(transport) {
|
|
|
2842
3352
|
* timeline read hydrates them on request, and `archived` chooses which of them come back.
|
|
2843
3353
|
*/
|
|
2844
3354
|
async function events(id, params = {}, options = {}) {
|
|
2845
|
-
await transport.require("timeline.events", options);
|
|
2846
3355
|
return {
|
|
2847
3356
|
data: (await transport.requestParsed(TimelineWithEvents, `/api/timeline/${id}`, {
|
|
2848
3357
|
...options,
|
|
@@ -2856,7 +3365,6 @@ function timelineResource(transport) {
|
|
|
2856
3365
|
}
|
|
2857
3366
|
/** Create a timeline from a full timeline body. */
|
|
2858
3367
|
async function create(params, options = {}) {
|
|
2859
|
-
await transport.require("timeline.create", options);
|
|
2860
3368
|
return transport.requestParsed(Timeline, "/api/timeline", {
|
|
2861
3369
|
...options,
|
|
2862
3370
|
method: "POST",
|
|
@@ -2868,7 +3376,6 @@ function timelineResource(transport) {
|
|
|
2868
3376
|
* cascades to every event on the timeline.
|
|
2869
3377
|
*/
|
|
2870
3378
|
async function update(id, params, options = {}) {
|
|
2871
|
-
await transport.require("timeline.update", options);
|
|
2872
3379
|
return transport.requestParsed(Timeline, `/api/timeline/${id}`, {
|
|
2873
3380
|
...options,
|
|
2874
3381
|
method: "PUT",
|
|
@@ -2880,12 +3387,10 @@ function timelineResource(transport) {
|
|
|
2880
3387
|
* not its own endpoint.
|
|
2881
3388
|
*/
|
|
2882
3389
|
async function archive(id, options = {}) {
|
|
2883
|
-
await transport.require("timeline.archive", options);
|
|
2884
3390
|
return update(id, { archived: true }, options);
|
|
2885
3391
|
}
|
|
2886
3392
|
/** Permanently delete a timeline and all its events by id. The server answers with no body. */
|
|
2887
3393
|
async function remove(id, options = {}) {
|
|
2888
|
-
await transport.require("timeline.delete", options);
|
|
2889
3394
|
await transport.requestRaw(`/api/timeline/${id}`, {
|
|
2890
3395
|
...options,
|
|
2891
3396
|
method: "DELETE",
|
|
@@ -2922,12 +3427,12 @@ TransformMemberRun.pick({
|
|
|
2922
3427
|
//#region ../client/src/resources/transform-dag-run.ts
|
|
2923
3428
|
const TransformMemberRunList = z.array(TransformMemberRun);
|
|
2924
3429
|
function transformDagRunResource(transport) {
|
|
3430
|
+
const { explain } = explainer(transport, "transformDagRun");
|
|
2925
3431
|
/**
|
|
2926
3432
|
* List the transform runs a DAG run coordinated so far. A member run is inserted when its
|
|
2927
3433
|
* transform starts, so the list grows while the DAG run is in progress.
|
|
2928
3434
|
*/
|
|
2929
3435
|
async function transformRuns(runId, options = {}) {
|
|
2930
|
-
await transport.require("transformDagRun.transformRuns", options);
|
|
2931
3436
|
return {
|
|
2932
3437
|
data: await transport.requestParsed(TransformMemberRunList, `/api/transform-dag-run/${runId}/transform-runs`, { ...options }),
|
|
2933
3438
|
total: null
|
|
@@ -2935,7 +3440,6 @@ function transformDagRunResource(transport) {
|
|
|
2935
3440
|
}
|
|
2936
3441
|
/** Cancel an in-progress DAG run and request cancellation of its still-running transforms. */
|
|
2937
3442
|
async function cancel(runId, options = {}) {
|
|
2938
|
-
await transport.require("transformDagRun.cancel", options);
|
|
2939
3443
|
await transport.requestRaw(`/api/transform-dag-run/${runId}/cancel`, {
|
|
2940
3444
|
...options,
|
|
2941
3445
|
method: "POST",
|
|
@@ -2943,8 +3447,8 @@ function transformDagRunResource(transport) {
|
|
|
2943
3447
|
});
|
|
2944
3448
|
}
|
|
2945
3449
|
return {
|
|
2946
|
-
transformRuns,
|
|
2947
|
-
cancel
|
|
3450
|
+
transformRuns: explain("transformRuns", transformRuns),
|
|
3451
|
+
cancel: explain("cancel", cancel)
|
|
2948
3452
|
};
|
|
2949
3453
|
}
|
|
2950
3454
|
//#endregion
|
|
@@ -3092,12 +3596,12 @@ function lensPath(id, lensId) {
|
|
|
3092
3596
|
return `/api/ee/transforms/${id}/inspect/${encodeURIComponent(lensId)}`;
|
|
3093
3597
|
}
|
|
3094
3598
|
function transformInspectorResource(transport) {
|
|
3599
|
+
const { explain } = explainer(transport, "transformInspector");
|
|
3095
3600
|
/**
|
|
3096
3601
|
* Discover what the inspector can show for a transform: its source and target tables with field
|
|
3097
3602
|
* statistics, the fields its query touches, and the lenses available on it.
|
|
3098
3603
|
*/
|
|
3099
3604
|
async function discover(id, options = {}) {
|
|
3100
|
-
await transport.require("transformInspector.discover", options);
|
|
3101
3605
|
return transport.requestParsed(TransformInspection, `/api/ee/transforms/${id}/inspect`, { ...options });
|
|
3102
3606
|
}
|
|
3103
3607
|
/**
|
|
@@ -3106,7 +3610,6 @@ function transformInspectorResource(transport) {
|
|
|
3106
3610
|
* the `params` its trigger emitted.
|
|
3107
3611
|
*/
|
|
3108
3612
|
async function lens(id, lensId, params = {}, options = {}) {
|
|
3109
|
-
await transport.require("transformInspector.lens", options);
|
|
3110
3613
|
return transport.requestParsed(TransformLens, lensPath(id, lensId), {
|
|
3111
3614
|
...options,
|
|
3112
3615
|
query: { join_step: params.join_step }
|
|
@@ -3118,7 +3621,6 @@ function transformInspectorResource(transport) {
|
|
|
3118
3621
|
* opened with.
|
|
3119
3622
|
*/
|
|
3120
3623
|
async function query(id, lensId, params, options = {}) {
|
|
3121
|
-
await transport.require("transformInspector.query", options);
|
|
3122
3624
|
return transport.requestParsed(CardQueryResult, `${lensPath(id, lensId)}/query`, {
|
|
3123
3625
|
...options,
|
|
3124
3626
|
method: "POST",
|
|
@@ -3126,17 +3628,17 @@ function transformInspectorResource(transport) {
|
|
|
3126
3628
|
});
|
|
3127
3629
|
}
|
|
3128
3630
|
return {
|
|
3129
|
-
discover,
|
|
3130
|
-
lens,
|
|
3131
|
-
query
|
|
3631
|
+
discover: explain("discover", discover),
|
|
3632
|
+
lens: explain("lens", lens),
|
|
3633
|
+
query: explain("query", query)
|
|
3132
3634
|
};
|
|
3133
3635
|
}
|
|
3134
3636
|
//#endregion
|
|
3135
3637
|
//#region ../client/src/resources/transform-job.ts
|
|
3136
3638
|
function transformJobResource(transport) {
|
|
3639
|
+
const { explain } = explainer(transport, "transformJob");
|
|
3137
3640
|
/** List every transform job the caller can see. */
|
|
3138
3641
|
async function list(options = {}) {
|
|
3139
|
-
await transport.require("transformJob.list", options);
|
|
3140
3642
|
const { features } = await transport.server(options);
|
|
3141
3643
|
return {
|
|
3142
3644
|
data: await transport.requestParsed(z.array(transformJobSchema(features)), "/api/transform-job", { ...options }),
|
|
@@ -3145,13 +3647,11 @@ function transformJobResource(transport) {
|
|
|
3145
3647
|
}
|
|
3146
3648
|
/** Get one transform job by id. */
|
|
3147
3649
|
async function get(id, options = {}) {
|
|
3148
|
-
await transport.require("transformJob.get", options);
|
|
3149
3650
|
const { features } = await transport.server(options);
|
|
3150
3651
|
return transport.requestParsed(transformJobSchema(features), `/api/transform-job/${id}`, { ...options });
|
|
3151
3652
|
}
|
|
3152
3653
|
/** Create a transform job — a schedule plus the tags it runs — from a full body. */
|
|
3153
3654
|
async function create(params, options = {}) {
|
|
3154
|
-
await transport.require("transformJob.create", options);
|
|
3155
3655
|
const { features } = await transport.server(options);
|
|
3156
3656
|
return transport.requestParsed(transformJobSchema(features), "/api/transform-job", {
|
|
3157
3657
|
...options,
|
|
@@ -3161,7 +3661,6 @@ function transformJobResource(transport) {
|
|
|
3161
3661
|
}
|
|
3162
3662
|
/** Update a transform job by id, patching only the fields the body carries. */
|
|
3163
3663
|
async function update(id, params, options = {}) {
|
|
3164
|
-
await transport.require("transformJob.update", options);
|
|
3165
3664
|
const { features } = await transport.server(options);
|
|
3166
3665
|
return transport.requestParsed(transformJobSchema(features), `/api/transform-job/${id}`, {
|
|
3167
3666
|
...options,
|
|
@@ -3171,7 +3670,6 @@ function transformJobResource(transport) {
|
|
|
3171
3670
|
}
|
|
3172
3671
|
/** Delete a transform job by id, leaving the transforms it ran untouched. */
|
|
3173
3672
|
async function remove(id, options = {}) {
|
|
3174
|
-
await transport.require("transformJob.delete", options);
|
|
3175
3673
|
await transport.requestRaw(`/api/transform-job/${id}`, {
|
|
3176
3674
|
...options,
|
|
3177
3675
|
method: "DELETE",
|
|
@@ -3183,7 +3681,6 @@ function transformJobResource(transport) {
|
|
|
3183
3681
|
* plus those transforms' dependencies; `run_all` re-runs dependencies that are already fresh.
|
|
3184
3682
|
*/
|
|
3185
3683
|
async function run(id, params = {}, options = {}) {
|
|
3186
|
-
await transport.require("transformJob.run", options);
|
|
3187
3684
|
const { features } = await transport.server(options);
|
|
3188
3685
|
return transport.requestParsed(transformJobRunResultSchema(features), `/api/transform-job/${id}/run`, {
|
|
3189
3686
|
...options,
|
|
@@ -3193,7 +3690,6 @@ function transformJobResource(transport) {
|
|
|
3193
3690
|
}
|
|
3194
3691
|
/** List the transforms a job would execute, resolved from the job's tags. */
|
|
3195
3692
|
async function transforms(id, options = {}) {
|
|
3196
|
-
await transport.require("transformJob.transforms", options);
|
|
3197
3693
|
const { features } = await transport.server(options);
|
|
3198
3694
|
return {
|
|
3199
3695
|
data: await transport.requestParsed(z.array(transformRowSchema(features)), `/api/transform-job/${id}/transforms`, { ...options }),
|
|
@@ -3205,7 +3701,6 @@ function transformJobResource(transport) {
|
|
|
3205
3701
|
* manual runs ignore the flag. The path carries no id and is a different endpoint from `update`.
|
|
3206
3702
|
*/
|
|
3207
3703
|
async function setActive(active, options = {}) {
|
|
3208
|
-
await transport.require("transformJob.setActive", options);
|
|
3209
3704
|
return transport.requestParsed(TransformJobActiveResult, "/api/transform-job/active", {
|
|
3210
3705
|
...options,
|
|
3211
3706
|
method: "PUT",
|
|
@@ -3213,14 +3708,14 @@ function transformJobResource(transport) {
|
|
|
3213
3708
|
});
|
|
3214
3709
|
}
|
|
3215
3710
|
return {
|
|
3216
|
-
list,
|
|
3217
|
-
get,
|
|
3218
|
-
create,
|
|
3219
|
-
update,
|
|
3220
|
-
delete: remove,
|
|
3221
|
-
run,
|
|
3222
|
-
transforms,
|
|
3223
|
-
setActive
|
|
3711
|
+
list: explain("list", list),
|
|
3712
|
+
get: explain("get", get),
|
|
3713
|
+
create: explain("create", create),
|
|
3714
|
+
update: explain("update", update),
|
|
3715
|
+
delete: explain("delete", remove),
|
|
3716
|
+
run: explain("run", run),
|
|
3717
|
+
transforms: explain("transforms", transforms),
|
|
3718
|
+
setActive: explain("setActive", setActive)
|
|
3224
3719
|
};
|
|
3225
3720
|
}
|
|
3226
3721
|
//#endregion
|
|
@@ -3280,14 +3775,13 @@ function libraryPath(path) {
|
|
|
3280
3775
|
return `/api/ee/transforms-python/library/${encodeURIComponent(path)}`;
|
|
3281
3776
|
}
|
|
3282
3777
|
function transformPythonResource(transport) {
|
|
3778
|
+
const { explain } = explainer(transport, "transformPython");
|
|
3283
3779
|
/** Get the Python library at a path, the user module every Python transform can import. */
|
|
3284
3780
|
async function getLibrary(path, options = {}) {
|
|
3285
|
-
await transport.require("transformPython.getLibrary", options);
|
|
3286
3781
|
return transport.requestParsed(PythonLibrary, libraryPath(path), { ...options });
|
|
3287
3782
|
}
|
|
3288
3783
|
/** Replace the source of the Python library at a path, creating the library when none exists. */
|
|
3289
3784
|
async function updateLibrary(path, params, options = {}) {
|
|
3290
|
-
await transport.require("transformPython.updateLibrary", options);
|
|
3291
3785
|
return transport.requestParsed(PythonLibrary, libraryPath(path), {
|
|
3292
3786
|
...options,
|
|
3293
3787
|
method: "PUT",
|
|
@@ -3300,7 +3794,6 @@ function transformPythonResource(transport) {
|
|
|
3300
3794
|
* sizes are capped at 100 rows.
|
|
3301
3795
|
*/
|
|
3302
3796
|
async function testRun(params, options = {}) {
|
|
3303
|
-
await transport.require("transformPython.testRun", options);
|
|
3304
3797
|
return transport.requestParsed(PythonApiTestRun, "/api/ee/transforms-python/test-run", {
|
|
3305
3798
|
...options,
|
|
3306
3799
|
method: "POST",
|
|
@@ -3308,9 +3801,9 @@ function transformPythonResource(transport) {
|
|
|
3308
3801
|
});
|
|
3309
3802
|
}
|
|
3310
3803
|
return {
|
|
3311
|
-
getLibrary,
|
|
3312
|
-
updateLibrary,
|
|
3313
|
-
testRun
|
|
3804
|
+
getLibrary: explain("getLibrary", getLibrary),
|
|
3805
|
+
updateLibrary: explain("updateLibrary", updateLibrary),
|
|
3806
|
+
testRun: explain("testRun", testRun)
|
|
3314
3807
|
};
|
|
3315
3808
|
}
|
|
3316
3809
|
//#endregion
|
|
@@ -3324,6 +3817,7 @@ const TransformRunKickoff = z.object({
|
|
|
3324
3817
|
});
|
|
3325
3818
|
const TransformDagTransformList = z.array(TransformDagTransform);
|
|
3326
3819
|
function transformResource(transport) {
|
|
3820
|
+
const { explain, explainWalk } = explainer(transport, "transform");
|
|
3327
3821
|
async function requestTransformList(path, opts) {
|
|
3328
3822
|
const { features } = await transport.server(opts);
|
|
3329
3823
|
return {
|
|
@@ -3337,18 +3831,15 @@ function transformResource(transport) {
|
|
|
3337
3831
|
}
|
|
3338
3832
|
/** List every transform the caller can see. */
|
|
3339
3833
|
async function list(options = {}) {
|
|
3340
|
-
await transport.require("transform.list", options);
|
|
3341
3834
|
return requestTransformList("/api/transform", { ...options });
|
|
3342
3835
|
}
|
|
3343
3836
|
/** Get one transform by id. */
|
|
3344
3837
|
async function get(id, options = {}) {
|
|
3345
|
-
await transport.require("transform.get", options);
|
|
3346
3838
|
const { features } = await transport.server(options);
|
|
3347
3839
|
return transport.requestParsed(transformDetailSchema(features), `/api/transform/${id}`, { ...options });
|
|
3348
3840
|
}
|
|
3349
3841
|
/** Create a transform — a query plus the warehouse table it writes — from a full body. */
|
|
3350
3842
|
async function create(params, options = {}) {
|
|
3351
|
-
await transport.require("transform.create", options);
|
|
3352
3843
|
return requestTransform("/api/transform", {
|
|
3353
3844
|
...options,
|
|
3354
3845
|
method: "POST",
|
|
@@ -3357,7 +3848,6 @@ function transformResource(transport) {
|
|
|
3357
3848
|
}
|
|
3358
3849
|
/** Update a transform by id, patching only the fields the body carries. */
|
|
3359
3850
|
async function update(id, params, options = {}) {
|
|
3360
|
-
await transport.require("transform.update", options);
|
|
3361
3851
|
return requestTransform(`/api/transform/${id}`, {
|
|
3362
3852
|
...options,
|
|
3363
3853
|
method: "PUT",
|
|
@@ -3366,7 +3856,6 @@ function transformResource(transport) {
|
|
|
3366
3856
|
}
|
|
3367
3857
|
/** Delete a transform by id, leaving any table it already materialized in place. */
|
|
3368
3858
|
async function remove(id, options = {}) {
|
|
3369
|
-
await transport.require("transform.delete", options);
|
|
3370
3859
|
await transport.requestRaw(`/api/transform/${id}`, {
|
|
3371
3860
|
...options,
|
|
3372
3861
|
method: "DELETE",
|
|
@@ -3375,7 +3864,6 @@ function transformResource(transport) {
|
|
|
3375
3864
|
}
|
|
3376
3865
|
/** Drop a transform's materialized output table, keeping the transform definition. */
|
|
3377
3866
|
async function deleteTable(id, options = {}) {
|
|
3378
|
-
await transport.require("transform.deleteTable", options);
|
|
3379
3867
|
await transport.requestRaw(`/api/transform/${id}/table`, {
|
|
3380
3868
|
...options,
|
|
3381
3869
|
method: "DELETE",
|
|
@@ -3384,12 +3872,10 @@ function transformResource(transport) {
|
|
|
3384
3872
|
}
|
|
3385
3873
|
/** List the upstream transforms a transform depends on. */
|
|
3386
3874
|
async function dependencies(id, options = {}) {
|
|
3387
|
-
await transport.require("transform.dependencies", options);
|
|
3388
3875
|
return requestTransformList(`/api/transform/${id}/dependencies`, { ...options });
|
|
3389
3876
|
}
|
|
3390
3877
|
/** Request cancellation of a transform's current run. */
|
|
3391
3878
|
async function cancel(id, options = {}) {
|
|
3392
|
-
await transport.require("transform.cancel", options);
|
|
3393
3879
|
await transport.requestRaw(`/api/transform/${id}/cancel`, {
|
|
3394
3880
|
...options,
|
|
3395
3881
|
method: "POST",
|
|
@@ -3398,12 +3884,10 @@ function transformResource(transport) {
|
|
|
3398
3884
|
}
|
|
3399
3885
|
/** Get one transform run by run id — not by the id of the transform that produced it. */
|
|
3400
3886
|
async function getRun(runId, options = {}) {
|
|
3401
|
-
await transport.require("transform.getRun", options);
|
|
3402
3887
|
return transport.requestParsed(TransformRun, `/api/transform/run/${runId}`, { ...options });
|
|
3403
3888
|
}
|
|
3404
3889
|
/** Walk the run history, newest first, optionally narrowed to a single transform. */
|
|
3405
3890
|
async function* runPages(params = {}, options = {}) {
|
|
3406
|
-
await transport.require("transform.runPages", options);
|
|
3407
3891
|
yield* paginatePages(transport, "/api/transform/run", TransformRun, {
|
|
3408
3892
|
query: { "transform-ids": params["transform-ids"] },
|
|
3409
3893
|
...options.offset !== void 0 && { offset: options.offset },
|
|
@@ -3417,7 +3901,6 @@ function transformResource(transport) {
|
|
|
3417
3901
|
* DAG reprocess run, or a standalone transform run — and never a member run of a job or DAG.
|
|
3418
3902
|
*/
|
|
3419
3903
|
async function* runSummaryPages(params = {}, options = {}) {
|
|
3420
|
-
await transport.require("transform.runSummaryPages", options);
|
|
3421
3904
|
yield* paginatePages(transport, "/api/transform/runs", TransformRunSummary, {
|
|
3422
3905
|
query: {
|
|
3423
3906
|
types: params.types,
|
|
@@ -3440,7 +3923,6 @@ function transformResource(transport) {
|
|
|
3440
3923
|
* source rather than the rows past the last checkpoint.
|
|
3441
3924
|
*/
|
|
3442
3925
|
async function resetCheckpoint(id, options = {}) {
|
|
3443
|
-
await transport.require("transform.resetCheckpoint", options);
|
|
3444
3926
|
await transport.requestRaw(`/api/transform/${id}/reset-checkpoint`, {
|
|
3445
3927
|
...options,
|
|
3446
3928
|
method: "POST",
|
|
@@ -3455,7 +3937,6 @@ function transformResource(transport) {
|
|
|
3455
3937
|
* `runSummaryPages` with `types: ["dag"]`, and the member runs off `transformDagRun.transformRuns`.
|
|
3456
3938
|
*/
|
|
3457
3939
|
async function runDag(id, params, options = {}) {
|
|
3458
|
-
await transport.require("transform.runDag", options);
|
|
3459
3940
|
return transport.requestParsed(TransformDagRunResult, `/api/transform/${id}/run-dag`, {
|
|
3460
3941
|
...options,
|
|
3461
3942
|
method: "POST",
|
|
@@ -3464,7 +3945,6 @@ function transformResource(transport) {
|
|
|
3464
3945
|
}
|
|
3465
3946
|
/** Preview the transforms a DAG reprocess seeded at a transform would run, in execution order. */
|
|
3466
3947
|
async function dagTransforms(id, params, options = {}) {
|
|
3467
|
-
await transport.require("transform.dagTransforms", options);
|
|
3468
3948
|
return {
|
|
3469
3949
|
data: await transport.requestParsed(TransformDagTransformList, `/api/transform/${id}/dag-transforms`, {
|
|
3470
3950
|
...options,
|
|
@@ -3479,7 +3959,6 @@ function transformResource(transport) {
|
|
|
3479
3959
|
* A server that answers no run id started nothing, and there is then nothing to poll.
|
|
3480
3960
|
*/
|
|
3481
3961
|
async function run(id, params = {}, options = {}) {
|
|
3482
|
-
await transport.require("transform.run", options);
|
|
3483
3962
|
const kickoff = await transport.requestParsed(TransformRunKickoff, `/api/transform/${id}/run`, {
|
|
3484
3963
|
...options,
|
|
3485
3964
|
method: "POST"
|
|
@@ -3520,30 +3999,30 @@ function transformResource(transport) {
|
|
|
3520
3999
|
}
|
|
3521
4000
|
}
|
|
3522
4001
|
return {
|
|
3523
|
-
list,
|
|
3524
|
-
get,
|
|
3525
|
-
create,
|
|
3526
|
-
update,
|
|
3527
|
-
delete: remove,
|
|
3528
|
-
deleteTable,
|
|
3529
|
-
dependencies,
|
|
3530
|
-
cancel,
|
|
3531
|
-
getRun,
|
|
3532
|
-
runPages,
|
|
3533
|
-
runSummaryPages,
|
|
3534
|
-
resetCheckpoint,
|
|
3535
|
-
runDag,
|
|
3536
|
-
dagTransforms,
|
|
3537
|
-
run
|
|
4002
|
+
list: explain("list", list),
|
|
4003
|
+
get: explain("get", get),
|
|
4004
|
+
create: explain("create", create),
|
|
4005
|
+
update: explain("update", update),
|
|
4006
|
+
delete: explain("delete", remove),
|
|
4007
|
+
deleteTable: explain("deleteTable", deleteTable),
|
|
4008
|
+
dependencies: explain("dependencies", dependencies),
|
|
4009
|
+
cancel: explain("cancel", cancel),
|
|
4010
|
+
getRun: explain("getRun", getRun),
|
|
4011
|
+
runPages: explainWalk("runPages", runPages),
|
|
4012
|
+
runSummaryPages: explainWalk("runSummaryPages", runSummaryPages),
|
|
4013
|
+
resetCheckpoint: explain("resetCheckpoint", resetCheckpoint),
|
|
4014
|
+
runDag: explain("runDag", runDag),
|
|
4015
|
+
dagTransforms: explain("dagTransforms", dagTransforms),
|
|
4016
|
+
run: explain("run", run)
|
|
3538
4017
|
};
|
|
3539
4018
|
}
|
|
3540
4019
|
//#endregion
|
|
3541
4020
|
//#region ../client/src/resources/transform-tag.ts
|
|
3542
4021
|
const TransformTagApiList = z.array(TransformTag);
|
|
3543
4022
|
function transformTagResource(transport) {
|
|
4023
|
+
const { explain } = explainer(transport, "transformTag");
|
|
3544
4024
|
/** List every transform tag the caller can see, built-in tags included. */
|
|
3545
4025
|
async function list(options = {}) {
|
|
3546
|
-
await transport.require("transformTag.list", options);
|
|
3547
4026
|
return {
|
|
3548
4027
|
data: await transport.requestParsed(TransformTagApiList, "/api/transform-tag", { ...options }),
|
|
3549
4028
|
total: null
|
|
@@ -3551,7 +4030,6 @@ function transformTagResource(transport) {
|
|
|
3551
4030
|
}
|
|
3552
4031
|
/** Create a transform tag, the label a transform job matches transforms on. */
|
|
3553
4032
|
async function create(params, options = {}) {
|
|
3554
|
-
await transport.require("transformTag.create", options);
|
|
3555
4033
|
return transport.requestParsed(TransformTag, "/api/transform-tag", {
|
|
3556
4034
|
...options,
|
|
3557
4035
|
method: "POST",
|
|
@@ -3560,7 +4038,6 @@ function transformTagResource(transport) {
|
|
|
3560
4038
|
}
|
|
3561
4039
|
/** Rename a transform tag by id. */
|
|
3562
4040
|
async function update(id, params, options = {}) {
|
|
3563
|
-
await transport.require("transformTag.update", options);
|
|
3564
4041
|
return transport.requestParsed(TransformTag, `/api/transform-tag/${id}`, {
|
|
3565
4042
|
...options,
|
|
3566
4043
|
method: "PUT",
|
|
@@ -3569,7 +4046,6 @@ function transformTagResource(transport) {
|
|
|
3569
4046
|
}
|
|
3570
4047
|
/** Delete a transform tag by id, detaching it from every transform and job carrying it. */
|
|
3571
4048
|
async function remove(id, options = {}) {
|
|
3572
|
-
await transport.require("transformTag.delete", options);
|
|
3573
4049
|
await transport.requestRaw(`/api/transform-tag/${id}`, {
|
|
3574
4050
|
...options,
|
|
3575
4051
|
method: "DELETE",
|
|
@@ -3577,19 +4053,180 @@ function transformTagResource(transport) {
|
|
|
3577
4053
|
});
|
|
3578
4054
|
}
|
|
3579
4055
|
return {
|
|
3580
|
-
list,
|
|
3581
|
-
create,
|
|
3582
|
-
update,
|
|
3583
|
-
delete: remove
|
|
4056
|
+
list: explain("list", list),
|
|
4057
|
+
create: explain("create", create),
|
|
4058
|
+
update: explain("update", update),
|
|
4059
|
+
delete: explain("delete", remove)
|
|
3584
4060
|
};
|
|
3585
4061
|
}
|
|
3586
4062
|
//#endregion
|
|
4063
|
+
//#region ../client/src/domain/transform-test.ts
|
|
4064
|
+
const TransformTestTable = z.object({
|
|
4065
|
+
schema: z.string().min(1).nullable().optional(),
|
|
4066
|
+
name: z.string().min(1)
|
|
4067
|
+
}).loose();
|
|
4068
|
+
const TransformTestColumn = z.object({
|
|
4069
|
+
name: z.string().min(1),
|
|
4070
|
+
cast_type: z.string().min(1)
|
|
4071
|
+
}).loose();
|
|
4072
|
+
const TransformTestCell = z.union([
|
|
4073
|
+
z.boolean(),
|
|
4074
|
+
z.number(),
|
|
4075
|
+
z.string(),
|
|
4076
|
+
z.null()
|
|
4077
|
+
]);
|
|
4078
|
+
const TransformTestRow = z.record(z.string(), TransformTestCell);
|
|
4079
|
+
const TransformTestSqlInput = z.object({
|
|
4080
|
+
table: TransformTestTable,
|
|
4081
|
+
format: z.literal("sql"),
|
|
4082
|
+
sql: z.string().min(1)
|
|
4083
|
+
}).loose();
|
|
4084
|
+
const TransformTestRowsInput = z.object({
|
|
4085
|
+
table: TransformTestTable,
|
|
4086
|
+
format: z.literal("rows"),
|
|
4087
|
+
columns: z.array(TransformTestColumn).min(1),
|
|
4088
|
+
rows: z.array(TransformTestRow)
|
|
4089
|
+
}).loose();
|
|
4090
|
+
const TransformTestInput = z.discriminatedUnion("format", [TransformTestSqlInput, TransformTestRowsInput]);
|
|
4091
|
+
const TransformTestEqualsSqlExpectation = z.object({
|
|
4092
|
+
type: z.literal("equals"),
|
|
4093
|
+
name: z.string().min(1),
|
|
4094
|
+
format: z.literal("sql"),
|
|
4095
|
+
sql: z.string().min(1)
|
|
4096
|
+
}).loose();
|
|
4097
|
+
const TransformTestEqualsRowsExpectation = z.object({
|
|
4098
|
+
type: z.literal("equals"),
|
|
4099
|
+
name: z.string().min(1),
|
|
4100
|
+
format: z.literal("rows"),
|
|
4101
|
+
columns: z.array(TransformTestColumn).min(1),
|
|
4102
|
+
rows: z.array(TransformTestRow)
|
|
4103
|
+
}).loose();
|
|
4104
|
+
const TransformTestEqualsExpectation = z.discriminatedUnion("format", [TransformTestEqualsSqlExpectation, TransformTestEqualsRowsExpectation]);
|
|
4105
|
+
const TransformTestEmptyExpectation = z.object({
|
|
4106
|
+
type: z.literal("empty"),
|
|
4107
|
+
name: z.string().min(1),
|
|
4108
|
+
sql: z.string().min(1)
|
|
4109
|
+
}).loose();
|
|
4110
|
+
const TransformTestExpectation = z.discriminatedUnion("type", [TransformTestEqualsExpectation, TransformTestEmptyExpectation]);
|
|
4111
|
+
const TransformTest = z.object({
|
|
4112
|
+
id: z.number().int(),
|
|
4113
|
+
entity_id: z.string(),
|
|
4114
|
+
transform_id: z.number().int(),
|
|
4115
|
+
creator_id: z.number().int(),
|
|
4116
|
+
name: z.string(),
|
|
4117
|
+
description: z.string().nullable(),
|
|
4118
|
+
inputs: z.array(TransformTestInput),
|
|
4119
|
+
expectations: z.array(TransformTestExpectation),
|
|
4120
|
+
created_at: z.string(),
|
|
4121
|
+
updated_at: z.string()
|
|
4122
|
+
}).loose();
|
|
4123
|
+
TransformTest.pick({
|
|
4124
|
+
id: true,
|
|
4125
|
+
transform_id: true,
|
|
4126
|
+
name: true,
|
|
4127
|
+
description: true
|
|
4128
|
+
}).strip();
|
|
4129
|
+
z.object({
|
|
4130
|
+
transform_id: z.number().int().positive(),
|
|
4131
|
+
name: z.string().min(1),
|
|
4132
|
+
description: z.string().nullable().optional(),
|
|
4133
|
+
inputs: z.array(TransformTestInput),
|
|
4134
|
+
expectations: z.array(TransformTestExpectation)
|
|
4135
|
+
}).loose();
|
|
4136
|
+
z.object({
|
|
4137
|
+
transform_id: z.number().int().positive().optional(),
|
|
4138
|
+
name: z.string().min(1).optional(),
|
|
4139
|
+
description: z.string().nullable().optional(),
|
|
4140
|
+
inputs: z.array(TransformTestInput).optional(),
|
|
4141
|
+
expectations: z.array(TransformTestExpectation).optional()
|
|
4142
|
+
}).loose();
|
|
4143
|
+
const TransformTestResultColumn = z.object({
|
|
4144
|
+
name: z.string(),
|
|
4145
|
+
database_type: z.string()
|
|
4146
|
+
}).loose();
|
|
4147
|
+
const TransformTestExpectationError = z.object({
|
|
4148
|
+
type: z.string(),
|
|
4149
|
+
message: z.string()
|
|
4150
|
+
}).loose();
|
|
4151
|
+
const TransformTestCellMismatch = z.object({
|
|
4152
|
+
column: z.string(),
|
|
4153
|
+
expected: TransformTestCell,
|
|
4154
|
+
actual: TransformTestCell
|
|
4155
|
+
}).loose();
|
|
4156
|
+
const TransformTestRowCounts = z.object({
|
|
4157
|
+
actual: z.number().int(),
|
|
4158
|
+
expected: z.number().int()
|
|
4159
|
+
}).loose();
|
|
4160
|
+
const TransformTestEqualsFindings = z.object({
|
|
4161
|
+
name: z.string(),
|
|
4162
|
+
type: z.literal("equals"),
|
|
4163
|
+
status: z.enum(["passed", "failed"]),
|
|
4164
|
+
columns: z.array(TransformTestResultColumn),
|
|
4165
|
+
"row-counts": TransformTestRowCounts,
|
|
4166
|
+
"extra-rows": z.array(TransformTestRow),
|
|
4167
|
+
"missing-rows": z.array(TransformTestRow),
|
|
4168
|
+
"cell-mismatches": z.array(TransformTestCellMismatch),
|
|
4169
|
+
truncated: z.number().int()
|
|
4170
|
+
}).loose();
|
|
4171
|
+
const TransformTestEqualsError = z.object({
|
|
4172
|
+
name: z.string(),
|
|
4173
|
+
type: z.literal("equals"),
|
|
4174
|
+
status: z.literal("error"),
|
|
4175
|
+
error: TransformTestExpectationError
|
|
4176
|
+
}).loose();
|
|
4177
|
+
const TransformTestEqualsResult = z.discriminatedUnion("status", [TransformTestEqualsFindings, TransformTestEqualsError]);
|
|
4178
|
+
const TransformTestEmptyPassed = z.object({
|
|
4179
|
+
name: z.string(),
|
|
4180
|
+
type: z.literal("empty"),
|
|
4181
|
+
status: z.literal("passed")
|
|
4182
|
+
}).loose();
|
|
4183
|
+
const TransformTestEmptyFailed = z.object({
|
|
4184
|
+
name: z.string(),
|
|
4185
|
+
type: z.literal("empty"),
|
|
4186
|
+
status: z.literal("failed"),
|
|
4187
|
+
columns: z.array(TransformTestResultColumn),
|
|
4188
|
+
sample: z.array(TransformTestRow),
|
|
4189
|
+
truncated: z.number().int()
|
|
4190
|
+
}).loose();
|
|
4191
|
+
const TransformTestEmptyError = z.object({
|
|
4192
|
+
name: z.string(),
|
|
4193
|
+
type: z.literal("empty"),
|
|
4194
|
+
status: z.literal("error"),
|
|
4195
|
+
error: TransformTestExpectationError
|
|
4196
|
+
}).loose();
|
|
4197
|
+
const TransformTestEmptyResult = z.discriminatedUnion("status", [
|
|
4198
|
+
TransformTestEmptyPassed,
|
|
4199
|
+
TransformTestEmptyFailed,
|
|
4200
|
+
TransformTestEmptyError
|
|
4201
|
+
]);
|
|
4202
|
+
const TransformTestExpectationResult = z.discriminatedUnion("type", [TransformTestEqualsResult, TransformTestEmptyResult]);
|
|
4203
|
+
const TransformTestRunResult = z.object({
|
|
4204
|
+
status: z.enum(["passed", "failed"]),
|
|
4205
|
+
expectations: z.array(TransformTestExpectationResult),
|
|
4206
|
+
tables: z.record(z.string(), z.string())
|
|
4207
|
+
}).loose();
|
|
4208
|
+
z.enum([
|
|
4209
|
+
"transform-test.unknown-column",
|
|
4210
|
+
"transform-test.ambiguous-column",
|
|
4211
|
+
"transform-test.missing-inputs",
|
|
4212
|
+
"transform-test.unused-inputs",
|
|
4213
|
+
"transform-test.duplicate-input-table",
|
|
4214
|
+
"transform-test.unparseable-source",
|
|
4215
|
+
"transform-test.unremapped-reference",
|
|
4216
|
+
"transform-test.unsupported-transform",
|
|
4217
|
+
"transform-test.unsupported-driver",
|
|
4218
|
+
"transform-test.transform-failed",
|
|
4219
|
+
"transform-test.setup-failed",
|
|
4220
|
+
"transform-test.expectation-failed",
|
|
4221
|
+
"transform-test.unsupported-format"
|
|
4222
|
+
]);
|
|
4223
|
+
//#endregion
|
|
3587
4224
|
//#region ../client/src/resources/transform-test.ts
|
|
3588
4225
|
const TransformTestApiList = z.array(TransformTest);
|
|
3589
4226
|
function transformTestResource(transport) {
|
|
4227
|
+
const { explain } = explainer(transport, "transformTest");
|
|
3590
4228
|
/** List the transform tests, optionally only those of one transform. */
|
|
3591
4229
|
async function list(params = {}, options = {}) {
|
|
3592
|
-
await transport.require("transformTest.list", options);
|
|
3593
4230
|
return {
|
|
3594
4231
|
data: await transport.requestParsed(TransformTestApiList, "/api/ee/transform-test", {
|
|
3595
4232
|
...options,
|
|
@@ -3600,7 +4237,6 @@ function transformTestResource(transport) {
|
|
|
3600
4237
|
}
|
|
3601
4238
|
/** Get a transform test by id. */
|
|
3602
4239
|
async function get(id, options = {}) {
|
|
3603
|
-
await transport.require("transformTest.get", options);
|
|
3604
4240
|
return transport.requestParsed(TransformTest, `/api/ee/transform-test/${id}`, { ...options });
|
|
3605
4241
|
}
|
|
3606
4242
|
/**
|
|
@@ -3609,7 +4245,6 @@ function transformTestResource(transport) {
|
|
|
3609
4245
|
* `TransformTestRefusalCode` when they cannot run.
|
|
3610
4246
|
*/
|
|
3611
4247
|
async function create(params, options = {}) {
|
|
3612
|
-
await transport.require("transformTest.create", options);
|
|
3613
4248
|
return transport.requestParsed(TransformTest, "/api/ee/transform-test", {
|
|
3614
4249
|
...options,
|
|
3615
4250
|
method: "POST",
|
|
@@ -3621,7 +4256,6 @@ function transformTestResource(transport) {
|
|
|
3621
4256
|
* transform, inputs or expectations is validated as on create, with the same refusal.
|
|
3622
4257
|
*/
|
|
3623
4258
|
async function update(id, params, options = {}) {
|
|
3624
|
-
await transport.require("transformTest.update", options);
|
|
3625
4259
|
return transport.requestParsed(TransformTest, `/api/ee/transform-test/${id}`, {
|
|
3626
4260
|
...options,
|
|
3627
4261
|
method: "PUT",
|
|
@@ -3630,7 +4264,6 @@ function transformTestResource(transport) {
|
|
|
3630
4264
|
}
|
|
3631
4265
|
/** Delete a transform test by id. */
|
|
3632
4266
|
async function remove(id, options = {}) {
|
|
3633
|
-
await transport.require("transformTest.delete", options);
|
|
3634
4267
|
await transport.requestRaw(`/api/ee/transform-test/${id}`, {
|
|
3635
4268
|
...options,
|
|
3636
4269
|
method: "DELETE",
|
|
@@ -3644,19 +4277,18 @@ function transformTestResource(transport) {
|
|
|
3644
4277
|
* refused and nothing meaningful ran.
|
|
3645
4278
|
*/
|
|
3646
4279
|
async function run(id, options = {}) {
|
|
3647
|
-
await transport.require("transformTest.run", options);
|
|
3648
4280
|
return transport.requestParsed(TransformTestRunResult, `/api/ee/transform-test/${id}/run`, {
|
|
3649
4281
|
...options,
|
|
3650
4282
|
method: "POST"
|
|
3651
4283
|
});
|
|
3652
4284
|
}
|
|
3653
4285
|
return {
|
|
3654
|
-
list,
|
|
3655
|
-
get,
|
|
3656
|
-
create,
|
|
3657
|
-
update,
|
|
3658
|
-
delete: remove,
|
|
3659
|
-
run
|
|
4286
|
+
list: explain("list", list),
|
|
4287
|
+
get: explain("get", get),
|
|
4288
|
+
create: explain("create", create),
|
|
4289
|
+
update: explain("update", update),
|
|
4290
|
+
delete: explain("delete", remove),
|
|
4291
|
+
run: explain("run", run)
|
|
3660
4292
|
};
|
|
3661
4293
|
}
|
|
3662
4294
|
//#endregion
|
|
@@ -3670,7 +4302,6 @@ function uploadResource(transport) {
|
|
|
3670
4302
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
3671
4303
|
*/
|
|
3672
4304
|
async function createFromCsv(file, params, options = {}) {
|
|
3673
|
-
await transport.require("upload.createFromCsv", options);
|
|
3674
4305
|
const form = buildCsvFormData(file);
|
|
3675
4306
|
form.append("collection_id", params.collection_id);
|
|
3676
4307
|
const response = await transport.requestRaw("/api/upload/csv", {
|
|
@@ -3723,7 +4354,6 @@ CurrentUser.pick({
|
|
|
3723
4354
|
function userResource(transport) {
|
|
3724
4355
|
/** Get the user the request's credentials authenticate as. */
|
|
3725
4356
|
async function current(options = {}) {
|
|
3726
|
-
await transport.require("user.current", options);
|
|
3727
4357
|
return transport.requestParsed(CurrentUser, "/api/user/current", { ...options });
|
|
3728
4358
|
}
|
|
3729
4359
|
return { current };
|
|
@@ -3774,6 +4404,8 @@ function createClient(config, options) {
|
|
|
3774
4404
|
upload: uploadResource(transport),
|
|
3775
4405
|
user: userResource(transport),
|
|
3776
4406
|
server: transport.server,
|
|
4407
|
+
verifiedServer: transport.verifiedServer,
|
|
4408
|
+
probesSettled: transport.probesSettled,
|
|
3777
4409
|
requestParsed: transport.requestParsed,
|
|
3778
4410
|
requestRaw: transport.requestRaw,
|
|
3779
4411
|
requestStream: transport.requestStream
|