@metabase/cli 0.4.1-alpha.release-65-integration-branch.5cbe297 → 0.5.1
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-hv29px39.mjs → add-collection-BnGPc0Vv.mjs} +4 -4
- package/dist/{alert-DnTCjYIu.mjs → alert-DQkF8jYR.mjs} +7 -7
- package/dist/{alerts-_ELZ12ye.mjs → alerts-BncG7w1_.mjs} +6 -6
- package/dist/{append-B9sLLPYT.mjs → append-D4ekYCuD.mjs} +4 -4
- package/dist/{archive-BhIeDoLk.mjs → archive-BQ4OA-R4.mjs} +3 -3
- package/dist/{archive-CwBk5vxQ.mjs → archive-BXr9kFNz.mjs} +3 -3
- package/dist/{archive-BdX0Uvf2.mjs → archive-BifJXCHF.mjs} +3 -3
- package/dist/{archive-DD8C0Krt2.mjs → archive-CxNUeWII2.mjs} +4 -4
- package/dist/{archive-BRX5fKPb.mjs → archive-D2WCz3Oe.mjs} +3 -3
- package/dist/{archive-DDUSmbjn.mjs → archive-DjHLWUfC.mjs} +5 -5
- package/dist/{archive-CdfyB0To.mjs → archive-DnCF-Ecm.mjs} +5 -5
- package/dist/{archive-ZnHzMJaM.mjs → archive-DocyUIME.mjs} +5 -5
- package/dist/{archive-CKWWyI1i.mjs → archive-DpCP4yR1.mjs} +4 -4
- package/dist/{archive-D22VL5Bx.mjs → archive-EhW6blNc.mjs} +3 -3
- package/dist/{archive-3MQyc5Ih.mjs → archive-ULXc3xzI.mjs} +3 -3
- package/dist/{archive-BAHzxkPR.mjs → archive-v08JESCM.mjs} +5 -5
- package/dist/{auth-Ca84a5fv.mjs → auth-BPbsm8yV.mjs} +5 -5
- package/dist/{body-BVrHLwJU.mjs → body-BkkQkGJg.mjs} +2 -3
- package/dist/{branch-flag-Czh8JT1G.mjs → branch-flag-CTDvbaBa.mjs} +1 -1
- package/dist/{branches-frSt4WcS.mjs → branches-lPHtM_xR.mjs} +3 -3
- package/dist/{breaking-zlgxxwdd.mjs → breaking-DjMlCD96.mjs} +6 -6
- package/dist/{broken-BUAzpKLc.mjs → broken-DUX3GWS5.mjs} +8 -8
- package/dist/{bulk-discard-values-DU15VJPb.mjs → bulk-discard-values-Dl8Lzypf.mjs} +5 -5
- package/dist/{bulk-edit-TSa4lNkM.mjs → bulk-edit-N2xLVtfG.mjs} +5 -5
- package/dist/{bulk-rescan-values-BYJAsPC1.mjs → bulk-rescan-values-BK2rH2Ik.mjs} +5 -5
- package/dist/{bulk-sync-schema-DtaOwEtW.mjs → bulk-sync-schema-BKJc9Rzq.mjs} +5 -5
- package/dist/{cancel-DCOvSQsq.mjs → cancel-DzkRVGIk.mjs} +3 -3
- package/dist/{cancel-task-osQLETJh.mjs → cancel-task-BiTlDY7a.mjs} +3 -3
- package/dist/card-BTBDBsu-.mjs +25 -0
- package/dist/{cards-B1pyPl77.mjs → cards-CSL8ukS1.mjs} +4 -4
- package/dist/cli.mjs +619 -198
- package/dist/{client-1UIUBA_V.mjs → client-BBwYPXDW.mjs} +443 -380
- package/dist/collection-B19phRNU.mjs +21 -0
- package/dist/{collection-BgPYl2c5.mjs → collection-B2wRXyTF.mjs} +11 -3
- package/dist/{collection-DPiztU_l.mjs → collection-CTnYZ2Jj.mjs} +1 -1
- package/dist/{collection-namespace-Cizv6-94.mjs → collection-namespace-B1OkM7p1.mjs} +1 -2
- package/dist/{content-translation-XnHISl5-.mjs → content-translation-DoGTQnvH.mjs} +3 -3
- package/dist/{copy-DAKdksBm.mjs → copy-BYukQEtf.mjs} +4 -4
- package/dist/{copy-BCVW48Jq.mjs → copy-XlCT3gvG.mjs} +6 -6
- package/dist/{copy-flags-D7NTgAz1.mjs → copy-flags-ByB2MicL.mjs} +2 -2
- package/dist/{create-qCgnB-oZ.mjs → create-3_oY9KHY.mjs} +3 -3
- package/dist/{create-KyeJjhxH2.mjs → create-B3fm5I-l2.mjs} +4 -4
- package/dist/{create-CuhQ1Khk.mjs → create-BP39g5Jk.mjs} +5 -5
- package/dist/{create-cqbpWaOi.mjs → create-Bl9sS7v7.mjs} +4 -4
- package/dist/{create-BzE4q6F_.mjs → create-Bnb1abv_.mjs} +4 -4
- package/dist/{create-Br5JxAeE.mjs → create-CLZ7w83R.mjs} +4 -4
- package/dist/{create-B5eo9PSN.mjs → create-CYoarNrb.mjs} +4 -4
- package/dist/{create-B57D5E-o.mjs → create-CbT4SPHV.mjs} +5 -5
- package/dist/{create-BfA1pnmS.mjs → create-Cdt8u1wF.mjs} +6 -6
- package/dist/{create-C7fVbQbQ.mjs → create-CqF7vqXC.mjs} +4 -4
- package/dist/{create-XnYN2zoc.mjs → create-CrtIqtXN.mjs} +6 -6
- package/dist/{create-B1ifNWcE.mjs → create-D5_c5SHo.mjs} +5 -5
- package/dist/{create-CW4WF073.mjs → create-DBNxy5rN.mjs} +5 -5
- package/dist/{create-g4pZa_Xa.mjs → create-DZJQp3r7.mjs} +3 -3
- package/dist/{create-D2iZsTRV.mjs → create-DxMmIHI4.mjs} +5 -5
- package/dist/{create-BOvtRwRz.mjs → create-T4PZPVE9.mjs} +4 -4
- package/dist/{create-branch-Cli5cXvk.mjs → create-branch-CUNFdKyB.mjs} +3 -3
- package/dist/{create-D7zeAm-0.mjs → create-jr4xJgSI.mjs} +4 -4
- package/dist/{create--b64pMVM.mjs → create-k1Bo7h7g.mjs} +5 -5
- package/dist/{csv-CWIcAm_-.mjs → csv-CcnhKpuj.mjs} +4 -4
- package/dist/{current-task-eBjusv2o.mjs → current-task-nl2z8sHI.mjs} +4 -4
- package/dist/dashboard-BP0p62fp.mjs +35 -0
- package/dist/data-action-Cr17CfgE.mjs +24 -0
- package/dist/{database-BT7DVgSr.mjs → database-2HYCdHqK.mjs} +1 -1
- package/dist/{database-Kq2AL5jH.mjs → database-BewllGR7.mjs} +1 -1
- package/dist/{dataset-BmvCxGoE.mjs → dataset-D-x2Ho2i.mjs} +3 -14
- package/dist/{db-_H9HAS6H.mjs → db-m5bQcXdn.mjs} +7 -7
- package/dist/{delete-B0O-mf63.mjs → delete-BOma6qIq.mjs} +4 -4
- package/dist/{delete-BMvVkW-m.mjs → delete-BUCh72Bq.mjs} +4 -4
- package/dist/{delete-CynbfvS2.mjs → delete-BXMc-g3Q.mjs} +4 -4
- package/dist/{delete-BW5QXiyJ.mjs → delete-BrbmJ3sn.mjs} +4 -4
- package/dist/{delete-C7QmQlp9.mjs → delete-DulVnxD_.mjs} +4 -4
- package/dist/{delete-DPU-uQxU.mjs → delete-Oc2GE170.mjs} +4 -4
- package/dist/{delete-DOn2uk0u.mjs → delete-QHsiWYay.mjs} +4 -4
- package/dist/{delete-runtime-Cc7TASG7.mjs → delete-runtime-CkNvZfFA.mjs} +1 -1
- package/dist/{delete-table-DFqm6bAq.mjs → delete-table-LgJ17t6j.mjs} +4 -4
- package/dist/{delete-BztmAfPc.mjs → delete-xf2n7SNg.mjs} +4 -4
- package/dist/{dependencies-DZ-kMsDa.mjs → dependencies-BE5QW7Lb.mjs} +5 -5
- package/dist/{dependency-BHKwKjhO.mjs → dependency-BACk_cVP.mjs} +2 -2
- package/dist/dependency-BlTYysYG.mjs +15 -0
- package/dist/{dependency-B4J1bwFh.mjs → dependency-qP1hx2YN.mjs} +1 -1
- package/dist/{dependents-Ia2fe_KK.mjs → dependents-mbgHQNiv.mjs} +9 -9
- package/dist/{dirty-BrXAPows.mjs → dirty-fYlA1N1e.mjs} +4 -4
- package/dist/{discard-values-B9mTgFbV.mjs → discard-values-cIRBP5nT.mjs} +5 -5
- package/dist/document-BaRQ5fBd.mjs +20 -0
- package/dist/{document-BsaKF8UR.mjs → document-DVIu3Vj4.mjs} +1 -1
- package/dist/{document-COQzVLjW.mjs → document-UMG6me2P.mjs} +1 -1
- package/dist/{download-CZdsDdhG.mjs → download-CokVpeK6.mjs} +2 -2
- package/dist/{eid-DsGNJ16z.mjs → eid-CaumGUFD.mjs} +5 -5
- package/dist/{eid-translation-zEHtr5Oc.mjs → eid-translation-Ba92VOHe.mjs} +1 -0
- package/dist/{entity-BFajNdQp.mjs → entity-BHYJtRxp.mjs} +2 -2
- package/dist/{entry-flags-BaZUoxUR.mjs → entry-flags-DjICpQkc.mjs} +4 -4
- package/dist/{events-rkMIMpnS.mjs → events-jEo9fytL.mjs} +6 -6
- package/dist/{execute-DvBse4aF.mjs → execute-H09Qus4_.mjs} +4 -4
- package/dist/{export-BCkzx9zk.mjs → export-YtdZo7LZ.mjs} +7 -7
- package/dist/{export-flags-TuwwRhD6.mjs → export-flags-DzW0Ktkh.mjs} +2 -2
- package/dist/{export-preflight-p7botJyN.mjs → export-preflight-Db_TChn5.mjs} +5 -5
- package/dist/field-DktidTPB.mjs +21 -0
- package/dist/{fields-CJVXOLbB.mjs → fields-DUXlosmx.mjs} +4 -4
- package/dist/{filter-flags-yY4RM312.mjs → filter-flags-DTDcU7OI.mjs} +3 -3
- package/dist/{fks-B6hjrdJP.mjs → fks-Cl2sq-c0.mjs} +6 -6
- package/dist/{get-D1p-5ibI.mjs → get-B-WT6ess.mjs} +5 -5
- package/dist/{get-DpYTiE4D.mjs → get-BilMWiFq.mjs} +5 -5
- package/dist/{get-CWBN0G-4.mjs → get-Bt_zhviQ.mjs} +3 -3
- package/dist/{get-DsbDxSpy.mjs → get-CM596pmP.mjs} +3 -3
- package/dist/{get-BxUgfEPW.mjs → get-CTtzccwr.mjs} +4 -4
- package/dist/{get-oLc5jgid2.mjs → get-CVMWhbA82.mjs} +4 -4
- package/dist/{get-Eforof5e.mjs → get-CWyRAHpz.mjs} +5 -5
- package/dist/{get-BvUNnNRq.mjs → get-C_rjxpoe.mjs} +3 -3
- package/dist/{get-B5L4BUGK.mjs → get-Cg0up9gm.mjs} +3 -3
- package/dist/{get-xmhwvAQJ.mjs → get-CqlNUyFY.mjs} +5 -5
- package/dist/{get-CXeFg0F2.mjs → get-DPiNYmXo.mjs} +5 -5
- package/dist/{get-DyS1mRI6.mjs → get-DTDvlLT3.mjs} +3 -3
- package/dist/{get-C6uLL4bJ.mjs → get-DtcChy9h.mjs} +5 -5
- package/dist/{get-BYtFVJHT.mjs → get-DyK_bvg6.mjs} +4 -4
- package/dist/{get-Do1LC4I52.mjs → get-DySyvotk2.mjs} +5 -5
- package/dist/{get-D7HPiBB0.mjs → get-FxCF0rxc.mjs} +4 -4
- package/dist/{get-CoJcgFky.mjs → get-GuWW4J24.mjs} +5 -5
- package/dist/{get-DPlSzRI0.mjs → get-XS-6R81E.mjs} +3 -3
- package/dist/{get-zotSW00n.mjs → get-oQNL6pLY.mjs} +3 -3
- package/dist/{get-BwKQw_rR.mjs → get-okEsnkvC.mjs} +3 -3
- package/dist/{get-run-bU-d--nM.mjs → get-run-CFGxSm_S.mjs} +4 -4
- package/dist/{get-DSjITPED.mjs → get-zPMv3kZE.mjs} +5 -5
- package/dist/git-sync-BwkWeJAz.mjs +29 -0
- package/dist/{git-sync-DspFahwV.mjs → git-sync-Ch1V1fEK.mjs} +1 -1
- package/dist/glossary-BSgRQ2HO.mjs +14 -0
- package/dist/{glossary-Del97Jqg.mjs → glossary-BkGmCI4X.mjs} +1 -1
- package/dist/{glossary-CdW6RFId.mjs → glossary-QN-hHfOT.mjs} +1 -1
- package/dist/{graph-CHXJ6mB7.mjs → graph-Wf4e0XIa.mjs} +6 -6
- package/dist/{group-D4w87fXL.mjs → group-81zoQcEP.mjs} +1 -1
- package/dist/{has-remote-changes-BXEe8Sv5.mjs → has-remote-changes-BsS3XB0P.mjs} +3 -3
- package/dist/{import-DZQb4bUo.mjs → import-CXd5HdB0.mjs} +7 -7
- package/dist/{input-DjHsV-kD.mjs → input-AXP2LCOV.mjs} +1 -1
- package/dist/{is-dirty-DwQ2_CCB.mjs → is-dirty-TDcfeOde.mjs} +2 -2
- package/dist/{items-DNdICQ7M.mjs → items-BxvhoxYm.mjs} +6 -6
- package/dist/{key-BTaxFn_7.mjs → key-CUPbYxkk.mjs} +1 -1
- package/dist/{library-C4M8TF2N.mjs → library-BKSFtIV9.mjs} +2 -2
- package/dist/{library-DmkpcZPb.mjs → library-DuIrR8LS.mjs} +1 -1
- package/dist/{library-Cdj_uj1H.mjs → library-DzejNMqX.mjs} +5 -5
- package/dist/{list-DoLqxEjq.mjs → list-3Dh5yeq-.mjs} +3 -3
- package/dist/{list-__Q79YXk.mjs → list-6nGX0Rqc.mjs} +4 -4
- package/dist/{list-hINHmdMT.mjs → list-B3Pngm2j.mjs} +5 -5
- package/dist/{list-89ZgbsvW.mjs → list-B5kRBn7k.mjs} +5 -5
- package/dist/{list-DsyvHBAv.mjs → list-B8V_OESj.mjs} +3 -3
- package/dist/{list-DcHPY1Ti.mjs → list-BUjI5G0_.mjs} +4 -4
- package/dist/{list-gqemddWB.mjs → list-Ba7u5Ai9.mjs} +4 -4
- package/dist/{list-CzxrdWB8.mjs → list-CCCy6cBI.mjs} +6 -6
- package/dist/{list-Owrzgtkc.mjs → list-CRXTfEFk.mjs} +3 -3
- package/dist/{list-C7PV9fYj.mjs → list-CUj04tg3.mjs} +6 -6
- package/dist/{list-OKHcr76i.mjs → list-Cf6A9Csi.mjs} +3 -3
- package/dist/{list-F4ENGV7j.mjs → list-Cr5Hoq-E.mjs} +4 -4
- package/dist/{list-C-WGqRqN.mjs → list-D1Q8H3jn.mjs} +6 -6
- package/dist/{list-epIn3uFZ.mjs → list-D7yH-64Z.mjs} +5 -5
- package/dist/{list-M20foZz2.mjs → list-DA1Fo6S6.mjs} +3 -3
- package/dist/{list-CYlPgzD1.mjs → list-D_fsrh8w.mjs} +5 -5
- package/dist/{list-03zaydQe.mjs → list-DjlfBd7W.mjs} +3 -3
- package/dist/{list-CZoaZc9g.mjs → list-DlgjWr4r.mjs} +5 -5
- package/dist/{list-2EWHwbQs.mjs → list-Do-Xnz0I.mjs} +4 -4
- package/dist/{list-Be6kDkyT.mjs → list-DpK3ioES.mjs} +6 -6
- package/dist/{list-CLJvuPXn.mjs → list-DsR58aMq.mjs} +5 -5
- package/dist/{login-BSqGgX8S.mjs → login-CXbCAcuB.mjs} +6 -6
- package/dist/{logout-D92O4mQK.mjs → logout-B6IGzkpG.mjs} +2 -2
- package/dist/{measure-DhX_mmZK.mjs → measure-CbMgNhlG.mjs} +6 -6
- package/dist/{notification-Cuuwoksm.mjs → notification-W2SVssyY.mjs} +1 -1
- package/dist/{parameter-values-BY2WA9qg.mjs → parameter-values-IWi_DNhp.mjs} +3 -3
- package/dist/{parse-enum-BPKULOSw.mjs → parse-enum-B2rGiC6p.mjs} +1 -1
- package/dist/{parse-id-CxHp2akh.mjs → parse-id-Cv5eM7sg.mjs} +1 -1
- package/dist/{parse-ref-8bVX_9IN.mjs → parse-ref-DW7ik6ny.mjs} +1 -1
- package/dist/{parse-text-Bm6eUa-p.mjs → parse-text-CUCSWOeR.mjs} +1 -1
- package/dist/{path-D4Q1ZzCw.mjs → path-C9r9urRK.mjs} +4 -4
- package/dist/{poll-DFZRtTA_.mjs → poll-CX3jPtXb.mjs} +2 -2
- package/dist/{preflight-BPaonBKy.mjs → preflight-CiTgqNUy.mjs} +2 -3
- package/dist/{process-B3Di_HtV.mjs → process-DImdroix.mjs} +1 -1
- package/dist/{publish-BhgyrJ6c.mjs → publish-xH1n74gd.mjs} +4 -4
- package/dist/{pulse-CIza18rO.mjs → pulse-CFj1WfIC.mjs} +1 -1
- package/dist/{query-CsBe-kJn.mjs → query-BOCw0FBK.mjs} +4 -4
- package/dist/{query-DvlGWjgG.mjs → query-CN3BW5WB.mjs} +11 -12
- package/dist/{remapping-CCEivr42.mjs → remapping-Dggq2Fv0.mjs} +4 -4
- package/dist/{remove-collection-LpgGBYCl.mjs → remove-collection-cxSSFxza.mjs} +4 -4
- package/dist/{render-D5ehBS-7.mjs → render-Cb8Hct67.mjs} +2 -2
- package/dist/{replace-DeWgkbfZ.mjs → replace-wVi_XDut.mjs} +4 -4
- package/dist/requirement-check-BE3PuGsC.mjs +60 -0
- package/dist/{rescan-values-CDDVS1tl.mjs → rescan-values-C7b1Ghy-.mjs} +5 -5
- package/dist/{rescan-values-aF18XAv6.mjs → rescan-values-DAp_avxY.mjs} +5 -5
- package/dist/{run-DyGMvbMm.mjs → run-CE6ei7s7.mjs} +4 -4
- package/dist/{run-Buf4B8QH.mjs → run-CUGs7mok.mjs} +4 -4
- package/dist/{run-DlG3Ykj1.mjs → run-DywhwjNG.mjs} +6 -6
- package/dist/{runs-D2CXgW10.mjs → runs--14jim_0.mjs} +5 -5
- package/dist/{runtime-X6hVzSMW.mjs → runtime-Ckns6TKK.mjs} +248 -534
- package/dist/{schema-tables-FUOIyoUU.mjs → schema-tables-CeW0kpA5.mjs} +6 -6
- package/dist/{schemas-CmfNeqzG.mjs → schemas-CHMD0HrY.mjs} +4 -4
- package/dist/{search-BAXg-EJw.mjs → search-CXRavT7G.mjs} +1 -0
- package/dist/{search-Dl7xzF6Q.mjs → search-DyC54bGf.mjs} +7 -7
- package/dist/{search-CLpDqYpY.mjs → search-LmBQu1Jg.mjs} +5 -5
- package/dist/{segment-Bx28f38v.mjs → segment-DeT_Yic6.mjs} +6 -6
- package/dist/{send-CoGV2zkp.mjs → send-BWSJMrDL.mjs} +3 -3
- package/dist/{set-active-CbiJv8Hg.mjs → set-active-C5cLG4_O.mjs} +4 -4
- package/dist/{set-CrR9IumN.mjs → set-r2xiXrT_.mjs} +4 -4
- package/dist/{set-sensitivity-CTHVxpnU.mjs → set-sensitivity-BMLKazua.mjs} +4 -4
- package/dist/{setting-CUxdweNJ.mjs → setting-CDRusiqP.mjs} +4 -4
- package/dist/{setup-4uySIB-t.mjs → setup-BZaJVww3.mjs} +3 -3
- package/dist/{signal-0Yt8JTMn.mjs → signal-4tpCIkqR.mjs} +1 -1
- package/dist/{skill-list-goCqmfFF.mjs → skill-list-D2FhhYS3.mjs} +9 -3
- package/dist/{skills-86j411Qz.mjs → skills-9ihLaSmM.mjs} +3 -3
- package/dist/{skills-DeWsLMwC.mjs → skills-y2iVfW9k.mjs} +2 -2
- package/dist/snippet-Ce0o33Rj.mjs +19 -0
- package/dist/{stash-BNKlbxtD.mjs → stash-Dj-mnIa4.mjs} +5 -5
- package/dist/{status-B7bwHLkk.mjs → status-B2Xj_q8f.mjs} +3 -3
- package/dist/{status-Bul9El-c.mjs → status-BtWz7Pzn.mjs} +16 -7
- package/dist/{subscription-DPAGevGU.mjs → subscription-Dh--agqE.mjs} +6 -6
- package/dist/{subscriptions-DIRsHcKP.mjs → subscriptions-CJ8Jdp9A.mjs} +6 -6
- package/dist/{summary-CeVt80Kb.mjs → summary-ccbkNDbC.mjs} +3 -3
- package/dist/{sync-schema-B9fLcZoa.mjs → sync-schema-CewUOkmm.mjs} +6 -6
- package/dist/{sync-schema-oDTbhjW1.mjs → sync-schema-HwQectZd.mjs} +5 -5
- package/dist/{sync-task-BAMOf6_t.mjs → sync-task-DEzhiaU9.mjs} +3 -3
- package/dist/{table-BFIOH6Pb.mjs → table-CFZU-BRg.mjs} +2 -2
- package/dist/table-P9ovwcaB.mjs +26 -0
- package/dist/{table-selector-flags-BUR0-n6j.mjs → table-selector-flags-p4jun7-v.mjs} +3 -3
- package/dist/{table-Ci6bhZkb.mjs → table-zAdW5_3H.mjs} +5 -5
- package/dist/{text-CdtEI6l6.mjs → text-C2VwKF9D.mjs} +1 -1
- package/dist/{timeline-uDCmwGFT.mjs → timeline-BgWz8DYK.mjs} +1 -1
- package/dist/timeline-DLaBypse.mjs +21 -0
- package/dist/{timeline-CCSU2qOS.mjs → timeline-DcsCXNUt.mjs} +5 -1
- package/dist/{timeline-event-BY2Hj72e.mjs → timeline-event-xAAR6b-v.mjs} +6 -6
- package/dist/transform-10YE8j5K.mjs +28 -0
- package/dist/{transform-D4z70PIN.mjs → transform-CWGJB8N_.mjs} +1 -1
- package/dist/{transform-job-AaMZRxAq.mjs → transform-job-BBZ5L46v.mjs} +1 -1
- package/dist/{transform-job-CCguL6LP.mjs → transform-job-ClX8e5iO.mjs} +2 -1
- package/dist/transform-job-D97AutnF.mjs +22 -0
- package/dist/{transform-tag-CzjLKILh.mjs → transform-tag-BXusFzMX.mjs} +5 -5
- package/dist/{transform-test-BjjCQ2xm.mjs → transform-test-Bexa_K89.mjs} +5 -1
- package/dist/{transform-test-DHGKeuHa.mjs → transform-test-C49ZTK7M.mjs} +1 -1
- package/dist/transform-test-UsH1udc4.mjs +16 -0
- package/dist/{transforms-0uhBh0H4.mjs → transforms-CqV01bMM.mjs} +5 -5
- package/dist/{transport-BmNrb0Lz.mjs → transport-0qFZhEuP.mjs} +144 -41
- package/dist/{tree-oBEw8hDj.mjs → tree-Td6rgyOa.mjs} +3 -3
- package/dist/{unpublish-BJ9OKN1N.mjs → unpublish-D4XTZsFW.mjs} +3 -3
- package/dist/{unreferenced-D6UyA_Nu.mjs → unreferenced-cR-l4zXN.mjs} +6 -6
- package/dist/{update-7zsDGAWa.mjs → update-BJL5975i.mjs} +6 -6
- package/dist/{update-DGwGjOnH.mjs → update-BqkqMOVN.mjs} +5 -5
- package/dist/{update-CKe2Bwpv.mjs → update-C4iu-vQm.mjs} +6 -6
- package/dist/{update-D9eLVWFQ.mjs → update-C7Wmom6x.mjs} +5 -5
- package/dist/{update-BK_SpSrG.mjs → update-C7dZLbsf.mjs} +4 -4
- package/dist/{update-BZgHTH5P.mjs → update-C8xdeYI2.mjs} +4 -4
- package/dist/{update-BqrZ-8IK.mjs → update-CHB4kvz-.mjs} +8 -8
- package/dist/{update-M7yDPmJ-.mjs → update-CND9At61.mjs} +5 -5
- package/dist/{update-bGDXSnBh.mjs → update-CpwWURTp.mjs} +5 -5
- package/dist/{update-CKJ6Das4.mjs → update-D-cTgmb0.mjs} +6 -6
- package/dist/{update-DKagaJw0.mjs → update-DFK2iUSP.mjs} +6 -6
- package/dist/{update-Cv6IMLyu.mjs → update-DROjGe3Q.mjs} +4 -4
- package/dist/{update-Bdi40RG0.mjs → update-DZHgr6Gu.mjs} +5 -5
- package/dist/{update-CHA4VWKT.mjs → update-Dk0E66Va.mjs} +5 -5
- package/dist/{update-DgmlUn64.mjs → update-DqCZsbLh.mjs} +6 -6
- package/dist/{update-CEhub3Wz.mjs → update-DrVc25R8.mjs} +5 -5
- package/dist/{update-CYQkWLpZ.mjs → update-Js0IiqCq.mjs} +6 -6
- package/dist/{update-DneJLQXe.mjs → update-dQeji6RA.mjs} +7 -7
- package/dist/{update-dashcard-DNsf-gJ1.mjs → update-dashcard-BmkfJ7t9.mjs} +4 -4
- package/dist/{update-CXfLvhmK.mjs → update-nGURR-y6.mjs} +6 -6
- package/dist/{upgrade-BRJH93W0.mjs → upgrade-B6V_1cUh.mjs} +4 -4
- package/dist/{upload-DJB7DMA1.mjs → upload-B6zBzMPW.mjs} +2 -2
- package/dist/{upload-CGz8u2wG.mjs → upload-CfYmBv51.mjs} +3 -3
- package/dist/upload-Dvjfz16M.mjs +13 -0
- package/dist/{uuid-3qmLYgrG.mjs → uuid-B8ynbkqG.mjs} +2 -2
- package/dist/{validate-D_iikPkc.mjs → validate-C7DGkwb-.mjs} +1 -1
- package/dist/{validate-query-DIcK8alR.mjs → validate-query-CYUvRvVM.mjs} +2 -2
- package/dist/{values-Bn2Wcz4t.mjs → values-36bzf9cK.mjs} +3 -3
- package/dist/{verify-CA4PEHlO.mjs → verify-BdW-GExk.mjs} +3 -3
- package/dist/{verify-C2K-zMuQ.mjs → verify-C4Tml4fD.mjs} +3 -3
- package/dist/{verify-CCX2N98Q.mjs → verify-Cs9e28YA.mjs} +4 -4
- package/dist/{wait-DCgCJBqX.mjs → wait-BV6JOdgi.mjs} +6 -6
- package/dist/{wait-flags-aRQHTjwg.mjs → wait-flags-DjyK03cm.mjs} +2 -2
- package/dist/{window-2ybHqui2.mjs → window-CfK_j6m1.mjs} +1 -1
- package/package.json +1 -1
- package/skill-data/core/SKILL.md +11 -5
- package/skill-data/dashboard/SKILL.md +6 -0
- package/skill-data/git-sync/SKILL.md +64 -14
- package/dist/card-P2-4BkXV.mjs +0 -25
- package/dist/collection-BI8Fy9vS.mjs +0 -21
- package/dist/dashboard-C-YJxDam.mjs +0 -35
- package/dist/data-action-DER3EMD7.mjs +0 -24
- package/dist/dependency-v9fHJ-N-.mjs +0 -15
- package/dist/document-CGvUmD9i.mjs +0 -20
- package/dist/field-C9AcBe8x.mjs +0 -21
- package/dist/git-sync-D4R0cHvk.mjs +0 -29
- package/dist/glossary-C6EnoEXg.mjs +0 -14
- package/dist/snippet-C3gamfFK.mjs +0 -19
- package/dist/table-C--k-JXA.mjs +0 -26
- package/dist/timeline-ClMmTycg.mjs +0 -21
- package/dist/transform-BZaNFGeK.mjs +0 -28
- package/dist/transform-job-QMI2qWin.mjs +0 -22
- package/dist/transform-test-B9le1jKH.mjs +0 -16
- package/dist/upload-T5wS1XtL.mjs +0 -13
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as formatZodIssue, Et as TimeoutError, Mt as isNonInterruptFailure, P as CapabilityError, Q as parseJsonOrPlain, Tt as ResponseShapeError, W as HttpError, Y as isPlainObject, kt as errorMessage, n as isMethodKey, r as methodRequirements, wt as PartialWriteError, xt as InternalError, yt as ConfigError } from "./cli.mjs";
|
|
2
2
|
import { n as Notification, r as NotificationCardPayload, t as CARD_PAYLOAD_TYPE } from "./notification-DaA4bBHW.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
3
|
+
import { L as profileFromProperties, Z as SessionProperties } from "./runtime-Ckns6TKK.mjs";
|
|
4
|
+
import { n as featureFailures, r as supersedingFeature, t as checkFeatures } from "./requirement-check-BE3PuGsC.mjs";
|
|
5
|
+
import { n as createTransport } from "./transport-0qFZhEuP.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 { r as PivotExportFormat, t as DatasetQuery } from "./query-BKtMP5eH.mjs";
|
|
8
9
|
import { a as CardQueryResult, t as Card } from "./card-P5mBknsl.mjs";
|
|
9
|
-
import { b as TableVisibilityType, p as TableQueryMetadata, s as TableDataLayerTier, t as Table, u as TableForeignKey, v as TableSelectors } from "./table-
|
|
10
|
-
import { t as Database } from "./database-
|
|
10
|
+
import { b as TableVisibilityType, p as TableQueryMetadata, s as TableDataLayerTier, t as Table, u as TableForeignKey, v as TableSelectors } from "./table-zAdW5_3H.mjs";
|
|
11
|
+
import { t as Database } from "./database-2HYCdHqK.mjs";
|
|
11
12
|
import { t as Snippet } from "./snippet-CyKAgIZl.mjs";
|
|
12
|
-
import {
|
|
13
|
-
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-
|
|
13
|
+
import { r as QueryMetadata, t as CompiledQuery } from "./dataset-D-x2Ho2i.mjs";
|
|
14
|
+
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-B2wRXyTF.mjs";
|
|
14
15
|
import { t as ContentTranslationUploadResult } from "./content-translation-BrHOrmj9.mjs";
|
|
15
16
|
import { l as Dashcard, o as DashboardDetail, r as DashboardCopy, t as Dashboard } from "./dashboard-Phz3XpRM.mjs";
|
|
16
17
|
import { a as DataActionExecuteResult, s as dataActionSchema } from "./data-action-DZxVB_hD.mjs";
|
|
17
|
-
import { r as pollUntil } from "./poll-
|
|
18
|
-
import { i as DependencyEntity, s as DependencyGraph, t as BreakingSource, u as DependencyNode } from "./dependency-
|
|
19
|
-
import { t as Document } from "./document-
|
|
20
|
-
import { i as EidTranslateResult } from "./eid-translation-
|
|
18
|
+
import { r as pollUntil } from "./poll-CX3jPtXb.mjs";
|
|
19
|
+
import { i as DependencyEntity, s as DependencyGraph, t as BreakingSource, u as DependencyNode } from "./dependency-qP1hx2YN.mjs";
|
|
20
|
+
import { t as Document } from "./document-UMG6me2P.mjs";
|
|
21
|
+
import { i as EidTranslateResult } from "./eid-translation-Ba92VOHe.mjs";
|
|
21
22
|
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";
|
|
22
|
-
import { t as Glossary } from "./glossary-
|
|
23
|
-
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-
|
|
23
|
+
import { t as Glossary } from "./glossary-QN-hHfOT.mjs";
|
|
24
|
+
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-BKSFtIV9.mjs";
|
|
24
25
|
import { t as Measure } from "./measure-CtfjqJFP.mjs";
|
|
25
26
|
import { r as ModerationReviewCreateInput, t as ModerationReview } from "./moderation-review-BdqEH6N1.mjs";
|
|
26
27
|
import { t as Pulse } from "./pulse-Dz0ocv0l.mjs";
|
|
27
28
|
import { a as TransformDagTransform, d as TransformTarget, g as transformRowSchema, h as transformDetailSchema, i as TransformDagRunResult, l as TransformRunSummary, m as isTransformRunTerminal, o as TransformRun, p as isTransformRunFailed, u as TransformSourceTableEntry } from "./transform-DiW7bREn.mjs";
|
|
28
|
-
import { r as SearchResult } from "./search-
|
|
29
|
+
import { r as SearchResult } from "./search-CXRavT7G.mjs";
|
|
29
30
|
import { t as Segment } from "./segment-OsrbUDzw.mjs";
|
|
30
31
|
import { t as Setting } from "./setting-DTiLCW8g.mjs";
|
|
31
32
|
import { n as SetupResult } from "./setup-CO7QV-ao.mjs";
|
|
32
|
-
import { i as TimelineEvent, t as Timeline } from "./timeline-
|
|
33
|
-
import { c as transformJobSchema, n as TransformJobActiveResult, s as transformJobRunResultSchema } from "./transform-job-
|
|
33
|
+
import { i as TimelineEvent, t as Timeline } from "./timeline-DcsCXNUt.mjs";
|
|
34
|
+
import { c as transformJobSchema, n as TransformJobActiveResult, s as transformJobRunResultSchema } from "./transform-job-ClX8e5iO.mjs";
|
|
34
35
|
import { t as TransformTag } from "./transform-tag-CcElGp3C.mjs";
|
|
35
|
-
import { i as TransformTestRunResult, t as TransformTest } from "./transform-test-
|
|
36
|
+
import { i as TransformTestRunResult, t as TransformTest } from "./transform-test-Bexa_K89.mjs";
|
|
36
37
|
import { t as UploadResult } from "./upload-DAtxBBms.mjs";
|
|
37
38
|
import { z } from "zod";
|
|
38
39
|
//#region \0rolldown/runtime.js
|
|
@@ -48,14 +49,14 @@ var __exportAll = (all, no_symbols) => {
|
|
|
48
49
|
};
|
|
49
50
|
//#endregion
|
|
50
51
|
//#region ../client/src/resources/pivot-export.ts
|
|
51
|
-
const
|
|
52
|
+
const PivotedExportsProperty = SessionProperties.extend({ "enable-pivoted-exports": z.boolean() });
|
|
52
53
|
/**
|
|
53
54
|
* Refuse a pivoted export the server would answer with plain rows: a JSON export keeps only the
|
|
54
55
|
* ungrouped rows, and an admin can turn pivoted CSV and XLSX exports off.
|
|
55
56
|
*/
|
|
56
57
|
async function assertPivotedExport(transport, format, options) {
|
|
57
58
|
if (!PivotExportFormat.safeParse(format).success) throw new ConfigError(`a ${format} export cannot be pivoted; only ${PivotExportFormat.options.join(" and ")} can`);
|
|
58
|
-
if (!(await transport.
|
|
59
|
+
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");
|
|
59
60
|
}
|
|
60
61
|
//#endregion
|
|
61
62
|
//#region ../client/src/resources/card.ts
|
|
@@ -63,7 +64,6 @@ const CardApiList = z.array(Card);
|
|
|
63
64
|
function cardResource(transport) {
|
|
64
65
|
/** List cards. `f` picks a server-side preset; `model_id` scopes the presets that need an id. */
|
|
65
66
|
async function list(params = {}, options = {}) {
|
|
66
|
-
await transport.require("card.list", options);
|
|
67
67
|
return {
|
|
68
68
|
data: await transport.requestParsed(CardApiList, "/api/card", {
|
|
69
69
|
...options,
|
|
@@ -77,12 +77,10 @@ function cardResource(transport) {
|
|
|
77
77
|
}
|
|
78
78
|
/** Get one card by id. */
|
|
79
79
|
async function get(id, options = {}) {
|
|
80
|
-
await transport.require("card.get", options);
|
|
81
80
|
return transport.requestParsed(Card, `/api/card/${id}`, { ...options });
|
|
82
81
|
}
|
|
83
82
|
/** Create a card — a question, a model, or a metric — from a full card body. */
|
|
84
83
|
async function create(params, options = {}) {
|
|
85
|
-
await transport.require("card.create", options);
|
|
86
84
|
return transport.requestParsed(Card, "/api/card", {
|
|
87
85
|
...options,
|
|
88
86
|
method: "POST",
|
|
@@ -91,7 +89,6 @@ function cardResource(transport) {
|
|
|
91
89
|
}
|
|
92
90
|
/** Update a card by id, patching only the fields the body carries. */
|
|
93
91
|
async function update(id, params, options = {}) {
|
|
94
|
-
await transport.require("card.update", options);
|
|
95
92
|
return transport.requestParsed(Card, `/api/card/${id}`, {
|
|
96
93
|
...options,
|
|
97
94
|
method: "PUT",
|
|
@@ -100,12 +97,10 @@ function cardResource(transport) {
|
|
|
100
97
|
}
|
|
101
98
|
/** Archive (soft-delete) a card by id. Metabase models this as an update, not its own endpoint. */
|
|
102
99
|
async function archive(id, options = {}) {
|
|
103
|
-
await transport.require("card.archive", options);
|
|
104
100
|
return update(id, { archived: true }, options);
|
|
105
101
|
}
|
|
106
102
|
/** Run a saved card and return the query result envelope. */
|
|
107
103
|
async function query(id, params, options = {}) {
|
|
108
|
-
await transport.require("card.query", options);
|
|
109
104
|
return transport.requestParsed(CardQueryResult, `/api/card/${id}/query`, {
|
|
110
105
|
...options,
|
|
111
106
|
method: "POST",
|
|
@@ -121,9 +116,8 @@ function cardResource(transport) {
|
|
|
121
116
|
* there before the wire.
|
|
122
117
|
*/
|
|
123
118
|
async function exportQuery(id, format, params, options = {}) {
|
|
124
|
-
await transport.require("card.exportQuery", options);
|
|
125
|
-
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
126
119
|
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
120
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
127
121
|
const body = new URLSearchParams({
|
|
128
122
|
parameters: JSON.stringify(params.parameters),
|
|
129
123
|
format_rows: String(params.format_rows),
|
|
@@ -141,7 +135,6 @@ function cardResource(transport) {
|
|
|
141
135
|
* snippets its query references, plus the card's own metadata for a model or a native query.
|
|
142
136
|
*/
|
|
143
137
|
async function queryMetadata(id, options = {}) {
|
|
144
|
-
await transport.require("card.queryMetadata", options);
|
|
145
138
|
return transport.requestParsed(QueryMetadata, `/api/card/${id}/query_metadata`, { ...options });
|
|
146
139
|
}
|
|
147
140
|
return {
|
|
@@ -227,7 +220,6 @@ function collectionResource(transport) {
|
|
|
227
220
|
* preset includes the Library collections that match it.
|
|
228
221
|
*/
|
|
229
222
|
async function list(params = {}, options = {}) {
|
|
230
|
-
await transport.require("collection.list", options);
|
|
231
223
|
return {
|
|
232
224
|
data: await transport.requestParsed(CollectionApiList, "/api/collection", {
|
|
233
225
|
...options,
|
|
@@ -238,12 +230,10 @@ function collectionResource(transport) {
|
|
|
238
230
|
}
|
|
239
231
|
/** Get one collection by id, entity id, or the `root`/`trash` alias. */
|
|
240
232
|
async function get(ref, options = {}) {
|
|
241
|
-
await transport.require("collection.get", options);
|
|
242
233
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, { ...options });
|
|
243
234
|
}
|
|
244
235
|
/** Create a new collection. */
|
|
245
236
|
async function create(params, options = {}) {
|
|
246
|
-
await transport.require("collection.create", options);
|
|
247
237
|
return transport.requestParsed(Collection, "/api/collection", {
|
|
248
238
|
...options,
|
|
249
239
|
method: "POST",
|
|
@@ -256,7 +246,6 @@ function collectionResource(transport) {
|
|
|
256
246
|
* moves a collection into the trash ignores `parent_id`.
|
|
257
247
|
*/
|
|
258
248
|
async function update(ref, params, options = {}) {
|
|
259
|
-
await transport.require("collection.update", options);
|
|
260
249
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, {
|
|
261
250
|
...options,
|
|
262
251
|
method: "PUT",
|
|
@@ -265,7 +254,6 @@ function collectionResource(transport) {
|
|
|
265
254
|
}
|
|
266
255
|
/** Archive (soft-delete) a collection. Metabase models this as an update, not its own endpoint. */
|
|
267
256
|
async function archive(ref, options = {}) {
|
|
268
|
-
await transport.require("collection.archive", options);
|
|
269
257
|
return update(ref, { archived: true }, options);
|
|
270
258
|
}
|
|
271
259
|
/**
|
|
@@ -273,7 +261,6 @@ function collectionResource(transport) {
|
|
|
273
261
|
* the caller consumes pages rather than a single list and decides how far to pull.
|
|
274
262
|
*/
|
|
275
263
|
async function* itemPages(ref, params = {}, options = {}) {
|
|
276
|
-
await transport.require("collection.itemPages", options);
|
|
277
264
|
yield* paginatePages(transport, `/api/collection/${refPath(ref)}/items`, CollectionItem, {
|
|
278
265
|
query: {
|
|
279
266
|
models: params.models,
|
|
@@ -288,7 +275,6 @@ function collectionResource(transport) {
|
|
|
288
275
|
}
|
|
289
276
|
/** Fetch the collection hierarchy as a forest of nested nodes. */
|
|
290
277
|
async function tree(params = {}, options = {}) {
|
|
291
|
-
await transport.require("collection.tree", options);
|
|
292
278
|
return {
|
|
293
279
|
data: await transport.requestParsed(CollectionTreeApiList, "/api/collection/tree", {
|
|
294
280
|
...options,
|
|
@@ -308,6 +294,128 @@ function collectionResource(transport) {
|
|
|
308
294
|
};
|
|
309
295
|
}
|
|
310
296
|
//#endregion
|
|
297
|
+
//#region ../client/src/version/refusal.ts
|
|
298
|
+
const REFUSAL_STATUSES = /* @__PURE__ */ new Set([
|
|
299
|
+
400,
|
|
300
|
+
402,
|
|
301
|
+
404
|
|
302
|
+
]);
|
|
303
|
+
function explainer(transport, namespace) {
|
|
304
|
+
function explain(name, method, parameters) {
|
|
305
|
+
const key = methodKey(namespace, name);
|
|
306
|
+
return async (...args) => {
|
|
307
|
+
try {
|
|
308
|
+
return await method(...args);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
throw await explainRefusal(transport, callRequirement(key, parameters, args), error);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function explainWalk(name, method, parameters) {
|
|
315
|
+
const key = methodKey(namespace, name);
|
|
316
|
+
return async function* (...args) {
|
|
317
|
+
try {
|
|
318
|
+
yield* method(...args);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
throw await explainRefusal(transport, callRequirement(key, parameters, args), error);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function refuse(name, method) {
|
|
325
|
+
const key = methodKey(namespace, name);
|
|
326
|
+
return async (...args) => {
|
|
327
|
+
await transport.requireFeatures(methodRequirements(key), trailingWait(args));
|
|
328
|
+
return method(...args);
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function refuseAfterReading(name, reader, method) {
|
|
332
|
+
const key = methodKey(namespace, name);
|
|
333
|
+
return async (options = {}) => {
|
|
334
|
+
const properties = await transport.probe(reader, options);
|
|
335
|
+
const profile = profileFromProperties(properties);
|
|
336
|
+
for (const failure of featureFailures(methodRequirements(key), profile)) transport.refuseBeforeSending(new CapabilityError(failure));
|
|
337
|
+
return method(properties);
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
explain,
|
|
342
|
+
explainWalk,
|
|
343
|
+
refuse,
|
|
344
|
+
refuseAfterReading
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function trailingWait(args) {
|
|
348
|
+
const last = args.at(-1);
|
|
349
|
+
if (isPlainObject(last) && last["signal"] instanceof AbortSignal) return { signal: last["signal"] };
|
|
350
|
+
return {};
|
|
351
|
+
}
|
|
352
|
+
function methodKey(namespace, name) {
|
|
353
|
+
const key = `${namespace}.${name}`;
|
|
354
|
+
if (!isMethodKey(key)) throw new InternalError(`no requirements entry for ${key}`);
|
|
355
|
+
return key;
|
|
356
|
+
}
|
|
357
|
+
function callRequirement(key, parameters, args) {
|
|
358
|
+
return {
|
|
359
|
+
parameters: parameters === void 0 ? [] : parameters(...args),
|
|
360
|
+
method: methodRequirements(key)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* The error a call failed with, as a `CapabilityError` carrying the server's answer when the server
|
|
365
|
+
* refused it for lacking one of the features `call` needs, and unchanged otherwise. The server's own
|
|
366
|
+
* refusal is the verdict; the profile only names what was missing. The explaining probe is bounded
|
|
367
|
+
* by its own timeout and the client's signal; the error stands when the probe fails, and an
|
|
368
|
+
* interrupt surfaces as one.
|
|
369
|
+
*/
|
|
370
|
+
async function explainRefusal(transport, call, error) {
|
|
371
|
+
if (!mayBeRefusal(call, error)) return error;
|
|
372
|
+
let profile;
|
|
373
|
+
try {
|
|
374
|
+
profile = await transport.verifiedServer();
|
|
375
|
+
} catch (probeError) {
|
|
376
|
+
if (isNonInterruptFailure(probeError)) return error;
|
|
377
|
+
throw probeError;
|
|
378
|
+
}
|
|
379
|
+
const failure = explainedFailure(call, profile, error);
|
|
380
|
+
return failure === null ? error : new CapabilityError(failure, error);
|
|
381
|
+
}
|
|
382
|
+
/** Every feature `call` needs, parameters first, since a parameter's floor sits above its method's. */
|
|
383
|
+
function callFeatures(call) {
|
|
384
|
+
return [...call.parameters.map((parameter) => parameter.feature), ...call.method];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Whether `error` may be the server refusing `call` for a feature it lacks. A call that needs no
|
|
388
|
+
* feature cannot be, and neither can a 404 for a missing row, which comes from a route the server
|
|
389
|
+
* serves.
|
|
390
|
+
*/
|
|
391
|
+
function mayBeRefusal(call, error) {
|
|
392
|
+
if (callFeatures(call).length === 0) return false;
|
|
393
|
+
if (!(error instanceof HttpError) || error.kind === "resource-missing") return false;
|
|
394
|
+
return REFUSAL_STATUSES.has(error.status);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* The feature `profile` lacks that explains `refusal` of `call`, or `null` when the server has every
|
|
398
|
+
* feature the call needs, so it failed for some other reason, or when the refusal points elsewhere.
|
|
399
|
+
*/
|
|
400
|
+
function explainedFailure(call, profile, refusal) {
|
|
401
|
+
const failure = checkFeatures(callFeatures(call), profile);
|
|
402
|
+
if (failure === null || pointsElsewhere(call, profile, refusal)) return null;
|
|
403
|
+
return failure;
|
|
404
|
+
}
|
|
405
|
+
function pointsElsewhere(call, profile, refusal) {
|
|
406
|
+
if (refusal.status !== 400 || checkFeatures(call.method, profile) !== null) return false;
|
|
407
|
+
const named = rejectedFields(refusal);
|
|
408
|
+
const gated = new Set(call.parameters.filter((parameter) => checkFeatures([parameter.feature], profile) !== null).flatMap((parameter) => parameter.fields));
|
|
409
|
+
return named.some((field) => !gated.has(field));
|
|
410
|
+
}
|
|
411
|
+
function rejectedFields(refusal) {
|
|
412
|
+
return [refusal.fieldErrors, refusal.specificFieldErrors].flatMap((errors) => errors === null ? [] : Object.keys(errors).map(topLevelField));
|
|
413
|
+
}
|
|
414
|
+
function topLevelField(path) {
|
|
415
|
+
const end = path.indexOf(".");
|
|
416
|
+
return end === -1 ? path : path.slice(0, end);
|
|
417
|
+
}
|
|
418
|
+
//#endregion
|
|
311
419
|
//#region ../client/src/resources/csv-upload.ts
|
|
312
420
|
const CSV_CONTENT_TYPE = "text/csv";
|
|
313
421
|
function buildCsvFormData(file) {
|
|
@@ -319,14 +427,13 @@ function buildCsvFormData(file) {
|
|
|
319
427
|
//#region ../client/src/resources/content-translation.ts
|
|
320
428
|
const CONTENT_TRANSLATION_PATH = "/api/ee/content-translation";
|
|
321
429
|
function contentTranslationResource(transport) {
|
|
430
|
+
const { explain } = explainer(transport, "contentTranslation");
|
|
322
431
|
/** Download the complete content translation dictionary as CSV. */
|
|
323
432
|
async function download(options = {}) {
|
|
324
|
-
await transport.require("contentTranslation.download", options);
|
|
325
433
|
return transport.requestStream(`${CONTENT_TRANSLATION_PATH}/csv`, options);
|
|
326
434
|
}
|
|
327
435
|
/** Replace the complete content translation dictionary with the attached CSV. */
|
|
328
436
|
async function upload(file, options = {}) {
|
|
329
|
-
await transport.require("contentTranslation.upload", options);
|
|
330
437
|
const form = buildCsvFormData(file);
|
|
331
438
|
return transport.requestParsed(ContentTranslationUploadResult, `${CONTENT_TRANSLATION_PATH}/upload-dictionary`, {
|
|
332
439
|
...options,
|
|
@@ -335,8 +442,8 @@ function contentTranslationResource(transport) {
|
|
|
335
442
|
});
|
|
336
443
|
}
|
|
337
444
|
return {
|
|
338
|
-
download,
|
|
339
|
-
upload
|
|
445
|
+
download: explain("download", download),
|
|
446
|
+
upload: explain("upload", upload)
|
|
340
447
|
};
|
|
341
448
|
}
|
|
342
449
|
//#endregion
|
|
@@ -347,7 +454,6 @@ function dashboardResource(transport) {
|
|
|
347
454
|
const card = cardResource(transport);
|
|
348
455
|
/** List dashboards. `f` picks a server-side preset. */
|
|
349
456
|
async function list(params = {}, options = {}) {
|
|
350
|
-
await transport.require("dashboard.list", options);
|
|
351
457
|
return {
|
|
352
458
|
data: await transport.requestParsed(DashboardApiList, "/api/dashboard", {
|
|
353
459
|
...options,
|
|
@@ -358,7 +464,6 @@ function dashboardResource(transport) {
|
|
|
358
464
|
}
|
|
359
465
|
/** Get one dashboard by id, with its dashcards, tabs and parameters. */
|
|
360
466
|
async function get(id, options = {}) {
|
|
361
|
-
await transport.require("dashboard.get", options);
|
|
362
467
|
return transport.requestParsed(DashboardDetail, `/api/dashboard/${id}`, { ...options });
|
|
363
468
|
}
|
|
364
469
|
/**
|
|
@@ -366,7 +471,6 @@ function dashboardResource(transport) {
|
|
|
366
471
|
* puts content on a fresh dashboard.
|
|
367
472
|
*/
|
|
368
473
|
async function create(params, options = {}) {
|
|
369
|
-
await transport.require("dashboard.create", options);
|
|
370
474
|
return transport.requestParsed(Dashboard, "/api/dashboard", {
|
|
371
475
|
...options,
|
|
372
476
|
method: "POST",
|
|
@@ -378,7 +482,6 @@ function dashboardResource(transport) {
|
|
|
378
482
|
* full replacements: whatever the body omits is deleted from the dashboard.
|
|
379
483
|
*/
|
|
380
484
|
async function update(id, params, options = {}) {
|
|
381
|
-
await transport.require("dashboard.update", options);
|
|
382
485
|
return transport.requestParsed(DashboardDetail, `/api/dashboard/${id}`, {
|
|
383
486
|
...options,
|
|
384
487
|
method: "PUT",
|
|
@@ -396,7 +499,6 @@ function dashboardResource(transport) {
|
|
|
396
499
|
* is duplicated. Action, link and placeholder dashcards are not copied.
|
|
397
500
|
*/
|
|
398
501
|
async function copy(id, params = {}, options = {}) {
|
|
399
|
-
await transport.require("dashboard.copy", options);
|
|
400
502
|
return transport.requestParsed(DashboardCopy, `/api/dashboard/${id}/copy`, {
|
|
401
503
|
...options,
|
|
402
504
|
method: "POST",
|
|
@@ -409,7 +511,6 @@ function dashboardResource(transport) {
|
|
|
409
511
|
* an archive is a state change on the dashboard itself, and its dashcards are not part of it.
|
|
410
512
|
*/
|
|
411
513
|
async function archive(id, options = {}) {
|
|
412
|
-
await transport.require("dashboard.archive", options);
|
|
413
514
|
return transport.requestParsed(Dashboard, `/api/dashboard/${id}`, {
|
|
414
515
|
...options,
|
|
415
516
|
method: "PUT",
|
|
@@ -422,7 +523,6 @@ function dashboardResource(transport) {
|
|
|
422
523
|
* currently stands.
|
|
423
524
|
*/
|
|
424
525
|
async function updateDashcard(dashboardId, dashcardId, params, options = {}) {
|
|
425
|
-
await transport.require("dashboard.updateDashcard", options);
|
|
426
526
|
const dashboard = await get(dashboardId, options);
|
|
427
527
|
const target = dashboard.dashcards.find((dashcard) => dashcard.id === dashcardId);
|
|
428
528
|
if (target === void 0) throw new ConfigError(`dashcard ${dashcardId} not found on dashboard ${dashboardId}`);
|
|
@@ -441,7 +541,6 @@ function dashboardResource(transport) {
|
|
|
441
541
|
* any other way propagates as the failure it was.
|
|
442
542
|
*/
|
|
443
543
|
async function checkCardReferences(dashcards, options = {}) {
|
|
444
|
-
await transport.require("dashboard.checkCardReferences", options);
|
|
445
544
|
const grouped = groupByCardId(collectCardReferences(dashcards));
|
|
446
545
|
return (await Promise.all(Array.from(grouped.entries()).map(async ([cardId, references]) => ({
|
|
447
546
|
references,
|
|
@@ -461,7 +560,7 @@ function dashboardResource(transport) {
|
|
|
461
560
|
} catch (error) {
|
|
462
561
|
if (!(error instanceof HttpError)) throw error;
|
|
463
562
|
if (error.kind === "resource-missing") return { reason: "missing" };
|
|
464
|
-
if (error.kind === "auth") return {
|
|
563
|
+
if (error.kind === "auth" || error.kind === "forbidden") return {
|
|
465
564
|
reason: "unreadable",
|
|
466
565
|
detail: error.userMessage
|
|
467
566
|
};
|
|
@@ -472,7 +571,6 @@ function dashboardResource(transport) {
|
|
|
472
571
|
* Fetch the selectable values for a dashboard parameter, through the chain-filter value endpoint.
|
|
473
572
|
*/
|
|
474
573
|
async function parameterValues(dashboardId, parameterId, options = {}) {
|
|
475
|
-
await transport.require("dashboard.parameterValues", options);
|
|
476
574
|
return transport.requestParsed(ParameterValues, `/api/dashboard/${dashboardId}/params/${encodeURIComponent(parameterId)}/values`, { ...options });
|
|
477
575
|
}
|
|
478
576
|
/**
|
|
@@ -480,7 +578,6 @@ function dashboardResource(transport) {
|
|
|
480
578
|
* substring, capped at the first 1000 matches.
|
|
481
579
|
*/
|
|
482
580
|
async function searchParameterValues(dashboardId, parameterId, query, options = {}) {
|
|
483
|
-
await transport.require("dashboard.searchParameterValues", options);
|
|
484
581
|
const parameter = encodeURIComponent(parameterId);
|
|
485
582
|
return transport.requestParsed(ParameterValues, `/api/dashboard/${dashboardId}/params/${parameter}/search/${encodeURIComponent(query)}`, { ...options });
|
|
486
583
|
}
|
|
@@ -533,14 +630,20 @@ function stripEntityId(dashcard) {
|
|
|
533
630
|
}
|
|
534
631
|
//#endregion
|
|
535
632
|
//#region ../client/src/resources/data-action.ts
|
|
633
|
+
function collectionFeatures(params) {
|
|
634
|
+
return params.collection_id === void 0 ? [] : [{
|
|
635
|
+
feature: "dataActionCollections",
|
|
636
|
+
fields: ["collection_id"]
|
|
637
|
+
}];
|
|
638
|
+
}
|
|
536
639
|
function dataActionResource(transport) {
|
|
640
|
+
const { explain } = explainer(transport, "dataAction");
|
|
537
641
|
async function readSchema(options) {
|
|
538
642
|
const { features } = await transport.server(options);
|
|
539
643
|
return dataActionSchema(features);
|
|
540
644
|
}
|
|
541
645
|
/** List the unarchived data actions the caller can see. `GET /api/action` answers a bare array. */
|
|
542
646
|
async function list(options = {}) {
|
|
543
|
-
await transport.require("dataAction.list", options);
|
|
544
647
|
const schema = await readSchema(options);
|
|
545
648
|
return {
|
|
546
649
|
data: await transport.requestParsed(z.array(schema), "/api/action", { ...options }),
|
|
@@ -549,13 +652,11 @@ function dataActionResource(transport) {
|
|
|
549
652
|
}
|
|
550
653
|
/** Get one unarchived data action by id. */
|
|
551
654
|
async function get(id, options = {}) {
|
|
552
|
-
await transport.require("dataAction.get", options);
|
|
553
655
|
const schema = await readSchema(options);
|
|
554
656
|
return transport.requestParsed(schema, `/api/action/${id}`, { ...options });
|
|
555
657
|
}
|
|
556
658
|
/** Create a data action — a parameterized native query that writes to a database — in a collection. */
|
|
557
659
|
async function create(params, options = {}) {
|
|
558
|
-
await transport.require("dataAction.create", options);
|
|
559
660
|
const schema = await readSchema(options);
|
|
560
661
|
return transport.requestParsed(schema, "/api/action", {
|
|
561
662
|
...options,
|
|
@@ -565,7 +666,6 @@ function dataActionResource(transport) {
|
|
|
565
666
|
}
|
|
566
667
|
/** Update a data action by id, patching only the fields the body carries. */
|
|
567
668
|
async function update(id, params, options = {}) {
|
|
568
|
-
await transport.require("dataAction.update", options);
|
|
569
669
|
const schema = await readSchema(options);
|
|
570
670
|
return transport.requestParsed(schema, `/api/action/${id}`, {
|
|
571
671
|
...options,
|
|
@@ -575,12 +675,10 @@ function dataActionResource(transport) {
|
|
|
575
675
|
}
|
|
576
676
|
/** Archive (soft-delete) a data action by id. Metabase models this as an update, not its own endpoint. */
|
|
577
677
|
async function archive(id, options = {}) {
|
|
578
|
-
await transport.require("dataAction.archive", options);
|
|
579
678
|
return update(id, { archived: true }, options);
|
|
580
679
|
}
|
|
581
680
|
/** Delete a data action by id, removing it and the dashboard buttons that run it. */
|
|
582
681
|
async function remove(id, options = {}) {
|
|
583
|
-
await transport.require("dataAction.delete", options);
|
|
584
682
|
await transport.requestRaw(`/api/action/${id}`, {
|
|
585
683
|
...options,
|
|
586
684
|
method: "DELETE",
|
|
@@ -589,7 +687,6 @@ function dataActionResource(transport) {
|
|
|
589
687
|
}
|
|
590
688
|
/** Execute a data action by id with parameter values keyed by parameter id. */
|
|
591
689
|
async function execute(id, params, options = {}) {
|
|
592
|
-
await transport.require("dataAction.execute", options);
|
|
593
690
|
return transport.requestParsed(DataActionExecuteResult, `/api/action/${id}/execute`, {
|
|
594
691
|
...options,
|
|
595
692
|
method: "POST",
|
|
@@ -599,8 +696,8 @@ function dataActionResource(transport) {
|
|
|
599
696
|
return {
|
|
600
697
|
list,
|
|
601
698
|
get,
|
|
602
|
-
create,
|
|
603
|
-
update,
|
|
699
|
+
create: explain("create", create),
|
|
700
|
+
update: explain("update", update, (_id, params) => collectionFeatures(params)),
|
|
604
701
|
archive,
|
|
605
702
|
delete: remove,
|
|
606
703
|
execute
|
|
@@ -619,7 +716,6 @@ const SYNC_COMPLETE = "complete";
|
|
|
619
716
|
function databaseResource(transport) {
|
|
620
717
|
/** List databases. `include` hydrates each database's tables; `saved` adds the Saved Questions virtual database. */
|
|
621
718
|
async function list(params = {}, options = {}) {
|
|
622
|
-
await transport.require("database.list", options);
|
|
623
719
|
const response = await transport.requestParsed(DatabaseApiList, "/api/database", {
|
|
624
720
|
...options,
|
|
625
721
|
query: {
|
|
@@ -634,7 +730,6 @@ function databaseResource(transport) {
|
|
|
634
730
|
}
|
|
635
731
|
/** Get one database by id. `include` hydrates its tables, and `tables.fields` their fields too. */
|
|
636
732
|
async function get(id, params = {}, options = {}) {
|
|
637
|
-
await transport.require("database.get", options);
|
|
638
733
|
return transport.requestParsed(Database, `/api/database/${id}`, {
|
|
639
734
|
...options,
|
|
640
735
|
query: { include: params.include }
|
|
@@ -642,7 +737,6 @@ function databaseResource(transport) {
|
|
|
642
737
|
}
|
|
643
738
|
/** List the schema names in a database. */
|
|
644
739
|
async function schemas(id, options = {}) {
|
|
645
|
-
await transport.require("database.schemas", options);
|
|
646
740
|
return {
|
|
647
741
|
data: await transport.requestParsed(DatabaseApiSchemaList, `/api/database/${id}/schemas`, { ...options }),
|
|
648
742
|
total: null
|
|
@@ -650,7 +744,6 @@ function databaseResource(transport) {
|
|
|
650
744
|
}
|
|
651
745
|
/** List the tables one schema of a database holds. */
|
|
652
746
|
async function schemaTables(id, schema, options = {}) {
|
|
653
|
-
await transport.require("database.schemaTables", options);
|
|
654
747
|
return {
|
|
655
748
|
data: await transport.requestParsed(DatabaseApiSchemaTableList, `/api/database/${id}/schema/${encodeURIComponent(schema)}`, { ...options }),
|
|
656
749
|
total: null
|
|
@@ -661,7 +754,6 @@ function databaseResource(transport) {
|
|
|
661
754
|
* pass `wait` to poll the database until it reports its initial sync complete.
|
|
662
755
|
*/
|
|
663
756
|
async function syncSchema(id, params = {}, options = {}) {
|
|
664
|
-
await transport.require("database.syncSchema", options);
|
|
665
757
|
const ack = await transport.requestParsed(DatabaseTaskAck, `/api/database/${id}/sync_schema`, {
|
|
666
758
|
...options,
|
|
667
759
|
method: "POST"
|
|
@@ -683,7 +775,6 @@ function databaseResource(transport) {
|
|
|
683
775
|
}
|
|
684
776
|
/** Trigger a rescan of the cached field values across a database. */
|
|
685
777
|
async function rescanValues(id, options = {}) {
|
|
686
|
-
await transport.require("database.rescanValues", options);
|
|
687
778
|
return {
|
|
688
779
|
id,
|
|
689
780
|
status: (await transport.requestParsed(DatabaseTaskAck, `/api/database/${id}/rescan_values`, {
|
|
@@ -709,7 +800,6 @@ function datasetResource(transport) {
|
|
|
709
800
|
* MBQL, or native — rather than a reference to a saved one, so nothing here is a card.
|
|
710
801
|
*/
|
|
711
802
|
async function query(body, options = {}) {
|
|
712
|
-
await transport.require("dataset.query", options);
|
|
713
803
|
return transport.requestParsed(CardQueryResult, "/api/dataset", {
|
|
714
804
|
...options,
|
|
715
805
|
method: "POST",
|
|
@@ -721,9 +811,7 @@ function datasetResource(transport) {
|
|
|
721
811
|
* defaults server-side to true.
|
|
722
812
|
*/
|
|
723
813
|
async function native(datasetQuery, params = {}, options = {}) {
|
|
724
|
-
|
|
725
|
-
const { features } = await transport.server(options);
|
|
726
|
-
return transport.requestParsed(compiledQuerySchema(features), "/api/dataset/native", {
|
|
814
|
+
return transport.requestParsed(CompiledQuery, "/api/dataset/native", {
|
|
727
815
|
...options,
|
|
728
816
|
method: "POST",
|
|
729
817
|
body: {
|
|
@@ -738,7 +826,6 @@ function datasetResource(transport) {
|
|
|
738
826
|
* widens the field filter.
|
|
739
827
|
*/
|
|
740
828
|
async function queryMetadata(datasetQuery, options = {}) {
|
|
741
|
-
await transport.require("dataset.queryMetadata", options);
|
|
742
829
|
return transport.requestParsed(QueryMetadata, "/api/dataset/query_metadata", {
|
|
743
830
|
...options,
|
|
744
831
|
method: "POST",
|
|
@@ -757,9 +844,8 @@ function datasetResource(transport) {
|
|
|
757
844
|
* there before the wire.
|
|
758
845
|
*/
|
|
759
846
|
async function exportQuery(format, params, options = {}) {
|
|
760
|
-
await transport.require("dataset.exportQuery", options);
|
|
761
|
-
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
762
847
|
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
848
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
763
849
|
const exported = params.pivot_results ? {
|
|
764
850
|
...params.query,
|
|
765
851
|
"was-pivot": true
|
|
@@ -787,15 +873,20 @@ function datasetResource(transport) {
|
|
|
787
873
|
//#region ../client/src/resources/dependency.ts
|
|
788
874
|
const DependencyNodeApiList = z.array(DependencyNode);
|
|
789
875
|
const DependencyEntityApiList = z.array(DependencyEntity);
|
|
876
|
+
function graphTypeFeatures(type) {
|
|
877
|
+
return type === "measure" ? [{
|
|
878
|
+
feature: "measureDependencyGraph",
|
|
879
|
+
fields: ["type"]
|
|
880
|
+
}] : [];
|
|
881
|
+
}
|
|
790
882
|
function dependencyResource(transport) {
|
|
883
|
+
const { explain, explainWalk } = explainer(transport, "dependency");
|
|
791
884
|
/**
|
|
792
885
|
* The upstream dependency graph of one entity. `nodes` holds the starting entity plus every
|
|
793
886
|
* entity it depends on, directly or transitively; each edge runs from the dependent to what it
|
|
794
887
|
* depends on. A measure starts a graph only on a server with `measureDependencyGraph`.
|
|
795
888
|
*/
|
|
796
889
|
async function graph(type, id, options = {}) {
|
|
797
|
-
await transport.require("dependency.graph", options);
|
|
798
|
-
if (type === "measure") await transport.requireFeatures(["measureDependencyGraph"], options);
|
|
799
890
|
return transport.requestParsed(DependencyGraph, "/api/ee/dependencies/graph", {
|
|
800
891
|
...options,
|
|
801
892
|
query: {
|
|
@@ -811,7 +902,6 @@ function dependencyResource(transport) {
|
|
|
811
902
|
* personal collections, and `sort-column` / `sort-direction` order the result.
|
|
812
903
|
*/
|
|
813
904
|
async function dependents(type, id, params = {}, options = {}) {
|
|
814
|
-
await transport.require("dependency.dependents", options);
|
|
815
905
|
return {
|
|
816
906
|
data: await transport.requestParsed(DependencyNodeApiList, "/api/ee/dependencies/graph/dependents", {
|
|
817
907
|
...options,
|
|
@@ -837,7 +927,6 @@ function dependencyResource(transport) {
|
|
|
837
927
|
* `sort-direction` order the result.
|
|
838
928
|
*/
|
|
839
929
|
async function broken(type, id, params = {}, options = {}) {
|
|
840
|
-
await transport.require("dependency.broken", options);
|
|
841
930
|
return {
|
|
842
931
|
data: await transport.requestParsed(DependencyEntityApiList, "/api/ee/dependencies/graph/broken", {
|
|
843
932
|
...options,
|
|
@@ -864,7 +953,6 @@ function dependencyResource(transport) {
|
|
|
864
953
|
* This endpoint pages on the server, so the caller consumes pages and decides how far to pull.
|
|
865
954
|
*/
|
|
866
955
|
async function* unreferencedPages(params = {}, options = {}) {
|
|
867
|
-
await transport.require("dependency.unreferencedPages", options);
|
|
868
956
|
if (queryLeavesNoKind(params)) {
|
|
869
957
|
yield {
|
|
870
958
|
items: [],
|
|
@@ -888,7 +976,6 @@ function dependencyResource(transport) {
|
|
|
888
976
|
* caller consumes pages and decides how far to pull.
|
|
889
977
|
*/
|
|
890
978
|
async function* breakingPages(params = {}, options = {}) {
|
|
891
|
-
await transport.require("dependency.breakingPages", options);
|
|
892
979
|
if (queryLeavesNoKind(params)) {
|
|
893
980
|
yield {
|
|
894
981
|
items: [],
|
|
@@ -905,11 +992,11 @@ function dependencyResource(transport) {
|
|
|
905
992
|
});
|
|
906
993
|
}
|
|
907
994
|
return {
|
|
908
|
-
graph,
|
|
909
|
-
dependents,
|
|
910
|
-
broken,
|
|
911
|
-
unreferencedPages,
|
|
912
|
-
breakingPages
|
|
995
|
+
graph: explain("graph", graph, graphTypeFeatures),
|
|
996
|
+
dependents: explain("dependents", dependents),
|
|
997
|
+
broken: explain("broken", broken),
|
|
998
|
+
unreferencedPages: explainWalk("unreferencedPages", unreferencedPages),
|
|
999
|
+
breakingPages: explainWalk("breakingPages", breakingPages)
|
|
913
1000
|
};
|
|
914
1001
|
}
|
|
915
1002
|
function queryLeavesNoKind(params) {
|
|
@@ -931,9 +1018,9 @@ function itemListQuery(params) {
|
|
|
931
1018
|
//#region ../client/src/resources/document.ts
|
|
932
1019
|
const DocumentApiList = z.object({ items: z.array(Document) }).loose();
|
|
933
1020
|
function documentResource(transport) {
|
|
1021
|
+
const { explain } = explainer(transport, "document");
|
|
934
1022
|
/** Get existing documents. */
|
|
935
1023
|
async function list(options = {}) {
|
|
936
|
-
await transport.require("document.list", options);
|
|
937
1024
|
return {
|
|
938
1025
|
data: (await transport.requestParsed(DocumentApiList, "/api/document", { ...options })).items,
|
|
939
1026
|
total: null
|
|
@@ -941,12 +1028,10 @@ function documentResource(transport) {
|
|
|
941
1028
|
}
|
|
942
1029
|
/** Return an existing document by id. */
|
|
943
1030
|
async function get(id, options = {}) {
|
|
944
|
-
await transport.require("document.get", options);
|
|
945
1031
|
return transport.requestParsed(Document, `/api/document/${id}`, { ...options });
|
|
946
1032
|
}
|
|
947
1033
|
/** Create a new document. */
|
|
948
1034
|
async function create(params, options = {}) {
|
|
949
|
-
await transport.require("document.create", options);
|
|
950
1035
|
return transport.requestParsed(Document, "/api/document", {
|
|
951
1036
|
...options,
|
|
952
1037
|
method: "POST",
|
|
@@ -955,7 +1040,6 @@ function documentResource(transport) {
|
|
|
955
1040
|
}
|
|
956
1041
|
/** Update an existing document by id. */
|
|
957
1042
|
async function update(id, params, options = {}) {
|
|
958
|
-
await transport.require("document.update", options);
|
|
959
1043
|
return transport.requestParsed(Document, `/api/document/${id}`, {
|
|
960
1044
|
...options,
|
|
961
1045
|
method: "PUT",
|
|
@@ -964,7 +1048,6 @@ function documentResource(transport) {
|
|
|
964
1048
|
}
|
|
965
1049
|
/** Archive (soft-delete) a document by id. Metabase models this as an update, not its own endpoint. */
|
|
966
1050
|
async function archive(id, options = {}) {
|
|
967
|
-
await transport.require("document.archive", options);
|
|
968
1051
|
return update(id, { archived: true }, options);
|
|
969
1052
|
}
|
|
970
1053
|
/**
|
|
@@ -972,7 +1055,6 @@ function documentResource(transport) {
|
|
|
972
1055
|
* it. An archived source is not found.
|
|
973
1056
|
*/
|
|
974
1057
|
async function copy(id, params = {}, options = {}) {
|
|
975
|
-
await transport.require("document.copy", options);
|
|
976
1058
|
return transport.requestParsed(Document, `/api/document/${id}/copy`, {
|
|
977
1059
|
...options,
|
|
978
1060
|
method: "POST",
|
|
@@ -985,7 +1067,7 @@ function documentResource(transport) {
|
|
|
985
1067
|
create,
|
|
986
1068
|
update,
|
|
987
1069
|
archive,
|
|
988
|
-
copy
|
|
1070
|
+
copy: explain("copy", copy)
|
|
989
1071
|
};
|
|
990
1072
|
}
|
|
991
1073
|
//#endregion
|
|
@@ -997,7 +1079,6 @@ function eidTranslationResource(transport) {
|
|
|
997
1079
|
* lookup succeeded — the numeric id.
|
|
998
1080
|
*/
|
|
999
1081
|
async function translate(params, options = {}) {
|
|
1000
|
-
await transport.require("eidTranslation.translate", options);
|
|
1001
1082
|
return transport.requestParsed(EidTranslateResult, "/api/eid-translation/translate", {
|
|
1002
1083
|
...options,
|
|
1003
1084
|
method: "POST",
|
|
@@ -1051,6 +1132,7 @@ const Erd = z.object({
|
|
|
1051
1132
|
//#region ../client/src/resources/erd.ts
|
|
1052
1133
|
const ERD_PATH = "/api/ee/erd";
|
|
1053
1134
|
function erdResource(transport) {
|
|
1135
|
+
const { explain } = explainer(transport, "erd");
|
|
1054
1136
|
/**
|
|
1055
1137
|
* Get the entity relationship diagram of a database: the readable tables as nodes, each with its
|
|
1056
1138
|
* fields, and the foreign keys between them as edges. `table-ids` makes those tables the focal
|
|
@@ -1058,7 +1140,6 @@ function erdResource(transport) {
|
|
|
1058
1140
|
* none.
|
|
1059
1141
|
*/
|
|
1060
1142
|
async function get(params, options = {}) {
|
|
1061
|
-
await transport.require("erd.get", options);
|
|
1062
1143
|
return transport.requestParsed(Erd, ERD_PATH, {
|
|
1063
1144
|
...options,
|
|
1064
1145
|
query: {
|
|
@@ -1068,7 +1149,7 @@ function erdResource(transport) {
|
|
|
1068
1149
|
}
|
|
1069
1150
|
});
|
|
1070
1151
|
}
|
|
1071
|
-
return { get };
|
|
1152
|
+
return { get: explain("get", get) };
|
|
1072
1153
|
}
|
|
1073
1154
|
//#endregion
|
|
1074
1155
|
//#region ../client/src/resources/optional-parsed.ts
|
|
@@ -1086,12 +1167,12 @@ async function fetchOptionalParsed(client, path, schema, options = {}) {
|
|
|
1086
1167
|
//#region ../client/src/resources/field.ts
|
|
1087
1168
|
const FieldApiSummary = z.tuple([z.tuple([z.literal("count"), z.number().int()]), z.tuple([z.literal("distincts"), z.number().int()])]);
|
|
1088
1169
|
function fieldResource(transport) {
|
|
1170
|
+
const { refuse } = explainer(transport, "field");
|
|
1089
1171
|
/**
|
|
1090
1172
|
* Get one field by id. A server that labels data sensitivity answers `data_sensitivity`, `null`
|
|
1091
1173
|
* for an unlabelled field; any other answers the field without the key (`hasDataSensitivity`).
|
|
1092
1174
|
*/
|
|
1093
1175
|
async function get(id, options = {}) {
|
|
1094
|
-
await transport.require("field.get", options);
|
|
1095
1176
|
const { features } = await transport.server(options);
|
|
1096
1177
|
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, { ...options });
|
|
1097
1178
|
}
|
|
@@ -1102,7 +1183,6 @@ function fieldResource(transport) {
|
|
|
1102
1183
|
* the label is refused there before the wire. Answers the field the way `get` does.
|
|
1103
1184
|
*/
|
|
1104
1185
|
async function update(id, params, options = {}) {
|
|
1105
|
-
await transport.require("field.update", options);
|
|
1106
1186
|
await transport.requireFeatures(params.data_sensitivity === void 0 ? [] : ["fieldDataSensitivity"], options);
|
|
1107
1187
|
const { features } = await transport.server(options);
|
|
1108
1188
|
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, {
|
|
@@ -1113,11 +1193,11 @@ function fieldResource(transport) {
|
|
|
1113
1193
|
}
|
|
1114
1194
|
/**
|
|
1115
1195
|
* Label a field's data sensitivity by hand. A person's label is never overwritten by the server's
|
|
1116
|
-
* classifier; `null` withdraws it, so whatever label the classifier wrote shows again.
|
|
1117
|
-
* the
|
|
1196
|
+
* classifier; `null` withdraws it, so whatever label the classifier wrote shows again. A server
|
|
1197
|
+
* without the column drops the key silently, so the label is refused there before the wire.
|
|
1198
|
+
* Answers the field with the label it now carries.
|
|
1118
1199
|
*/
|
|
1119
1200
|
async function setDataSensitivity(id, params, options = {}) {
|
|
1120
|
-
await transport.require("field.setDataSensitivity", options);
|
|
1121
1201
|
return transport.requestParsed(FieldWithDataSensitivity, `/api/field/${id}`, {
|
|
1122
1202
|
...options,
|
|
1123
1203
|
method: "PUT",
|
|
@@ -1133,7 +1213,6 @@ function fieldResource(transport) {
|
|
|
1133
1213
|
* `value` against the display value, ignoring both `searchId` and `limit`.
|
|
1134
1214
|
*/
|
|
1135
1215
|
async function search(id, searchId, params, options = {}) {
|
|
1136
|
-
await transport.require("field.search", options);
|
|
1137
1216
|
return transport.requestParsed(FieldSearchMatches, `/api/field/${id}/search/${searchId}`, {
|
|
1138
1217
|
...options,
|
|
1139
1218
|
query: {
|
|
@@ -1148,7 +1227,6 @@ function fieldResource(transport) {
|
|
|
1148
1227
|
* `value` and ignores any text after it, refusing only a `value` that has none.
|
|
1149
1228
|
*/
|
|
1150
1229
|
async function remapping(id, remappedId, params, options = {}) {
|
|
1151
|
-
await transport.require("field.remapping", options);
|
|
1152
1230
|
const { features } = await transport.server(options);
|
|
1153
1231
|
return fetchOptionalParsed(transport, `/api/field/${id}/remapping/${remappedId}`, fieldRemappedValueSchema(features, id === remappedId), {
|
|
1154
1232
|
...options,
|
|
@@ -1157,7 +1235,6 @@ function fieldResource(transport) {
|
|
|
1157
1235
|
}
|
|
1158
1236
|
/** Get the row count and the distinct-value count for a field. */
|
|
1159
1237
|
async function summary(id, options = {}) {
|
|
1160
|
-
await transport.require("field.summary", options);
|
|
1161
1238
|
const [[, count], [, distincts]] = await transport.requestParsed(FieldApiSummary, `/api/field/${id}/summary`, { ...options });
|
|
1162
1239
|
return {
|
|
1163
1240
|
field_id: id,
|
|
@@ -1167,13 +1244,12 @@ function fieldResource(transport) {
|
|
|
1167
1244
|
}
|
|
1168
1245
|
/** Get the cached distinct values Metabase holds for a field. */
|
|
1169
1246
|
async function values(id, options = {}) {
|
|
1170
|
-
await transport.require("field.values", options);
|
|
1171
1247
|
return transport.requestParsed(FieldValues, `/api/field/${id}/values`, { ...options });
|
|
1172
1248
|
}
|
|
1173
1249
|
return {
|
|
1174
1250
|
get,
|
|
1175
1251
|
update,
|
|
1176
|
-
setDataSensitivity,
|
|
1252
|
+
setDataSensitivity: refuse("setDataSensitivity", setDataSensitivity),
|
|
1177
1253
|
search,
|
|
1178
1254
|
remapping,
|
|
1179
1255
|
summary,
|
|
@@ -1205,22 +1281,22 @@ const SyncStashStarted = z.object({
|
|
|
1205
1281
|
task_id: z.number().int().positive()
|
|
1206
1282
|
});
|
|
1207
1283
|
const RemoteSyncSetting = z.string().nullable();
|
|
1208
|
-
const RemoteSyncBranchProperty =
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1284
|
+
const RemoteSyncBranchProperty = SessionProperties.extend({ "remote-sync-branch": z.string().nullable().optional() });
|
|
1285
|
+
const RemoteSyncUrlProperty = SessionProperties.extend({ "remote-sync-url": z.string().nullable().optional() });
|
|
1286
|
+
const BRANCH_UNREADABLE_MESSAGE = "the remote-sync-branch setting is not readable: only admins see it, and a server without remote sync has none";
|
|
1287
|
+
const BRANCH_UNSET_MESSAGE = "git-sync tracks no branch: the remote-sync-branch setting is unset";
|
|
1211
1288
|
function isRemoteUnreadable(error) {
|
|
1212
1289
|
if (!(error instanceof HttpError)) return false;
|
|
1213
|
-
return error.status ===
|
|
1290
|
+
return error.status === 403 || error.status === 404;
|
|
1214
1291
|
}
|
|
1215
1292
|
function gitSyncResource(transport) {
|
|
1293
|
+
const { explain, refuse, refuseAfterReading } = explainer(transport, "gitSync");
|
|
1216
1294
|
/** Get the running or most recently finished sync task, or null when the server has none. */
|
|
1217
1295
|
async function currentTask(options = {}) {
|
|
1218
|
-
await transport.require("gitSync.currentTask", options);
|
|
1219
1296
|
return fetchOptionalParsed(transport, "/api/ee/remote-sync/current-task", SyncTask, options);
|
|
1220
1297
|
}
|
|
1221
1298
|
/** Request cancellation of the running sync task, and answer it in the state that left it. */
|
|
1222
1299
|
async function cancelTask(options = {}) {
|
|
1223
|
-
await transport.require("gitSync.cancelTask", options);
|
|
1224
1300
|
return transport.requestParsed(SyncTask, "/api/ee/remote-sync/current-task/cancel", {
|
|
1225
1301
|
...options,
|
|
1226
1302
|
method: "POST"
|
|
@@ -1228,12 +1304,10 @@ function gitSyncResource(transport) {
|
|
|
1228
1304
|
}
|
|
1229
1305
|
/** Whether Metabase holds content changes the remote has not been told about. */
|
|
1230
1306
|
async function isDirty(options = {}) {
|
|
1231
|
-
await transport.require("gitSync.isDirty", options);
|
|
1232
1307
|
return (await transport.requestParsed(SyncDirtyFlag, "/api/ee/remote-sync/is-dirty", { ...options })).is_dirty;
|
|
1233
1308
|
}
|
|
1234
1309
|
/** List the objects whose local state differs from the remote. */
|
|
1235
1310
|
async function dirty(options = {}) {
|
|
1236
|
-
await transport.require("gitSync.dirty", options);
|
|
1237
1311
|
return {
|
|
1238
1312
|
data: (await transport.requestParsed(SyncDirtyList, "/api/ee/remote-sync/dirty", { ...options })).dirty,
|
|
1239
1313
|
total: null
|
|
@@ -1244,7 +1318,6 @@ function gitSyncResource(transport) {
|
|
|
1244
1318
|
* comparison; `force-refresh` re-reads the remote instead.
|
|
1245
1319
|
*/
|
|
1246
1320
|
async function hasRemoteChanges(params = {}, options = {}) {
|
|
1247
|
-
await transport.require("gitSync.hasRemoteChanges", options);
|
|
1248
1321
|
return transport.requestParsed(SyncRemoteChanges, "/api/ee/remote-sync/has-remote-changes", {
|
|
1249
1322
|
...options,
|
|
1250
1323
|
query: { "force-refresh": params["force-refresh"] }
|
|
@@ -1255,14 +1328,16 @@ function gitSyncResource(transport) {
|
|
|
1255
1328
|
* pass `wait` to poll that task until it reaches a terminal status. A server already up to date
|
|
1256
1329
|
* answers no task id, and there is then nothing to poll. A server that guards the tracked branch
|
|
1257
1330
|
* refuses with 409 when `expected_branch` disagrees with the setting; left out, it is the tracked
|
|
1258
|
-
* branch as read just before the request
|
|
1259
|
-
*
|
|
1331
|
+
* branch as read just before the request, on every server with remote sync. A server that guards
|
|
1332
|
+
* the branch also requires it, so there the request is refused before the wire when the setting
|
|
1333
|
+
* is unset or hidden from the caller; a server without the guard ignores the assertion, and gets
|
|
1334
|
+
* none when there is none to read. `merge` folds the remote's changes into local content by a
|
|
1335
|
+
* three-way merge and keeps un-pushed local changes, where a plain import refuses on a dirty
|
|
1260
1336
|
* instance.
|
|
1261
1337
|
*/
|
|
1262
1338
|
async function importFromRemote(params = {}, options = {}) {
|
|
1263
|
-
await
|
|
1339
|
+
const expectedBranch = await expectedBranchFor(params.expected_branch, options);
|
|
1264
1340
|
await requireMerge(params.merge, options);
|
|
1265
|
-
const expectedBranch = await guardedBranch(params.expected_branch, options);
|
|
1266
1341
|
const started = await transport.requestParsed(SyncImportStarted, "/api/ee/remote-sync/import", {
|
|
1267
1342
|
...options,
|
|
1268
1343
|
method: "POST",
|
|
@@ -1284,28 +1359,38 @@ function gitSyncResource(transport) {
|
|
|
1284
1359
|
final: await settle(params.wait, options)
|
|
1285
1360
|
};
|
|
1286
1361
|
}
|
|
1287
|
-
async function
|
|
1288
|
-
if (
|
|
1362
|
+
async function expectedBranchFor(given, options) {
|
|
1363
|
+
if (given !== void 0) return given;
|
|
1364
|
+
return importAssertion(await readTrackedBranch(options));
|
|
1289
1365
|
}
|
|
1290
|
-
async function
|
|
1366
|
+
async function exportBranchFor(given, options) {
|
|
1291
1367
|
if (given !== void 0) return given;
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1368
|
+
return exportBranch(await readTrackedBranch(options));
|
|
1369
|
+
}
|
|
1370
|
+
async function readTrackedBranch(options) {
|
|
1371
|
+
const properties = await transport.probe(RemoteSyncBranchProperty, options);
|
|
1372
|
+
return {
|
|
1373
|
+
tracked: properties["remote-sync-branch"],
|
|
1374
|
+
features: profileFromProperties(properties).features
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
async function requireMerge(merge, options) {
|
|
1378
|
+
if (merge === true) await transport.requireFeatures(["remoteSyncMerge"], options);
|
|
1295
1379
|
}
|
|
1296
1380
|
/**
|
|
1297
1381
|
* Export Metabase's content to the remote. The endpoint queues a task and returns at once; pass
|
|
1298
1382
|
* `wait` to poll that task until it reaches a terminal status. A server that guards the tracked
|
|
1299
|
-
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked
|
|
1300
|
-
* as read just before the request
|
|
1301
|
-
*
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
1383
|
+
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked
|
|
1384
|
+
* branch as read just before the request, and the request is refused before the wire when the
|
|
1385
|
+
* setting is unset or hidden from the caller. A server without the guard exports to a named
|
|
1386
|
+
* `branch` and tracks it from then on; left out, it gets none and exports to its own. When the
|
|
1387
|
+
* remote has moved past the last sync, a plain export ends in a `conflict` task where `merge` is
|
|
1388
|
+
* available, and is refused with 400 before any task elsewhere; `merge` folds the remote's
|
|
1389
|
+
* changes in by a three-way merge instead, and `force` overwrites them.
|
|
1304
1390
|
*/
|
|
1305
1391
|
async function exportToRemote(params = {}, options = {}) {
|
|
1306
|
-
await
|
|
1392
|
+
const branchName = await exportBranchFor(params.branch, options);
|
|
1307
1393
|
await requireMerge(params.merge, options);
|
|
1308
|
-
const branchName = await guardedBranch(params.branch, options);
|
|
1309
1394
|
const started = await transport.requestParsed(SyncExportStarted, "/api/ee/remote-sync/export", {
|
|
1310
1395
|
...options,
|
|
1311
1396
|
method: "POST",
|
|
@@ -1333,7 +1418,6 @@ function gitSyncResource(transport) {
|
|
|
1333
1418
|
* otherwise.
|
|
1334
1419
|
*/
|
|
1335
1420
|
async function exportPreflight(params, options = {}) {
|
|
1336
|
-
await transport.require("gitSync.exportPreflight", options);
|
|
1337
1421
|
return transport.requestParsed(SyncExportPreflight, "/api/ee/remote-sync/export-preflight", {
|
|
1338
1422
|
...options,
|
|
1339
1423
|
query: { branch: params.branch }
|
|
@@ -1344,7 +1428,6 @@ function gitSyncResource(transport) {
|
|
|
1344
1428
|
* queues a task and returns at once; pass `wait` to poll that task to a terminal status.
|
|
1345
1429
|
*/
|
|
1346
1430
|
async function stash(params, options = {}) {
|
|
1347
|
-
await transport.require("gitSync.stash", options);
|
|
1348
1431
|
const started = await transport.requestParsed(SyncStashStarted, "/api/ee/remote-sync/stash", {
|
|
1349
1432
|
...options,
|
|
1350
1433
|
method: "POST",
|
|
@@ -1367,7 +1450,6 @@ function gitSyncResource(transport) {
|
|
|
1367
1450
|
}
|
|
1368
1451
|
/** List the branches the configured remote carries. */
|
|
1369
1452
|
async function branches(options = {}) {
|
|
1370
|
-
await transport.require("gitSync.branches", options);
|
|
1371
1453
|
return {
|
|
1372
1454
|
data: (await transport.requestParsed(SyncBranchList, "/api/ee/remote-sync/branches", { ...options })).items,
|
|
1373
1455
|
total: null
|
|
@@ -1375,7 +1457,6 @@ function gitSyncResource(transport) {
|
|
|
1375
1457
|
}
|
|
1376
1458
|
/** Create a branch on the remote and point git-sync at it. */
|
|
1377
1459
|
async function createBranch(params, options = {}) {
|
|
1378
|
-
await transport.require("gitSync.createBranch", options);
|
|
1379
1460
|
return transport.requestParsed(SyncBranchCreated, "/api/ee/remote-sync/create-branch", {
|
|
1380
1461
|
...options,
|
|
1381
1462
|
method: "POST",
|
|
@@ -1384,19 +1465,42 @@ function gitSyncResource(transport) {
|
|
|
1384
1465
|
}
|
|
1385
1466
|
/**
|
|
1386
1467
|
* Mark one collection as git-synced, or unmark it. The server cascades the flag to descendants
|
|
1387
|
-
* by location prefix, and may queue an
|
|
1468
|
+
* by location prefix, and may queue an import task to bring the instance in line with the
|
|
1469
|
+
* remote. A server without `remoteSyncCollectionsOnlyUpdate` validates every settings update as
|
|
1470
|
+
* a remote configuration and fails one naming no remote, after it has applied the flag, so it is
|
|
1471
|
+
* sent the remote's URL and its token as the server shows it, which it checks against the remote
|
|
1472
|
+
* and keeps as they are, or a blank URL when no remote is configured, which clears the remote
|
|
1473
|
+
* settings that are already empty.
|
|
1388
1474
|
*/
|
|
1389
1475
|
async function setCollectionSynced(collectionId, synced, options = {}) {
|
|
1390
|
-
await transport.require("gitSync.setCollectionSynced", options);
|
|
1391
1476
|
return transport.requestParsed(SyncSettingsUpdateResult, "/api/ee/remote-sync/settings", {
|
|
1392
1477
|
...options,
|
|
1393
1478
|
method: "PUT",
|
|
1394
|
-
body:
|
|
1479
|
+
body: await settingsUpdateBody({ [collectionId]: synced }, options)
|
|
1395
1480
|
});
|
|
1396
1481
|
}
|
|
1482
|
+
async function settingsUpdateBody(collections, options) {
|
|
1483
|
+
const { features } = await transport.server(options);
|
|
1484
|
+
if (features.remoteSyncCollectionsOnlyUpdate) return { collections };
|
|
1485
|
+
const url = (await transport.probe(RemoteSyncUrlProperty, options))["remote-sync-url"];
|
|
1486
|
+
if (url === void 0) return { collections };
|
|
1487
|
+
if (url === null) return {
|
|
1488
|
+
collections,
|
|
1489
|
+
"remote-sync-url": ""
|
|
1490
|
+
};
|
|
1491
|
+
const token = await fetchOptionalParsed(transport, "/api/setting/remote-sync-token", RemoteSyncSetting, options);
|
|
1492
|
+
if (token === null) return {
|
|
1493
|
+
collections,
|
|
1494
|
+
"remote-sync-url": url
|
|
1495
|
+
};
|
|
1496
|
+
return {
|
|
1497
|
+
collections,
|
|
1498
|
+
"remote-sync-url": url,
|
|
1499
|
+
"remote-sync-token": token
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1397
1502
|
/** The collections currently in git-sync's scope. */
|
|
1398
1503
|
async function syncedCollections(options = {}) {
|
|
1399
|
-
await transport.require("gitSync.syncedCollections", options);
|
|
1400
1504
|
return {
|
|
1401
1505
|
data: (await listCollectionsAs(transport, SyncScopeCollection, options)).filter((entry) => entry.is_remote_synced === true),
|
|
1402
1506
|
total: null
|
|
@@ -1404,7 +1508,6 @@ function gitSyncResource(transport) {
|
|
|
1404
1508
|
}
|
|
1405
1509
|
/** The remote's URL, or null when none is configured or the caller may not read it. */
|
|
1406
1510
|
async function remoteUrl(options = {}) {
|
|
1407
|
-
await transport.require("gitSync.remoteUrl", options);
|
|
1408
1511
|
try {
|
|
1409
1512
|
const url = await fetchOptionalParsed(transport, "/api/setting/remote-sync-url", RemoteSyncSetting, options);
|
|
1410
1513
|
return url === "" ? null : url;
|
|
@@ -1414,21 +1517,22 @@ function gitSyncResource(transport) {
|
|
|
1414
1517
|
}
|
|
1415
1518
|
}
|
|
1416
1519
|
/**
|
|
1417
|
-
* The branch git-sync tracks, or null when none is configured. Refuses when the
|
|
1418
|
-
* read the setting (it is admin-only)
|
|
1419
|
-
*
|
|
1520
|
+
* The branch git-sync tracks, or null when none is configured. Refuses when the server has no
|
|
1521
|
+
* remote sync or the caller may not read the setting (it is admin-only), rather than reading
|
|
1522
|
+
* either as unset.
|
|
1420
1523
|
*/
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
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");
|
|
1524
|
+
function branch(properties) {
|
|
1525
|
+
const tracked = properties["remote-sync-branch"];
|
|
1526
|
+
if (tracked === void 0) throw new ConfigError(BRANCH_UNREADABLE_MESSAGE);
|
|
1425
1527
|
return tracked;
|
|
1426
1528
|
}
|
|
1427
|
-
/**
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1529
|
+
/**
|
|
1530
|
+
* The branch git-sync tracks, refused when none is configured, the server has no remote sync, or
|
|
1531
|
+
* the caller may not read it.
|
|
1532
|
+
*/
|
|
1533
|
+
function trackedBranch(properties) {
|
|
1534
|
+
const tracked = branch(properties);
|
|
1535
|
+
if (tracked === null) throw new ConfigError(BRANCH_UNSET_MESSAGE);
|
|
1432
1536
|
return tracked;
|
|
1433
1537
|
}
|
|
1434
1538
|
/**
|
|
@@ -1436,7 +1540,6 @@ function gitSyncResource(transport) {
|
|
|
1436
1540
|
* answers null, which is terminal in the same sense: nothing further will happen.
|
|
1437
1541
|
*/
|
|
1438
1542
|
async function waitForTask(wait, options = {}) {
|
|
1439
|
-
await transport.require("gitSync.waitForTask", options);
|
|
1440
1543
|
return settle(wait, options);
|
|
1441
1544
|
}
|
|
1442
1545
|
async function settle(wait, options) {
|
|
@@ -1446,25 +1549,38 @@ function gitSyncResource(transport) {
|
|
|
1446
1549
|
}), (task) => task === null || isSyncTaskTerminal(task.status), wait);
|
|
1447
1550
|
}
|
|
1448
1551
|
return {
|
|
1449
|
-
currentTask,
|
|
1450
|
-
cancelTask,
|
|
1451
|
-
isDirty,
|
|
1452
|
-
dirty,
|
|
1453
|
-
hasRemoteChanges,
|
|
1454
|
-
import: importFromRemote,
|
|
1455
|
-
export: exportToRemote,
|
|
1456
|
-
exportPreflight,
|
|
1457
|
-
stash,
|
|
1458
|
-
branches,
|
|
1459
|
-
createBranch,
|
|
1460
|
-
setCollectionSynced,
|
|
1461
|
-
syncedCollections,
|
|
1552
|
+
currentTask: explain("currentTask", currentTask),
|
|
1553
|
+
cancelTask: explain("cancelTask", cancelTask),
|
|
1554
|
+
isDirty: explain("isDirty", isDirty),
|
|
1555
|
+
dirty: explain("dirty", dirty),
|
|
1556
|
+
hasRemoteChanges: explain("hasRemoteChanges", hasRemoteChanges),
|
|
1557
|
+
import: explain("import", importFromRemote),
|
|
1558
|
+
export: explain("export", exportToRemote),
|
|
1559
|
+
exportPreflight: explain("exportPreflight", exportPreflight),
|
|
1560
|
+
stash: explain("stash", stash),
|
|
1561
|
+
branches: explain("branches", branches),
|
|
1562
|
+
createBranch: explain("createBranch", createBranch),
|
|
1563
|
+
setCollectionSynced: explain("setCollectionSynced", setCollectionSynced),
|
|
1564
|
+
syncedCollections: refuse("syncedCollections", syncedCollections),
|
|
1462
1565
|
remoteUrl,
|
|
1463
|
-
branch,
|
|
1464
|
-
trackedBranch,
|
|
1465
|
-
waitForTask
|
|
1566
|
+
branch: refuseAfterReading("branch", RemoteSyncBranchProperty, branch),
|
|
1567
|
+
trackedBranch: refuseAfterReading("trackedBranch", RemoteSyncBranchProperty, trackedBranch),
|
|
1568
|
+
waitForTask: explain("waitForTask", waitForTask)
|
|
1466
1569
|
};
|
|
1467
1570
|
}
|
|
1571
|
+
function importAssertion(read) {
|
|
1572
|
+
if (!read.features.remoteSync) return;
|
|
1573
|
+
if (read.features.remoteSyncBranchGuard) return requiredBranch(read.tracked);
|
|
1574
|
+
return typeof read.tracked === "string" ? read.tracked : void 0;
|
|
1575
|
+
}
|
|
1576
|
+
function exportBranch(read) {
|
|
1577
|
+
if (!read.features.remoteSync || !read.features.remoteSyncBranchGuard) return;
|
|
1578
|
+
return requiredBranch(read.tracked);
|
|
1579
|
+
}
|
|
1580
|
+
function requiredBranch(tracked) {
|
|
1581
|
+
if (typeof tracked === "string") return tracked;
|
|
1582
|
+
throw new ConfigError(tracked === null ? BRANCH_UNSET_MESSAGE : BRANCH_UNREADABLE_MESSAGE);
|
|
1583
|
+
}
|
|
1468
1584
|
//#endregion
|
|
1469
1585
|
//#region ../client/src/resources/glossary.ts
|
|
1470
1586
|
const GlossaryApiList = z.object({ data: z.array(Glossary) }).loose();
|
|
@@ -1474,7 +1590,6 @@ function glossaryResource(transport) {
|
|
|
1474
1590
|
* contains it, case-insensitively.
|
|
1475
1591
|
*/
|
|
1476
1592
|
async function list(params = {}, options = {}) {
|
|
1477
|
-
await transport.require("glossary.list", options);
|
|
1478
1593
|
return {
|
|
1479
1594
|
data: (await transport.requestParsed(GlossaryApiList, "/api/glossary", {
|
|
1480
1595
|
...options,
|
|
@@ -1485,7 +1600,6 @@ function glossaryResource(transport) {
|
|
|
1485
1600
|
}
|
|
1486
1601
|
/** Create a glossary entry, a term and its definition. Terms are unique. */
|
|
1487
1602
|
async function create(params, options = {}) {
|
|
1488
|
-
await transport.require("glossary.create", options);
|
|
1489
1603
|
return transport.requestParsed(Glossary, "/api/glossary", {
|
|
1490
1604
|
...options,
|
|
1491
1605
|
method: "POST",
|
|
@@ -1494,7 +1608,6 @@ function glossaryResource(transport) {
|
|
|
1494
1608
|
}
|
|
1495
1609
|
/** Replace the term and definition of a glossary entry by id. */
|
|
1496
1610
|
async function update(id, params, options = {}) {
|
|
1497
|
-
await transport.require("glossary.update", options);
|
|
1498
1611
|
return transport.requestParsed(Glossary, `/api/glossary/${id}`, {
|
|
1499
1612
|
...options,
|
|
1500
1613
|
method: "PUT",
|
|
@@ -1503,7 +1616,6 @@ function glossaryResource(transport) {
|
|
|
1503
1616
|
}
|
|
1504
1617
|
/** Delete a glossary entry by id. */
|
|
1505
1618
|
async function remove(id, options = {}) {
|
|
1506
|
-
await transport.require("glossary.delete", options);
|
|
1507
1619
|
await transport.requestRaw(`/api/glossary/${id}`, {
|
|
1508
1620
|
...options,
|
|
1509
1621
|
method: "DELETE",
|
|
@@ -1536,9 +1648,9 @@ const UNPUBLISH_TABLES_PATH = "/api/ee/data-studio/table/unpublish-tables";
|
|
|
1536
1648
|
const LIBRARY_DATA_TYPE = "library-data";
|
|
1537
1649
|
const PublishTablesResponse = z.object({ target_collection: Collection.nullable() });
|
|
1538
1650
|
function libraryResource(transport) {
|
|
1651
|
+
const { explain } = explainer(transport, "library");
|
|
1539
1652
|
/** Get the Library root and its child collections, or `null` on an instance that has none. */
|
|
1540
1653
|
async function get(options = {}) {
|
|
1541
|
-
await transport.require("library.get", options);
|
|
1542
1654
|
const { features } = await transport.server(options);
|
|
1543
1655
|
const wire = await transport.requestParsed(libraryWireSchema(features), LIBRARY_ROOT_PATH, { ...options });
|
|
1544
1656
|
if (wire === null) return null;
|
|
@@ -1557,7 +1669,6 @@ function libraryResource(transport) {
|
|
|
1557
1669
|
* back from a refetch, which together make this idempotent.
|
|
1558
1670
|
*/
|
|
1559
1671
|
async function create(options = {}) {
|
|
1560
|
-
await transport.require("library.create", options);
|
|
1561
1672
|
const existing = await get(options);
|
|
1562
1673
|
if (existing !== null) return existing;
|
|
1563
1674
|
await transport.requestRaw(LIBRARY_ROOT_PATH, {
|
|
@@ -1570,7 +1681,6 @@ function libraryResource(transport) {
|
|
|
1570
1681
|
}
|
|
1571
1682
|
/** The id of the Library's Data collection, creating the Library first when it does not exist. */
|
|
1572
1683
|
async function ensureDataCollectionId(options = {}) {
|
|
1573
|
-
await transport.require("library.ensureDataCollectionId", options);
|
|
1574
1684
|
const data = (await create(options)).effective_children.find((child) => child.type === LIBRARY_DATA_TYPE);
|
|
1575
1685
|
if (data === void 0) throw new Error("Library has no Data collection to publish into");
|
|
1576
1686
|
if (typeof data.id !== "number") throw new Error(`Library Data collection has a non-numeric id ${String(data.id)}`);
|
|
@@ -1583,7 +1693,6 @@ function libraryResource(transport) {
|
|
|
1583
1693
|
* `LibraryPublishTablesInput` is refused before any request.
|
|
1584
1694
|
*/
|
|
1585
1695
|
async function publishTables(params, options = {}) {
|
|
1586
|
-
await transport.require("library.publishTables", options);
|
|
1587
1696
|
const body = parseRequestBody(LibraryPublishTablesInput, params, "tables to publish");
|
|
1588
1697
|
return (await transport.requestParsed(PublishTablesResponse, PUBLISH_TABLES_PATH, {
|
|
1589
1698
|
...options,
|
|
@@ -1597,7 +1706,6 @@ function libraryResource(transport) {
|
|
|
1597
1706
|
* table, so selectors that do not match `TableSelectors` are refused before any request.
|
|
1598
1707
|
*/
|
|
1599
1708
|
async function unpublishTables(params, options = {}) {
|
|
1600
|
-
await transport.require("library.unpublishTables", options);
|
|
1601
1709
|
const body = parseRequestBody(TableSelectors, params, "table selectors");
|
|
1602
1710
|
await transport.requestRaw(UNPUBLISH_TABLES_PATH, {
|
|
1603
1711
|
...options,
|
|
@@ -1607,20 +1715,20 @@ function libraryResource(transport) {
|
|
|
1607
1715
|
});
|
|
1608
1716
|
}
|
|
1609
1717
|
return {
|
|
1610
|
-
get,
|
|
1611
|
-
create,
|
|
1612
|
-
ensureDataCollectionId,
|
|
1613
|
-
publishTables,
|
|
1614
|
-
unpublishTables
|
|
1718
|
+
get: explain("get", get),
|
|
1719
|
+
create: explain("create", create),
|
|
1720
|
+
ensureDataCollectionId: explain("ensureDataCollectionId", ensureDataCollectionId),
|
|
1721
|
+
publishTables: explain("publishTables", publishTables),
|
|
1722
|
+
unpublishTables: explain("unpublishTables", unpublishTables)
|
|
1615
1723
|
};
|
|
1616
1724
|
}
|
|
1617
1725
|
//#endregion
|
|
1618
1726
|
//#region ../client/src/resources/measure.ts
|
|
1619
1727
|
const MeasureApiList = z.array(Measure);
|
|
1620
1728
|
function measureResource(transport) {
|
|
1729
|
+
const { explain } = explainer(transport, "measure");
|
|
1621
1730
|
/** List measures. */
|
|
1622
1731
|
async function list(options = {}) {
|
|
1623
|
-
await transport.require("measure.list", options);
|
|
1624
1732
|
return {
|
|
1625
1733
|
data: await transport.requestParsed(MeasureApiList, "/api/measure", { ...options }),
|
|
1626
1734
|
total: null
|
|
@@ -1628,12 +1736,10 @@ function measureResource(transport) {
|
|
|
1628
1736
|
}
|
|
1629
1737
|
/** Get one measure by id. */
|
|
1630
1738
|
async function get(id, options = {}) {
|
|
1631
|
-
await transport.require("measure.get", options);
|
|
1632
1739
|
return transport.requestParsed(Measure, `/api/measure/${id}`, { ...options });
|
|
1633
1740
|
}
|
|
1634
1741
|
/** Create a measure — a saved aggregation tied to a table — from a full measure body. */
|
|
1635
1742
|
async function create(params, options = {}) {
|
|
1636
|
-
await transport.require("measure.create", options);
|
|
1637
1743
|
return transport.requestParsed(Measure, "/api/measure", {
|
|
1638
1744
|
...options,
|
|
1639
1745
|
method: "POST",
|
|
@@ -1645,7 +1751,6 @@ function measureResource(transport) {
|
|
|
1645
1751
|
* required and lands in the audit log.
|
|
1646
1752
|
*/
|
|
1647
1753
|
async function update(id, params, options = {}) {
|
|
1648
|
-
await transport.require("measure.update", options);
|
|
1649
1754
|
return transport.requestParsed(Measure, `/api/measure/${id}`, {
|
|
1650
1755
|
...options,
|
|
1651
1756
|
method: "PUT",
|
|
@@ -1657,18 +1762,17 @@ function measureResource(transport) {
|
|
|
1657
1762
|
* so it carries the same required `revision_message`.
|
|
1658
1763
|
*/
|
|
1659
1764
|
async function archive(id, params, options = {}) {
|
|
1660
|
-
await transport.require("measure.archive", options);
|
|
1661
1765
|
return update(id, {
|
|
1662
1766
|
archived: true,
|
|
1663
1767
|
revision_message: params.revision_message
|
|
1664
1768
|
}, options);
|
|
1665
1769
|
}
|
|
1666
1770
|
return {
|
|
1667
|
-
list,
|
|
1668
|
-
get,
|
|
1669
|
-
create,
|
|
1670
|
-
update,
|
|
1671
|
-
archive
|
|
1771
|
+
list: explain("list", list),
|
|
1772
|
+
get: explain("get", get),
|
|
1773
|
+
create: explain("create", create),
|
|
1774
|
+
update: explain("update", update),
|
|
1775
|
+
archive: explain("archive", archive)
|
|
1672
1776
|
};
|
|
1673
1777
|
}
|
|
1674
1778
|
//#endregion
|
|
@@ -1809,12 +1913,12 @@ const MetricDimensionListing = z.object({
|
|
|
1809
1913
|
//#endregion
|
|
1810
1914
|
//#region ../client/src/resources/metric.ts
|
|
1811
1915
|
function metricResource(transport) {
|
|
1916
|
+
const { explain } = explainer(transport, "metric");
|
|
1812
1917
|
/**
|
|
1813
1918
|
* Run a metric definition and return the query result envelope: one metric or measure, or an
|
|
1814
1919
|
* arithmetic expression over several, each leaf filtered and projected on its own `lib/uuid`.
|
|
1815
1920
|
*/
|
|
1816
1921
|
async function query(definition, options = {}) {
|
|
1817
|
-
await transport.require("metric.query", options);
|
|
1818
1922
|
return transport.requestParsed(CardQueryResult, "/api/metric/dataset", {
|
|
1819
1923
|
...options,
|
|
1820
1924
|
method: "POST",
|
|
@@ -1826,7 +1930,6 @@ function metricResource(transport) {
|
|
|
1826
1930
|
* Takes the same definition `query` does.
|
|
1827
1931
|
*/
|
|
1828
1932
|
async function breakoutValues(definition, options = {}) {
|
|
1829
|
-
await transport.require("metric.breakoutValues", options);
|
|
1830
1933
|
return transport.requestParsed(MetricBreakoutValues, "/api/metric/breakout-values", {
|
|
1831
1934
|
...options,
|
|
1832
1935
|
method: "POST",
|
|
@@ -1838,7 +1941,6 @@ function metricResource(transport) {
|
|
|
1838
1941
|
* add, grouped by source table. `query` filters both by a name substring.
|
|
1839
1942
|
*/
|
|
1840
1943
|
async function dimensions(id, params = {}, options = {}) {
|
|
1841
|
-
await transport.require("metric.dimensions", options);
|
|
1842
1944
|
return transport.requestParsed(MetricDimensionListing, `/api/metric/${id}/dimension`, {
|
|
1843
1945
|
...options,
|
|
1844
1946
|
query: {
|
|
@@ -1849,14 +1951,15 @@ function metricResource(transport) {
|
|
|
1849
1951
|
});
|
|
1850
1952
|
}
|
|
1851
1953
|
return {
|
|
1852
|
-
query,
|
|
1853
|
-
breakoutValues,
|
|
1854
|
-
dimensions
|
|
1954
|
+
query: explain("query", query),
|
|
1955
|
+
breakoutValues: explain("breakoutValues", breakoutValues),
|
|
1956
|
+
dimensions: explain("dimensions", dimensions)
|
|
1855
1957
|
};
|
|
1856
1958
|
}
|
|
1857
1959
|
//#endregion
|
|
1858
1960
|
//#region ../client/src/resources/moderation-review.ts
|
|
1859
1961
|
function moderationReviewResource(transport) {
|
|
1962
|
+
const { explain } = explainer(transport, "moderationReview");
|
|
1860
1963
|
/**
|
|
1861
1964
|
* Create a moderation review on a card or dashboard. `status: "verified"` marks the item
|
|
1862
1965
|
* verified, `null` or an absent status records a note that leaves it unverified. The new review
|
|
@@ -1864,7 +1967,6 @@ function moderationReviewResource(transport) {
|
|
|
1864
1967
|
* A body that does not match `ModerationReviewCreateInput` is refused before any request.
|
|
1865
1968
|
*/
|
|
1866
1969
|
async function create(params, options = {}) {
|
|
1867
|
-
await transport.require("moderationReview.create", options);
|
|
1868
1970
|
const body = parseRequestBody(ModerationReviewCreateInput, params, "moderation review");
|
|
1869
1971
|
return transport.requestParsed(ModerationReview, "/api/moderation-review", {
|
|
1870
1972
|
...options,
|
|
@@ -1872,7 +1974,7 @@ function moderationReviewResource(transport) {
|
|
|
1872
1974
|
body
|
|
1873
1975
|
});
|
|
1874
1976
|
}
|
|
1875
|
-
return { create };
|
|
1977
|
+
return { create: explain("create", create) };
|
|
1876
1978
|
}
|
|
1877
1979
|
//#endregion
|
|
1878
1980
|
//#region ../client/src/resources/notification.ts
|
|
@@ -1903,7 +2005,6 @@ function notificationResource(transport) {
|
|
|
1903
2005
|
* the request pins the card payload type.
|
|
1904
2006
|
*/
|
|
1905
2007
|
async function list(params = {}, options = {}) {
|
|
1906
|
-
await transport.require("notification.list", options);
|
|
1907
2008
|
return {
|
|
1908
2009
|
data: await transport.requestParsed(NotificationApiList, "/api/notification", {
|
|
1909
2010
|
...options,
|
|
@@ -1920,12 +2021,10 @@ function notificationResource(transport) {
|
|
|
1920
2021
|
}
|
|
1921
2022
|
/** Get one question alert by id, refusing an id that names a system-event notification. */
|
|
1922
2023
|
async function get(id, options = {}) {
|
|
1923
|
-
await transport.require("notification.get", options);
|
|
1924
2024
|
return assertCardAlert(await transport.requestParsed(Notification, `/api/notification/${id}`, { ...options }));
|
|
1925
2025
|
}
|
|
1926
2026
|
/** Create a question alert — its card payload, schedules and handlers — from a full body. */
|
|
1927
2027
|
async function create(params, options = {}) {
|
|
1928
|
-
await transport.require("notification.create", options);
|
|
1929
2028
|
return transport.requestParsed(Notification, "/api/notification", {
|
|
1930
2029
|
...options,
|
|
1931
2030
|
method: "POST",
|
|
@@ -1934,7 +2033,6 @@ function notificationResource(transport) {
|
|
|
1934
2033
|
}
|
|
1935
2034
|
/** Update a question alert by id, merging the patch over the stored notification. */
|
|
1936
2035
|
async function update(id, params, options = {}) {
|
|
1937
|
-
await transport.require("notification.update", options);
|
|
1938
2036
|
const current = await get(id, options);
|
|
1939
2037
|
return transport.requestParsed(Notification, `/api/notification/${id}`, {
|
|
1940
2038
|
...options,
|
|
@@ -1944,7 +2042,6 @@ function notificationResource(transport) {
|
|
|
1944
2042
|
}
|
|
1945
2043
|
/** Archive a question alert by id, stopping every delivery. Metabase models this as an update. */
|
|
1946
2044
|
async function archive(id, options = {}) {
|
|
1947
|
-
await transport.require("notification.archive", options);
|
|
1948
2045
|
return update(id, { active: false }, options);
|
|
1949
2046
|
}
|
|
1950
2047
|
/**
|
|
@@ -1952,7 +2049,6 @@ function notificationResource(transport) {
|
|
|
1952
2049
|
* body.
|
|
1953
2050
|
*/
|
|
1954
2051
|
async function send(id, options = {}) {
|
|
1955
|
-
await transport.require("notification.send", options);
|
|
1956
2052
|
await get(id, options);
|
|
1957
2053
|
await transport.requestRaw(`/api/notification/${id}/send`, {
|
|
1958
2054
|
...options,
|
|
@@ -2012,7 +2108,6 @@ function permissionResource(transport) {
|
|
|
2012
2108
|
* one database is the question.
|
|
2013
2109
|
*/
|
|
2014
2110
|
async function graph(options = {}) {
|
|
2015
|
-
await transport.require("permission.graph", options);
|
|
2016
2111
|
return transport.requestParsed(PermissionsGraph, "/api/permissions/graph", { ...options });
|
|
2017
2112
|
}
|
|
2018
2113
|
/**
|
|
@@ -2021,12 +2116,10 @@ function permissionResource(transport) {
|
|
|
2021
2116
|
* group up under the database and read `view-data` and `create-queries` down to the table id.
|
|
2022
2117
|
*/
|
|
2023
2118
|
async function databaseGraph(id, options = {}) {
|
|
2024
|
-
await transport.require("permission.databaseGraph", options);
|
|
2025
2119
|
return transport.requestParsed(PermissionsGraph, `/api/permissions/graph/db/${id}`, { ...options });
|
|
2026
2120
|
}
|
|
2027
2121
|
/** Get the data permissions of one group on every database. Admin only. */
|
|
2028
2122
|
async function groupGraph(id, options = {}) {
|
|
2029
|
-
await transport.require("permission.groupGraph", options);
|
|
2030
2123
|
return transport.requestParsed(PermissionsGraph, `/api/permissions/graph/group/${id}`, { ...options });
|
|
2031
2124
|
}
|
|
2032
2125
|
return {
|
|
@@ -2048,7 +2141,6 @@ function mergePulseUpdate(current, patch) {
|
|
|
2048
2141
|
function pulseResource(transport) {
|
|
2049
2142
|
/** List dashboard subscriptions, optionally narrowed to one dashboard or to the archived ones. */
|
|
2050
2143
|
async function list(params = {}, options = {}) {
|
|
2051
|
-
await transport.require("pulse.list", options);
|
|
2052
2144
|
return {
|
|
2053
2145
|
data: await transport.requestParsed(PulseApiList, "/api/pulse", {
|
|
2054
2146
|
...options,
|
|
@@ -2062,21 +2154,27 @@ function pulseResource(transport) {
|
|
|
2062
2154
|
}
|
|
2063
2155
|
/** Get one dashboard subscription by id. */
|
|
2064
2156
|
async function get(id, options = {}) {
|
|
2065
|
-
await transport.require("pulse.get", options);
|
|
2066
2157
|
return transport.requestParsed(Pulse, `/api/pulse/${id}`, { ...options });
|
|
2067
2158
|
}
|
|
2068
|
-
/**
|
|
2159
|
+
/**
|
|
2160
|
+
* Create a dashboard subscription — its cards and its delivery channels — from a full body.
|
|
2161
|
+
* Non-empty `parameters` need the `dashboard_subscription_filters` premium feature, and are
|
|
2162
|
+
* refused before the wire without it.
|
|
2163
|
+
*/
|
|
2069
2164
|
async function create(params, options = {}) {
|
|
2070
|
-
await
|
|
2165
|
+
await requireSubscriptionFilters(params.parameters, options);
|
|
2071
2166
|
return transport.requestParsed(Pulse, "/api/pulse", {
|
|
2072
2167
|
...options,
|
|
2073
2168
|
method: "POST",
|
|
2074
2169
|
body: params
|
|
2075
2170
|
});
|
|
2076
2171
|
}
|
|
2077
|
-
/**
|
|
2172
|
+
/**
|
|
2173
|
+
* Update a dashboard subscription by id, carrying the server-defaulted flags forward. Non-empty
|
|
2174
|
+
* `parameters` are refused as on `create`.
|
|
2175
|
+
*/
|
|
2078
2176
|
async function update(id, params, options = {}) {
|
|
2079
|
-
await
|
|
2177
|
+
await requireSubscriptionFilters(params.parameters, options);
|
|
2080
2178
|
const current = await get(id, options);
|
|
2081
2179
|
return transport.requestParsed(Pulse, `/api/pulse/${id}`, {
|
|
2082
2180
|
...options,
|
|
@@ -2089,9 +2187,11 @@ function pulseResource(transport) {
|
|
|
2089
2187
|
* update, and disables each of the subscription's channels as a side effect.
|
|
2090
2188
|
*/
|
|
2091
2189
|
async function archive(id, options = {}) {
|
|
2092
|
-
await transport.require("pulse.archive", options);
|
|
2093
2190
|
return update(id, { archived: true }, options);
|
|
2094
2191
|
}
|
|
2192
|
+
async function requireSubscriptionFilters(parameters, options) {
|
|
2193
|
+
if (parameters !== void 0 && parameters.length > 0) await transport.requireFeatures(["dashboardSubscriptionFilters"], options);
|
|
2194
|
+
}
|
|
2095
2195
|
return {
|
|
2096
2196
|
list,
|
|
2097
2197
|
get,
|
|
@@ -2194,6 +2294,7 @@ const ReplacementRunStarted = z.object({ run_id: z.number().int().positive() });
|
|
|
2194
2294
|
const ReplacementApiRunList = z.array(ReplacementRun);
|
|
2195
2295
|
const CancelRunResponse = z.object({ success: z.literal(true) });
|
|
2196
2296
|
function replacementResource(transport) {
|
|
2297
|
+
const { explain } = explainer(transport, "replacement");
|
|
2197
2298
|
/**
|
|
2198
2299
|
* Check whether every usage of a source entity could be rewritten to read a target entity
|
|
2199
2300
|
* instead: the two must share a database, the target must not depend on the source, every source
|
|
@@ -2201,7 +2302,6 @@ function replacementResource(transport) {
|
|
|
2201
2302
|
* implicitly or sandboxed. Admins only.
|
|
2202
2303
|
*/
|
|
2203
2304
|
async function checkReplaceSource(params, options = {}) {
|
|
2204
|
-
await transport.require("replacement.checkReplaceSource", options);
|
|
2205
2305
|
return transport.requestParsed(ReplacementCheck, "/api/ee/replacement/check-replace-source", {
|
|
2206
2306
|
...options,
|
|
2207
2307
|
method: "POST",
|
|
@@ -2214,7 +2314,6 @@ function replacementResource(transport) {
|
|
|
2214
2314
|
* while another replacement is running. Admins only.
|
|
2215
2315
|
*/
|
|
2216
2316
|
async function replaceSource(params, options = {}) {
|
|
2217
|
-
await transport.require("replacement.replaceSource", options);
|
|
2218
2317
|
return transport.requestParsed(ReplacementRunStarted, "/api/ee/replacement/replace-source", {
|
|
2219
2318
|
...options,
|
|
2220
2319
|
method: "POST",
|
|
@@ -2229,7 +2328,6 @@ function replacementResource(transport) {
|
|
|
2229
2328
|
* replacement is running. Admins only.
|
|
2230
2329
|
*/
|
|
2231
2330
|
async function replaceModelWithTransform(params, options = {}) {
|
|
2232
|
-
await transport.require("replacement.replaceModelWithTransform", options);
|
|
2233
2331
|
return transport.requestParsed(ReplacementRunStarted, "/api/ee/replacement/replace-model-with-transform", {
|
|
2234
2332
|
...options,
|
|
2235
2333
|
method: "POST",
|
|
@@ -2241,7 +2339,6 @@ function replacementResource(transport) {
|
|
|
2241
2339
|
* `false` only the ended ones. Admins only.
|
|
2242
2340
|
*/
|
|
2243
2341
|
async function listRuns(params = {}, options = {}) {
|
|
2244
|
-
await transport.require("replacement.listRuns", options);
|
|
2245
2342
|
return {
|
|
2246
2343
|
data: await transport.requestParsed(ReplacementApiRunList, "/api/ee/replacement/runs", {
|
|
2247
2344
|
...options,
|
|
@@ -2252,24 +2349,22 @@ function replacementResource(transport) {
|
|
|
2252
2349
|
}
|
|
2253
2350
|
/** Get one replacement run by id, with its status and progress. Admins only. */
|
|
2254
2351
|
async function getRun(id, options = {}) {
|
|
2255
|
-
await transport.require("replacement.getRun", options);
|
|
2256
2352
|
return transport.requestParsed(ReplacementRun, `/api/ee/replacement/runs/${id}`, { ...options });
|
|
2257
2353
|
}
|
|
2258
2354
|
/** Cancel the replacement in progress. Refuses with a 409 once the run has ended. Admins only. */
|
|
2259
2355
|
async function cancelRun(id, options = {}) {
|
|
2260
|
-
await transport.require("replacement.cancelRun", options);
|
|
2261
2356
|
await transport.requestParsed(CancelRunResponse, `/api/ee/replacement/runs/${id}/cancel`, {
|
|
2262
2357
|
...options,
|
|
2263
2358
|
method: "POST"
|
|
2264
2359
|
});
|
|
2265
2360
|
}
|
|
2266
2361
|
return {
|
|
2267
|
-
checkReplaceSource,
|
|
2268
|
-
replaceSource,
|
|
2269
|
-
replaceModelWithTransform,
|
|
2270
|
-
listRuns,
|
|
2271
|
-
getRun,
|
|
2272
|
-
cancelRun
|
|
2362
|
+
checkReplaceSource: explain("checkReplaceSource", checkReplaceSource),
|
|
2363
|
+
replaceSource: explain("replaceSource", replaceSource),
|
|
2364
|
+
replaceModelWithTransform: explain("replaceModelWithTransform", replaceModelWithTransform),
|
|
2365
|
+
listRuns: explain("listRuns", listRuns),
|
|
2366
|
+
getRun: explain("getRun", getRun),
|
|
2367
|
+
cancelRun: explain("cancelRun", cancelRun)
|
|
2273
2368
|
};
|
|
2274
2369
|
}
|
|
2275
2370
|
//#endregion
|
|
@@ -2280,7 +2375,8 @@ const RevisionEntity = z.enum([
|
|
|
2280
2375
|
"document",
|
|
2281
2376
|
"measure",
|
|
2282
2377
|
"segment",
|
|
2283
|
-
"transform"
|
|
2378
|
+
"transform",
|
|
2379
|
+
"transform-test"
|
|
2284
2380
|
]);
|
|
2285
2381
|
const RevisionUser = z.object({
|
|
2286
2382
|
id: z.number().int(),
|
|
@@ -2344,17 +2440,20 @@ const RevisionApiRevert = z.union([Revision.transform((revision) => ({
|
|
|
2344
2440
|
}))]);
|
|
2345
2441
|
const ENTITY_FEATURES = {
|
|
2346
2442
|
measure: "measures",
|
|
2347
|
-
transform: "transforms"
|
|
2443
|
+
transform: "transforms",
|
|
2444
|
+
"transform-test": "transformTests"
|
|
2348
2445
|
};
|
|
2349
2446
|
function entityFeatures(entity) {
|
|
2350
2447
|
const feature = ENTITY_FEATURES[entity];
|
|
2351
|
-
return feature === void 0 ? [] : [
|
|
2448
|
+
return feature === void 0 ? [] : [{
|
|
2449
|
+
feature,
|
|
2450
|
+
fields: ["entity"]
|
|
2451
|
+
}];
|
|
2352
2452
|
}
|
|
2353
2453
|
function revisionResource(transport) {
|
|
2454
|
+
const { explain } = explainer(transport, "revision");
|
|
2354
2455
|
/** List the revisions of an entity the caller may read, newest first, each with its diff. */
|
|
2355
2456
|
async function list(entity, id, options = {}) {
|
|
2356
|
-
await transport.require("revision.list", options);
|
|
2357
|
-
await transport.requireFeatures(entityFeatures(entity), options);
|
|
2358
2457
|
return {
|
|
2359
2458
|
data: await transport.requestParsed(RevisionApiList, `/api/revision/${entity}/${id}`, { ...options }),
|
|
2360
2459
|
total: null
|
|
@@ -2365,8 +2464,6 @@ function revisionResource(transport) {
|
|
|
2365
2464
|
* card the permission to run the query being restored.
|
|
2366
2465
|
*/
|
|
2367
2466
|
async function revert(params, options = {}) {
|
|
2368
|
-
await transport.require("revision.revert", options);
|
|
2369
|
-
await transport.requireFeatures(entityFeatures(params.entity), options);
|
|
2370
2467
|
return transport.requestParsed(RevisionApiRevert, "/api/revision/revert", {
|
|
2371
2468
|
...options,
|
|
2372
2469
|
method: "POST",
|
|
@@ -2374,8 +2471,8 @@ function revisionResource(transport) {
|
|
|
2374
2471
|
});
|
|
2375
2472
|
}
|
|
2376
2473
|
return {
|
|
2377
|
-
list,
|
|
2378
|
-
revert
|
|
2474
|
+
list: explain("list", list, entityFeatures),
|
|
2475
|
+
revert: explain("revert", revert, (params) => entityFeatures(params.entity))
|
|
2379
2476
|
};
|
|
2380
2477
|
}
|
|
2381
2478
|
//#endregion
|
|
@@ -2384,7 +2481,14 @@ const SearchApiResponse = z.object({
|
|
|
2384
2481
|
data: z.array(SearchResult),
|
|
2385
2482
|
total: z.number().int().nonnegative()
|
|
2386
2483
|
}).loose();
|
|
2484
|
+
function verifiedFeatures(params = {}) {
|
|
2485
|
+
return params.verified === true ? [{
|
|
2486
|
+
feature: "contentVerification",
|
|
2487
|
+
fields: ["verified"]
|
|
2488
|
+
}] : [];
|
|
2489
|
+
}
|
|
2387
2490
|
function searchResource(transport) {
|
|
2491
|
+
const { explain } = explainer(transport, "search");
|
|
2388
2492
|
/**
|
|
2389
2493
|
* Search over the instance's content, ranked against `q`. `models` narrows which kinds of entity
|
|
2390
2494
|
* may match, `archived` swaps the active set for the archived one, `table_db_id` restricts to
|
|
@@ -2394,9 +2498,12 @@ function searchResource(transport) {
|
|
|
2394
2498
|
* actions, transforms), `include_metadata` attaches each card's `result_metadata`,
|
|
2395
2499
|
* `include_dashboard_questions` also matches questions saved into a dashboard (excluded by
|
|
2396
2500
|
* default), and `limit`/`offset` are the window the server applies before ranking hydration.
|
|
2501
|
+
* `verified` needs the `content_verification` premium feature, and is refused before the wire
|
|
2502
|
+
* without it: a server granting neither it nor `official_collections` refuses the filter, and one
|
|
2503
|
+
* granting `official_collections` alone takes it without applying it.
|
|
2397
2504
|
*/
|
|
2398
2505
|
async function query(params = {}, options = {}) {
|
|
2399
|
-
await transport.
|
|
2506
|
+
await transport.requireFeatures(verifiedFeatures(params).map((requirement) => requirement.feature), options);
|
|
2400
2507
|
const response = await transport.requestParsed(SearchApiResponse, "/api/search", {
|
|
2401
2508
|
...options,
|
|
2402
2509
|
query: {
|
|
@@ -2419,7 +2526,7 @@ function searchResource(transport) {
|
|
|
2419
2526
|
total: response.total
|
|
2420
2527
|
};
|
|
2421
2528
|
}
|
|
2422
|
-
return { query };
|
|
2529
|
+
return { query: explain("query", query, verifiedFeatures) };
|
|
2423
2530
|
}
|
|
2424
2531
|
//#endregion
|
|
2425
2532
|
//#region ../client/src/resources/segment.ts
|
|
@@ -2427,7 +2534,6 @@ const SegmentApiList = z.array(Segment);
|
|
|
2427
2534
|
function segmentResource(transport) {
|
|
2428
2535
|
/** List every segment the caller can see. */
|
|
2429
2536
|
async function list(options = {}) {
|
|
2430
|
-
await transport.require("segment.list", options);
|
|
2431
2537
|
return {
|
|
2432
2538
|
data: await transport.requestParsed(SegmentApiList, "/api/segment", { ...options }),
|
|
2433
2539
|
total: null
|
|
@@ -2435,12 +2541,10 @@ function segmentResource(transport) {
|
|
|
2435
2541
|
}
|
|
2436
2542
|
/** Get one segment by id. */
|
|
2437
2543
|
async function get(id, options = {}) {
|
|
2438
|
-
await transport.require("segment.get", options);
|
|
2439
2544
|
return transport.requestParsed(Segment, `/api/segment/${id}`, { ...options });
|
|
2440
2545
|
}
|
|
2441
2546
|
/** Create a segment — a reusable row filter over one table — from a full segment body. */
|
|
2442
2547
|
async function create(params, options = {}) {
|
|
2443
|
-
await transport.require("segment.create", options);
|
|
2444
2548
|
return transport.requestParsed(Segment, "/api/segment", {
|
|
2445
2549
|
...options,
|
|
2446
2550
|
method: "POST",
|
|
@@ -2452,7 +2556,6 @@ function segmentResource(transport) {
|
|
|
2452
2556
|
* required: Metabase records every segment change in its revision history.
|
|
2453
2557
|
*/
|
|
2454
2558
|
async function update(id, params, options = {}) {
|
|
2455
|
-
await transport.require("segment.update", options);
|
|
2456
2559
|
return transport.requestParsed(Segment, `/api/segment/${id}`, {
|
|
2457
2560
|
...options,
|
|
2458
2561
|
method: "PUT",
|
|
@@ -2464,7 +2567,6 @@ function segmentResource(transport) {
|
|
|
2464
2567
|
* same required `revision_message`.
|
|
2465
2568
|
*/
|
|
2466
2569
|
async function archive(id, params, options = {}) {
|
|
2467
|
-
await transport.require("segment.archive", options);
|
|
2468
2570
|
return update(id, {
|
|
2469
2571
|
archived: true,
|
|
2470
2572
|
revision_message: params.revision_message
|
|
@@ -2489,7 +2591,6 @@ function rethrowSettingError(error, key) {
|
|
|
2489
2591
|
function settingResource(transport) {
|
|
2490
2592
|
/** Get all settings and their values, with descriptions and environment-variable names. */
|
|
2491
2593
|
async function list(options = {}) {
|
|
2492
|
-
await transport.require("setting.list", options);
|
|
2493
2594
|
return {
|
|
2494
2595
|
data: await transport.requestParsed(SettingApiList, "/api/setting", { ...options }),
|
|
2495
2596
|
total: null
|
|
@@ -2500,12 +2601,10 @@ function settingResource(transport) {
|
|
|
2500
2601
|
* reaches the caller as `null`; a string-typed setting answers bare text rather than JSON.
|
|
2501
2602
|
*/
|
|
2502
2603
|
async function get(key, options = {}) {
|
|
2503
|
-
await transport.require("setting.get", options);
|
|
2504
2604
|
return fetchOptionalParsed(transport, `/api/setting/${encodeURIComponent(key)}`, z.unknown(), options).catch((error) => rethrowSettingError(error, key));
|
|
2505
2605
|
}
|
|
2506
2606
|
/** Set a single setting's value by key. The endpoint answers 204 with no body. */
|
|
2507
2607
|
async function set(key, value, options = {}) {
|
|
2508
|
-
await transport.require("setting.set", options);
|
|
2509
2608
|
await transport.requestRaw(`/api/setting/${encodeURIComponent(key)}`, {
|
|
2510
2609
|
...options,
|
|
2511
2610
|
method: "PUT",
|
|
@@ -2527,7 +2626,6 @@ function setupResource(transport) {
|
|
|
2527
2626
|
* applying the instance preferences. The setup token is single-use.
|
|
2528
2627
|
*/
|
|
2529
2628
|
async function create(params, options = {}) {
|
|
2530
|
-
await transport.require("setup.create", options);
|
|
2531
2629
|
return transport.requestParsed(SetupResult, "/api/setup", {
|
|
2532
2630
|
...options,
|
|
2533
2631
|
method: "POST",
|
|
@@ -2542,7 +2640,6 @@ const SnippetApiList = z.array(Snippet);
|
|
|
2542
2640
|
function snippetResource(transport) {
|
|
2543
2641
|
/** List native query snippets. `archived` swaps the listing to archived snippets. */
|
|
2544
2642
|
async function list(params = {}, options = {}) {
|
|
2545
|
-
await transport.require("snippet.list", options);
|
|
2546
2643
|
return {
|
|
2547
2644
|
data: await transport.requestParsed(SnippetApiList, "/api/native-query-snippet", {
|
|
2548
2645
|
...options,
|
|
@@ -2553,12 +2650,10 @@ function snippetResource(transport) {
|
|
|
2553
2650
|
}
|
|
2554
2651
|
/** Get one native query snippet by id. */
|
|
2555
2652
|
async function get(id, options = {}) {
|
|
2556
|
-
await transport.require("snippet.get", options);
|
|
2557
2653
|
return transport.requestParsed(Snippet, `/api/native-query-snippet/${id}`, { ...options });
|
|
2558
2654
|
}
|
|
2559
2655
|
/** Create a native query snippet from a name, its SQL fragment, and an optional collection. */
|
|
2560
2656
|
async function create(params, options = {}) {
|
|
2561
|
-
await transport.require("snippet.create", options);
|
|
2562
2657
|
return transport.requestParsed(Snippet, "/api/native-query-snippet", {
|
|
2563
2658
|
...options,
|
|
2564
2659
|
method: "POST",
|
|
@@ -2567,7 +2662,6 @@ function snippetResource(transport) {
|
|
|
2567
2662
|
}
|
|
2568
2663
|
/** Update a native query snippet by id, patching only the fields the body carries. */
|
|
2569
2664
|
async function update(id, params, options = {}) {
|
|
2570
|
-
await transport.require("snippet.update", options);
|
|
2571
2665
|
return transport.requestParsed(Snippet, `/api/native-query-snippet/${id}`, {
|
|
2572
2666
|
...options,
|
|
2573
2667
|
method: "PUT",
|
|
@@ -2579,7 +2673,6 @@ function snippetResource(transport) {
|
|
|
2579
2673
|
* own endpoint.
|
|
2580
2674
|
*/
|
|
2581
2675
|
async function archive(id, options = {}) {
|
|
2582
|
-
await transport.require("snippet.archive", options);
|
|
2583
2676
|
return update(id, { archived: true }, options);
|
|
2584
2677
|
}
|
|
2585
2678
|
return {
|
|
@@ -2609,13 +2702,23 @@ const LIST_PARAM_FEATURES = [
|
|
|
2609
2702
|
["published-only", "tableListPublishedFilter"]
|
|
2610
2703
|
];
|
|
2611
2704
|
function listParamFeatures(params) {
|
|
2612
|
-
return LIST_PARAM_FEATURES.filter(([param]) => params[param]
|
|
2705
|
+
return LIST_PARAM_FEATURES.filter(([param]) => params[param] === true).map(([, feature]) => feature);
|
|
2706
|
+
}
|
|
2707
|
+
function dataLayerFeatures(value, field) {
|
|
2708
|
+
return isTierName(value) ? [{
|
|
2709
|
+
feature: "tableDataLayerTiers",
|
|
2710
|
+
fields: [field]
|
|
2711
|
+
}] : [];
|
|
2712
|
+
}
|
|
2713
|
+
function isTierName(value) {
|
|
2714
|
+
return value !== null && value !== void 0 && TableDataLayerTier.safeParse(value).success;
|
|
2613
2715
|
}
|
|
2614
2716
|
const UPLOAD_UPDATE_PATHS = {
|
|
2615
2717
|
append: "append-csv",
|
|
2616
2718
|
replace: "replace-csv"
|
|
2617
2719
|
};
|
|
2618
2720
|
function tableResource(transport) {
|
|
2721
|
+
const { explain } = explainer(transport, "table");
|
|
2619
2722
|
/**
|
|
2620
2723
|
* List every table the caller can see, across all databases. `term` matches names and display
|
|
2621
2724
|
* names; `can-query` and `can-write` keep only the tables the caller may query or edit the
|
|
@@ -2623,9 +2726,7 @@ function tableResource(transport) {
|
|
|
2623
2726
|
* tables nothing depends on; `published-only` keeps tables published to the library.
|
|
2624
2727
|
*/
|
|
2625
2728
|
async function list(params = {}, options = {}) {
|
|
2626
|
-
await transport.require("table.list", options);
|
|
2627
2729
|
await transport.requireFeatures(listParamFeatures(params), options);
|
|
2628
|
-
await requireDataLayer(params["data-layer"], options);
|
|
2629
2730
|
return {
|
|
2630
2731
|
data: await transport.requestParsed(TableApiList, "/api/table", {
|
|
2631
2732
|
...options,
|
|
@@ -2649,31 +2750,30 @@ function tableResource(transport) {
|
|
|
2649
2750
|
}
|
|
2650
2751
|
/** Get one table by id, without its fields. */
|
|
2651
2752
|
async function get(id, options = {}) {
|
|
2652
|
-
await transport.require("table.get", options);
|
|
2653
2753
|
return transport.requestParsed(Table, `/api/table/${id}`, { ...options });
|
|
2654
2754
|
}
|
|
2655
2755
|
/** Update a table by id, patching only the fields the body carries. */
|
|
2656
2756
|
async function update(id, params, options = {}) {
|
|
2657
|
-
await
|
|
2658
|
-
await
|
|
2659
|
-
|
|
2757
|
+
await refuseRewrittenDataLayer(params.data_layer, options);
|
|
2758
|
+
await requireDataAuthorityNull(params.data_authority, options);
|
|
2759
|
+
const path = `/api/table/${id}`;
|
|
2760
|
+
const updated = await transport.requestParsed(Table, path, {
|
|
2660
2761
|
...options,
|
|
2661
2762
|
method: "PUT",
|
|
2662
2763
|
body: params
|
|
2663
2764
|
});
|
|
2765
|
+
assertCollectionWritten(path, params.collection_id, updated);
|
|
2766
|
+
return updated;
|
|
2664
2767
|
}
|
|
2665
|
-
async function
|
|
2666
|
-
if (value === null || value === void 0) return;
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
const { features } = await transport.server(options);
|
|
2672
|
-
if (features.tableDataLayerTiers) throw new ConfigError(`data_layer "${value}" is a medallion name; this server names a table's layer ${TableDataLayerTier.options.join(", ")}`);
|
|
2768
|
+
async function refuseRewrittenDataLayer(value, options) {
|
|
2769
|
+
if (value === null || value === void 0 || isTierName(value)) return;
|
|
2770
|
+
const profile = await transport.preflightServer(options);
|
|
2771
|
+
if (profile === null) return;
|
|
2772
|
+
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`);
|
|
2773
|
+
if (failure !== null) transport.refuseBeforeSending(new CapabilityError(failure));
|
|
2673
2774
|
}
|
|
2674
2775
|
/** Get a table by id with its fields hydrated — the metadata the query builder runs on. */
|
|
2675
2776
|
async function queryMetadata(id, options = {}) {
|
|
2676
|
-
await transport.require("table.queryMetadata", options);
|
|
2677
2777
|
return transport.requestParsed(TableQueryMetadata, `/api/table/${id}/query_metadata`, { ...options });
|
|
2678
2778
|
}
|
|
2679
2779
|
/**
|
|
@@ -2681,7 +2781,6 @@ function tableResource(transport) {
|
|
|
2681
2781
|
* fields answers no content, which is the same empty list.
|
|
2682
2782
|
*/
|
|
2683
2783
|
async function fks(id, options = {}) {
|
|
2684
|
-
await transport.require("table.fks", options);
|
|
2685
2784
|
return {
|
|
2686
2785
|
data: await fetchOptionalParsed(transport, `/api/table/${id}/fks`, TableApiFks, { ...options }) ?? [],
|
|
2687
2786
|
total: null
|
|
@@ -2693,7 +2792,6 @@ function tableResource(transport) {
|
|
|
2693
2792
|
* connect to is a 422.
|
|
2694
2793
|
*/
|
|
2695
2794
|
async function syncSchema(id, options = {}) {
|
|
2696
|
-
await transport.require("table.syncSchema", options);
|
|
2697
2795
|
return {
|
|
2698
2796
|
id,
|
|
2699
2797
|
status: (await transport.requestParsed(SyncSchemaResponse, `/api/table/${id}/sync_schema`, {
|
|
@@ -2707,7 +2805,6 @@ function tableResource(transport) {
|
|
|
2707
2805
|
* cached set. The scan runs after the call returns.
|
|
2708
2806
|
*/
|
|
2709
2807
|
async function rescanValues(id, options = {}) {
|
|
2710
|
-
await transport.require("table.rescanValues", options);
|
|
2711
2808
|
return {
|
|
2712
2809
|
id,
|
|
2713
2810
|
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/rescan_values`, {
|
|
@@ -2722,7 +2819,6 @@ function tableResource(transport) {
|
|
|
2722
2819
|
* time the field's values are read.
|
|
2723
2820
|
*/
|
|
2724
2821
|
async function discardValues(id, options = {}) {
|
|
2725
|
-
await transport.require("table.discardValues", options);
|
|
2726
2822
|
return {
|
|
2727
2823
|
id,
|
|
2728
2824
|
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/discard_values`, {
|
|
@@ -2737,7 +2833,6 @@ function tableResource(transport) {
|
|
|
2737
2833
|
* selectors matched a table.
|
|
2738
2834
|
*/
|
|
2739
2835
|
async function bulkEdit(params, options = {}) {
|
|
2740
|
-
await transport.require("table.bulkEdit", options);
|
|
2741
2836
|
await requireDataAuthorityNull(params.data_authority, options);
|
|
2742
2837
|
await transport.requestParsed(BulkEditResponse, BULK_EDIT_PATH, {
|
|
2743
2838
|
...options,
|
|
@@ -2771,7 +2866,6 @@ function tableResource(transport) {
|
|
|
2771
2866
|
* call is a 422; the syncs run after it returns.
|
|
2772
2867
|
*/
|
|
2773
2868
|
async function bulkSyncSchema(selectors, options = {}) {
|
|
2774
|
-
await transport.require("table.bulkSyncSchema", options);
|
|
2775
2869
|
return postSelection(BULK_SYNC_SCHEMA_PATH, selectors, options);
|
|
2776
2870
|
}
|
|
2777
2871
|
/**
|
|
@@ -2779,7 +2873,6 @@ function tableResource(transport) {
|
|
|
2779
2873
|
* are already cached. The scans run after the call returns.
|
|
2780
2874
|
*/
|
|
2781
2875
|
async function bulkRescanValues(selectors, options = {}) {
|
|
2782
|
-
await transport.require("table.bulkRescanValues", options);
|
|
2783
2876
|
return postSelection(BULK_RESCAN_VALUES_PATH, selectors, options);
|
|
2784
2877
|
}
|
|
2785
2878
|
/**
|
|
@@ -2787,7 +2880,6 @@ function tableResource(transport) {
|
|
|
2787
2880
|
* included. No scan recreates a discarded set.
|
|
2788
2881
|
*/
|
|
2789
2882
|
async function bulkDiscardValues(selectors, options = {}) {
|
|
2790
|
-
await transport.require("table.bulkDiscardValues", options);
|
|
2791
2883
|
return postSelection(BULK_DISCARD_VALUES_PATH, selectors, options);
|
|
2792
2884
|
}
|
|
2793
2885
|
async function updateFromCsv(id, action, file, options) {
|
|
@@ -2809,7 +2901,6 @@ function tableResource(transport) {
|
|
|
2809
2901
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
2810
2902
|
*/
|
|
2811
2903
|
async function appendCsv(id, file, options = {}) {
|
|
2812
|
-
await transport.require("table.appendCsv", options);
|
|
2813
2904
|
return updateFromCsv(id, "append", file, options);
|
|
2814
2905
|
}
|
|
2815
2906
|
/**
|
|
@@ -2819,37 +2910,53 @@ function tableResource(transport) {
|
|
|
2819
2910
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
2820
2911
|
*/
|
|
2821
2912
|
async function replaceCsv(id, file, options = {}) {
|
|
2822
|
-
await transport.require("table.replaceCsv", options);
|
|
2823
2913
|
return updateFromCsv(id, "replace", file, options);
|
|
2824
2914
|
}
|
|
2825
2915
|
return {
|
|
2826
|
-
list,
|
|
2916
|
+
list: explain("list", list, (params) => dataLayerFeatures(params?.["data-layer"], "data-layer")),
|
|
2827
2917
|
get,
|
|
2828
|
-
update,
|
|
2918
|
+
update: explain("update", update, (_id, params) => dataLayerFeatures(params.data_layer, "data_layer")),
|
|
2829
2919
|
queryMetadata,
|
|
2830
2920
|
fks,
|
|
2831
2921
|
syncSchema,
|
|
2832
2922
|
rescanValues,
|
|
2833
2923
|
discardValues,
|
|
2834
|
-
bulkEdit,
|
|
2835
|
-
bulkSyncSchema,
|
|
2836
|
-
bulkRescanValues,
|
|
2837
|
-
bulkDiscardValues,
|
|
2924
|
+
bulkEdit: explain("bulkEdit", bulkEdit),
|
|
2925
|
+
bulkSyncSchema: explain("bulkSyncSchema", bulkSyncSchema),
|
|
2926
|
+
bulkRescanValues: explain("bulkRescanValues", bulkRescanValues),
|
|
2927
|
+
bulkDiscardValues: explain("bulkDiscardValues", bulkDiscardValues),
|
|
2838
2928
|
appendCsv,
|
|
2839
2929
|
replaceCsv
|
|
2840
2930
|
};
|
|
2841
2931
|
}
|
|
2932
|
+
function assertCollectionWritten(path, requested, table) {
|
|
2933
|
+
if (requested === void 0 || table.collection_id === requested) return;
|
|
2934
|
+
const answered = table.collection_id === void 0 ? { kind: "unreported" } : {
|
|
2935
|
+
kind: "reported",
|
|
2936
|
+
value: table.collection_id
|
|
2937
|
+
};
|
|
2938
|
+
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`, {
|
|
2939
|
+
method: "PUT",
|
|
2940
|
+
path,
|
|
2941
|
+
field: "collection_id",
|
|
2942
|
+
requested,
|
|
2943
|
+
answered
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
function describeCollectionAnswer(answered) {
|
|
2947
|
+
if (answered.kind === "unreported") return "it answered the table without reporting a collection";
|
|
2948
|
+
if (answered.value === null) return "the table stays in no collection";
|
|
2949
|
+
return `the table stays in collection ${answered.value}`;
|
|
2950
|
+
}
|
|
2842
2951
|
//#endregion
|
|
2843
2952
|
//#region ../client/src/resources/timeline-event.ts
|
|
2844
2953
|
function timelineEventResource(transport) {
|
|
2845
2954
|
/** Get one timeline event by id. */
|
|
2846
2955
|
async function get(id, options = {}) {
|
|
2847
|
-
await transport.require("timelineEvent.get", options);
|
|
2848
2956
|
return transport.requestParsed(TimelineEvent, `/api/timeline-event/${id}`, { ...options });
|
|
2849
2957
|
}
|
|
2850
2958
|
/** Create a timeline event on an existing timeline. */
|
|
2851
2959
|
async function create(params, options = {}) {
|
|
2852
|
-
await transport.require("timelineEvent.create", options);
|
|
2853
2960
|
return transport.requestParsed(TimelineEvent, "/api/timeline-event", {
|
|
2854
2961
|
...options,
|
|
2855
2962
|
method: "POST",
|
|
@@ -2861,7 +2968,6 @@ function timelineEventResource(transport) {
|
|
|
2861
2968
|
* the event to another timeline.
|
|
2862
2969
|
*/
|
|
2863
2970
|
async function update(id, params, options = {}) {
|
|
2864
|
-
await transport.require("timelineEvent.update", options);
|
|
2865
2971
|
return transport.requestParsed(TimelineEvent, `/api/timeline-event/${id}`, {
|
|
2866
2972
|
...options,
|
|
2867
2973
|
method: "PUT",
|
|
@@ -2873,12 +2979,10 @@ function timelineEventResource(transport) {
|
|
|
2873
2979
|
* endpoint.
|
|
2874
2980
|
*/
|
|
2875
2981
|
async function archive(id, options = {}) {
|
|
2876
|
-
await transport.require("timelineEvent.archive", options);
|
|
2877
2982
|
return update(id, { archived: true }, options);
|
|
2878
2983
|
}
|
|
2879
2984
|
/** Permanently delete a timeline event by id. The server answers with no body. */
|
|
2880
2985
|
async function remove(id, options = {}) {
|
|
2881
|
-
await transport.require("timelineEvent.delete", options);
|
|
2882
2986
|
await transport.requestRaw(`/api/timeline-event/${id}`, {
|
|
2883
2987
|
...options,
|
|
2884
2988
|
method: "DELETE",
|
|
@@ -2900,7 +3004,6 @@ const TimelineWithEvents = Timeline.required({ events: true });
|
|
|
2900
3004
|
function timelineResource(transport) {
|
|
2901
3005
|
/** List timelines. `archived` swaps the active listing for the archived one. */
|
|
2902
3006
|
async function list(params = {}, options = {}) {
|
|
2903
|
-
await transport.require("timeline.list", options);
|
|
2904
3007
|
return {
|
|
2905
3008
|
data: await transport.requestParsed(TimelineApiList, "/api/timeline", {
|
|
2906
3009
|
...options,
|
|
@@ -2911,7 +3014,6 @@ function timelineResource(transport) {
|
|
|
2911
3014
|
}
|
|
2912
3015
|
/** Get one timeline by id, without its events. */
|
|
2913
3016
|
async function get(id, options = {}) {
|
|
2914
|
-
await transport.require("timeline.get", options);
|
|
2915
3017
|
return transport.requestParsed(Timeline, `/api/timeline/${id}`, { ...options });
|
|
2916
3018
|
}
|
|
2917
3019
|
/**
|
|
@@ -2919,7 +3021,6 @@ function timelineResource(transport) {
|
|
|
2919
3021
|
* timeline read hydrates them on request, and `archived` chooses which of them come back.
|
|
2920
3022
|
*/
|
|
2921
3023
|
async function events(id, params = {}, options = {}) {
|
|
2922
|
-
await transport.require("timeline.events", options);
|
|
2923
3024
|
return {
|
|
2924
3025
|
data: (await transport.requestParsed(TimelineWithEvents, `/api/timeline/${id}`, {
|
|
2925
3026
|
...options,
|
|
@@ -2933,7 +3034,6 @@ function timelineResource(transport) {
|
|
|
2933
3034
|
}
|
|
2934
3035
|
/** Create a timeline from a full timeline body. */
|
|
2935
3036
|
async function create(params, options = {}) {
|
|
2936
|
-
await transport.require("timeline.create", options);
|
|
2937
3037
|
return transport.requestParsed(Timeline, "/api/timeline", {
|
|
2938
3038
|
...options,
|
|
2939
3039
|
method: "POST",
|
|
@@ -2945,7 +3045,6 @@ function timelineResource(transport) {
|
|
|
2945
3045
|
* cascades to every event on the timeline.
|
|
2946
3046
|
*/
|
|
2947
3047
|
async function update(id, params, options = {}) {
|
|
2948
|
-
await transport.require("timeline.update", options);
|
|
2949
3048
|
return transport.requestParsed(Timeline, `/api/timeline/${id}`, {
|
|
2950
3049
|
...options,
|
|
2951
3050
|
method: "PUT",
|
|
@@ -2957,12 +3056,10 @@ function timelineResource(transport) {
|
|
|
2957
3056
|
* not its own endpoint.
|
|
2958
3057
|
*/
|
|
2959
3058
|
async function archive(id, options = {}) {
|
|
2960
|
-
await transport.require("timeline.archive", options);
|
|
2961
3059
|
return update(id, { archived: true }, options);
|
|
2962
3060
|
}
|
|
2963
3061
|
/** Permanently delete a timeline and all its events by id. The server answers with no body. */
|
|
2964
3062
|
async function remove(id, options = {}) {
|
|
2965
|
-
await transport.require("timeline.delete", options);
|
|
2966
3063
|
await transport.requestRaw(`/api/timeline/${id}`, {
|
|
2967
3064
|
...options,
|
|
2968
3065
|
method: "DELETE",
|
|
@@ -2999,12 +3096,12 @@ TransformMemberRun.pick({
|
|
|
2999
3096
|
//#region ../client/src/resources/transform-dag-run.ts
|
|
3000
3097
|
const TransformMemberRunList = z.array(TransformMemberRun);
|
|
3001
3098
|
function transformDagRunResource(transport) {
|
|
3099
|
+
const { explain } = explainer(transport, "transformDagRun");
|
|
3002
3100
|
/**
|
|
3003
3101
|
* List the transform runs a DAG run coordinated so far. A member run is inserted when its
|
|
3004
3102
|
* transform starts, so the list grows while the DAG run is in progress.
|
|
3005
3103
|
*/
|
|
3006
3104
|
async function transformRuns(runId, options = {}) {
|
|
3007
|
-
await transport.require("transformDagRun.transformRuns", options);
|
|
3008
3105
|
return {
|
|
3009
3106
|
data: await transport.requestParsed(TransformMemberRunList, `/api/transform-dag-run/${runId}/transform-runs`, { ...options }),
|
|
3010
3107
|
total: null
|
|
@@ -3012,7 +3109,6 @@ function transformDagRunResource(transport) {
|
|
|
3012
3109
|
}
|
|
3013
3110
|
/** Cancel an in-progress DAG run and request cancellation of its still-running transforms. */
|
|
3014
3111
|
async function cancel(runId, options = {}) {
|
|
3015
|
-
await transport.require("transformDagRun.cancel", options);
|
|
3016
3112
|
await transport.requestRaw(`/api/transform-dag-run/${runId}/cancel`, {
|
|
3017
3113
|
...options,
|
|
3018
3114
|
method: "POST",
|
|
@@ -3020,8 +3116,8 @@ function transformDagRunResource(transport) {
|
|
|
3020
3116
|
});
|
|
3021
3117
|
}
|
|
3022
3118
|
return {
|
|
3023
|
-
transformRuns,
|
|
3024
|
-
cancel
|
|
3119
|
+
transformRuns: explain("transformRuns", transformRuns),
|
|
3120
|
+
cancel: explain("cancel", cancel)
|
|
3025
3121
|
};
|
|
3026
3122
|
}
|
|
3027
3123
|
//#endregion
|
|
@@ -3169,12 +3265,12 @@ function lensPath(id, lensId) {
|
|
|
3169
3265
|
return `/api/ee/transforms/${id}/inspect/${encodeURIComponent(lensId)}`;
|
|
3170
3266
|
}
|
|
3171
3267
|
function transformInspectorResource(transport) {
|
|
3268
|
+
const { explain } = explainer(transport, "transformInspector");
|
|
3172
3269
|
/**
|
|
3173
3270
|
* Discover what the inspector can show for a transform: its source and target tables with field
|
|
3174
3271
|
* statistics, the fields its query touches, and the lenses available on it.
|
|
3175
3272
|
*/
|
|
3176
3273
|
async function discover(id, options = {}) {
|
|
3177
|
-
await transport.require("transformInspector.discover", options);
|
|
3178
3274
|
return transport.requestParsed(TransformInspection, `/api/ee/transforms/${id}/inspect`, { ...options });
|
|
3179
3275
|
}
|
|
3180
3276
|
/**
|
|
@@ -3183,7 +3279,6 @@ function transformInspectorResource(transport) {
|
|
|
3183
3279
|
* the `params` its trigger emitted.
|
|
3184
3280
|
*/
|
|
3185
3281
|
async function lens(id, lensId, params = {}, options = {}) {
|
|
3186
|
-
await transport.require("transformInspector.lens", options);
|
|
3187
3282
|
return transport.requestParsed(TransformLens, lensPath(id, lensId), {
|
|
3188
3283
|
...options,
|
|
3189
3284
|
query: { join_step: params.join_step }
|
|
@@ -3195,7 +3290,6 @@ function transformInspectorResource(transport) {
|
|
|
3195
3290
|
* opened with.
|
|
3196
3291
|
*/
|
|
3197
3292
|
async function query(id, lensId, params, options = {}) {
|
|
3198
|
-
await transport.require("transformInspector.query", options);
|
|
3199
3293
|
return transport.requestParsed(CardQueryResult, `${lensPath(id, lensId)}/query`, {
|
|
3200
3294
|
...options,
|
|
3201
3295
|
method: "POST",
|
|
@@ -3203,17 +3297,17 @@ function transformInspectorResource(transport) {
|
|
|
3203
3297
|
});
|
|
3204
3298
|
}
|
|
3205
3299
|
return {
|
|
3206
|
-
discover,
|
|
3207
|
-
lens,
|
|
3208
|
-
query
|
|
3300
|
+
discover: explain("discover", discover),
|
|
3301
|
+
lens: explain("lens", lens),
|
|
3302
|
+
query: explain("query", query)
|
|
3209
3303
|
};
|
|
3210
3304
|
}
|
|
3211
3305
|
//#endregion
|
|
3212
3306
|
//#region ../client/src/resources/transform-job.ts
|
|
3213
3307
|
function transformJobResource(transport) {
|
|
3308
|
+
const { explain } = explainer(transport, "transformJob");
|
|
3214
3309
|
/** List every transform job the caller can see. */
|
|
3215
3310
|
async function list(options = {}) {
|
|
3216
|
-
await transport.require("transformJob.list", options);
|
|
3217
3311
|
const { features } = await transport.server(options);
|
|
3218
3312
|
return {
|
|
3219
3313
|
data: await transport.requestParsed(z.array(transformJobSchema(features)), "/api/transform-job", { ...options }),
|
|
@@ -3222,13 +3316,11 @@ function transformJobResource(transport) {
|
|
|
3222
3316
|
}
|
|
3223
3317
|
/** Get one transform job by id. */
|
|
3224
3318
|
async function get(id, options = {}) {
|
|
3225
|
-
await transport.require("transformJob.get", options);
|
|
3226
3319
|
const { features } = await transport.server(options);
|
|
3227
3320
|
return transport.requestParsed(transformJobSchema(features), `/api/transform-job/${id}`, { ...options });
|
|
3228
3321
|
}
|
|
3229
3322
|
/** Create a transform job — a schedule plus the tags it runs — from a full body. */
|
|
3230
3323
|
async function create(params, options = {}) {
|
|
3231
|
-
await transport.require("transformJob.create", options);
|
|
3232
3324
|
const { features } = await transport.server(options);
|
|
3233
3325
|
return transport.requestParsed(transformJobSchema(features), "/api/transform-job", {
|
|
3234
3326
|
...options,
|
|
@@ -3238,7 +3330,6 @@ function transformJobResource(transport) {
|
|
|
3238
3330
|
}
|
|
3239
3331
|
/** Update a transform job by id, patching only the fields the body carries. */
|
|
3240
3332
|
async function update(id, params, options = {}) {
|
|
3241
|
-
await transport.require("transformJob.update", options);
|
|
3242
3333
|
const { features } = await transport.server(options);
|
|
3243
3334
|
return transport.requestParsed(transformJobSchema(features), `/api/transform-job/${id}`, {
|
|
3244
3335
|
...options,
|
|
@@ -3248,7 +3339,6 @@ function transformJobResource(transport) {
|
|
|
3248
3339
|
}
|
|
3249
3340
|
/** Delete a transform job by id, leaving the transforms it ran untouched. */
|
|
3250
3341
|
async function remove(id, options = {}) {
|
|
3251
|
-
await transport.require("transformJob.delete", options);
|
|
3252
3342
|
await transport.requestRaw(`/api/transform-job/${id}`, {
|
|
3253
3343
|
...options,
|
|
3254
3344
|
method: "DELETE",
|
|
@@ -3260,7 +3350,6 @@ function transformJobResource(transport) {
|
|
|
3260
3350
|
* plus those transforms' dependencies; `run_all` re-runs dependencies that are already fresh.
|
|
3261
3351
|
*/
|
|
3262
3352
|
async function run(id, params = {}, options = {}) {
|
|
3263
|
-
await transport.require("transformJob.run", options);
|
|
3264
3353
|
const { features } = await transport.server(options);
|
|
3265
3354
|
return transport.requestParsed(transformJobRunResultSchema(features), `/api/transform-job/${id}/run`, {
|
|
3266
3355
|
...options,
|
|
@@ -3270,7 +3359,6 @@ function transformJobResource(transport) {
|
|
|
3270
3359
|
}
|
|
3271
3360
|
/** List the transforms a job would execute, resolved from the job's tags. */
|
|
3272
3361
|
async function transforms(id, options = {}) {
|
|
3273
|
-
await transport.require("transformJob.transforms", options);
|
|
3274
3362
|
const { features } = await transport.server(options);
|
|
3275
3363
|
return {
|
|
3276
3364
|
data: await transport.requestParsed(z.array(transformRowSchema(features)), `/api/transform-job/${id}/transforms`, { ...options }),
|
|
@@ -3282,7 +3370,6 @@ function transformJobResource(transport) {
|
|
|
3282
3370
|
* manual runs ignore the flag. The path carries no id and is a different endpoint from `update`.
|
|
3283
3371
|
*/
|
|
3284
3372
|
async function setActive(active, options = {}) {
|
|
3285
|
-
await transport.require("transformJob.setActive", options);
|
|
3286
3373
|
return transport.requestParsed(TransformJobActiveResult, "/api/transform-job/active", {
|
|
3287
3374
|
...options,
|
|
3288
3375
|
method: "PUT",
|
|
@@ -3290,14 +3377,14 @@ function transformJobResource(transport) {
|
|
|
3290
3377
|
});
|
|
3291
3378
|
}
|
|
3292
3379
|
return {
|
|
3293
|
-
list,
|
|
3294
|
-
get,
|
|
3295
|
-
create,
|
|
3296
|
-
update,
|
|
3297
|
-
delete: remove,
|
|
3298
|
-
run,
|
|
3299
|
-
transforms,
|
|
3300
|
-
setActive
|
|
3380
|
+
list: explain("list", list),
|
|
3381
|
+
get: explain("get", get),
|
|
3382
|
+
create: explain("create", create),
|
|
3383
|
+
update: explain("update", update),
|
|
3384
|
+
delete: explain("delete", remove),
|
|
3385
|
+
run: explain("run", run),
|
|
3386
|
+
transforms: explain("transforms", transforms),
|
|
3387
|
+
setActive: explain("setActive", setActive)
|
|
3301
3388
|
};
|
|
3302
3389
|
}
|
|
3303
3390
|
//#endregion
|
|
@@ -3357,14 +3444,13 @@ function libraryPath(path) {
|
|
|
3357
3444
|
return `/api/ee/transforms-python/library/${encodeURIComponent(path)}`;
|
|
3358
3445
|
}
|
|
3359
3446
|
function transformPythonResource(transport) {
|
|
3447
|
+
const { explain } = explainer(transport, "transformPython");
|
|
3360
3448
|
/** Get the Python library at a path, the user module every Python transform can import. */
|
|
3361
3449
|
async function getLibrary(path, options = {}) {
|
|
3362
|
-
await transport.require("transformPython.getLibrary", options);
|
|
3363
3450
|
return transport.requestParsed(PythonLibrary, libraryPath(path), { ...options });
|
|
3364
3451
|
}
|
|
3365
3452
|
/** Replace the source of the Python library at a path, creating the library when none exists. */
|
|
3366
3453
|
async function updateLibrary(path, params, options = {}) {
|
|
3367
|
-
await transport.require("transformPython.updateLibrary", options);
|
|
3368
3454
|
return transport.requestParsed(PythonLibrary, libraryPath(path), {
|
|
3369
3455
|
...options,
|
|
3370
3456
|
method: "PUT",
|
|
@@ -3377,7 +3463,6 @@ function transformPythonResource(transport) {
|
|
|
3377
3463
|
* sizes are capped at 100 rows.
|
|
3378
3464
|
*/
|
|
3379
3465
|
async function testRun(params, options = {}) {
|
|
3380
|
-
await transport.require("transformPython.testRun", options);
|
|
3381
3466
|
return transport.requestParsed(PythonApiTestRun, "/api/ee/transforms-python/test-run", {
|
|
3382
3467
|
...options,
|
|
3383
3468
|
method: "POST",
|
|
@@ -3385,9 +3470,9 @@ function transformPythonResource(transport) {
|
|
|
3385
3470
|
});
|
|
3386
3471
|
}
|
|
3387
3472
|
return {
|
|
3388
|
-
getLibrary,
|
|
3389
|
-
updateLibrary,
|
|
3390
|
-
testRun
|
|
3473
|
+
getLibrary: explain("getLibrary", getLibrary),
|
|
3474
|
+
updateLibrary: explain("updateLibrary", updateLibrary),
|
|
3475
|
+
testRun: explain("testRun", testRun)
|
|
3391
3476
|
};
|
|
3392
3477
|
}
|
|
3393
3478
|
//#endregion
|
|
@@ -3401,6 +3486,7 @@ const TransformRunKickoff = z.object({
|
|
|
3401
3486
|
});
|
|
3402
3487
|
const TransformDagTransformList = z.array(TransformDagTransform);
|
|
3403
3488
|
function transformResource(transport) {
|
|
3489
|
+
const { explain, explainWalk } = explainer(transport, "transform");
|
|
3404
3490
|
async function requestTransformList(path, opts) {
|
|
3405
3491
|
const { features } = await transport.server(opts);
|
|
3406
3492
|
return {
|
|
@@ -3414,18 +3500,15 @@ function transformResource(transport) {
|
|
|
3414
3500
|
}
|
|
3415
3501
|
/** List every transform the caller can see. */
|
|
3416
3502
|
async function list(options = {}) {
|
|
3417
|
-
await transport.require("transform.list", options);
|
|
3418
3503
|
return requestTransformList("/api/transform", { ...options });
|
|
3419
3504
|
}
|
|
3420
3505
|
/** Get one transform by id. */
|
|
3421
3506
|
async function get(id, options = {}) {
|
|
3422
|
-
await transport.require("transform.get", options);
|
|
3423
3507
|
const { features } = await transport.server(options);
|
|
3424
3508
|
return transport.requestParsed(transformDetailSchema(features), `/api/transform/${id}`, { ...options });
|
|
3425
3509
|
}
|
|
3426
3510
|
/** Create a transform — a query plus the warehouse table it writes — from a full body. */
|
|
3427
3511
|
async function create(params, options = {}) {
|
|
3428
|
-
await transport.require("transform.create", options);
|
|
3429
3512
|
return requestTransform("/api/transform", {
|
|
3430
3513
|
...options,
|
|
3431
3514
|
method: "POST",
|
|
@@ -3434,7 +3517,6 @@ function transformResource(transport) {
|
|
|
3434
3517
|
}
|
|
3435
3518
|
/** Update a transform by id, patching only the fields the body carries. */
|
|
3436
3519
|
async function update(id, params, options = {}) {
|
|
3437
|
-
await transport.require("transform.update", options);
|
|
3438
3520
|
return requestTransform(`/api/transform/${id}`, {
|
|
3439
3521
|
...options,
|
|
3440
3522
|
method: "PUT",
|
|
@@ -3443,7 +3525,6 @@ function transformResource(transport) {
|
|
|
3443
3525
|
}
|
|
3444
3526
|
/** Delete a transform by id, leaving any table it already materialized in place. */
|
|
3445
3527
|
async function remove(id, options = {}) {
|
|
3446
|
-
await transport.require("transform.delete", options);
|
|
3447
3528
|
await transport.requestRaw(`/api/transform/${id}`, {
|
|
3448
3529
|
...options,
|
|
3449
3530
|
method: "DELETE",
|
|
@@ -3452,7 +3533,6 @@ function transformResource(transport) {
|
|
|
3452
3533
|
}
|
|
3453
3534
|
/** Drop a transform's materialized output table, keeping the transform definition. */
|
|
3454
3535
|
async function deleteTable(id, options = {}) {
|
|
3455
|
-
await transport.require("transform.deleteTable", options);
|
|
3456
3536
|
await transport.requestRaw(`/api/transform/${id}/table`, {
|
|
3457
3537
|
...options,
|
|
3458
3538
|
method: "DELETE",
|
|
@@ -3461,12 +3541,10 @@ function transformResource(transport) {
|
|
|
3461
3541
|
}
|
|
3462
3542
|
/** List the upstream transforms a transform depends on. */
|
|
3463
3543
|
async function dependencies(id, options = {}) {
|
|
3464
|
-
await transport.require("transform.dependencies", options);
|
|
3465
3544
|
return requestTransformList(`/api/transform/${id}/dependencies`, { ...options });
|
|
3466
3545
|
}
|
|
3467
3546
|
/** Request cancellation of a transform's current run. */
|
|
3468
3547
|
async function cancel(id, options = {}) {
|
|
3469
|
-
await transport.require("transform.cancel", options);
|
|
3470
3548
|
await transport.requestRaw(`/api/transform/${id}/cancel`, {
|
|
3471
3549
|
...options,
|
|
3472
3550
|
method: "POST",
|
|
@@ -3475,12 +3553,10 @@ function transformResource(transport) {
|
|
|
3475
3553
|
}
|
|
3476
3554
|
/** Get one transform run by run id — not by the id of the transform that produced it. */
|
|
3477
3555
|
async function getRun(runId, options = {}) {
|
|
3478
|
-
await transport.require("transform.getRun", options);
|
|
3479
3556
|
return transport.requestParsed(TransformRun, `/api/transform/run/${runId}`, { ...options });
|
|
3480
3557
|
}
|
|
3481
3558
|
/** Walk the run history, newest first, optionally narrowed to a single transform. */
|
|
3482
3559
|
async function* runPages(params = {}, options = {}) {
|
|
3483
|
-
await transport.require("transform.runPages", options);
|
|
3484
3560
|
yield* paginatePages(transport, "/api/transform/run", TransformRun, {
|
|
3485
3561
|
query: { "transform-ids": params["transform-ids"] },
|
|
3486
3562
|
...options.offset !== void 0 && { offset: options.offset },
|
|
@@ -3494,7 +3570,6 @@ function transformResource(transport) {
|
|
|
3494
3570
|
* DAG reprocess run, or a standalone transform run — and never a member run of a job or DAG.
|
|
3495
3571
|
*/
|
|
3496
3572
|
async function* runSummaryPages(params = {}, options = {}) {
|
|
3497
|
-
await transport.require("transform.runSummaryPages", options);
|
|
3498
3573
|
yield* paginatePages(transport, "/api/transform/runs", TransformRunSummary, {
|
|
3499
3574
|
query: {
|
|
3500
3575
|
types: params.types,
|
|
@@ -3517,7 +3592,6 @@ function transformResource(transport) {
|
|
|
3517
3592
|
* source rather than the rows past the last checkpoint.
|
|
3518
3593
|
*/
|
|
3519
3594
|
async function resetCheckpoint(id, options = {}) {
|
|
3520
|
-
await transport.require("transform.resetCheckpoint", options);
|
|
3521
3595
|
await transport.requestRaw(`/api/transform/${id}/reset-checkpoint`, {
|
|
3522
3596
|
...options,
|
|
3523
3597
|
method: "POST",
|
|
@@ -3532,7 +3606,6 @@ function transformResource(transport) {
|
|
|
3532
3606
|
* `runSummaryPages` with `types: ["dag"]`, and the member runs off `transformDagRun.transformRuns`.
|
|
3533
3607
|
*/
|
|
3534
3608
|
async function runDag(id, params, options = {}) {
|
|
3535
|
-
await transport.require("transform.runDag", options);
|
|
3536
3609
|
return transport.requestParsed(TransformDagRunResult, `/api/transform/${id}/run-dag`, {
|
|
3537
3610
|
...options,
|
|
3538
3611
|
method: "POST",
|
|
@@ -3541,7 +3614,6 @@ function transformResource(transport) {
|
|
|
3541
3614
|
}
|
|
3542
3615
|
/** Preview the transforms a DAG reprocess seeded at a transform would run, in execution order. */
|
|
3543
3616
|
async function dagTransforms(id, params, options = {}) {
|
|
3544
|
-
await transport.require("transform.dagTransforms", options);
|
|
3545
3617
|
return {
|
|
3546
3618
|
data: await transport.requestParsed(TransformDagTransformList, `/api/transform/${id}/dag-transforms`, {
|
|
3547
3619
|
...options,
|
|
@@ -3556,7 +3628,6 @@ function transformResource(transport) {
|
|
|
3556
3628
|
* A server that answers no run id started nothing, and there is then nothing to poll.
|
|
3557
3629
|
*/
|
|
3558
3630
|
async function run(id, params = {}, options = {}) {
|
|
3559
|
-
await transport.require("transform.run", options);
|
|
3560
3631
|
const kickoff = await transport.requestParsed(TransformRunKickoff, `/api/transform/${id}/run`, {
|
|
3561
3632
|
...options,
|
|
3562
3633
|
method: "POST"
|
|
@@ -3597,30 +3668,30 @@ function transformResource(transport) {
|
|
|
3597
3668
|
}
|
|
3598
3669
|
}
|
|
3599
3670
|
return {
|
|
3600
|
-
list,
|
|
3601
|
-
get,
|
|
3602
|
-
create,
|
|
3603
|
-
update,
|
|
3604
|
-
delete: remove,
|
|
3605
|
-
deleteTable,
|
|
3606
|
-
dependencies,
|
|
3607
|
-
cancel,
|
|
3608
|
-
getRun,
|
|
3609
|
-
runPages,
|
|
3610
|
-
runSummaryPages,
|
|
3611
|
-
resetCheckpoint,
|
|
3612
|
-
runDag,
|
|
3613
|
-
dagTransforms,
|
|
3614
|
-
run
|
|
3671
|
+
list: explain("list", list),
|
|
3672
|
+
get: explain("get", get),
|
|
3673
|
+
create: explain("create", create),
|
|
3674
|
+
update: explain("update", update),
|
|
3675
|
+
delete: explain("delete", remove),
|
|
3676
|
+
deleteTable: explain("deleteTable", deleteTable),
|
|
3677
|
+
dependencies: explain("dependencies", dependencies),
|
|
3678
|
+
cancel: explain("cancel", cancel),
|
|
3679
|
+
getRun: explain("getRun", getRun),
|
|
3680
|
+
runPages: explainWalk("runPages", runPages),
|
|
3681
|
+
runSummaryPages: explainWalk("runSummaryPages", runSummaryPages),
|
|
3682
|
+
resetCheckpoint: explain("resetCheckpoint", resetCheckpoint),
|
|
3683
|
+
runDag: explain("runDag", runDag),
|
|
3684
|
+
dagTransforms: explain("dagTransforms", dagTransforms),
|
|
3685
|
+
run: explain("run", run)
|
|
3615
3686
|
};
|
|
3616
3687
|
}
|
|
3617
3688
|
//#endregion
|
|
3618
3689
|
//#region ../client/src/resources/transform-tag.ts
|
|
3619
3690
|
const TransformTagApiList = z.array(TransformTag);
|
|
3620
3691
|
function transformTagResource(transport) {
|
|
3692
|
+
const { explain } = explainer(transport, "transformTag");
|
|
3621
3693
|
/** List every transform tag the caller can see, built-in tags included. */
|
|
3622
3694
|
async function list(options = {}) {
|
|
3623
|
-
await transport.require("transformTag.list", options);
|
|
3624
3695
|
return {
|
|
3625
3696
|
data: await transport.requestParsed(TransformTagApiList, "/api/transform-tag", { ...options }),
|
|
3626
3697
|
total: null
|
|
@@ -3628,7 +3699,6 @@ function transformTagResource(transport) {
|
|
|
3628
3699
|
}
|
|
3629
3700
|
/** Create a transform tag, the label a transform job matches transforms on. */
|
|
3630
3701
|
async function create(params, options = {}) {
|
|
3631
|
-
await transport.require("transformTag.create", options);
|
|
3632
3702
|
return transport.requestParsed(TransformTag, "/api/transform-tag", {
|
|
3633
3703
|
...options,
|
|
3634
3704
|
method: "POST",
|
|
@@ -3637,7 +3707,6 @@ function transformTagResource(transport) {
|
|
|
3637
3707
|
}
|
|
3638
3708
|
/** Rename a transform tag by id. */
|
|
3639
3709
|
async function update(id, params, options = {}) {
|
|
3640
|
-
await transport.require("transformTag.update", options);
|
|
3641
3710
|
return transport.requestParsed(TransformTag, `/api/transform-tag/${id}`, {
|
|
3642
3711
|
...options,
|
|
3643
3712
|
method: "PUT",
|
|
@@ -3646,7 +3715,6 @@ function transformTagResource(transport) {
|
|
|
3646
3715
|
}
|
|
3647
3716
|
/** Delete a transform tag by id, detaching it from every transform and job carrying it. */
|
|
3648
3717
|
async function remove(id, options = {}) {
|
|
3649
|
-
await transport.require("transformTag.delete", options);
|
|
3650
3718
|
await transport.requestRaw(`/api/transform-tag/${id}`, {
|
|
3651
3719
|
...options,
|
|
3652
3720
|
method: "DELETE",
|
|
@@ -3654,19 +3722,19 @@ function transformTagResource(transport) {
|
|
|
3654
3722
|
});
|
|
3655
3723
|
}
|
|
3656
3724
|
return {
|
|
3657
|
-
list,
|
|
3658
|
-
create,
|
|
3659
|
-
update,
|
|
3660
|
-
delete: remove
|
|
3725
|
+
list: explain("list", list),
|
|
3726
|
+
create: explain("create", create),
|
|
3727
|
+
update: explain("update", update),
|
|
3728
|
+
delete: explain("delete", remove)
|
|
3661
3729
|
};
|
|
3662
3730
|
}
|
|
3663
3731
|
//#endregion
|
|
3664
3732
|
//#region ../client/src/resources/transform-test.ts
|
|
3665
3733
|
const TransformTestApiList = z.array(TransformTest);
|
|
3666
3734
|
function transformTestResource(transport) {
|
|
3735
|
+
const { explain } = explainer(transport, "transformTest");
|
|
3667
3736
|
/** List the transform tests, optionally only those of one transform. */
|
|
3668
3737
|
async function list(params = {}, options = {}) {
|
|
3669
|
-
await transport.require("transformTest.list", options);
|
|
3670
3738
|
return {
|
|
3671
3739
|
data: await transport.requestParsed(TransformTestApiList, "/api/ee/transform-test", {
|
|
3672
3740
|
...options,
|
|
@@ -3677,7 +3745,6 @@ function transformTestResource(transport) {
|
|
|
3677
3745
|
}
|
|
3678
3746
|
/** Get a transform test by id. */
|
|
3679
3747
|
async function get(id, options = {}) {
|
|
3680
|
-
await transport.require("transformTest.get", options);
|
|
3681
3748
|
return transport.requestParsed(TransformTest, `/api/ee/transform-test/${id}`, { ...options });
|
|
3682
3749
|
}
|
|
3683
3750
|
/**
|
|
@@ -3686,7 +3753,6 @@ function transformTestResource(transport) {
|
|
|
3686
3753
|
* `TransformTestRefusalCode` when they cannot run.
|
|
3687
3754
|
*/
|
|
3688
3755
|
async function create(params, options = {}) {
|
|
3689
|
-
await transport.require("transformTest.create", options);
|
|
3690
3756
|
return transport.requestParsed(TransformTest, "/api/ee/transform-test", {
|
|
3691
3757
|
...options,
|
|
3692
3758
|
method: "POST",
|
|
@@ -3698,7 +3764,6 @@ function transformTestResource(transport) {
|
|
|
3698
3764
|
* transform, inputs or expectations is validated as on create, with the same refusal.
|
|
3699
3765
|
*/
|
|
3700
3766
|
async function update(id, params, options = {}) {
|
|
3701
|
-
await transport.require("transformTest.update", options);
|
|
3702
3767
|
return transport.requestParsed(TransformTest, `/api/ee/transform-test/${id}`, {
|
|
3703
3768
|
...options,
|
|
3704
3769
|
method: "PUT",
|
|
@@ -3707,7 +3772,6 @@ function transformTestResource(transport) {
|
|
|
3707
3772
|
}
|
|
3708
3773
|
/** Delete a transform test by id. */
|
|
3709
3774
|
async function remove(id, options = {}) {
|
|
3710
|
-
await transport.require("transformTest.delete", options);
|
|
3711
3775
|
await transport.requestRaw(`/api/ee/transform-test/${id}`, {
|
|
3712
3776
|
...options,
|
|
3713
3777
|
method: "DELETE",
|
|
@@ -3721,19 +3785,18 @@ function transformTestResource(transport) {
|
|
|
3721
3785
|
* refused and nothing meaningful ran.
|
|
3722
3786
|
*/
|
|
3723
3787
|
async function run(id, options = {}) {
|
|
3724
|
-
await transport.require("transformTest.run", options);
|
|
3725
3788
|
return transport.requestParsed(TransformTestRunResult, `/api/ee/transform-test/${id}/run`, {
|
|
3726
3789
|
...options,
|
|
3727
3790
|
method: "POST"
|
|
3728
3791
|
});
|
|
3729
3792
|
}
|
|
3730
3793
|
return {
|
|
3731
|
-
list,
|
|
3732
|
-
get,
|
|
3733
|
-
create,
|
|
3734
|
-
update,
|
|
3735
|
-
delete: remove,
|
|
3736
|
-
run
|
|
3794
|
+
list: explain("list", list),
|
|
3795
|
+
get: explain("get", get),
|
|
3796
|
+
create: explain("create", create),
|
|
3797
|
+
update: explain("update", update),
|
|
3798
|
+
delete: explain("delete", remove),
|
|
3799
|
+
run: explain("run", run)
|
|
3737
3800
|
};
|
|
3738
3801
|
}
|
|
3739
3802
|
//#endregion
|
|
@@ -3747,7 +3810,6 @@ function uploadResource(transport) {
|
|
|
3747
3810
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
3748
3811
|
*/
|
|
3749
3812
|
async function createFromCsv(file, params, options = {}) {
|
|
3750
|
-
await transport.require("upload.createFromCsv", options);
|
|
3751
3813
|
const form = buildCsvFormData(file);
|
|
3752
3814
|
form.append("collection_id", params.collection_id);
|
|
3753
3815
|
const response = await transport.requestRaw("/api/upload/csv", {
|
|
@@ -3800,7 +3862,6 @@ CurrentUser.pick({
|
|
|
3800
3862
|
function userResource(transport) {
|
|
3801
3863
|
/** Get the user the request's credentials authenticate as. */
|
|
3802
3864
|
async function current(options = {}) {
|
|
3803
|
-
await transport.require("user.current", options);
|
|
3804
3865
|
return transport.requestParsed(CurrentUser, "/api/user/current", { ...options });
|
|
3805
3866
|
}
|
|
3806
3867
|
return { current };
|
|
@@ -3852,6 +3913,8 @@ function createClient(config, options) {
|
|
|
3852
3913
|
upload: uploadResource(transport),
|
|
3853
3914
|
user: userResource(transport),
|
|
3854
3915
|
server: transport.server,
|
|
3916
|
+
verifiedServer: transport.verifiedServer,
|
|
3917
|
+
probesSettled: transport.probesSettled,
|
|
3855
3918
|
requestParsed: transport.requestParsed,
|
|
3856
3919
|
requestRaw: transport.requestRaw,
|
|
3857
3920
|
requestStream: transport.requestStream
|