@metabase/cli 0.4.1-alpha.release-65-integration-branch.5cbe297 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-collection-hv29px39.mjs → add-collection-COtKcy6Y.mjs} +4 -4
- package/dist/{alert-DnTCjYIu.mjs → alert-DMlSjPEv.mjs} +7 -7
- package/dist/{alerts-_ELZ12ye.mjs → alerts-s3al16NZ.mjs} +6 -6
- package/dist/{append-B9sLLPYT.mjs → append-CbGmdeLw.mjs} +4 -4
- package/dist/{archive-CwBk5vxQ.mjs → archive-5EY9UJU_.mjs} +5 -5
- package/dist/{archive-DDUSmbjn.mjs → archive-BFmx4B91.mjs} +3 -3
- package/dist/{archive-CdfyB0To.mjs → archive-BZCYIBhQ.mjs} +3 -3
- package/dist/{archive-BRX5fKPb.mjs → archive-C_UHeanE.mjs} +3 -3
- package/dist/{archive-BhIeDoLk.mjs → archive-Ce4eArrs.mjs} +3 -3
- package/dist/{archive-BAHzxkPR.mjs → archive-CnDriekB.mjs} +5 -5
- package/dist/{archive-CKWWyI1i.mjs → archive-DDO2XX0E.mjs} +4 -4
- package/dist/{archive-DD8C0Krt2.mjs → archive-DW7x0IFB.mjs} +4 -4
- package/dist/{archive-D22VL5Bx.mjs → archive-DvbaSglr.mjs} +3 -3
- package/dist/{archive-ZnHzMJaM.mjs → archive-DzSdJVqE.mjs} +5 -5
- package/dist/{archive-BdX0Uvf2.mjs → archive-a05W75XH.mjs} +3 -3
- package/dist/{auth-Ca84a5fv.mjs → auth-DOz7KW5E.mjs} +5 -5
- package/dist/{body-BVrHLwJU.mjs → body-Di7940x_.mjs} +2 -3
- package/dist/{branch-flag-Czh8JT1G.mjs → branch-flag-CfJ8vwgu.mjs} +1 -1
- package/dist/{branches-frSt4WcS.mjs → branches-BC-vlk6i.mjs} +3 -3
- package/dist/{breaking-zlgxxwdd.mjs → breaking-ByrjsFjq.mjs} +6 -6
- package/dist/{broken-BUAzpKLc.mjs → broken-DLSk1T88.mjs} +8 -8
- package/dist/{bulk-discard-values-DU15VJPb.mjs → bulk-discard-values-VKGj8c7z.mjs} +5 -5
- package/dist/{bulk-edit-TSa4lNkM.mjs → bulk-edit-CpuleeCQ.mjs} +5 -5
- package/dist/{bulk-rescan-values-BYJAsPC1.mjs → bulk-rescan-values-DeGwiqLS.mjs} +5 -5
- package/dist/{bulk-sync-schema-DtaOwEtW.mjs → bulk-sync-schema-BKVhauW1.mjs} +5 -5
- package/dist/{cancel-DCOvSQsq.mjs → cancel-qKj45001.mjs} +3 -3
- package/dist/{cancel-task-osQLETJh.mjs → cancel-task-nmBBd1Ky.mjs} +3 -3
- package/dist/card-Buc-X4Mh.mjs +25 -0
- package/dist/{card-P5mBknsl.mjs → card-Bv4ZN0oV.mjs} +12 -2
- package/dist/{card-CQNUT9pm.mjs → card-DSrgU7lT.mjs} +1 -1
- package/dist/{cards-B1pyPl77.mjs → cards-Bq73s48q.mjs} +4 -4
- package/dist/cli.mjs +628 -239
- package/dist/{client-1UIUBA_V.mjs → client-BmURNkkW.mjs} +998 -444
- package/dist/{collection-DPiztU_l.mjs → collection-C03LXnCT.mjs} +1 -1
- package/dist/collection-D-sS0AFw.mjs +21 -0
- package/dist/{collection-namespace-Cizv6-94.mjs → collection-namespace-DaDzK31i.mjs} +1 -2
- package/dist/{collection-BgPYl2c5.mjs → collection-zrZoJfgR.mjs} +1 -1
- package/dist/{content-translation-XnHISl5-.mjs → content-translation-BlG41YrA.mjs} +3 -3
- package/dist/{copy-BCVW48Jq.mjs → copy-DOKlVa7H.mjs} +6 -6
- package/dist/{copy-DAKdksBm.mjs → copy-DTiYs_a2.mjs} +4 -4
- package/dist/{copy-flags-D7NTgAz1.mjs → copy-flags-BhGWC5kS.mjs} +2 -2
- package/dist/{create-qCgnB-oZ.mjs → create-1rLk8b5q.mjs} +3 -3
- package/dist/{create-BfA1pnmS.mjs → create-B1QDNU1x.mjs} +6 -6
- package/dist/{create-BOvtRwRz.mjs → create-B1bX6ngX.mjs} +4 -4
- package/dist/{create-XnYN2zoc.mjs → create-B5BwdPwT.mjs} +6 -6
- package/dist/{create-CuhQ1Khk.mjs → create-BKKNIe93.mjs} +3 -3
- package/dist/{create-KyeJjhxH2.mjs → create-BXX_n64u.mjs} +4 -4
- package/dist/{create-B1ifNWcE.mjs → create-BpwZJZxb.mjs} +5 -5
- package/dist/{create-B57D5E-o.mjs → create-C0kz0oYc.mjs} +3 -3
- package/dist/{create-CW4WF073.mjs → create-Cob0gLsA.mjs} +3 -3
- package/dist/{create-D7zeAm-0.mjs → create-DPggIXfn.mjs} +4 -4
- package/dist/{create-Br5JxAeE.mjs → create-DUytP2Y1.mjs} +4 -4
- package/dist/{create-cqbpWaOi.mjs → create-DraZzrLO.mjs} +6 -6
- package/dist/{create-B5eo9PSN.mjs → create-Duj_2YIF.mjs} +4 -4
- package/dist/{create-D2iZsTRV.mjs → create-JaFhqysM.mjs} +5 -5
- package/dist/{create-branch-Cli5cXvk.mjs → create-branch-B5jB4tKk.mjs} +3 -3
- package/dist/{create-BzE4q6F_.mjs → create-d0trLAzB.mjs} +4 -4
- package/dist/{create-g4pZa_Xa.mjs → create-vOw8gmBU.mjs} +3 -3
- package/dist/{csv-CWIcAm_-.mjs → csv-DfExp9H_.mjs} +4 -4
- package/dist/{current-task-eBjusv2o.mjs → current-task-CbXIH_RA.mjs} +4 -4
- package/dist/dashboard-DH_soSQp.mjs +35 -0
- package/dist/{database-BT7DVgSr.mjs → database-BdKkae1m.mjs} +2 -2
- package/dist/{database-Kq2AL5jH.mjs → database-DoNoZ8Yc.mjs} +1 -1
- package/dist/{dataset-BmvCxGoE.mjs → dataset-DZgJm5hP.mjs} +3 -14
- package/dist/{db-_H9HAS6H.mjs → db-C82M3Gpg.mjs} +7 -7
- package/dist/{delete-C7QmQlp9.mjs → delete-9xHRd2CG.mjs} +4 -4
- package/dist/{delete-BMvVkW-m.mjs → delete-B4BfGfiJ.mjs} +4 -4
- package/dist/{delete-BW5QXiyJ.mjs → delete-D0xFMRRq.mjs} +4 -4
- package/dist/{delete-CynbfvS2.mjs → delete-DJ7SNXiH.mjs} +4 -4
- package/dist/{delete-BztmAfPc.mjs → delete-hcw_4pBG.mjs} +4 -4
- package/dist/{delete-DOn2uk0u.mjs → delete-hwPqKXeY.mjs} +4 -4
- package/dist/{delete-runtime-Cc7TASG7.mjs → delete-runtime-DkgYUQ_n.mjs} +1 -1
- package/dist/{delete-table-DFqm6bAq.mjs → delete-table-DhTMft7u.mjs} +4 -4
- package/dist/{dependencies-DZ-kMsDa.mjs → dependencies-B8H2gA04.mjs} +5 -5
- package/dist/dependency-CLznxtLE.mjs +15 -0
- package/dist/{dependency-BHKwKjhO.mjs → dependency-Dc33mjgd.mjs} +2 -2
- package/dist/{dependency-B4J1bwFh.mjs → dependency-tQBxYjFX.mjs} +2 -2
- package/dist/{dependents-Ia2fe_KK.mjs → dependents-B2GcS5kB.mjs} +9 -9
- package/dist/{dirty-BrXAPows.mjs → dirty-Cz2rUd6S.mjs} +4 -4
- package/dist/{discard-values-B9mTgFbV.mjs → discard-values-QWuWGaVK.mjs} +5 -5
- package/dist/{document-COQzVLjW.mjs → document-BDobDiap.mjs} +1 -1
- package/dist/document-WhSEyI-3.mjs +20 -0
- package/dist/{document-BsaKF8UR.mjs → document-wfWbV0B_.mjs} +1 -1
- package/dist/{download-CZdsDdhG.mjs → download-Dw15fUMy.mjs} +2 -2
- package/dist/{eid-DsGNJ16z.mjs → eid-Ex4G-ZlN.mjs} +4 -4
- package/dist/{entity-BFajNdQp.mjs → entity-DBKPaMr5.mjs} +2 -2
- package/dist/{entry-flags-BaZUoxUR.mjs → entry-flags-DgY3IXFe.mjs} +4 -4
- package/dist/{events-rkMIMpnS.mjs → events-BibJ4ya0.mjs} +4 -4
- package/dist/{export-BCkzx9zk.mjs → export-U8MdaatW.mjs} +7 -7
- package/dist/{export-flags-TuwwRhD6.mjs → export-flags-BrfeyE8D.mjs} +3 -3
- package/dist/{export-preflight-p7botJyN.mjs → export-preflight-A28L45au.mjs} +5 -5
- package/dist/field-qF1KRJYB.mjs +21 -0
- package/dist/{fields-CJVXOLbB.mjs → fields-i1TFa_th.mjs} +4 -4
- package/dist/{filter-flags-yY4RM312.mjs → filter-flags-Bm3vuSM-.mjs} +4 -5
- package/dist/{fks-B6hjrdJP.mjs → fks-Dcfn3-73.mjs} +6 -6
- package/dist/{get-Eforof5e.mjs → get-B4_3DID6.mjs} +5 -5
- package/dist/{get-zotSW00n.mjs → get-BCdtCwaG.mjs} +3 -3
- package/dist/{get-DpYTiE4D.mjs → get-BKDTRMNU.mjs} +3 -3
- package/dist/{get-DsbDxSpy.mjs → get-BiPSePQc.mjs} +3 -3
- package/dist/{get-BYtFVJHT.mjs → get-C3ojRVJ_.mjs} +4 -4
- package/dist/{get-D7HPiBB0.mjs → get-CCLWWqTD.mjs} +4 -4
- package/dist/{get-CoJcgFky.mjs → get-CVpIQb8v.mjs} +5 -5
- package/dist/{get-CXeFg0F2.mjs → get-CcwRn8Y2.mjs} +5 -5
- package/dist/{get-CWBN0G-4.mjs → get-CobILQ_7.mjs} +5 -5
- package/dist/{get-DPlSzRI0.mjs → get-Cwj1Q-at.mjs} +3 -3
- package/dist/{get-C6uLL4bJ.mjs → get-D0NZ0ZO1.mjs} +3 -3
- package/dist/{get-BvUNnNRq.mjs → get-DPZUIPjv.mjs} +3 -3
- package/dist/{get-Do1LC4I52.mjs → get-DUwX-RU82.mjs} +3 -3
- package/dist/{get-BxUgfEPW.mjs → get-DqYn9mez.mjs} +4 -4
- package/dist/{get-oLc5jgid2.mjs → get-Dz3dNirC.mjs} +4 -4
- package/dist/{get-DyS1mRI6.mjs → get-EKBYludD.mjs} +3 -3
- package/dist/{get-D1p-5ibI.mjs → get-HFQSttF3.mjs} +5 -5
- package/dist/{get-xmhwvAQJ.mjs → get-IjoHBExI.mjs} +5 -5
- package/dist/{get-B5L4BUGK.mjs → get-hAoJtVCK.mjs} +3 -3
- package/dist/{get-run-bU-d--nM.mjs → get-run-B2SnsZSY.mjs} +4 -4
- package/dist/git-sync-BlgB_-8-.mjs +29 -0
- package/dist/{git-sync-DspFahwV.mjs → git-sync-Ch1V1fEK.mjs} +1 -1
- package/dist/{glossary-CdW6RFId.mjs → glossary-6LLpItof.mjs} +1 -1
- package/dist/glossary-BvVxqjxJ.mjs +14 -0
- package/dist/{glossary-Del97Jqg.mjs → glossary-wnuxoE_I.mjs} +1 -1
- package/dist/{graph-CHXJ6mB7.mjs → graph-CHlfxVy1.mjs} +6 -6
- package/dist/{group-D4w87fXL.mjs → group-B3X3LTcU.mjs} +1 -1
- package/dist/{has-remote-changes-BXEe8Sv5.mjs → has-remote-changes-DCMpqGgE.mjs} +3 -3
- package/dist/{import-DZQb4bUo.mjs → import-C7MZhKo6.mjs} +7 -7
- package/dist/{input-DjHsV-kD.mjs → input-sRdxNc3T.mjs} +1 -1
- package/dist/{is-dirty-DwQ2_CCB.mjs → is-dirty-DpLyHebS.mjs} +2 -2
- package/dist/{items-DNdICQ7M.mjs → items-1ME_eg27.mjs} +6 -6
- package/dist/{key-BTaxFn_7.mjs → key-DMtgTOwN.mjs} +1 -1
- package/dist/{library-DmkpcZPb.mjs → library-CP-9yepy.mjs} +1 -1
- package/dist/{library-Cdj_uj1H.mjs → library-D2ogEIfI.mjs} +5 -5
- package/dist/{library-C4M8TF2N.mjs → library-fWEv38OB.mjs} +2 -2
- package/dist/{list-CYlPgzD1.mjs → list-B0h-j1E7.mjs} +3 -3
- package/dist/{list-CLJvuPXn.mjs → list-BUZ91ctZ.mjs} +5 -5
- package/dist/{list-89ZgbsvW.mjs → list-BUwDGyIu.mjs} +5 -5
- package/dist/{list-C-WGqRqN.mjs → list-BXcDPU5d.mjs} +6 -6
- package/dist/{list-C7PV9fYj.mjs → list-BoVeF8QA.mjs} +6 -6
- package/dist/{list-M20foZz2.mjs → list-C1oEpUZG.mjs} +3 -3
- package/dist/{list-OKHcr76i.mjs → list-C9996vbS.mjs} +3 -3
- package/dist/{list-2EWHwbQs.mjs → list-CJ5FyCnL.mjs} +4 -4
- package/dist/{list-DcHPY1Ti.mjs → list-CQi3AgS5.mjs} +4 -4
- package/dist/{list-epIn3uFZ.mjs → list-CVh8DHOO.mjs} +5 -5
- package/dist/{list-03zaydQe.mjs → list-DBAEy3Rx.mjs} +3 -3
- package/dist/{list-hINHmdMT.mjs → list-DkCadZ2E.mjs} +3 -3
- package/dist/{list-DsyvHBAv.mjs → list-DzP7YXdZ.mjs} +3 -3
- package/dist/{list-gqemddWB.mjs → list-LMlJ4uAY.mjs} +6 -6
- package/dist/{list-F4ENGV7j.mjs → list-NINy7ybI.mjs} +4 -4
- package/dist/{list-CZoaZc9g.mjs → list-OXiXtdiM.mjs} +5 -5
- package/dist/{list-Be6kDkyT.mjs → list-STy8UHs2.mjs} +6 -6
- package/dist/{list-DoLqxEjq.mjs → list-Ud7F0XdU.mjs} +3 -3
- package/dist/{list-__Q79YXk.mjs → list-dCi6OaIE.mjs} +4 -4
- package/dist/{login-BSqGgX8S.mjs → login-BMON3Fft.mjs} +6 -6
- package/dist/{logout-D92O4mQK.mjs → logout-Cx934GzW.mjs} +2 -2
- package/dist/{measure-DhX_mmZK.mjs → measure-CUqJtDNm.mjs} +6 -6
- package/dist/{notification-Cuuwoksm.mjs → notification-CCZ9mrOw.mjs} +1 -1
- package/dist/{parameter-values-BY2WA9qg.mjs → parameter-values-DCrU02Az.mjs} +3 -3
- package/dist/{parse-enum-BPKULOSw.mjs → parse-enum-Do5Cptbv.mjs} +1 -1
- package/dist/{parse-id-CxHp2akh.mjs → parse-id-oCbmouCn.mjs} +1 -1
- package/dist/{parse-ref-8bVX_9IN.mjs → parse-ref-Df8gusl4.mjs} +1 -1
- package/dist/{parse-text-Bm6eUa-p.mjs → parse-text-CVrP7GmZ.mjs} +1 -1
- package/dist/{path-D4Q1ZzCw.mjs → path-DTuSDGd6.mjs} +4 -4
- package/dist/{poll-DFZRtTA_.mjs → poll-BKo5dK0l.mjs} +2 -2
- package/dist/{preflight-BPaonBKy.mjs → preflight-UeYuGDPr.mjs} +2 -3
- package/dist/{process-B3Di_HtV.mjs → process-BpmZXbcw.mjs} +1 -1
- package/dist/{publish-BhgyrJ6c.mjs → publish-D3GwLC-K.mjs} +4 -4
- package/dist/{pulse-CIza18rO.mjs → pulse-DzVPfb5J.mjs} +1 -1
- package/dist/{query-CsBe-kJn.mjs → query-B1Cp9EO6.mjs} +6 -6
- package/dist/{query-DvlGWjgG.mjs → query-Ct697cY-.mjs} +13 -15
- package/dist/{remapping-CCEivr42.mjs → remapping-RsoPWE7c.mjs} +4 -4
- package/dist/{remove-collection-LpgGBYCl.mjs → remove-collection-BJo3Wd9K.mjs} +4 -4
- package/dist/{render-D5ehBS-7.mjs → render-C_ev6qoW.mjs} +2 -2
- package/dist/{replace-DeWgkbfZ.mjs → replace-BSAGp3cR.mjs} +4 -4
- package/dist/requirement-check-T3bjQ0Mr.mjs +60 -0
- package/dist/{rescan-values-CDDVS1tl.mjs → rescan-values-CE62_2dc.mjs} +5 -5
- package/dist/{rescan-values-aF18XAv6.mjs → rescan-values-rpro4Kw2.mjs} +5 -5
- package/dist/{run-DyGMvbMm.mjs → run-B3BRodEp.mjs} +3 -3
- package/dist/{run-Buf4B8QH.mjs → run-BI1QO9Kn.mjs} +7 -7
- package/dist/{runs-D2CXgW10.mjs → runs-CUseZezf.mjs} +5 -5
- package/dist/{runtime-X6hVzSMW.mjs → runtime-C5Ha6Ou8.mjs} +248 -534
- package/dist/{schema-tables-FUOIyoUU.mjs → schema-tables-C7YkyRh0.mjs} +6 -6
- package/dist/{schemas-CmfNeqzG.mjs → schemas-Bj5sJ9uB.mjs} +4 -4
- package/dist/{search-CLpDqYpY.mjs → search-BsAwK7Rk.mjs} +5 -5
- package/dist/{search-Dl7xzF6Q.mjs → search-CB1A4HWx.mjs} +6 -6
- package/dist/{segment-Bx28f38v.mjs → segment-BEjaeLc5.mjs} +6 -6
- package/dist/{send-CoGV2zkp.mjs → send-Dfh8d1gG.mjs} +3 -3
- package/dist/{set-CrR9IumN.mjs → set-DA8dKyzi.mjs} +4 -4
- package/dist/{set-active-CbiJv8Hg.mjs → set-active-AyjKtBD-.mjs} +3 -3
- package/dist/{set-sensitivity-CTHVxpnU.mjs → set-sensitivity-D0613dHA.mjs} +4 -4
- package/dist/{setting-CUxdweNJ.mjs → setting-B-bxCkC0.mjs} +4 -4
- package/dist/{setup-4uySIB-t.mjs → setup-Bvvqlz9d.mjs} +3 -3
- package/dist/{signal-0Yt8JTMn.mjs → signal-Dm4N0Bcc.mjs} +1 -1
- package/dist/{skill-list-goCqmfFF.mjs → skill-list-DkuCf0k4.mjs} +9 -3
- package/dist/{skills-DeWsLMwC.mjs → skills-DlDCTGVE.mjs} +2 -2
- package/dist/{skills-86j411Qz.mjs → skills-DxrO7HUz.mjs} +3 -3
- package/dist/snippet-B7Kau5U8.mjs +19 -0
- package/dist/{stash-BNKlbxtD.mjs → stash-j9JPv3ns.mjs} +5 -5
- package/dist/{status-B7bwHLkk.mjs → status-DcbvbRc5.mjs} +3 -3
- package/dist/{status-Bul9El-c.mjs → status-DxNcrfnI.mjs} +16 -7
- package/dist/{subscription-DPAGevGU.mjs → subscription-CSR2t-xK.mjs} +6 -6
- package/dist/{subscriptions-DIRsHcKP.mjs → subscriptions--0YvW40u.mjs} +6 -6
- package/dist/{summary-CeVt80Kb.mjs → summary-Dslv78-r.mjs} +3 -3
- package/dist/{sync-schema-B9fLcZoa.mjs → sync-schema-BSJiokQf.mjs} +6 -6
- package/dist/{sync-schema-oDTbhjW1.mjs → sync-schema-Dgij5ICd.mjs} +5 -5
- package/dist/{sync-task-BAMOf6_t.mjs → sync-task-a1zyF1Qb.mjs} +4 -5
- package/dist/{table-BFIOH6Pb.mjs → table-Uvu1roSx.mjs} +2 -2
- package/dist/table-qjgz2eek.mjs +26 -0
- package/dist/{table-selector-flags-BUR0-n6j.mjs → table-selector-flags-B9OiA3Z1.mjs} +3 -3
- package/dist/{table-Ci6bhZkb.mjs → table-zAdW5_3H.mjs} +5 -5
- package/dist/{text-CdtEI6l6.mjs → text-821Rn8M9.mjs} +1 -1
- package/dist/timeline-CNkrY3e3.mjs +21 -0
- package/dist/{timeline-event-BY2Hj72e.mjs → timeline-event-CSzx8EDP.mjs} +6 -6
- package/dist/{transform-D4z70PIN.mjs → transform-BKX6tl3i.mjs} +1 -1
- package/dist/transform-Y86R7_2F.mjs +28 -0
- package/dist/transform-job-Cg_1jzWI.mjs +22 -0
- package/dist/{transform-tag-CzjLKILh.mjs → transform-tag-DHg7j15d.mjs} +5 -5
- package/dist/{transforms-0uhBh0H4.mjs → transforms-DwxPfMiG.mjs} +5 -5
- package/dist/{tree-oBEw8hDj.mjs → tree-COFmA20J.mjs} +3 -3
- package/dist/{unpublish-BJ9OKN1N.mjs → unpublish-BQClh4U7.mjs} +3 -3
- package/dist/{unreferenced-D6UyA_Nu.mjs → unreferenced-DKc0zZDl.mjs} +6 -6
- package/dist/{update-CKJ6Das4.mjs → update-B-wcIl8T.mjs} +4 -4
- package/dist/{update-BqrZ-8IK.mjs → update-B8jQTspb.mjs} +8 -8
- package/dist/{update-BZgHTH5P.mjs → update-BBLbEPEP.mjs} +4 -4
- package/dist/{update-M7yDPmJ-.mjs → update-BNwjjMk6.mjs} +5 -5
- package/dist/{update-CEhub3Wz.mjs → update-BVFfoopT.mjs} +5 -5
- package/dist/{update-DGwGjOnH.mjs → update-BVMgrMy7.mjs} +5 -5
- package/dist/{update-CKe2Bwpv.mjs → update-Bgy2amR0.mjs} +6 -6
- package/dist/{update-D9eLVWFQ.mjs → update-C9HK3i8s.mjs} +5 -5
- package/dist/{update-Cv6IMLyu.mjs → update-CFfr1ZdL.mjs} +4 -4
- package/dist/{update-BK_SpSrG.mjs → update-CMRWXoZ2.mjs} +4 -4
- package/dist/{update-DKagaJw0.mjs → update-CPmPnEvm.mjs} +6 -6
- package/dist/{update-bGDXSnBh.mjs → update-Ca8LSZer.mjs} +5 -5
- package/dist/{update-DgmlUn64.mjs → update-DJwu8UT2.mjs} +4 -4
- package/dist/{update-DneJLQXe.mjs → update-Dwr3n7_n.mjs} +7 -7
- package/dist/{update-CYQkWLpZ.mjs → update-MxvqmZ3z.mjs} +6 -6
- package/dist/{update-dashcard-DNsf-gJ1.mjs → update-dashcard-C8QN2Qcu.mjs} +4 -4
- package/dist/{update-CHA4VWKT.mjs → update-kM3TDlEp.mjs} +7 -7
- package/dist/{update-7zsDGAWa.mjs → update-myfBF5qV.mjs} +4 -4
- package/dist/{upgrade-BRJH93W0.mjs → upgrade-BBOEVIRa.mjs} +4 -4
- package/dist/{upload-CGz8u2wG.mjs → upload-CoBd4qBp.mjs} +3 -3
- package/dist/{upload-DJB7DMA1.mjs → upload-CxpXpjCf.mjs} +2 -2
- package/dist/upload-fcim1xtK.mjs +13 -0
- package/dist/{uuid-3qmLYgrG.mjs → uuid-DW30GY7B.mjs} +2 -2
- package/dist/{validate-D_iikPkc.mjs → validate-n85yMraN.mjs} +1 -1
- package/dist/{validate-query-DIcK8alR.mjs → validate-query-BwXvFFYi.mjs} +3 -7
- package/dist/{values-Bn2Wcz4t.mjs → values-B6eFE4K5.mjs} +3 -3
- package/dist/{verify-C2K-zMuQ.mjs → verify-BSq7x66g.mjs} +3 -3
- package/dist/{verify-CCX2N98Q.mjs → verify-DnU-wXIs.mjs} +4 -4
- package/dist/{verify-CA4PEHlO.mjs → verify-Dp8tUbP3.mjs} +3 -3
- package/dist/{wait-DCgCJBqX.mjs → wait-D6Tw4ZxO.mjs} +6 -6
- package/dist/{wait-flags-aRQHTjwg.mjs → wait-flags-BRUzMI6S.mjs} +2 -2
- package/dist/{window-2ybHqui2.mjs → window-BqMFpWMh.mjs} +1 -1
- package/package.json +1 -1
- package/skill-data/core/SKILL.md +5 -5
- package/skill-data/dashboard/SKILL.md +6 -0
- package/skill-data/git-sync/SKILL.md +64 -14
- package/skill-data/native-sql/SKILL.md +1 -1
- package/skill-data/transform/SKILL.md +1 -63
- package/dist/archive-3MQyc5Ih.mjs +0 -32
- package/dist/card-P2-4BkXV.mjs +0 -25
- package/dist/collection-BI8Fy9vS.mjs +0 -21
- package/dist/create--b64pMVM.mjs +0 -34
- package/dist/create-C7fVbQbQ.mjs +0 -48
- package/dist/dashboard-C-YJxDam.mjs +0 -35
- package/dist/data-action-DER3EMD7.mjs +0 -24
- package/dist/data-action-DZxVB_hD.mjs +0 -85
- package/dist/data-action-R9qxJDPA.mjs +0 -40
- package/dist/delete-B0O-mf63.mjs +0 -44
- package/dist/delete-DPU-uQxU.mjs +0 -44
- package/dist/dependency-v9fHJ-N-.mjs +0 -15
- package/dist/document-CGvUmD9i.mjs +0 -20
- package/dist/execute-DvBse4aF.mjs +0 -45
- package/dist/field-C9AcBe8x.mjs +0 -21
- package/dist/get-BwKQw_rR.mjs +0 -31
- package/dist/get-DSjITPED.mjs +0 -32
- package/dist/git-sync-D4R0cHvk.mjs +0 -29
- package/dist/glossary-C6EnoEXg.mjs +0 -14
- package/dist/list-CzxrdWB8.mjs +0 -35
- package/dist/list-Owrzgtkc.mjs +0 -28
- package/dist/query-BKtMP5eH.mjs +0 -13
- package/dist/run-DlG3Ykj1.mjs +0 -63
- 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/transform-test-BjjCQ2xm.mjs +0 -169
- package/dist/transform-test-DHGKeuHa.mjs +0 -32
- package/dist/transport-BmNrb0Lz.mjs +0 -313
- package/dist/update-Bdi40RG0.mjs +0 -52
- package/dist/update-CXfLvhmK.mjs +0 -41
- package/dist/upload-T5wS1XtL.mjs +0 -13
- package/skill-data/data-action/SKILL.md +0 -125
- package/skill-data/transform-test-plan/SKILL.md +0 -82
- package/skill-data/transform-test-plan/references/checklist.md +0 -32
- package/skill-data/transform-test-plan/references/checks.md +0 -156
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as isNonInterruptFailure, Ct as ResponseShapeError, Dt as errorMessage, Et as ValidationError, G as isRetryableStatus, J as isPlainObject, N as CapabilityError, Ot as formatZodIssue, St as PartialWriteError, U as HttpError, X as parseJson, Y as JSON_CONTENT_TYPE, Z as parseJsonOrPlain, n as isMethodKey, r as methodRequirements, vt as ConfigError, wt as TimeoutError, xt as NetworkError, yt as InternalError } from "./cli.mjs";
|
|
2
2
|
import { n as Notification, r as NotificationCardPayload, t as CARD_PAYLOAD_TYPE } from "./notification-DaA4bBHW.mjs";
|
|
3
|
-
import { P as
|
|
4
|
-
import { n as
|
|
3
|
+
import { B as credentialAuthHeader, F as probeBudget, J as createServerProfile, L as profileFromProperties, M as buildNetworkError, N as isConnectionClosed, P as PROBE_PATH, R as serverInfoFromProperties, V as credentialSecrets, W as normalizeUrl, Z as SessionProperties, z as assertCredentialHeaderSafe } from "./runtime-C5Ha6Ou8.mjs";
|
|
4
|
+
import { n as throwIfAborted, r as untilAborted, t as combineAborts } from "./signal-Dm4N0Bcc.mjs";
|
|
5
|
+
import { n as featureFailures, r as supersedingFeature, t as checkFeatures } from "./requirement-check-T3bjQ0Mr.mjs";
|
|
5
6
|
import { f as FieldWithDataSensitivity, h as fieldRemappedValueSchema, m as fieldDetailSchema, n as FieldBaseType, o as FieldSearchMatches, s as FieldSemanticType, u as FieldValues } from "./field-5nivEfd6.mjs";
|
|
6
7
|
import { a as TemporalUnit, r as ParameterValues } from "./parameter-Celg0wZa.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { t as Database } from "./database-BT7DVgSr.mjs";
|
|
8
|
+
import { a as CardQueryResult, d as PivotExportFormat, l as DatasetQuery, t as Card } from "./card-Bv4ZN0oV.mjs";
|
|
9
|
+
import { b as TableVisibilityType, p as TableQueryMetadata, s as TableDataLayerTier, t as Table, u as TableForeignKey, v as TableSelectors } from "./table-zAdW5_3H.mjs";
|
|
10
|
+
import { t as Database } from "./database-BdKkae1m.mjs";
|
|
11
11
|
import { t as Snippet } from "./snippet-CyKAgIZl.mjs";
|
|
12
|
-
import {
|
|
13
|
-
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-
|
|
12
|
+
import { r as QueryMetadata, t as CompiledQuery } from "./dataset-DZgJm5hP.mjs";
|
|
13
|
+
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-zrZoJfgR.mjs";
|
|
14
14
|
import { t as ContentTranslationUploadResult } from "./content-translation-BrHOrmj9.mjs";
|
|
15
15
|
import { l as Dashcard, o as DashboardDetail, r as DashboardCopy, t as Dashboard } from "./dashboard-Phz3XpRM.mjs";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { t as Document } from "./document-COQzVLjW.mjs";
|
|
16
|
+
import { r as pollUntil } from "./poll-BKo5dK0l.mjs";
|
|
17
|
+
import { i as DependencyEntity, s as DependencyGraph, t as BreakingSource, u as DependencyNode } from "./dependency-tQBxYjFX.mjs";
|
|
18
|
+
import { t as Document } from "./document-BDobDiap.mjs";
|
|
20
19
|
import { i as EidTranslateResult } from "./eid-translation-zEHtr5Oc.mjs";
|
|
21
20
|
import { c as SyncRemoteChanges, d as SyncTask, i as SyncExportPreflight, l as SyncSettingsUpdateResult, m as isSyncTaskTerminal, n as SyncDirtyItem, t as SyncBranchCreated } from "./git-sync-COokpYgc.mjs";
|
|
22
|
-
import { t as Glossary } from "./glossary-
|
|
23
|
-
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-
|
|
21
|
+
import { t as Glossary } from "./glossary-6LLpItof.mjs";
|
|
22
|
+
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-fWEv38OB.mjs";
|
|
24
23
|
import { t as Measure } from "./measure-CtfjqJFP.mjs";
|
|
25
24
|
import { r as ModerationReviewCreateInput, t as ModerationReview } from "./moderation-review-BdqEH6N1.mjs";
|
|
26
25
|
import { t as Pulse } from "./pulse-Dz0ocv0l.mjs";
|
|
@@ -32,9 +31,9 @@ import { n as SetupResult } from "./setup-CO7QV-ao.mjs";
|
|
|
32
31
|
import { i as TimelineEvent, t as Timeline } from "./timeline-CCSU2qOS.mjs";
|
|
33
32
|
import { c as transformJobSchema, n as TransformJobActiveResult, s as transformJobRunResultSchema } from "./transform-job-CCguL6LP.mjs";
|
|
34
33
|
import { t as TransformTag } from "./transform-tag-CcElGp3C.mjs";
|
|
35
|
-
import { i as TransformTestRunResult, t as TransformTest } from "./transform-test-BjjCQ2xm.mjs";
|
|
36
34
|
import { t as UploadResult } from "./upload-DAtxBBms.mjs";
|
|
37
35
|
import { z } from "zod";
|
|
36
|
+
import { setTimeout } from "node:timers/promises";
|
|
38
37
|
//#region \0rolldown/runtime.js
|
|
39
38
|
var __defProp = Object.defineProperty;
|
|
40
39
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -47,15 +46,425 @@ var __exportAll = (all, no_symbols) => {
|
|
|
47
46
|
return target;
|
|
48
47
|
};
|
|
49
48
|
//#endregion
|
|
49
|
+
//#region ../client/src/http/response-shape.ts
|
|
50
|
+
async function parseJsonResponse(text, schema, context) {
|
|
51
|
+
try {
|
|
52
|
+
return parseJson(text, schema, { source: context.url });
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error instanceof ValidationError) throw ResponseShapeError.fromZodIssues({
|
|
55
|
+
kind: "zod",
|
|
56
|
+
method: context.method,
|
|
57
|
+
url: context.url,
|
|
58
|
+
status: context.status,
|
|
59
|
+
zodIssues: error.developerDetail.zodIssues,
|
|
60
|
+
serverTag: await context.getServerTag(),
|
|
61
|
+
serverSkew: context.serverSkew
|
|
62
|
+
});
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region ../client/src/http/retry.ts
|
|
68
|
+
const BASE_DELAY_MS = 250;
|
|
69
|
+
const MAX_DELAY_MS = 8e3;
|
|
70
|
+
const MS_PER_SECOND = 1e3;
|
|
71
|
+
function backoffDelay({ attempt, retryAfterHeader }) {
|
|
72
|
+
const fromHeader = parseRetryAfter(retryAfterHeader);
|
|
73
|
+
if (fromHeader !== null) return fromHeader;
|
|
74
|
+
return Math.min(MAX_DELAY_MS, BASE_DELAY_MS * 2 ** attempt);
|
|
75
|
+
}
|
|
76
|
+
function parseRetryAfter(header) {
|
|
77
|
+
if (!header) return null;
|
|
78
|
+
const numeric = Number(header);
|
|
79
|
+
if (Number.isFinite(numeric) && numeric >= 0) return Math.round(numeric * MS_PER_SECOND);
|
|
80
|
+
const dateMs = Date.parse(header);
|
|
81
|
+
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function sleep(ms, signal) {
|
|
85
|
+
return setTimeout(ms, void 0, { signal });
|
|
86
|
+
}
|
|
87
|
+
async function runWithRetries(attempt, signal) {
|
|
88
|
+
let attemptIndex = 0;
|
|
89
|
+
while (true) {
|
|
90
|
+
const outcome = await attempt(attemptIndex);
|
|
91
|
+
if (outcome.kind === "success") return outcome.response;
|
|
92
|
+
await sleep(outcome.delayMs, signal);
|
|
93
|
+
attemptIndex += 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
97
|
+
const OCTET_STREAM_CONTENT_TYPE = "application/octet-stream";
|
|
98
|
+
const TEXT_CONTENT_TYPE_PREFIX = "text/";
|
|
99
|
+
const ERROR_BODY_BYTE_CAP = 64 * 1024;
|
|
100
|
+
const IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
|
|
101
|
+
"GET",
|
|
102
|
+
"HEAD",
|
|
103
|
+
"OPTIONS"
|
|
104
|
+
]);
|
|
105
|
+
function createTransport(config, options) {
|
|
106
|
+
const baseUrl = normalizeUrl(config.url);
|
|
107
|
+
assertCredentialHeaderSafe(config.credential);
|
|
108
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
109
|
+
let inForce = options.server ?? null;
|
|
110
|
+
let newest = null;
|
|
111
|
+
let probesStarted = 0;
|
|
112
|
+
let applied = null;
|
|
113
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
114
|
+
const getServerTag = options.getServerTag ?? (async () => tagOf(inForce));
|
|
115
|
+
const refreshCredential = options.refreshCredential;
|
|
116
|
+
let credential = config.credential;
|
|
117
|
+
const knownSecrets = new Set(credentialSecrets(credential));
|
|
118
|
+
const redactionContext = { knownSecrets };
|
|
119
|
+
let refreshInFlight = null;
|
|
120
|
+
function tryRefreshCredential() {
|
|
121
|
+
const refresh = refreshCredential;
|
|
122
|
+
if (credential.kind !== "oauth" || refresh === void 0) return Promise.resolve(false);
|
|
123
|
+
refreshInFlight ??= refreshOnce(refresh).finally(() => {
|
|
124
|
+
refreshInFlight = null;
|
|
125
|
+
});
|
|
126
|
+
return refreshInFlight;
|
|
127
|
+
}
|
|
128
|
+
async function refreshOnce(refresh) {
|
|
129
|
+
const next = await refresh();
|
|
130
|
+
if (next === null) return false;
|
|
131
|
+
credential = next;
|
|
132
|
+
for (const secret of credentialSecrets(next)) knownSecrets.add(secret);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async function attemptOnce(prepared, attempt) {
|
|
136
|
+
const hasRetriesLeft = attempt < prepared.retries;
|
|
137
|
+
const timeoutSignal = AbortSignal.timeout(prepared.timeoutMs);
|
|
138
|
+
const combined = combineAborts(timeoutSignal, prepared.cancelSignal);
|
|
139
|
+
let response;
|
|
140
|
+
try {
|
|
141
|
+
response = await fetchImpl(prepared.url, {
|
|
142
|
+
method: prepared.method,
|
|
143
|
+
headers: prepared.headers,
|
|
144
|
+
body: prepared.body,
|
|
145
|
+
signal: combined
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throwIfAborted(prepared.cancelSignal);
|
|
149
|
+
const isStaleSocketFirstTry = attempt === 0 && isConnectionClosed(error);
|
|
150
|
+
if (prepared.idempotent && (hasRetriesLeft || isStaleSocketFirstTry)) return {
|
|
151
|
+
kind: "retry",
|
|
152
|
+
delayMs: backoffDelay({ attempt })
|
|
153
|
+
};
|
|
154
|
+
if (timeoutSignal.aborted) throw new TimeoutError(`Request timed out after ${prepared.timeoutMs}ms`, {
|
|
155
|
+
kind: "http",
|
|
156
|
+
method: prepared.method,
|
|
157
|
+
url: prepared.url,
|
|
158
|
+
timeoutMs: prepared.timeoutMs
|
|
159
|
+
});
|
|
160
|
+
throw buildNetworkError(error, prepared.method, prepared.url);
|
|
161
|
+
}
|
|
162
|
+
const canRetryStatus = hasRetriesLeft && prepared.idempotent;
|
|
163
|
+
if (!response.ok && isRetryableStatus(response.status) && canRetryStatus) {
|
|
164
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
165
|
+
response.body?.cancel().catch(() => void 0);
|
|
166
|
+
return {
|
|
167
|
+
kind: "retry",
|
|
168
|
+
delayMs: backoffDelay({
|
|
169
|
+
attempt,
|
|
170
|
+
retryAfterHeader: retryAfter
|
|
171
|
+
})
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
const rawBody = await readBodyForError(response);
|
|
176
|
+
const serverTag = await getServerTag();
|
|
177
|
+
throw new HttpError({
|
|
178
|
+
status: response.status,
|
|
179
|
+
statusText: response.statusText,
|
|
180
|
+
method: prepared.method,
|
|
181
|
+
url: prepared.url,
|
|
182
|
+
responseHeaders: response.headers,
|
|
183
|
+
rawBody,
|
|
184
|
+
serverTag,
|
|
185
|
+
redactionContext
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
assertContentType(response, prepared);
|
|
189
|
+
return {
|
|
190
|
+
kind: "success",
|
|
191
|
+
response
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async function executeRaw(prepared) {
|
|
195
|
+
try {
|
|
196
|
+
return await runWithRetries((attempt) => attemptOnce(prepared, attempt), prepared.cancelSignal);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throwIfAborted(prepared.cancelSignal);
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function executeWithAuthRefresh(path, opts) {
|
|
203
|
+
const prepared = prepare(path, opts);
|
|
204
|
+
try {
|
|
205
|
+
return {
|
|
206
|
+
response: await executeRaw(prepared),
|
|
207
|
+
prepared
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof HttpError && error.status === 401) {
|
|
211
|
+
if (await tryRefreshCredential()) {
|
|
212
|
+
const retried = prepare(path, opts);
|
|
213
|
+
return {
|
|
214
|
+
response: await executeRaw(retried),
|
|
215
|
+
prepared: retried
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function prepare(path, opts = {}) {
|
|
223
|
+
const method = opts.method ?? "GET";
|
|
224
|
+
const expectContentType = opts.expectContentType ?? "json";
|
|
225
|
+
const url = buildUrl(baseUrl, path, opts.query);
|
|
226
|
+
const headers = new Headers();
|
|
227
|
+
const auth = credentialAuthHeader(credential);
|
|
228
|
+
headers.set(auth.name, auth.value);
|
|
229
|
+
headers.set("accept", acceptHeader(expectContentType));
|
|
230
|
+
headers.set("user-agent", options.userAgent);
|
|
231
|
+
let body = null;
|
|
232
|
+
if (opts.body !== void 0 && opts.body !== null) if (typeof opts.body === "string" || opts.body instanceof URLSearchParams) body = opts.body;
|
|
233
|
+
else if (opts.body instanceof FormData || opts.body instanceof ReadableStream) body = opts.body;
|
|
234
|
+
else if (opts.body instanceof Uint8Array) {
|
|
235
|
+
body = opts.body;
|
|
236
|
+
headers.set("content-type", OCTET_STREAM_CONTENT_TYPE);
|
|
237
|
+
} else {
|
|
238
|
+
body = JSON.stringify(opts.body);
|
|
239
|
+
headers.set("content-type", JSON_CONTENT_TYPE);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
url,
|
|
243
|
+
method,
|
|
244
|
+
headers,
|
|
245
|
+
body,
|
|
246
|
+
expectContentType,
|
|
247
|
+
retries: opts.retries ?? 3,
|
|
248
|
+
idempotent: opts.idempotent ?? IDEMPOTENT_METHODS.has(method),
|
|
249
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
250
|
+
cancelSignal: combineAborts(opts.signal, options.signal)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async function readJson(path, opts = {}) {
|
|
254
|
+
const chosenFor = inForce;
|
|
255
|
+
const { response, prepared } = await executeWithAuthRefresh(path, {
|
|
256
|
+
...opts,
|
|
257
|
+
expectContentType: "json"
|
|
258
|
+
});
|
|
259
|
+
return {
|
|
260
|
+
text: await response.text(),
|
|
261
|
+
context: {
|
|
262
|
+
method: prepared.method,
|
|
263
|
+
url: prepared.url,
|
|
264
|
+
status: response.status,
|
|
265
|
+
...namingOf(chosenFor)
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function namingOf(profile) {
|
|
270
|
+
if (profile === null) return {
|
|
271
|
+
getServerTag,
|
|
272
|
+
serverSkew: null
|
|
273
|
+
};
|
|
274
|
+
return {
|
|
275
|
+
getServerTag: async () => profile.version.tag,
|
|
276
|
+
serverSkew: profile.skew
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
async function runProbe(order, budget) {
|
|
280
|
+
const read = await readJson(PROBE_PATH, probeBudget(budget));
|
|
281
|
+
const properties = await parseJsonResponse(read.text, SessionProperties, read.context);
|
|
282
|
+
const info = serverInfoFromProperties(properties);
|
|
283
|
+
const profile = createServerProfile(info);
|
|
284
|
+
if (applied === null || applied.order < order) {
|
|
285
|
+
options.onServerProbed?.(info, profile);
|
|
286
|
+
applied = {
|
|
287
|
+
order,
|
|
288
|
+
profile
|
|
289
|
+
};
|
|
290
|
+
inForce = profile;
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
properties,
|
|
294
|
+
profile,
|
|
295
|
+
read
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function startProbe(budget) {
|
|
299
|
+
probesStarted += 1;
|
|
300
|
+
const probed = runProbe(probesStarted, budget);
|
|
301
|
+
newest = probed;
|
|
302
|
+
inFlight.add(probed);
|
|
303
|
+
probed.then(() => {
|
|
304
|
+
inFlight.delete(probed);
|
|
305
|
+
}, () => {
|
|
306
|
+
inFlight.delete(probed);
|
|
307
|
+
if (newest === probed) newest = null;
|
|
308
|
+
});
|
|
309
|
+
return probed;
|
|
310
|
+
}
|
|
311
|
+
function appliedProfile() {
|
|
312
|
+
return applied === null ? null : applied.profile;
|
|
313
|
+
}
|
|
314
|
+
function verifying() {
|
|
315
|
+
const settled = appliedProfile();
|
|
316
|
+
if (newest === null && settled !== null) return Promise.resolve(settled);
|
|
317
|
+
return (newest ?? startProbe({})).then((probed) => probed.profile, fallBackTo(appliedProfile));
|
|
318
|
+
}
|
|
319
|
+
function profileInForce() {
|
|
320
|
+
const current = inForce;
|
|
321
|
+
if (current === null) return verifying();
|
|
322
|
+
if (newest === null) return Promise.resolve(current);
|
|
323
|
+
return newest.then((probed) => probed.profile, fallBackTo(() => inForce));
|
|
324
|
+
}
|
|
325
|
+
async function server(wait = {}) {
|
|
326
|
+
throwIfAborted(wait.signal);
|
|
327
|
+
return untilAborted(profileInForce(), wait.signal);
|
|
328
|
+
}
|
|
329
|
+
async function verifiedServer(wait = {}) {
|
|
330
|
+
throwIfAborted(wait.signal);
|
|
331
|
+
return untilAborted(verifying(), wait.signal);
|
|
332
|
+
}
|
|
333
|
+
async function probe(reader, read = {}) {
|
|
334
|
+
const { signal, ...budget } = read;
|
|
335
|
+
throwIfAborted(signal);
|
|
336
|
+
const probed = await untilAborted(startProbe(budget), signal);
|
|
337
|
+
const context = {
|
|
338
|
+
...probed.read.context,
|
|
339
|
+
...namingOf(probed.profile)
|
|
340
|
+
};
|
|
341
|
+
return parseJsonResponse(probed.read.text, reader, context);
|
|
342
|
+
}
|
|
343
|
+
async function probesSettled() {
|
|
344
|
+
while (inFlight.size > 0) await Promise.allSettled(inFlight);
|
|
345
|
+
}
|
|
346
|
+
async function requireFeatures(features, wait = {}) {
|
|
347
|
+
if (features.length === 0) return;
|
|
348
|
+
const profile = await preflightServer(wait);
|
|
349
|
+
if (profile === null) return;
|
|
350
|
+
for (const failure of featureFailures(features, profile)) refuseBeforeSending(new CapabilityError(failure));
|
|
351
|
+
}
|
|
352
|
+
async function preflightServer(wait = {}) {
|
|
353
|
+
try {
|
|
354
|
+
return await verifiedServer(wait);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if (!isNonInterruptFailure(error)) throw error;
|
|
357
|
+
skipOrThrow({
|
|
358
|
+
kind: "unverified",
|
|
359
|
+
failure: error
|
|
360
|
+
});
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function refuseBeforeSending(refusal) {
|
|
365
|
+
skipOrThrow({
|
|
366
|
+
kind: "refused",
|
|
367
|
+
refusal
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function skipOrThrow(skipped) {
|
|
371
|
+
const hook = options.onPreflightSkipped;
|
|
372
|
+
if (hook === void 0) throw skippedError(skipped);
|
|
373
|
+
hook(skipped);
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
server,
|
|
377
|
+
verifiedServer,
|
|
378
|
+
probe,
|
|
379
|
+
requireFeatures,
|
|
380
|
+
preflightServer,
|
|
381
|
+
refuseBeforeSending,
|
|
382
|
+
probesSettled,
|
|
383
|
+
async requestRaw(path, opts) {
|
|
384
|
+
return (await executeWithAuthRefresh(path, opts ?? {})).response;
|
|
385
|
+
},
|
|
386
|
+
async requestParsed(schema, path, opts) {
|
|
387
|
+
const read = await readJson(path, opts);
|
|
388
|
+
return parseJsonResponse(read.text, schema, read.context);
|
|
389
|
+
},
|
|
390
|
+
async requestStream(path, opts) {
|
|
391
|
+
const { response, prepared } = await executeWithAuthRefresh(path, {
|
|
392
|
+
...opts,
|
|
393
|
+
expectContentType: opts?.expectContentType ?? "binary"
|
|
394
|
+
});
|
|
395
|
+
if (!response.body) throw new NetworkError("Response had no body to stream", {
|
|
396
|
+
method: prepared.method,
|
|
397
|
+
url: prepared.url,
|
|
398
|
+
cause: "missing body"
|
|
399
|
+
});
|
|
400
|
+
return response.body;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function skippedError(skipped) {
|
|
405
|
+
return skipped.kind === "refused" ? skipped.refusal : skipped.failure;
|
|
406
|
+
}
|
|
407
|
+
function fallBackTo(fallback) {
|
|
408
|
+
return (error) => {
|
|
409
|
+
const profile = fallback();
|
|
410
|
+
if (profile !== null && isNonInterruptFailure(error)) return profile;
|
|
411
|
+
throw error;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function tagOf(profile) {
|
|
415
|
+
return profile === null ? null : profile.version.tag;
|
|
416
|
+
}
|
|
417
|
+
function buildUrl(baseUrl, path, query) {
|
|
418
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
419
|
+
const target = new URL(baseUrl + normalizedPath);
|
|
420
|
+
if (query) for (const [key, value] of Object.entries(query)) {
|
|
421
|
+
if (value === void 0) continue;
|
|
422
|
+
if (Array.isArray(value)) for (const entry of value) target.searchParams.append(key, String(entry));
|
|
423
|
+
else target.searchParams.append(key, String(value));
|
|
424
|
+
}
|
|
425
|
+
return target.toString();
|
|
426
|
+
}
|
|
427
|
+
function acceptHeader(expected) {
|
|
428
|
+
if (expected === "json") return JSON_CONTENT_TYPE;
|
|
429
|
+
if (expected === "text") return "text/*";
|
|
430
|
+
return "*/*";
|
|
431
|
+
}
|
|
432
|
+
function assertContentType(response, prepared) {
|
|
433
|
+
if (prepared.expectContentType === "binary") return;
|
|
434
|
+
const contentType = response.headers.get("content-type");
|
|
435
|
+
if (contentType === null) throwContentTypeMismatch(response, prepared, prepared.expectContentType);
|
|
436
|
+
if (prepared.expectContentType === "json" && !contentType.includes("application/json")) throwContentTypeMismatch(response, prepared, "json");
|
|
437
|
+
if (prepared.expectContentType === "text" && !contentType.startsWith(TEXT_CONTENT_TYPE_PREFIX)) throwContentTypeMismatch(response, prepared, "text");
|
|
438
|
+
}
|
|
439
|
+
function throwContentTypeMismatch(response, prepared, expected) {
|
|
440
|
+
const actual = response.headers.get("content-type") ?? "no content-type";
|
|
441
|
+
throw new HttpError({
|
|
442
|
+
status: response.status,
|
|
443
|
+
statusText: response.statusText,
|
|
444
|
+
method: prepared.method,
|
|
445
|
+
url: prepared.url,
|
|
446
|
+
responseHeaders: response.headers,
|
|
447
|
+
rawBody: null,
|
|
448
|
+
overrideUserMessage: `Expected ${expected} response but got ${actual}`
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
async function readBodyForError(response) {
|
|
452
|
+
try {
|
|
453
|
+
return Buffer.from(await response.arrayBuffer()).subarray(0, ERROR_BODY_BYTE_CAP).toString("utf8");
|
|
454
|
+
} catch (error) {
|
|
455
|
+
return `[body read failed: ${errorMessage(error)}]`;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
//#endregion
|
|
50
459
|
//#region ../client/src/resources/pivot-export.ts
|
|
51
|
-
const
|
|
460
|
+
const PivotedExportsProperty = SessionProperties.extend({ "enable-pivoted-exports": z.boolean() });
|
|
52
461
|
/**
|
|
53
462
|
* Refuse a pivoted export the server would answer with plain rows: a JSON export keeps only the
|
|
54
463
|
* ungrouped rows, and an admin can turn pivoted CSV and XLSX exports off.
|
|
55
464
|
*/
|
|
56
465
|
async function assertPivotedExport(transport, format, options) {
|
|
57
466
|
if (!PivotExportFormat.safeParse(format).success) throw new ConfigError(`a ${format} export cannot be pivoted; only ${PivotExportFormat.options.join(" and ")} can`);
|
|
58
|
-
if (!(await transport.
|
|
467
|
+
if (!(await transport.probe(PivotedExportsProperty, options))["enable-pivoted-exports"]) throw new ConfigError("the server has pivoted exports turned off (the enable-pivoted-exports setting), so it would export the plain rows");
|
|
59
468
|
}
|
|
60
469
|
//#endregion
|
|
61
470
|
//#region ../client/src/resources/card.ts
|
|
@@ -63,7 +472,6 @@ const CardApiList = z.array(Card);
|
|
|
63
472
|
function cardResource(transport) {
|
|
64
473
|
/** List cards. `f` picks a server-side preset; `model_id` scopes the presets that need an id. */
|
|
65
474
|
async function list(params = {}, options = {}) {
|
|
66
|
-
await transport.require("card.list", options);
|
|
67
475
|
return {
|
|
68
476
|
data: await transport.requestParsed(CardApiList, "/api/card", {
|
|
69
477
|
...options,
|
|
@@ -77,12 +485,10 @@ function cardResource(transport) {
|
|
|
77
485
|
}
|
|
78
486
|
/** Get one card by id. */
|
|
79
487
|
async function get(id, options = {}) {
|
|
80
|
-
await transport.require("card.get", options);
|
|
81
488
|
return transport.requestParsed(Card, `/api/card/${id}`, { ...options });
|
|
82
489
|
}
|
|
83
490
|
/** Create a card — a question, a model, or a metric — from a full card body. */
|
|
84
491
|
async function create(params, options = {}) {
|
|
85
|
-
await transport.require("card.create", options);
|
|
86
492
|
return transport.requestParsed(Card, "/api/card", {
|
|
87
493
|
...options,
|
|
88
494
|
method: "POST",
|
|
@@ -91,7 +497,6 @@ function cardResource(transport) {
|
|
|
91
497
|
}
|
|
92
498
|
/** Update a card by id, patching only the fields the body carries. */
|
|
93
499
|
async function update(id, params, options = {}) {
|
|
94
|
-
await transport.require("card.update", options);
|
|
95
500
|
return transport.requestParsed(Card, `/api/card/${id}`, {
|
|
96
501
|
...options,
|
|
97
502
|
method: "PUT",
|
|
@@ -100,12 +505,10 @@ function cardResource(transport) {
|
|
|
100
505
|
}
|
|
101
506
|
/** Archive (soft-delete) a card by id. Metabase models this as an update, not its own endpoint. */
|
|
102
507
|
async function archive(id, options = {}) {
|
|
103
|
-
await transport.require("card.archive", options);
|
|
104
508
|
return update(id, { archived: true }, options);
|
|
105
509
|
}
|
|
106
510
|
/** Run a saved card and return the query result envelope. */
|
|
107
511
|
async function query(id, params, options = {}) {
|
|
108
|
-
await transport.require("card.query", options);
|
|
109
512
|
return transport.requestParsed(CardQueryResult, `/api/card/${id}/query`, {
|
|
110
513
|
...options,
|
|
111
514
|
method: "POST",
|
|
@@ -121,9 +524,8 @@ function cardResource(transport) {
|
|
|
121
524
|
* there before the wire.
|
|
122
525
|
*/
|
|
123
526
|
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
527
|
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
528
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
127
529
|
const body = new URLSearchParams({
|
|
128
530
|
parameters: JSON.stringify(params.parameters),
|
|
129
531
|
format_rows: String(params.format_rows),
|
|
@@ -141,7 +543,6 @@ function cardResource(transport) {
|
|
|
141
543
|
* snippets its query references, plus the card's own metadata for a model or a native query.
|
|
142
544
|
*/
|
|
143
545
|
async function queryMetadata(id, options = {}) {
|
|
144
|
-
await transport.require("card.queryMetadata", options);
|
|
145
546
|
return transport.requestParsed(QueryMetadata, `/api/card/${id}/query_metadata`, { ...options });
|
|
146
547
|
}
|
|
147
548
|
return {
|
|
@@ -227,7 +628,6 @@ function collectionResource(transport) {
|
|
|
227
628
|
* preset includes the Library collections that match it.
|
|
228
629
|
*/
|
|
229
630
|
async function list(params = {}, options = {}) {
|
|
230
|
-
await transport.require("collection.list", options);
|
|
231
631
|
return {
|
|
232
632
|
data: await transport.requestParsed(CollectionApiList, "/api/collection", {
|
|
233
633
|
...options,
|
|
@@ -238,12 +638,10 @@ function collectionResource(transport) {
|
|
|
238
638
|
}
|
|
239
639
|
/** Get one collection by id, entity id, or the `root`/`trash` alias. */
|
|
240
640
|
async function get(ref, options = {}) {
|
|
241
|
-
await transport.require("collection.get", options);
|
|
242
641
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, { ...options });
|
|
243
642
|
}
|
|
244
643
|
/** Create a new collection. */
|
|
245
644
|
async function create(params, options = {}) {
|
|
246
|
-
await transport.require("collection.create", options);
|
|
247
645
|
return transport.requestParsed(Collection, "/api/collection", {
|
|
248
646
|
...options,
|
|
249
647
|
method: "POST",
|
|
@@ -256,7 +654,6 @@ function collectionResource(transport) {
|
|
|
256
654
|
* moves a collection into the trash ignores `parent_id`.
|
|
257
655
|
*/
|
|
258
656
|
async function update(ref, params, options = {}) {
|
|
259
|
-
await transport.require("collection.update", options);
|
|
260
657
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, {
|
|
261
658
|
...options,
|
|
262
659
|
method: "PUT",
|
|
@@ -265,7 +662,6 @@ function collectionResource(transport) {
|
|
|
265
662
|
}
|
|
266
663
|
/** Archive (soft-delete) a collection. Metabase models this as an update, not its own endpoint. */
|
|
267
664
|
async function archive(ref, options = {}) {
|
|
268
|
-
await transport.require("collection.archive", options);
|
|
269
665
|
return update(ref, { archived: true }, options);
|
|
270
666
|
}
|
|
271
667
|
/**
|
|
@@ -273,7 +669,6 @@ function collectionResource(transport) {
|
|
|
273
669
|
* the caller consumes pages rather than a single list and decides how far to pull.
|
|
274
670
|
*/
|
|
275
671
|
async function* itemPages(ref, params = {}, options = {}) {
|
|
276
|
-
await transport.require("collection.itemPages", options);
|
|
277
672
|
yield* paginatePages(transport, `/api/collection/${refPath(ref)}/items`, CollectionItem, {
|
|
278
673
|
query: {
|
|
279
674
|
models: params.models,
|
|
@@ -288,7 +683,6 @@ function collectionResource(transport) {
|
|
|
288
683
|
}
|
|
289
684
|
/** Fetch the collection hierarchy as a forest of nested nodes. */
|
|
290
685
|
async function tree(params = {}, options = {}) {
|
|
291
|
-
await transport.require("collection.tree", options);
|
|
292
686
|
return {
|
|
293
687
|
data: await transport.requestParsed(CollectionTreeApiList, "/api/collection/tree", {
|
|
294
688
|
...options,
|
|
@@ -308,6 +702,128 @@ function collectionResource(transport) {
|
|
|
308
702
|
};
|
|
309
703
|
}
|
|
310
704
|
//#endregion
|
|
705
|
+
//#region ../client/src/version/refusal.ts
|
|
706
|
+
const REFUSAL_STATUSES = /* @__PURE__ */ new Set([
|
|
707
|
+
400,
|
|
708
|
+
402,
|
|
709
|
+
404
|
|
710
|
+
]);
|
|
711
|
+
function explainer(transport, namespace) {
|
|
712
|
+
function explain(name, method, parameters) {
|
|
713
|
+
const key = methodKey(namespace, name);
|
|
714
|
+
return async (...args) => {
|
|
715
|
+
try {
|
|
716
|
+
return await method(...args);
|
|
717
|
+
} catch (error) {
|
|
718
|
+
throw await explainRefusal(transport, callRequirement(key, parameters, args), error);
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function explainWalk(name, method, parameters) {
|
|
723
|
+
const key = methodKey(namespace, name);
|
|
724
|
+
return async function* (...args) {
|
|
725
|
+
try {
|
|
726
|
+
yield* method(...args);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
throw await explainRefusal(transport, callRequirement(key, parameters, args), error);
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function refuse(name, method) {
|
|
733
|
+
const key = methodKey(namespace, name);
|
|
734
|
+
return async (...args) => {
|
|
735
|
+
await transport.requireFeatures(methodRequirements(key), trailingWait(args));
|
|
736
|
+
return method(...args);
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function refuseAfterReading(name, reader, method) {
|
|
740
|
+
const key = methodKey(namespace, name);
|
|
741
|
+
return async (options = {}) => {
|
|
742
|
+
const properties = await transport.probe(reader, options);
|
|
743
|
+
const profile = profileFromProperties(properties);
|
|
744
|
+
for (const failure of featureFailures(methodRequirements(key), profile)) transport.refuseBeforeSending(new CapabilityError(failure));
|
|
745
|
+
return method(properties);
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
explain,
|
|
750
|
+
explainWalk,
|
|
751
|
+
refuse,
|
|
752
|
+
refuseAfterReading
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
function trailingWait(args) {
|
|
756
|
+
const last = args.at(-1);
|
|
757
|
+
if (isPlainObject(last) && last["signal"] instanceof AbortSignal) return { signal: last["signal"] };
|
|
758
|
+
return {};
|
|
759
|
+
}
|
|
760
|
+
function methodKey(namespace, name) {
|
|
761
|
+
const key = `${namespace}.${name}`;
|
|
762
|
+
if (!isMethodKey(key)) throw new InternalError(`no requirements entry for ${key}`);
|
|
763
|
+
return key;
|
|
764
|
+
}
|
|
765
|
+
function callRequirement(key, parameters, args) {
|
|
766
|
+
return {
|
|
767
|
+
parameters: parameters === void 0 ? [] : parameters(...args),
|
|
768
|
+
method: methodRequirements(key)
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* The error a call failed with, as a `CapabilityError` carrying the server's answer when the server
|
|
773
|
+
* refused it for lacking one of the features `call` needs, and unchanged otherwise. The server's own
|
|
774
|
+
* refusal is the verdict; the profile only names what was missing. The explaining probe is bounded
|
|
775
|
+
* by its own timeout and the client's signal; the error stands when the probe fails, and an
|
|
776
|
+
* interrupt surfaces as one.
|
|
777
|
+
*/
|
|
778
|
+
async function explainRefusal(transport, call, error) {
|
|
779
|
+
if (!mayBeRefusal(call, error)) return error;
|
|
780
|
+
let profile;
|
|
781
|
+
try {
|
|
782
|
+
profile = await transport.verifiedServer();
|
|
783
|
+
} catch (probeError) {
|
|
784
|
+
if (isNonInterruptFailure(probeError)) return error;
|
|
785
|
+
throw probeError;
|
|
786
|
+
}
|
|
787
|
+
const failure = explainedFailure(call, profile, error);
|
|
788
|
+
return failure === null ? error : new CapabilityError(failure, error);
|
|
789
|
+
}
|
|
790
|
+
/** Every feature `call` needs, parameters first, since a parameter's floor sits above its method's. */
|
|
791
|
+
function callFeatures(call) {
|
|
792
|
+
return [...call.parameters.map((parameter) => parameter.feature), ...call.method];
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Whether `error` may be the server refusing `call` for a feature it lacks. A call that needs no
|
|
796
|
+
* feature cannot be, and neither can a 404 for a missing row, which comes from a route the server
|
|
797
|
+
* serves.
|
|
798
|
+
*/
|
|
799
|
+
function mayBeRefusal(call, error) {
|
|
800
|
+
if (callFeatures(call).length === 0) return false;
|
|
801
|
+
if (!(error instanceof HttpError) || error.kind === "resource-missing") return false;
|
|
802
|
+
return REFUSAL_STATUSES.has(error.status);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* The feature `profile` lacks that explains `refusal` of `call`, or `null` when the server has every
|
|
806
|
+
* feature the call needs, so it failed for some other reason, or when the refusal points elsewhere.
|
|
807
|
+
*/
|
|
808
|
+
function explainedFailure(call, profile, refusal) {
|
|
809
|
+
const failure = checkFeatures(callFeatures(call), profile);
|
|
810
|
+
if (failure === null || pointsElsewhere(call, profile, refusal)) return null;
|
|
811
|
+
return failure;
|
|
812
|
+
}
|
|
813
|
+
function pointsElsewhere(call, profile, refusal) {
|
|
814
|
+
if (refusal.status !== 400 || checkFeatures(call.method, profile) !== null) return false;
|
|
815
|
+
const named = rejectedFields(refusal);
|
|
816
|
+
const gated = new Set(call.parameters.filter((parameter) => checkFeatures([parameter.feature], profile) !== null).flatMap((parameter) => parameter.fields));
|
|
817
|
+
return named.some((field) => !gated.has(field));
|
|
818
|
+
}
|
|
819
|
+
function rejectedFields(refusal) {
|
|
820
|
+
return [refusal.fieldErrors, refusal.specificFieldErrors].flatMap((errors) => errors === null ? [] : Object.keys(errors).map(topLevelField));
|
|
821
|
+
}
|
|
822
|
+
function topLevelField(path) {
|
|
823
|
+
const end = path.indexOf(".");
|
|
824
|
+
return end === -1 ? path : path.slice(0, end);
|
|
825
|
+
}
|
|
826
|
+
//#endregion
|
|
311
827
|
//#region ../client/src/resources/csv-upload.ts
|
|
312
828
|
const CSV_CONTENT_TYPE = "text/csv";
|
|
313
829
|
function buildCsvFormData(file) {
|
|
@@ -319,14 +835,13 @@ function buildCsvFormData(file) {
|
|
|
319
835
|
//#region ../client/src/resources/content-translation.ts
|
|
320
836
|
const CONTENT_TRANSLATION_PATH = "/api/ee/content-translation";
|
|
321
837
|
function contentTranslationResource(transport) {
|
|
838
|
+
const { explain } = explainer(transport, "contentTranslation");
|
|
322
839
|
/** Download the complete content translation dictionary as CSV. */
|
|
323
840
|
async function download(options = {}) {
|
|
324
|
-
await transport.require("contentTranslation.download", options);
|
|
325
841
|
return transport.requestStream(`${CONTENT_TRANSLATION_PATH}/csv`, options);
|
|
326
842
|
}
|
|
327
843
|
/** Replace the complete content translation dictionary with the attached CSV. */
|
|
328
844
|
async function upload(file, options = {}) {
|
|
329
|
-
await transport.require("contentTranslation.upload", options);
|
|
330
845
|
const form = buildCsvFormData(file);
|
|
331
846
|
return transport.requestParsed(ContentTranslationUploadResult, `${CONTENT_TRANSLATION_PATH}/upload-dictionary`, {
|
|
332
847
|
...options,
|
|
@@ -335,8 +850,8 @@ function contentTranslationResource(transport) {
|
|
|
335
850
|
});
|
|
336
851
|
}
|
|
337
852
|
return {
|
|
338
|
-
download,
|
|
339
|
-
upload
|
|
853
|
+
download: explain("download", download),
|
|
854
|
+
upload: explain("upload", upload)
|
|
340
855
|
};
|
|
341
856
|
}
|
|
342
857
|
//#endregion
|
|
@@ -347,7 +862,6 @@ function dashboardResource(transport) {
|
|
|
347
862
|
const card = cardResource(transport);
|
|
348
863
|
/** List dashboards. `f` picks a server-side preset. */
|
|
349
864
|
async function list(params = {}, options = {}) {
|
|
350
|
-
await transport.require("dashboard.list", options);
|
|
351
865
|
return {
|
|
352
866
|
data: await transport.requestParsed(DashboardApiList, "/api/dashboard", {
|
|
353
867
|
...options,
|
|
@@ -358,7 +872,6 @@ function dashboardResource(transport) {
|
|
|
358
872
|
}
|
|
359
873
|
/** Get one dashboard by id, with its dashcards, tabs and parameters. */
|
|
360
874
|
async function get(id, options = {}) {
|
|
361
|
-
await transport.require("dashboard.get", options);
|
|
362
875
|
return transport.requestParsed(DashboardDetail, `/api/dashboard/${id}`, { ...options });
|
|
363
876
|
}
|
|
364
877
|
/**
|
|
@@ -366,7 +879,6 @@ function dashboardResource(transport) {
|
|
|
366
879
|
* puts content on a fresh dashboard.
|
|
367
880
|
*/
|
|
368
881
|
async function create(params, options = {}) {
|
|
369
|
-
await transport.require("dashboard.create", options);
|
|
370
882
|
return transport.requestParsed(Dashboard, "/api/dashboard", {
|
|
371
883
|
...options,
|
|
372
884
|
method: "POST",
|
|
@@ -378,7 +890,6 @@ function dashboardResource(transport) {
|
|
|
378
890
|
* full replacements: whatever the body omits is deleted from the dashboard.
|
|
379
891
|
*/
|
|
380
892
|
async function update(id, params, options = {}) {
|
|
381
|
-
await transport.require("dashboard.update", options);
|
|
382
893
|
return transport.requestParsed(DashboardDetail, `/api/dashboard/${id}`, {
|
|
383
894
|
...options,
|
|
384
895
|
method: "PUT",
|
|
@@ -396,7 +907,6 @@ function dashboardResource(transport) {
|
|
|
396
907
|
* is duplicated. Action, link and placeholder dashcards are not copied.
|
|
397
908
|
*/
|
|
398
909
|
async function copy(id, params = {}, options = {}) {
|
|
399
|
-
await transport.require("dashboard.copy", options);
|
|
400
910
|
return transport.requestParsed(DashboardCopy, `/api/dashboard/${id}/copy`, {
|
|
401
911
|
...options,
|
|
402
912
|
method: "POST",
|
|
@@ -409,7 +919,6 @@ function dashboardResource(transport) {
|
|
|
409
919
|
* an archive is a state change on the dashboard itself, and its dashcards are not part of it.
|
|
410
920
|
*/
|
|
411
921
|
async function archive(id, options = {}) {
|
|
412
|
-
await transport.require("dashboard.archive", options);
|
|
413
922
|
return transport.requestParsed(Dashboard, `/api/dashboard/${id}`, {
|
|
414
923
|
...options,
|
|
415
924
|
method: "PUT",
|
|
@@ -422,7 +931,6 @@ function dashboardResource(transport) {
|
|
|
422
931
|
* currently stands.
|
|
423
932
|
*/
|
|
424
933
|
async function updateDashcard(dashboardId, dashcardId, params, options = {}) {
|
|
425
|
-
await transport.require("dashboard.updateDashcard", options);
|
|
426
934
|
const dashboard = await get(dashboardId, options);
|
|
427
935
|
const target = dashboard.dashcards.find((dashcard) => dashcard.id === dashcardId);
|
|
428
936
|
if (target === void 0) throw new ConfigError(`dashcard ${dashcardId} not found on dashboard ${dashboardId}`);
|
|
@@ -441,7 +949,6 @@ function dashboardResource(transport) {
|
|
|
441
949
|
* any other way propagates as the failure it was.
|
|
442
950
|
*/
|
|
443
951
|
async function checkCardReferences(dashcards, options = {}) {
|
|
444
|
-
await transport.require("dashboard.checkCardReferences", options);
|
|
445
952
|
const grouped = groupByCardId(collectCardReferences(dashcards));
|
|
446
953
|
return (await Promise.all(Array.from(grouped.entries()).map(async ([cardId, references]) => ({
|
|
447
954
|
references,
|
|
@@ -461,7 +968,7 @@ function dashboardResource(transport) {
|
|
|
461
968
|
} catch (error) {
|
|
462
969
|
if (!(error instanceof HttpError)) throw error;
|
|
463
970
|
if (error.kind === "resource-missing") return { reason: "missing" };
|
|
464
|
-
if (error.kind === "auth") return {
|
|
971
|
+
if (error.kind === "auth" || error.kind === "forbidden") return {
|
|
465
972
|
reason: "unreadable",
|
|
466
973
|
detail: error.userMessage
|
|
467
974
|
};
|
|
@@ -472,7 +979,6 @@ function dashboardResource(transport) {
|
|
|
472
979
|
* Fetch the selectable values for a dashboard parameter, through the chain-filter value endpoint.
|
|
473
980
|
*/
|
|
474
981
|
async function parameterValues(dashboardId, parameterId, options = {}) {
|
|
475
|
-
await transport.require("dashboard.parameterValues", options);
|
|
476
982
|
return transport.requestParsed(ParameterValues, `/api/dashboard/${dashboardId}/params/${encodeURIComponent(parameterId)}/values`, { ...options });
|
|
477
983
|
}
|
|
478
984
|
/**
|
|
@@ -480,7 +986,6 @@ function dashboardResource(transport) {
|
|
|
480
986
|
* substring, capped at the first 1000 matches.
|
|
481
987
|
*/
|
|
482
988
|
async function searchParameterValues(dashboardId, parameterId, query, options = {}) {
|
|
483
|
-
await transport.require("dashboard.searchParameterValues", options);
|
|
484
989
|
const parameter = encodeURIComponent(parameterId);
|
|
485
990
|
return transport.requestParsed(ParameterValues, `/api/dashboard/${dashboardId}/params/${parameter}/search/${encodeURIComponent(query)}`, { ...options });
|
|
486
991
|
}
|
|
@@ -532,81 +1037,6 @@ function stripEntityId(dashcard) {
|
|
|
532
1037
|
};
|
|
533
1038
|
}
|
|
534
1039
|
//#endregion
|
|
535
|
-
//#region ../client/src/resources/data-action.ts
|
|
536
|
-
function dataActionResource(transport) {
|
|
537
|
-
async function readSchema(options) {
|
|
538
|
-
const { features } = await transport.server(options);
|
|
539
|
-
return dataActionSchema(features);
|
|
540
|
-
}
|
|
541
|
-
/** List the unarchived data actions the caller can see. `GET /api/action` answers a bare array. */
|
|
542
|
-
async function list(options = {}) {
|
|
543
|
-
await transport.require("dataAction.list", options);
|
|
544
|
-
const schema = await readSchema(options);
|
|
545
|
-
return {
|
|
546
|
-
data: await transport.requestParsed(z.array(schema), "/api/action", { ...options }),
|
|
547
|
-
total: null
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
/** Get one unarchived data action by id. */
|
|
551
|
-
async function get(id, options = {}) {
|
|
552
|
-
await transport.require("dataAction.get", options);
|
|
553
|
-
const schema = await readSchema(options);
|
|
554
|
-
return transport.requestParsed(schema, `/api/action/${id}`, { ...options });
|
|
555
|
-
}
|
|
556
|
-
/** Create a data action — a parameterized native query that writes to a database — in a collection. */
|
|
557
|
-
async function create(params, options = {}) {
|
|
558
|
-
await transport.require("dataAction.create", options);
|
|
559
|
-
const schema = await readSchema(options);
|
|
560
|
-
return transport.requestParsed(schema, "/api/action", {
|
|
561
|
-
...options,
|
|
562
|
-
method: "POST",
|
|
563
|
-
body: params
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
/** Update a data action by id, patching only the fields the body carries. */
|
|
567
|
-
async function update(id, params, options = {}) {
|
|
568
|
-
await transport.require("dataAction.update", options);
|
|
569
|
-
const schema = await readSchema(options);
|
|
570
|
-
return transport.requestParsed(schema, `/api/action/${id}`, {
|
|
571
|
-
...options,
|
|
572
|
-
method: "PUT",
|
|
573
|
-
body: params
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
/** Archive (soft-delete) a data action by id. Metabase models this as an update, not its own endpoint. */
|
|
577
|
-
async function archive(id, options = {}) {
|
|
578
|
-
await transport.require("dataAction.archive", options);
|
|
579
|
-
return update(id, { archived: true }, options);
|
|
580
|
-
}
|
|
581
|
-
/** Delete a data action by id, removing it and the dashboard buttons that run it. */
|
|
582
|
-
async function remove(id, options = {}) {
|
|
583
|
-
await transport.require("dataAction.delete", options);
|
|
584
|
-
await transport.requestRaw(`/api/action/${id}`, {
|
|
585
|
-
...options,
|
|
586
|
-
method: "DELETE",
|
|
587
|
-
expectContentType: "binary"
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
/** Execute a data action by id with parameter values keyed by parameter id. */
|
|
591
|
-
async function execute(id, params, options = {}) {
|
|
592
|
-
await transport.require("dataAction.execute", options);
|
|
593
|
-
return transport.requestParsed(DataActionExecuteResult, `/api/action/${id}/execute`, {
|
|
594
|
-
...options,
|
|
595
|
-
method: "POST",
|
|
596
|
-
body: params
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
return {
|
|
600
|
-
list,
|
|
601
|
-
get,
|
|
602
|
-
create,
|
|
603
|
-
update,
|
|
604
|
-
archive,
|
|
605
|
-
delete: remove,
|
|
606
|
-
execute
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
//#endregion
|
|
610
1040
|
//#region ../client/src/resources/database.ts
|
|
611
1041
|
const DatabaseApiList = z.object({
|
|
612
1042
|
data: z.array(Database),
|
|
@@ -619,7 +1049,6 @@ const SYNC_COMPLETE = "complete";
|
|
|
619
1049
|
function databaseResource(transport) {
|
|
620
1050
|
/** List databases. `include` hydrates each database's tables; `saved` adds the Saved Questions virtual database. */
|
|
621
1051
|
async function list(params = {}, options = {}) {
|
|
622
|
-
await transport.require("database.list", options);
|
|
623
1052
|
const response = await transport.requestParsed(DatabaseApiList, "/api/database", {
|
|
624
1053
|
...options,
|
|
625
1054
|
query: {
|
|
@@ -634,7 +1063,6 @@ function databaseResource(transport) {
|
|
|
634
1063
|
}
|
|
635
1064
|
/** Get one database by id. `include` hydrates its tables, and `tables.fields` their fields too. */
|
|
636
1065
|
async function get(id, params = {}, options = {}) {
|
|
637
|
-
await transport.require("database.get", options);
|
|
638
1066
|
return transport.requestParsed(Database, `/api/database/${id}`, {
|
|
639
1067
|
...options,
|
|
640
1068
|
query: { include: params.include }
|
|
@@ -642,7 +1070,6 @@ function databaseResource(transport) {
|
|
|
642
1070
|
}
|
|
643
1071
|
/** List the schema names in a database. */
|
|
644
1072
|
async function schemas(id, options = {}) {
|
|
645
|
-
await transport.require("database.schemas", options);
|
|
646
1073
|
return {
|
|
647
1074
|
data: await transport.requestParsed(DatabaseApiSchemaList, `/api/database/${id}/schemas`, { ...options }),
|
|
648
1075
|
total: null
|
|
@@ -650,7 +1077,6 @@ function databaseResource(transport) {
|
|
|
650
1077
|
}
|
|
651
1078
|
/** List the tables one schema of a database holds. */
|
|
652
1079
|
async function schemaTables(id, schema, options = {}) {
|
|
653
|
-
await transport.require("database.schemaTables", options);
|
|
654
1080
|
return {
|
|
655
1081
|
data: await transport.requestParsed(DatabaseApiSchemaTableList, `/api/database/${id}/schema/${encodeURIComponent(schema)}`, { ...options }),
|
|
656
1082
|
total: null
|
|
@@ -661,7 +1087,6 @@ function databaseResource(transport) {
|
|
|
661
1087
|
* pass `wait` to poll the database until it reports its initial sync complete.
|
|
662
1088
|
*/
|
|
663
1089
|
async function syncSchema(id, params = {}, options = {}) {
|
|
664
|
-
await transport.require("database.syncSchema", options);
|
|
665
1090
|
const ack = await transport.requestParsed(DatabaseTaskAck, `/api/database/${id}/sync_schema`, {
|
|
666
1091
|
...options,
|
|
667
1092
|
method: "POST"
|
|
@@ -683,7 +1108,6 @@ function databaseResource(transport) {
|
|
|
683
1108
|
}
|
|
684
1109
|
/** Trigger a rescan of the cached field values across a database. */
|
|
685
1110
|
async function rescanValues(id, options = {}) {
|
|
686
|
-
await transport.require("database.rescanValues", options);
|
|
687
1111
|
return {
|
|
688
1112
|
id,
|
|
689
1113
|
status: (await transport.requestParsed(DatabaseTaskAck, `/api/database/${id}/rescan_values`, {
|
|
@@ -709,7 +1133,6 @@ function datasetResource(transport) {
|
|
|
709
1133
|
* MBQL, or native — rather than a reference to a saved one, so nothing here is a card.
|
|
710
1134
|
*/
|
|
711
1135
|
async function query(body, options = {}) {
|
|
712
|
-
await transport.require("dataset.query", options);
|
|
713
1136
|
return transport.requestParsed(CardQueryResult, "/api/dataset", {
|
|
714
1137
|
...options,
|
|
715
1138
|
method: "POST",
|
|
@@ -721,9 +1144,7 @@ function datasetResource(transport) {
|
|
|
721
1144
|
* defaults server-side to true.
|
|
722
1145
|
*/
|
|
723
1146
|
async function native(datasetQuery, params = {}, options = {}) {
|
|
724
|
-
|
|
725
|
-
const { features } = await transport.server(options);
|
|
726
|
-
return transport.requestParsed(compiledQuerySchema(features), "/api/dataset/native", {
|
|
1147
|
+
return transport.requestParsed(CompiledQuery, "/api/dataset/native", {
|
|
727
1148
|
...options,
|
|
728
1149
|
method: "POST",
|
|
729
1150
|
body: {
|
|
@@ -738,7 +1159,6 @@ function datasetResource(transport) {
|
|
|
738
1159
|
* widens the field filter.
|
|
739
1160
|
*/
|
|
740
1161
|
async function queryMetadata(datasetQuery, options = {}) {
|
|
741
|
-
await transport.require("dataset.queryMetadata", options);
|
|
742
1162
|
return transport.requestParsed(QueryMetadata, "/api/dataset/query_metadata", {
|
|
743
1163
|
...options,
|
|
744
1164
|
method: "POST",
|
|
@@ -757,9 +1177,8 @@ function datasetResource(transport) {
|
|
|
757
1177
|
* there before the wire.
|
|
758
1178
|
*/
|
|
759
1179
|
async function exportQuery(format, params, options = {}) {
|
|
760
|
-
await transport.require("dataset.exportQuery", options);
|
|
761
|
-
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
762
1180
|
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
1181
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
763
1182
|
const exported = params.pivot_results ? {
|
|
764
1183
|
...params.query,
|
|
765
1184
|
"was-pivot": true
|
|
@@ -787,15 +1206,20 @@ function datasetResource(transport) {
|
|
|
787
1206
|
//#region ../client/src/resources/dependency.ts
|
|
788
1207
|
const DependencyNodeApiList = z.array(DependencyNode);
|
|
789
1208
|
const DependencyEntityApiList = z.array(DependencyEntity);
|
|
1209
|
+
function graphTypeFeatures(type) {
|
|
1210
|
+
return type === "measure" ? [{
|
|
1211
|
+
feature: "measureDependencyGraph",
|
|
1212
|
+
fields: ["type"]
|
|
1213
|
+
}] : [];
|
|
1214
|
+
}
|
|
790
1215
|
function dependencyResource(transport) {
|
|
1216
|
+
const { explain, explainWalk } = explainer(transport, "dependency");
|
|
791
1217
|
/**
|
|
792
1218
|
* The upstream dependency graph of one entity. `nodes` holds the starting entity plus every
|
|
793
1219
|
* entity it depends on, directly or transitively; each edge runs from the dependent to what it
|
|
794
1220
|
* depends on. A measure starts a graph only on a server with `measureDependencyGraph`.
|
|
795
1221
|
*/
|
|
796
1222
|
async function graph(type, id, options = {}) {
|
|
797
|
-
await transport.require("dependency.graph", options);
|
|
798
|
-
if (type === "measure") await transport.requireFeatures(["measureDependencyGraph"], options);
|
|
799
1223
|
return transport.requestParsed(DependencyGraph, "/api/ee/dependencies/graph", {
|
|
800
1224
|
...options,
|
|
801
1225
|
query: {
|
|
@@ -811,7 +1235,6 @@ function dependencyResource(transport) {
|
|
|
811
1235
|
* personal collections, and `sort-column` / `sort-direction` order the result.
|
|
812
1236
|
*/
|
|
813
1237
|
async function dependents(type, id, params = {}, options = {}) {
|
|
814
|
-
await transport.require("dependency.dependents", options);
|
|
815
1238
|
return {
|
|
816
1239
|
data: await transport.requestParsed(DependencyNodeApiList, "/api/ee/dependencies/graph/dependents", {
|
|
817
1240
|
...options,
|
|
@@ -837,7 +1260,6 @@ function dependencyResource(transport) {
|
|
|
837
1260
|
* `sort-direction` order the result.
|
|
838
1261
|
*/
|
|
839
1262
|
async function broken(type, id, params = {}, options = {}) {
|
|
840
|
-
await transport.require("dependency.broken", options);
|
|
841
1263
|
return {
|
|
842
1264
|
data: await transport.requestParsed(DependencyEntityApiList, "/api/ee/dependencies/graph/broken", {
|
|
843
1265
|
...options,
|
|
@@ -864,7 +1286,6 @@ function dependencyResource(transport) {
|
|
|
864
1286
|
* This endpoint pages on the server, so the caller consumes pages and decides how far to pull.
|
|
865
1287
|
*/
|
|
866
1288
|
async function* unreferencedPages(params = {}, options = {}) {
|
|
867
|
-
await transport.require("dependency.unreferencedPages", options);
|
|
868
1289
|
if (queryLeavesNoKind(params)) {
|
|
869
1290
|
yield {
|
|
870
1291
|
items: [],
|
|
@@ -888,7 +1309,6 @@ function dependencyResource(transport) {
|
|
|
888
1309
|
* caller consumes pages and decides how far to pull.
|
|
889
1310
|
*/
|
|
890
1311
|
async function* breakingPages(params = {}, options = {}) {
|
|
891
|
-
await transport.require("dependency.breakingPages", options);
|
|
892
1312
|
if (queryLeavesNoKind(params)) {
|
|
893
1313
|
yield {
|
|
894
1314
|
items: [],
|
|
@@ -905,11 +1325,11 @@ function dependencyResource(transport) {
|
|
|
905
1325
|
});
|
|
906
1326
|
}
|
|
907
1327
|
return {
|
|
908
|
-
graph,
|
|
909
|
-
dependents,
|
|
910
|
-
broken,
|
|
911
|
-
unreferencedPages,
|
|
912
|
-
breakingPages
|
|
1328
|
+
graph: explain("graph", graph, graphTypeFeatures),
|
|
1329
|
+
dependents: explain("dependents", dependents),
|
|
1330
|
+
broken: explain("broken", broken),
|
|
1331
|
+
unreferencedPages: explainWalk("unreferencedPages", unreferencedPages),
|
|
1332
|
+
breakingPages: explainWalk("breakingPages", breakingPages)
|
|
913
1333
|
};
|
|
914
1334
|
}
|
|
915
1335
|
function queryLeavesNoKind(params) {
|
|
@@ -931,9 +1351,9 @@ function itemListQuery(params) {
|
|
|
931
1351
|
//#region ../client/src/resources/document.ts
|
|
932
1352
|
const DocumentApiList = z.object({ items: z.array(Document) }).loose();
|
|
933
1353
|
function documentResource(transport) {
|
|
1354
|
+
const { explain } = explainer(transport, "document");
|
|
934
1355
|
/** Get existing documents. */
|
|
935
1356
|
async function list(options = {}) {
|
|
936
|
-
await transport.require("document.list", options);
|
|
937
1357
|
return {
|
|
938
1358
|
data: (await transport.requestParsed(DocumentApiList, "/api/document", { ...options })).items,
|
|
939
1359
|
total: null
|
|
@@ -941,12 +1361,10 @@ function documentResource(transport) {
|
|
|
941
1361
|
}
|
|
942
1362
|
/** Return an existing document by id. */
|
|
943
1363
|
async function get(id, options = {}) {
|
|
944
|
-
await transport.require("document.get", options);
|
|
945
1364
|
return transport.requestParsed(Document, `/api/document/${id}`, { ...options });
|
|
946
1365
|
}
|
|
947
1366
|
/** Create a new document. */
|
|
948
1367
|
async function create(params, options = {}) {
|
|
949
|
-
await transport.require("document.create", options);
|
|
950
1368
|
return transport.requestParsed(Document, "/api/document", {
|
|
951
1369
|
...options,
|
|
952
1370
|
method: "POST",
|
|
@@ -955,7 +1373,6 @@ function documentResource(transport) {
|
|
|
955
1373
|
}
|
|
956
1374
|
/** Update an existing document by id. */
|
|
957
1375
|
async function update(id, params, options = {}) {
|
|
958
|
-
await transport.require("document.update", options);
|
|
959
1376
|
return transport.requestParsed(Document, `/api/document/${id}`, {
|
|
960
1377
|
...options,
|
|
961
1378
|
method: "PUT",
|
|
@@ -964,7 +1381,6 @@ function documentResource(transport) {
|
|
|
964
1381
|
}
|
|
965
1382
|
/** Archive (soft-delete) a document by id. Metabase models this as an update, not its own endpoint. */
|
|
966
1383
|
async function archive(id, options = {}) {
|
|
967
|
-
await transport.require("document.archive", options);
|
|
968
1384
|
return update(id, { archived: true }, options);
|
|
969
1385
|
}
|
|
970
1386
|
/**
|
|
@@ -972,7 +1388,6 @@ function documentResource(transport) {
|
|
|
972
1388
|
* it. An archived source is not found.
|
|
973
1389
|
*/
|
|
974
1390
|
async function copy(id, params = {}, options = {}) {
|
|
975
|
-
await transport.require("document.copy", options);
|
|
976
1391
|
return transport.requestParsed(Document, `/api/document/${id}/copy`, {
|
|
977
1392
|
...options,
|
|
978
1393
|
method: "POST",
|
|
@@ -985,7 +1400,7 @@ function documentResource(transport) {
|
|
|
985
1400
|
create,
|
|
986
1401
|
update,
|
|
987
1402
|
archive,
|
|
988
|
-
copy
|
|
1403
|
+
copy: explain("copy", copy)
|
|
989
1404
|
};
|
|
990
1405
|
}
|
|
991
1406
|
//#endregion
|
|
@@ -997,7 +1412,6 @@ function eidTranslationResource(transport) {
|
|
|
997
1412
|
* lookup succeeded — the numeric id.
|
|
998
1413
|
*/
|
|
999
1414
|
async function translate(params, options = {}) {
|
|
1000
|
-
await transport.require("eidTranslation.translate", options);
|
|
1001
1415
|
return transport.requestParsed(EidTranslateResult, "/api/eid-translation/translate", {
|
|
1002
1416
|
...options,
|
|
1003
1417
|
method: "POST",
|
|
@@ -1051,6 +1465,7 @@ const Erd = z.object({
|
|
|
1051
1465
|
//#region ../client/src/resources/erd.ts
|
|
1052
1466
|
const ERD_PATH = "/api/ee/erd";
|
|
1053
1467
|
function erdResource(transport) {
|
|
1468
|
+
const { explain } = explainer(transport, "erd");
|
|
1054
1469
|
/**
|
|
1055
1470
|
* Get the entity relationship diagram of a database: the readable tables as nodes, each with its
|
|
1056
1471
|
* fields, and the foreign keys between them as edges. `table-ids` makes those tables the focal
|
|
@@ -1058,7 +1473,6 @@ function erdResource(transport) {
|
|
|
1058
1473
|
* none.
|
|
1059
1474
|
*/
|
|
1060
1475
|
async function get(params, options = {}) {
|
|
1061
|
-
await transport.require("erd.get", options);
|
|
1062
1476
|
return transport.requestParsed(Erd, ERD_PATH, {
|
|
1063
1477
|
...options,
|
|
1064
1478
|
query: {
|
|
@@ -1068,7 +1482,7 @@ function erdResource(transport) {
|
|
|
1068
1482
|
}
|
|
1069
1483
|
});
|
|
1070
1484
|
}
|
|
1071
|
-
return { get };
|
|
1485
|
+
return { get: explain("get", get) };
|
|
1072
1486
|
}
|
|
1073
1487
|
//#endregion
|
|
1074
1488
|
//#region ../client/src/resources/optional-parsed.ts
|
|
@@ -1086,12 +1500,12 @@ async function fetchOptionalParsed(client, path, schema, options = {}) {
|
|
|
1086
1500
|
//#region ../client/src/resources/field.ts
|
|
1087
1501
|
const FieldApiSummary = z.tuple([z.tuple([z.literal("count"), z.number().int()]), z.tuple([z.literal("distincts"), z.number().int()])]);
|
|
1088
1502
|
function fieldResource(transport) {
|
|
1503
|
+
const { refuse } = explainer(transport, "field");
|
|
1089
1504
|
/**
|
|
1090
1505
|
* Get one field by id. A server that labels data sensitivity answers `data_sensitivity`, `null`
|
|
1091
1506
|
* for an unlabelled field; any other answers the field without the key (`hasDataSensitivity`).
|
|
1092
1507
|
*/
|
|
1093
1508
|
async function get(id, options = {}) {
|
|
1094
|
-
await transport.require("field.get", options);
|
|
1095
1509
|
const { features } = await transport.server(options);
|
|
1096
1510
|
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, { ...options });
|
|
1097
1511
|
}
|
|
@@ -1102,7 +1516,6 @@ function fieldResource(transport) {
|
|
|
1102
1516
|
* the label is refused there before the wire. Answers the field the way `get` does.
|
|
1103
1517
|
*/
|
|
1104
1518
|
async function update(id, params, options = {}) {
|
|
1105
|
-
await transport.require("field.update", options);
|
|
1106
1519
|
await transport.requireFeatures(params.data_sensitivity === void 0 ? [] : ["fieldDataSensitivity"], options);
|
|
1107
1520
|
const { features } = await transport.server(options);
|
|
1108
1521
|
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, {
|
|
@@ -1113,11 +1526,11 @@ function fieldResource(transport) {
|
|
|
1113
1526
|
}
|
|
1114
1527
|
/**
|
|
1115
1528
|
* 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
|
|
1529
|
+
* classifier; `null` withdraws it, so whatever label the classifier wrote shows again. A server
|
|
1530
|
+
* without the column drops the key silently, so the label is refused there before the wire.
|
|
1531
|
+
* Answers the field with the label it now carries.
|
|
1118
1532
|
*/
|
|
1119
1533
|
async function setDataSensitivity(id, params, options = {}) {
|
|
1120
|
-
await transport.require("field.setDataSensitivity", options);
|
|
1121
1534
|
return transport.requestParsed(FieldWithDataSensitivity, `/api/field/${id}`, {
|
|
1122
1535
|
...options,
|
|
1123
1536
|
method: "PUT",
|
|
@@ -1133,7 +1546,6 @@ function fieldResource(transport) {
|
|
|
1133
1546
|
* `value` against the display value, ignoring both `searchId` and `limit`.
|
|
1134
1547
|
*/
|
|
1135
1548
|
async function search(id, searchId, params, options = {}) {
|
|
1136
|
-
await transport.require("field.search", options);
|
|
1137
1549
|
return transport.requestParsed(FieldSearchMatches, `/api/field/${id}/search/${searchId}`, {
|
|
1138
1550
|
...options,
|
|
1139
1551
|
query: {
|
|
@@ -1148,7 +1560,6 @@ function fieldResource(transport) {
|
|
|
1148
1560
|
* `value` and ignores any text after it, refusing only a `value` that has none.
|
|
1149
1561
|
*/
|
|
1150
1562
|
async function remapping(id, remappedId, params, options = {}) {
|
|
1151
|
-
await transport.require("field.remapping", options);
|
|
1152
1563
|
const { features } = await transport.server(options);
|
|
1153
1564
|
return fetchOptionalParsed(transport, `/api/field/${id}/remapping/${remappedId}`, fieldRemappedValueSchema(features, id === remappedId), {
|
|
1154
1565
|
...options,
|
|
@@ -1157,7 +1568,6 @@ function fieldResource(transport) {
|
|
|
1157
1568
|
}
|
|
1158
1569
|
/** Get the row count and the distinct-value count for a field. */
|
|
1159
1570
|
async function summary(id, options = {}) {
|
|
1160
|
-
await transport.require("field.summary", options);
|
|
1161
1571
|
const [[, count], [, distincts]] = await transport.requestParsed(FieldApiSummary, `/api/field/${id}/summary`, { ...options });
|
|
1162
1572
|
return {
|
|
1163
1573
|
field_id: id,
|
|
@@ -1167,13 +1577,12 @@ function fieldResource(transport) {
|
|
|
1167
1577
|
}
|
|
1168
1578
|
/** Get the cached distinct values Metabase holds for a field. */
|
|
1169
1579
|
async function values(id, options = {}) {
|
|
1170
|
-
await transport.require("field.values", options);
|
|
1171
1580
|
return transport.requestParsed(FieldValues, `/api/field/${id}/values`, { ...options });
|
|
1172
1581
|
}
|
|
1173
1582
|
return {
|
|
1174
1583
|
get,
|
|
1175
1584
|
update,
|
|
1176
|
-
setDataSensitivity,
|
|
1585
|
+
setDataSensitivity: refuse("setDataSensitivity", setDataSensitivity),
|
|
1177
1586
|
search,
|
|
1178
1587
|
remapping,
|
|
1179
1588
|
summary,
|
|
@@ -1205,22 +1614,22 @@ const SyncStashStarted = z.object({
|
|
|
1205
1614
|
task_id: z.number().int().positive()
|
|
1206
1615
|
});
|
|
1207
1616
|
const RemoteSyncSetting = z.string().nullable();
|
|
1208
|
-
const RemoteSyncBranchProperty =
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1617
|
+
const RemoteSyncBranchProperty = SessionProperties.extend({ "remote-sync-branch": z.string().nullable().optional() });
|
|
1618
|
+
const RemoteSyncUrlProperty = SessionProperties.extend({ "remote-sync-url": z.string().nullable().optional() });
|
|
1619
|
+
const BRANCH_UNREADABLE_MESSAGE = "the remote-sync-branch setting is not readable: only admins see it, and a server without remote sync has none";
|
|
1620
|
+
const BRANCH_UNSET_MESSAGE = "git-sync tracks no branch: the remote-sync-branch setting is unset";
|
|
1211
1621
|
function isRemoteUnreadable(error) {
|
|
1212
1622
|
if (!(error instanceof HttpError)) return false;
|
|
1213
|
-
return error.status ===
|
|
1623
|
+
return error.status === 403 || error.status === 404;
|
|
1214
1624
|
}
|
|
1215
1625
|
function gitSyncResource(transport) {
|
|
1626
|
+
const { explain, refuse, refuseAfterReading } = explainer(transport, "gitSync");
|
|
1216
1627
|
/** Get the running or most recently finished sync task, or null when the server has none. */
|
|
1217
1628
|
async function currentTask(options = {}) {
|
|
1218
|
-
await transport.require("gitSync.currentTask", options);
|
|
1219
1629
|
return fetchOptionalParsed(transport, "/api/ee/remote-sync/current-task", SyncTask, options);
|
|
1220
1630
|
}
|
|
1221
1631
|
/** Request cancellation of the running sync task, and answer it in the state that left it. */
|
|
1222
1632
|
async function cancelTask(options = {}) {
|
|
1223
|
-
await transport.require("gitSync.cancelTask", options);
|
|
1224
1633
|
return transport.requestParsed(SyncTask, "/api/ee/remote-sync/current-task/cancel", {
|
|
1225
1634
|
...options,
|
|
1226
1635
|
method: "POST"
|
|
@@ -1228,12 +1637,10 @@ function gitSyncResource(transport) {
|
|
|
1228
1637
|
}
|
|
1229
1638
|
/** Whether Metabase holds content changes the remote has not been told about. */
|
|
1230
1639
|
async function isDirty(options = {}) {
|
|
1231
|
-
await transport.require("gitSync.isDirty", options);
|
|
1232
1640
|
return (await transport.requestParsed(SyncDirtyFlag, "/api/ee/remote-sync/is-dirty", { ...options })).is_dirty;
|
|
1233
1641
|
}
|
|
1234
1642
|
/** List the objects whose local state differs from the remote. */
|
|
1235
1643
|
async function dirty(options = {}) {
|
|
1236
|
-
await transport.require("gitSync.dirty", options);
|
|
1237
1644
|
return {
|
|
1238
1645
|
data: (await transport.requestParsed(SyncDirtyList, "/api/ee/remote-sync/dirty", { ...options })).dirty,
|
|
1239
1646
|
total: null
|
|
@@ -1244,7 +1651,6 @@ function gitSyncResource(transport) {
|
|
|
1244
1651
|
* comparison; `force-refresh` re-reads the remote instead.
|
|
1245
1652
|
*/
|
|
1246
1653
|
async function hasRemoteChanges(params = {}, options = {}) {
|
|
1247
|
-
await transport.require("gitSync.hasRemoteChanges", options);
|
|
1248
1654
|
return transport.requestParsed(SyncRemoteChanges, "/api/ee/remote-sync/has-remote-changes", {
|
|
1249
1655
|
...options,
|
|
1250
1656
|
query: { "force-refresh": params["force-refresh"] }
|
|
@@ -1255,14 +1661,16 @@ function gitSyncResource(transport) {
|
|
|
1255
1661
|
* pass `wait` to poll that task until it reaches a terminal status. A server already up to date
|
|
1256
1662
|
* answers no task id, and there is then nothing to poll. A server that guards the tracked branch
|
|
1257
1663
|
* 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
|
-
*
|
|
1664
|
+
* branch as read just before the request, on every server with remote sync. A server that guards
|
|
1665
|
+
* the branch also requires it, so there the request is refused before the wire when the setting
|
|
1666
|
+
* is unset or hidden from the caller; a server without the guard ignores the assertion, and gets
|
|
1667
|
+
* none when there is none to read. `merge` folds the remote's changes into local content by a
|
|
1668
|
+
* three-way merge and keeps un-pushed local changes, where a plain import refuses on a dirty
|
|
1260
1669
|
* instance.
|
|
1261
1670
|
*/
|
|
1262
1671
|
async function importFromRemote(params = {}, options = {}) {
|
|
1263
|
-
await
|
|
1672
|
+
const expectedBranch = await expectedBranchFor(params.expected_branch, options);
|
|
1264
1673
|
await requireMerge(params.merge, options);
|
|
1265
|
-
const expectedBranch = await guardedBranch(params.expected_branch, options);
|
|
1266
1674
|
const started = await transport.requestParsed(SyncImportStarted, "/api/ee/remote-sync/import", {
|
|
1267
1675
|
...options,
|
|
1268
1676
|
method: "POST",
|
|
@@ -1284,28 +1692,38 @@ function gitSyncResource(transport) {
|
|
|
1284
1692
|
final: await settle(params.wait, options)
|
|
1285
1693
|
};
|
|
1286
1694
|
}
|
|
1287
|
-
async function
|
|
1288
|
-
if (
|
|
1695
|
+
async function expectedBranchFor(given, options) {
|
|
1696
|
+
if (given !== void 0) return given;
|
|
1697
|
+
return importAssertion(await readTrackedBranch(options));
|
|
1289
1698
|
}
|
|
1290
|
-
async function
|
|
1699
|
+
async function exportBranchFor(given, options) {
|
|
1291
1700
|
if (given !== void 0) return given;
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1701
|
+
return exportBranch(await readTrackedBranch(options));
|
|
1702
|
+
}
|
|
1703
|
+
async function readTrackedBranch(options) {
|
|
1704
|
+
const properties = await transport.probe(RemoteSyncBranchProperty, options);
|
|
1705
|
+
return {
|
|
1706
|
+
tracked: properties["remote-sync-branch"],
|
|
1707
|
+
features: profileFromProperties(properties).features
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
async function requireMerge(merge, options) {
|
|
1711
|
+
if (merge === true) await transport.requireFeatures(["remoteSyncMerge"], options);
|
|
1295
1712
|
}
|
|
1296
1713
|
/**
|
|
1297
1714
|
* Export Metabase's content to the remote. The endpoint queues a task and returns at once; pass
|
|
1298
1715
|
* `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
|
-
*
|
|
1716
|
+
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked
|
|
1717
|
+
* branch as read just before the request, and the request is refused before the wire when the
|
|
1718
|
+
* setting is unset or hidden from the caller. A server without the guard exports to a named
|
|
1719
|
+
* `branch` and tracks it from then on; left out, it gets none and exports to its own. When the
|
|
1720
|
+
* remote has moved past the last sync, a plain export ends in a `conflict` task where `merge` is
|
|
1721
|
+
* available, and is refused with 400 before any task elsewhere; `merge` folds the remote's
|
|
1722
|
+
* changes in by a three-way merge instead, and `force` overwrites them.
|
|
1304
1723
|
*/
|
|
1305
1724
|
async function exportToRemote(params = {}, options = {}) {
|
|
1306
|
-
await
|
|
1725
|
+
const branchName = await exportBranchFor(params.branch, options);
|
|
1307
1726
|
await requireMerge(params.merge, options);
|
|
1308
|
-
const branchName = await guardedBranch(params.branch, options);
|
|
1309
1727
|
const started = await transport.requestParsed(SyncExportStarted, "/api/ee/remote-sync/export", {
|
|
1310
1728
|
...options,
|
|
1311
1729
|
method: "POST",
|
|
@@ -1333,7 +1751,6 @@ function gitSyncResource(transport) {
|
|
|
1333
1751
|
* otherwise.
|
|
1334
1752
|
*/
|
|
1335
1753
|
async function exportPreflight(params, options = {}) {
|
|
1336
|
-
await transport.require("gitSync.exportPreflight", options);
|
|
1337
1754
|
return transport.requestParsed(SyncExportPreflight, "/api/ee/remote-sync/export-preflight", {
|
|
1338
1755
|
...options,
|
|
1339
1756
|
query: { branch: params.branch }
|
|
@@ -1344,7 +1761,6 @@ function gitSyncResource(transport) {
|
|
|
1344
1761
|
* queues a task and returns at once; pass `wait` to poll that task to a terminal status.
|
|
1345
1762
|
*/
|
|
1346
1763
|
async function stash(params, options = {}) {
|
|
1347
|
-
await transport.require("gitSync.stash", options);
|
|
1348
1764
|
const started = await transport.requestParsed(SyncStashStarted, "/api/ee/remote-sync/stash", {
|
|
1349
1765
|
...options,
|
|
1350
1766
|
method: "POST",
|
|
@@ -1367,7 +1783,6 @@ function gitSyncResource(transport) {
|
|
|
1367
1783
|
}
|
|
1368
1784
|
/** List the branches the configured remote carries. */
|
|
1369
1785
|
async function branches(options = {}) {
|
|
1370
|
-
await transport.require("gitSync.branches", options);
|
|
1371
1786
|
return {
|
|
1372
1787
|
data: (await transport.requestParsed(SyncBranchList, "/api/ee/remote-sync/branches", { ...options })).items,
|
|
1373
1788
|
total: null
|
|
@@ -1375,7 +1790,6 @@ function gitSyncResource(transport) {
|
|
|
1375
1790
|
}
|
|
1376
1791
|
/** Create a branch on the remote and point git-sync at it. */
|
|
1377
1792
|
async function createBranch(params, options = {}) {
|
|
1378
|
-
await transport.require("gitSync.createBranch", options);
|
|
1379
1793
|
return transport.requestParsed(SyncBranchCreated, "/api/ee/remote-sync/create-branch", {
|
|
1380
1794
|
...options,
|
|
1381
1795
|
method: "POST",
|
|
@@ -1384,19 +1798,42 @@ function gitSyncResource(transport) {
|
|
|
1384
1798
|
}
|
|
1385
1799
|
/**
|
|
1386
1800
|
* Mark one collection as git-synced, or unmark it. The server cascades the flag to descendants
|
|
1387
|
-
* by location prefix, and may queue an
|
|
1801
|
+
* by location prefix, and may queue an import task to bring the instance in line with the
|
|
1802
|
+
* remote. A server without `remoteSyncCollectionsOnlyUpdate` validates every settings update as
|
|
1803
|
+
* a remote configuration and fails one naming no remote, after it has applied the flag, so it is
|
|
1804
|
+
* sent the remote's URL and its token as the server shows it, which it checks against the remote
|
|
1805
|
+
* and keeps as they are, or a blank URL when no remote is configured, which clears the remote
|
|
1806
|
+
* settings that are already empty.
|
|
1388
1807
|
*/
|
|
1389
1808
|
async function setCollectionSynced(collectionId, synced, options = {}) {
|
|
1390
|
-
await transport.require("gitSync.setCollectionSynced", options);
|
|
1391
1809
|
return transport.requestParsed(SyncSettingsUpdateResult, "/api/ee/remote-sync/settings", {
|
|
1392
1810
|
...options,
|
|
1393
1811
|
method: "PUT",
|
|
1394
|
-
body:
|
|
1812
|
+
body: await settingsUpdateBody({ [collectionId]: synced }, options)
|
|
1395
1813
|
});
|
|
1396
1814
|
}
|
|
1815
|
+
async function settingsUpdateBody(collections, options) {
|
|
1816
|
+
const { features } = await transport.server(options);
|
|
1817
|
+
if (features.remoteSyncCollectionsOnlyUpdate) return { collections };
|
|
1818
|
+
const url = (await transport.probe(RemoteSyncUrlProperty, options))["remote-sync-url"];
|
|
1819
|
+
if (url === void 0) return { collections };
|
|
1820
|
+
if (url === null) return {
|
|
1821
|
+
collections,
|
|
1822
|
+
"remote-sync-url": ""
|
|
1823
|
+
};
|
|
1824
|
+
const token = await fetchOptionalParsed(transport, "/api/setting/remote-sync-token", RemoteSyncSetting, options);
|
|
1825
|
+
if (token === null) return {
|
|
1826
|
+
collections,
|
|
1827
|
+
"remote-sync-url": url
|
|
1828
|
+
};
|
|
1829
|
+
return {
|
|
1830
|
+
collections,
|
|
1831
|
+
"remote-sync-url": url,
|
|
1832
|
+
"remote-sync-token": token
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1397
1835
|
/** The collections currently in git-sync's scope. */
|
|
1398
1836
|
async function syncedCollections(options = {}) {
|
|
1399
|
-
await transport.require("gitSync.syncedCollections", options);
|
|
1400
1837
|
return {
|
|
1401
1838
|
data: (await listCollectionsAs(transport, SyncScopeCollection, options)).filter((entry) => entry.is_remote_synced === true),
|
|
1402
1839
|
total: null
|
|
@@ -1404,7 +1841,6 @@ function gitSyncResource(transport) {
|
|
|
1404
1841
|
}
|
|
1405
1842
|
/** The remote's URL, or null when none is configured or the caller may not read it. */
|
|
1406
1843
|
async function remoteUrl(options = {}) {
|
|
1407
|
-
await transport.require("gitSync.remoteUrl", options);
|
|
1408
1844
|
try {
|
|
1409
1845
|
const url = await fetchOptionalParsed(transport, "/api/setting/remote-sync-url", RemoteSyncSetting, options);
|
|
1410
1846
|
return url === "" ? null : url;
|
|
@@ -1414,21 +1850,22 @@ function gitSyncResource(transport) {
|
|
|
1414
1850
|
}
|
|
1415
1851
|
}
|
|
1416
1852
|
/**
|
|
1417
|
-
* The branch git-sync tracks, or null when none is configured. Refuses when the
|
|
1418
|
-
* read the setting (it is admin-only)
|
|
1419
|
-
*
|
|
1853
|
+
* The branch git-sync tracks, or null when none is configured. Refuses when the server has no
|
|
1854
|
+
* remote sync or the caller may not read the setting (it is admin-only), rather than reading
|
|
1855
|
+
* either as unset.
|
|
1420
1856
|
*/
|
|
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");
|
|
1857
|
+
function branch(properties) {
|
|
1858
|
+
const tracked = properties["remote-sync-branch"];
|
|
1859
|
+
if (tracked === void 0) throw new ConfigError(BRANCH_UNREADABLE_MESSAGE);
|
|
1425
1860
|
return tracked;
|
|
1426
1861
|
}
|
|
1427
|
-
/**
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1862
|
+
/**
|
|
1863
|
+
* The branch git-sync tracks, refused when none is configured, the server has no remote sync, or
|
|
1864
|
+
* the caller may not read it.
|
|
1865
|
+
*/
|
|
1866
|
+
function trackedBranch(properties) {
|
|
1867
|
+
const tracked = branch(properties);
|
|
1868
|
+
if (tracked === null) throw new ConfigError(BRANCH_UNSET_MESSAGE);
|
|
1432
1869
|
return tracked;
|
|
1433
1870
|
}
|
|
1434
1871
|
/**
|
|
@@ -1436,7 +1873,6 @@ function gitSyncResource(transport) {
|
|
|
1436
1873
|
* answers null, which is terminal in the same sense: nothing further will happen.
|
|
1437
1874
|
*/
|
|
1438
1875
|
async function waitForTask(wait, options = {}) {
|
|
1439
|
-
await transport.require("gitSync.waitForTask", options);
|
|
1440
1876
|
return settle(wait, options);
|
|
1441
1877
|
}
|
|
1442
1878
|
async function settle(wait, options) {
|
|
@@ -1446,25 +1882,38 @@ function gitSyncResource(transport) {
|
|
|
1446
1882
|
}), (task) => task === null || isSyncTaskTerminal(task.status), wait);
|
|
1447
1883
|
}
|
|
1448
1884
|
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,
|
|
1885
|
+
currentTask: explain("currentTask", currentTask),
|
|
1886
|
+
cancelTask: explain("cancelTask", cancelTask),
|
|
1887
|
+
isDirty: explain("isDirty", isDirty),
|
|
1888
|
+
dirty: explain("dirty", dirty),
|
|
1889
|
+
hasRemoteChanges: explain("hasRemoteChanges", hasRemoteChanges),
|
|
1890
|
+
import: explain("import", importFromRemote),
|
|
1891
|
+
export: explain("export", exportToRemote),
|
|
1892
|
+
exportPreflight: explain("exportPreflight", exportPreflight),
|
|
1893
|
+
stash: explain("stash", stash),
|
|
1894
|
+
branches: explain("branches", branches),
|
|
1895
|
+
createBranch: explain("createBranch", createBranch),
|
|
1896
|
+
setCollectionSynced: explain("setCollectionSynced", setCollectionSynced),
|
|
1897
|
+
syncedCollections: refuse("syncedCollections", syncedCollections),
|
|
1462
1898
|
remoteUrl,
|
|
1463
|
-
branch,
|
|
1464
|
-
trackedBranch,
|
|
1465
|
-
waitForTask
|
|
1899
|
+
branch: refuseAfterReading("branch", RemoteSyncBranchProperty, branch),
|
|
1900
|
+
trackedBranch: refuseAfterReading("trackedBranch", RemoteSyncBranchProperty, trackedBranch),
|
|
1901
|
+
waitForTask: explain("waitForTask", waitForTask)
|
|
1466
1902
|
};
|
|
1467
1903
|
}
|
|
1904
|
+
function importAssertion(read) {
|
|
1905
|
+
if (!read.features.remoteSync) return;
|
|
1906
|
+
if (read.features.remoteSyncBranchGuard) return requiredBranch(read.tracked);
|
|
1907
|
+
return typeof read.tracked === "string" ? read.tracked : void 0;
|
|
1908
|
+
}
|
|
1909
|
+
function exportBranch(read) {
|
|
1910
|
+
if (!read.features.remoteSync || !read.features.remoteSyncBranchGuard) return;
|
|
1911
|
+
return requiredBranch(read.tracked);
|
|
1912
|
+
}
|
|
1913
|
+
function requiredBranch(tracked) {
|
|
1914
|
+
if (typeof tracked === "string") return tracked;
|
|
1915
|
+
throw new ConfigError(tracked === null ? BRANCH_UNSET_MESSAGE : BRANCH_UNREADABLE_MESSAGE);
|
|
1916
|
+
}
|
|
1468
1917
|
//#endregion
|
|
1469
1918
|
//#region ../client/src/resources/glossary.ts
|
|
1470
1919
|
const GlossaryApiList = z.object({ data: z.array(Glossary) }).loose();
|
|
@@ -1474,7 +1923,6 @@ function glossaryResource(transport) {
|
|
|
1474
1923
|
* contains it, case-insensitively.
|
|
1475
1924
|
*/
|
|
1476
1925
|
async function list(params = {}, options = {}) {
|
|
1477
|
-
await transport.require("glossary.list", options);
|
|
1478
1926
|
return {
|
|
1479
1927
|
data: (await transport.requestParsed(GlossaryApiList, "/api/glossary", {
|
|
1480
1928
|
...options,
|
|
@@ -1485,7 +1933,6 @@ function glossaryResource(transport) {
|
|
|
1485
1933
|
}
|
|
1486
1934
|
/** Create a glossary entry, a term and its definition. Terms are unique. */
|
|
1487
1935
|
async function create(params, options = {}) {
|
|
1488
|
-
await transport.require("glossary.create", options);
|
|
1489
1936
|
return transport.requestParsed(Glossary, "/api/glossary", {
|
|
1490
1937
|
...options,
|
|
1491
1938
|
method: "POST",
|
|
@@ -1494,7 +1941,6 @@ function glossaryResource(transport) {
|
|
|
1494
1941
|
}
|
|
1495
1942
|
/** Replace the term and definition of a glossary entry by id. */
|
|
1496
1943
|
async function update(id, params, options = {}) {
|
|
1497
|
-
await transport.require("glossary.update", options);
|
|
1498
1944
|
return transport.requestParsed(Glossary, `/api/glossary/${id}`, {
|
|
1499
1945
|
...options,
|
|
1500
1946
|
method: "PUT",
|
|
@@ -1503,7 +1949,6 @@ function glossaryResource(transport) {
|
|
|
1503
1949
|
}
|
|
1504
1950
|
/** Delete a glossary entry by id. */
|
|
1505
1951
|
async function remove(id, options = {}) {
|
|
1506
|
-
await transport.require("glossary.delete", options);
|
|
1507
1952
|
await transport.requestRaw(`/api/glossary/${id}`, {
|
|
1508
1953
|
...options,
|
|
1509
1954
|
method: "DELETE",
|
|
@@ -1536,9 +1981,9 @@ const UNPUBLISH_TABLES_PATH = "/api/ee/data-studio/table/unpublish-tables";
|
|
|
1536
1981
|
const LIBRARY_DATA_TYPE = "library-data";
|
|
1537
1982
|
const PublishTablesResponse = z.object({ target_collection: Collection.nullable() });
|
|
1538
1983
|
function libraryResource(transport) {
|
|
1984
|
+
const { explain } = explainer(transport, "library");
|
|
1539
1985
|
/** Get the Library root and its child collections, or `null` on an instance that has none. */
|
|
1540
1986
|
async function get(options = {}) {
|
|
1541
|
-
await transport.require("library.get", options);
|
|
1542
1987
|
const { features } = await transport.server(options);
|
|
1543
1988
|
const wire = await transport.requestParsed(libraryWireSchema(features), LIBRARY_ROOT_PATH, { ...options });
|
|
1544
1989
|
if (wire === null) return null;
|
|
@@ -1557,7 +2002,6 @@ function libraryResource(transport) {
|
|
|
1557
2002
|
* back from a refetch, which together make this idempotent.
|
|
1558
2003
|
*/
|
|
1559
2004
|
async function create(options = {}) {
|
|
1560
|
-
await transport.require("library.create", options);
|
|
1561
2005
|
const existing = await get(options);
|
|
1562
2006
|
if (existing !== null) return existing;
|
|
1563
2007
|
await transport.requestRaw(LIBRARY_ROOT_PATH, {
|
|
@@ -1570,7 +2014,6 @@ function libraryResource(transport) {
|
|
|
1570
2014
|
}
|
|
1571
2015
|
/** The id of the Library's Data collection, creating the Library first when it does not exist. */
|
|
1572
2016
|
async function ensureDataCollectionId(options = {}) {
|
|
1573
|
-
await transport.require("library.ensureDataCollectionId", options);
|
|
1574
2017
|
const data = (await create(options)).effective_children.find((child) => child.type === LIBRARY_DATA_TYPE);
|
|
1575
2018
|
if (data === void 0) throw new Error("Library has no Data collection to publish into");
|
|
1576
2019
|
if (typeof data.id !== "number") throw new Error(`Library Data collection has a non-numeric id ${String(data.id)}`);
|
|
@@ -1583,7 +2026,6 @@ function libraryResource(transport) {
|
|
|
1583
2026
|
* `LibraryPublishTablesInput` is refused before any request.
|
|
1584
2027
|
*/
|
|
1585
2028
|
async function publishTables(params, options = {}) {
|
|
1586
|
-
await transport.require("library.publishTables", options);
|
|
1587
2029
|
const body = parseRequestBody(LibraryPublishTablesInput, params, "tables to publish");
|
|
1588
2030
|
return (await transport.requestParsed(PublishTablesResponse, PUBLISH_TABLES_PATH, {
|
|
1589
2031
|
...options,
|
|
@@ -1597,7 +2039,6 @@ function libraryResource(transport) {
|
|
|
1597
2039
|
* table, so selectors that do not match `TableSelectors` are refused before any request.
|
|
1598
2040
|
*/
|
|
1599
2041
|
async function unpublishTables(params, options = {}) {
|
|
1600
|
-
await transport.require("library.unpublishTables", options);
|
|
1601
2042
|
const body = parseRequestBody(TableSelectors, params, "table selectors");
|
|
1602
2043
|
await transport.requestRaw(UNPUBLISH_TABLES_PATH, {
|
|
1603
2044
|
...options,
|
|
@@ -1607,20 +2048,20 @@ function libraryResource(transport) {
|
|
|
1607
2048
|
});
|
|
1608
2049
|
}
|
|
1609
2050
|
return {
|
|
1610
|
-
get,
|
|
1611
|
-
create,
|
|
1612
|
-
ensureDataCollectionId,
|
|
1613
|
-
publishTables,
|
|
1614
|
-
unpublishTables
|
|
2051
|
+
get: explain("get", get),
|
|
2052
|
+
create: explain("create", create),
|
|
2053
|
+
ensureDataCollectionId: explain("ensureDataCollectionId", ensureDataCollectionId),
|
|
2054
|
+
publishTables: explain("publishTables", publishTables),
|
|
2055
|
+
unpublishTables: explain("unpublishTables", unpublishTables)
|
|
1615
2056
|
};
|
|
1616
2057
|
}
|
|
1617
2058
|
//#endregion
|
|
1618
2059
|
//#region ../client/src/resources/measure.ts
|
|
1619
2060
|
const MeasureApiList = z.array(Measure);
|
|
1620
2061
|
function measureResource(transport) {
|
|
2062
|
+
const { explain } = explainer(transport, "measure");
|
|
1621
2063
|
/** List measures. */
|
|
1622
2064
|
async function list(options = {}) {
|
|
1623
|
-
await transport.require("measure.list", options);
|
|
1624
2065
|
return {
|
|
1625
2066
|
data: await transport.requestParsed(MeasureApiList, "/api/measure", { ...options }),
|
|
1626
2067
|
total: null
|
|
@@ -1628,12 +2069,10 @@ function measureResource(transport) {
|
|
|
1628
2069
|
}
|
|
1629
2070
|
/** Get one measure by id. */
|
|
1630
2071
|
async function get(id, options = {}) {
|
|
1631
|
-
await transport.require("measure.get", options);
|
|
1632
2072
|
return transport.requestParsed(Measure, `/api/measure/${id}`, { ...options });
|
|
1633
2073
|
}
|
|
1634
2074
|
/** Create a measure — a saved aggregation tied to a table — from a full measure body. */
|
|
1635
2075
|
async function create(params, options = {}) {
|
|
1636
|
-
await transport.require("measure.create", options);
|
|
1637
2076
|
return transport.requestParsed(Measure, "/api/measure", {
|
|
1638
2077
|
...options,
|
|
1639
2078
|
method: "POST",
|
|
@@ -1645,7 +2084,6 @@ function measureResource(transport) {
|
|
|
1645
2084
|
* required and lands in the audit log.
|
|
1646
2085
|
*/
|
|
1647
2086
|
async function update(id, params, options = {}) {
|
|
1648
|
-
await transport.require("measure.update", options);
|
|
1649
2087
|
return transport.requestParsed(Measure, `/api/measure/${id}`, {
|
|
1650
2088
|
...options,
|
|
1651
2089
|
method: "PUT",
|
|
@@ -1657,18 +2095,17 @@ function measureResource(transport) {
|
|
|
1657
2095
|
* so it carries the same required `revision_message`.
|
|
1658
2096
|
*/
|
|
1659
2097
|
async function archive(id, params, options = {}) {
|
|
1660
|
-
await transport.require("measure.archive", options);
|
|
1661
2098
|
return update(id, {
|
|
1662
2099
|
archived: true,
|
|
1663
2100
|
revision_message: params.revision_message
|
|
1664
2101
|
}, options);
|
|
1665
2102
|
}
|
|
1666
2103
|
return {
|
|
1667
|
-
list,
|
|
1668
|
-
get,
|
|
1669
|
-
create,
|
|
1670
|
-
update,
|
|
1671
|
-
archive
|
|
2104
|
+
list: explain("list", list),
|
|
2105
|
+
get: explain("get", get),
|
|
2106
|
+
create: explain("create", create),
|
|
2107
|
+
update: explain("update", update),
|
|
2108
|
+
archive: explain("archive", archive)
|
|
1672
2109
|
};
|
|
1673
2110
|
}
|
|
1674
2111
|
//#endregion
|
|
@@ -1809,12 +2246,12 @@ const MetricDimensionListing = z.object({
|
|
|
1809
2246
|
//#endregion
|
|
1810
2247
|
//#region ../client/src/resources/metric.ts
|
|
1811
2248
|
function metricResource(transport) {
|
|
2249
|
+
const { explain } = explainer(transport, "metric");
|
|
1812
2250
|
/**
|
|
1813
2251
|
* Run a metric definition and return the query result envelope: one metric or measure, or an
|
|
1814
2252
|
* arithmetic expression over several, each leaf filtered and projected on its own `lib/uuid`.
|
|
1815
2253
|
*/
|
|
1816
2254
|
async function query(definition, options = {}) {
|
|
1817
|
-
await transport.require("metric.query", options);
|
|
1818
2255
|
return transport.requestParsed(CardQueryResult, "/api/metric/dataset", {
|
|
1819
2256
|
...options,
|
|
1820
2257
|
method: "POST",
|
|
@@ -1826,7 +2263,6 @@ function metricResource(transport) {
|
|
|
1826
2263
|
* Takes the same definition `query` does.
|
|
1827
2264
|
*/
|
|
1828
2265
|
async function breakoutValues(definition, options = {}) {
|
|
1829
|
-
await transport.require("metric.breakoutValues", options);
|
|
1830
2266
|
return transport.requestParsed(MetricBreakoutValues, "/api/metric/breakout-values", {
|
|
1831
2267
|
...options,
|
|
1832
2268
|
method: "POST",
|
|
@@ -1838,7 +2274,6 @@ function metricResource(transport) {
|
|
|
1838
2274
|
* add, grouped by source table. `query` filters both by a name substring.
|
|
1839
2275
|
*/
|
|
1840
2276
|
async function dimensions(id, params = {}, options = {}) {
|
|
1841
|
-
await transport.require("metric.dimensions", options);
|
|
1842
2277
|
return transport.requestParsed(MetricDimensionListing, `/api/metric/${id}/dimension`, {
|
|
1843
2278
|
...options,
|
|
1844
2279
|
query: {
|
|
@@ -1849,14 +2284,15 @@ function metricResource(transport) {
|
|
|
1849
2284
|
});
|
|
1850
2285
|
}
|
|
1851
2286
|
return {
|
|
1852
|
-
query,
|
|
1853
|
-
breakoutValues,
|
|
1854
|
-
dimensions
|
|
2287
|
+
query: explain("query", query),
|
|
2288
|
+
breakoutValues: explain("breakoutValues", breakoutValues),
|
|
2289
|
+
dimensions: explain("dimensions", dimensions)
|
|
1855
2290
|
};
|
|
1856
2291
|
}
|
|
1857
2292
|
//#endregion
|
|
1858
2293
|
//#region ../client/src/resources/moderation-review.ts
|
|
1859
2294
|
function moderationReviewResource(transport) {
|
|
2295
|
+
const { explain } = explainer(transport, "moderationReview");
|
|
1860
2296
|
/**
|
|
1861
2297
|
* Create a moderation review on a card or dashboard. `status: "verified"` marks the item
|
|
1862
2298
|
* verified, `null` or an absent status records a note that leaves it unverified. The new review
|
|
@@ -1864,7 +2300,6 @@ function moderationReviewResource(transport) {
|
|
|
1864
2300
|
* A body that does not match `ModerationReviewCreateInput` is refused before any request.
|
|
1865
2301
|
*/
|
|
1866
2302
|
async function create(params, options = {}) {
|
|
1867
|
-
await transport.require("moderationReview.create", options);
|
|
1868
2303
|
const body = parseRequestBody(ModerationReviewCreateInput, params, "moderation review");
|
|
1869
2304
|
return transport.requestParsed(ModerationReview, "/api/moderation-review", {
|
|
1870
2305
|
...options,
|
|
@@ -1872,7 +2307,7 @@ function moderationReviewResource(transport) {
|
|
|
1872
2307
|
body
|
|
1873
2308
|
});
|
|
1874
2309
|
}
|
|
1875
|
-
return { create };
|
|
2310
|
+
return { create: explain("create", create) };
|
|
1876
2311
|
}
|
|
1877
2312
|
//#endregion
|
|
1878
2313
|
//#region ../client/src/resources/notification.ts
|
|
@@ -1903,7 +2338,6 @@ function notificationResource(transport) {
|
|
|
1903
2338
|
* the request pins the card payload type.
|
|
1904
2339
|
*/
|
|
1905
2340
|
async function list(params = {}, options = {}) {
|
|
1906
|
-
await transport.require("notification.list", options);
|
|
1907
2341
|
return {
|
|
1908
2342
|
data: await transport.requestParsed(NotificationApiList, "/api/notification", {
|
|
1909
2343
|
...options,
|
|
@@ -1920,12 +2354,10 @@ function notificationResource(transport) {
|
|
|
1920
2354
|
}
|
|
1921
2355
|
/** Get one question alert by id, refusing an id that names a system-event notification. */
|
|
1922
2356
|
async function get(id, options = {}) {
|
|
1923
|
-
await transport.require("notification.get", options);
|
|
1924
2357
|
return assertCardAlert(await transport.requestParsed(Notification, `/api/notification/${id}`, { ...options }));
|
|
1925
2358
|
}
|
|
1926
2359
|
/** Create a question alert — its card payload, schedules and handlers — from a full body. */
|
|
1927
2360
|
async function create(params, options = {}) {
|
|
1928
|
-
await transport.require("notification.create", options);
|
|
1929
2361
|
return transport.requestParsed(Notification, "/api/notification", {
|
|
1930
2362
|
...options,
|
|
1931
2363
|
method: "POST",
|
|
@@ -1934,7 +2366,6 @@ function notificationResource(transport) {
|
|
|
1934
2366
|
}
|
|
1935
2367
|
/** Update a question alert by id, merging the patch over the stored notification. */
|
|
1936
2368
|
async function update(id, params, options = {}) {
|
|
1937
|
-
await transport.require("notification.update", options);
|
|
1938
2369
|
const current = await get(id, options);
|
|
1939
2370
|
return transport.requestParsed(Notification, `/api/notification/${id}`, {
|
|
1940
2371
|
...options,
|
|
@@ -1944,7 +2375,6 @@ function notificationResource(transport) {
|
|
|
1944
2375
|
}
|
|
1945
2376
|
/** Archive a question alert by id, stopping every delivery. Metabase models this as an update. */
|
|
1946
2377
|
async function archive(id, options = {}) {
|
|
1947
|
-
await transport.require("notification.archive", options);
|
|
1948
2378
|
return update(id, { active: false }, options);
|
|
1949
2379
|
}
|
|
1950
2380
|
/**
|
|
@@ -1952,7 +2382,6 @@ function notificationResource(transport) {
|
|
|
1952
2382
|
* body.
|
|
1953
2383
|
*/
|
|
1954
2384
|
async function send(id, options = {}) {
|
|
1955
|
-
await transport.require("notification.send", options);
|
|
1956
2385
|
await get(id, options);
|
|
1957
2386
|
await transport.requestRaw(`/api/notification/${id}/send`, {
|
|
1958
2387
|
...options,
|
|
@@ -2012,7 +2441,6 @@ function permissionResource(transport) {
|
|
|
2012
2441
|
* one database is the question.
|
|
2013
2442
|
*/
|
|
2014
2443
|
async function graph(options = {}) {
|
|
2015
|
-
await transport.require("permission.graph", options);
|
|
2016
2444
|
return transport.requestParsed(PermissionsGraph, "/api/permissions/graph", { ...options });
|
|
2017
2445
|
}
|
|
2018
2446
|
/**
|
|
@@ -2021,12 +2449,10 @@ function permissionResource(transport) {
|
|
|
2021
2449
|
* group up under the database and read `view-data` and `create-queries` down to the table id.
|
|
2022
2450
|
*/
|
|
2023
2451
|
async function databaseGraph(id, options = {}) {
|
|
2024
|
-
await transport.require("permission.databaseGraph", options);
|
|
2025
2452
|
return transport.requestParsed(PermissionsGraph, `/api/permissions/graph/db/${id}`, { ...options });
|
|
2026
2453
|
}
|
|
2027
2454
|
/** Get the data permissions of one group on every database. Admin only. */
|
|
2028
2455
|
async function groupGraph(id, options = {}) {
|
|
2029
|
-
await transport.require("permission.groupGraph", options);
|
|
2030
2456
|
return transport.requestParsed(PermissionsGraph, `/api/permissions/graph/group/${id}`, { ...options });
|
|
2031
2457
|
}
|
|
2032
2458
|
return {
|
|
@@ -2048,7 +2474,6 @@ function mergePulseUpdate(current, patch) {
|
|
|
2048
2474
|
function pulseResource(transport) {
|
|
2049
2475
|
/** List dashboard subscriptions, optionally narrowed to one dashboard or to the archived ones. */
|
|
2050
2476
|
async function list(params = {}, options = {}) {
|
|
2051
|
-
await transport.require("pulse.list", options);
|
|
2052
2477
|
return {
|
|
2053
2478
|
data: await transport.requestParsed(PulseApiList, "/api/pulse", {
|
|
2054
2479
|
...options,
|
|
@@ -2062,21 +2487,27 @@ function pulseResource(transport) {
|
|
|
2062
2487
|
}
|
|
2063
2488
|
/** Get one dashboard subscription by id. */
|
|
2064
2489
|
async function get(id, options = {}) {
|
|
2065
|
-
await transport.require("pulse.get", options);
|
|
2066
2490
|
return transport.requestParsed(Pulse, `/api/pulse/${id}`, { ...options });
|
|
2067
2491
|
}
|
|
2068
|
-
/**
|
|
2492
|
+
/**
|
|
2493
|
+
* Create a dashboard subscription — its cards and its delivery channels — from a full body.
|
|
2494
|
+
* Non-empty `parameters` need the `dashboard_subscription_filters` premium feature, and are
|
|
2495
|
+
* refused before the wire without it.
|
|
2496
|
+
*/
|
|
2069
2497
|
async function create(params, options = {}) {
|
|
2070
|
-
await
|
|
2498
|
+
await requireSubscriptionFilters(params.parameters, options);
|
|
2071
2499
|
return transport.requestParsed(Pulse, "/api/pulse", {
|
|
2072
2500
|
...options,
|
|
2073
2501
|
method: "POST",
|
|
2074
2502
|
body: params
|
|
2075
2503
|
});
|
|
2076
2504
|
}
|
|
2077
|
-
/**
|
|
2505
|
+
/**
|
|
2506
|
+
* Update a dashboard subscription by id, carrying the server-defaulted flags forward. Non-empty
|
|
2507
|
+
* `parameters` are refused as on `create`.
|
|
2508
|
+
*/
|
|
2078
2509
|
async function update(id, params, options = {}) {
|
|
2079
|
-
await
|
|
2510
|
+
await requireSubscriptionFilters(params.parameters, options);
|
|
2080
2511
|
const current = await get(id, options);
|
|
2081
2512
|
return transport.requestParsed(Pulse, `/api/pulse/${id}`, {
|
|
2082
2513
|
...options,
|
|
@@ -2089,9 +2520,11 @@ function pulseResource(transport) {
|
|
|
2089
2520
|
* update, and disables each of the subscription's channels as a side effect.
|
|
2090
2521
|
*/
|
|
2091
2522
|
async function archive(id, options = {}) {
|
|
2092
|
-
await transport.require("pulse.archive", options);
|
|
2093
2523
|
return update(id, { archived: true }, options);
|
|
2094
2524
|
}
|
|
2525
|
+
async function requireSubscriptionFilters(parameters, options) {
|
|
2526
|
+
if (parameters !== void 0 && parameters.length > 0) await transport.requireFeatures(["dashboardSubscriptionFilters"], options);
|
|
2527
|
+
}
|
|
2095
2528
|
return {
|
|
2096
2529
|
list,
|
|
2097
2530
|
get,
|
|
@@ -2194,6 +2627,7 @@ const ReplacementRunStarted = z.object({ run_id: z.number().int().positive() });
|
|
|
2194
2627
|
const ReplacementApiRunList = z.array(ReplacementRun);
|
|
2195
2628
|
const CancelRunResponse = z.object({ success: z.literal(true) });
|
|
2196
2629
|
function replacementResource(transport) {
|
|
2630
|
+
const { explain } = explainer(transport, "replacement");
|
|
2197
2631
|
/**
|
|
2198
2632
|
* Check whether every usage of a source entity could be rewritten to read a target entity
|
|
2199
2633
|
* instead: the two must share a database, the target must not depend on the source, every source
|
|
@@ -2201,7 +2635,6 @@ function replacementResource(transport) {
|
|
|
2201
2635
|
* implicitly or sandboxed. Admins only.
|
|
2202
2636
|
*/
|
|
2203
2637
|
async function checkReplaceSource(params, options = {}) {
|
|
2204
|
-
await transport.require("replacement.checkReplaceSource", options);
|
|
2205
2638
|
return transport.requestParsed(ReplacementCheck, "/api/ee/replacement/check-replace-source", {
|
|
2206
2639
|
...options,
|
|
2207
2640
|
method: "POST",
|
|
@@ -2214,7 +2647,6 @@ function replacementResource(transport) {
|
|
|
2214
2647
|
* while another replacement is running. Admins only.
|
|
2215
2648
|
*/
|
|
2216
2649
|
async function replaceSource(params, options = {}) {
|
|
2217
|
-
await transport.require("replacement.replaceSource", options);
|
|
2218
2650
|
return transport.requestParsed(ReplacementRunStarted, "/api/ee/replacement/replace-source", {
|
|
2219
2651
|
...options,
|
|
2220
2652
|
method: "POST",
|
|
@@ -2229,7 +2661,6 @@ function replacementResource(transport) {
|
|
|
2229
2661
|
* replacement is running. Admins only.
|
|
2230
2662
|
*/
|
|
2231
2663
|
async function replaceModelWithTransform(params, options = {}) {
|
|
2232
|
-
await transport.require("replacement.replaceModelWithTransform", options);
|
|
2233
2664
|
return transport.requestParsed(ReplacementRunStarted, "/api/ee/replacement/replace-model-with-transform", {
|
|
2234
2665
|
...options,
|
|
2235
2666
|
method: "POST",
|
|
@@ -2241,7 +2672,6 @@ function replacementResource(transport) {
|
|
|
2241
2672
|
* `false` only the ended ones. Admins only.
|
|
2242
2673
|
*/
|
|
2243
2674
|
async function listRuns(params = {}, options = {}) {
|
|
2244
|
-
await transport.require("replacement.listRuns", options);
|
|
2245
2675
|
return {
|
|
2246
2676
|
data: await transport.requestParsed(ReplacementApiRunList, "/api/ee/replacement/runs", {
|
|
2247
2677
|
...options,
|
|
@@ -2252,24 +2682,22 @@ function replacementResource(transport) {
|
|
|
2252
2682
|
}
|
|
2253
2683
|
/** Get one replacement run by id, with its status and progress. Admins only. */
|
|
2254
2684
|
async function getRun(id, options = {}) {
|
|
2255
|
-
await transport.require("replacement.getRun", options);
|
|
2256
2685
|
return transport.requestParsed(ReplacementRun, `/api/ee/replacement/runs/${id}`, { ...options });
|
|
2257
2686
|
}
|
|
2258
2687
|
/** Cancel the replacement in progress. Refuses with a 409 once the run has ended. Admins only. */
|
|
2259
2688
|
async function cancelRun(id, options = {}) {
|
|
2260
|
-
await transport.require("replacement.cancelRun", options);
|
|
2261
2689
|
await transport.requestParsed(CancelRunResponse, `/api/ee/replacement/runs/${id}/cancel`, {
|
|
2262
2690
|
...options,
|
|
2263
2691
|
method: "POST"
|
|
2264
2692
|
});
|
|
2265
2693
|
}
|
|
2266
2694
|
return {
|
|
2267
|
-
checkReplaceSource,
|
|
2268
|
-
replaceSource,
|
|
2269
|
-
replaceModelWithTransform,
|
|
2270
|
-
listRuns,
|
|
2271
|
-
getRun,
|
|
2272
|
-
cancelRun
|
|
2695
|
+
checkReplaceSource: explain("checkReplaceSource", checkReplaceSource),
|
|
2696
|
+
replaceSource: explain("replaceSource", replaceSource),
|
|
2697
|
+
replaceModelWithTransform: explain("replaceModelWithTransform", replaceModelWithTransform),
|
|
2698
|
+
listRuns: explain("listRuns", listRuns),
|
|
2699
|
+
getRun: explain("getRun", getRun),
|
|
2700
|
+
cancelRun: explain("cancelRun", cancelRun)
|
|
2273
2701
|
};
|
|
2274
2702
|
}
|
|
2275
2703
|
//#endregion
|
|
@@ -2348,13 +2776,15 @@ const ENTITY_FEATURES = {
|
|
|
2348
2776
|
};
|
|
2349
2777
|
function entityFeatures(entity) {
|
|
2350
2778
|
const feature = ENTITY_FEATURES[entity];
|
|
2351
|
-
return feature === void 0 ? [] : [
|
|
2779
|
+
return feature === void 0 ? [] : [{
|
|
2780
|
+
feature,
|
|
2781
|
+
fields: ["entity"]
|
|
2782
|
+
}];
|
|
2352
2783
|
}
|
|
2353
2784
|
function revisionResource(transport) {
|
|
2785
|
+
const { explain } = explainer(transport, "revision");
|
|
2354
2786
|
/** List the revisions of an entity the caller may read, newest first, each with its diff. */
|
|
2355
2787
|
async function list(entity, id, options = {}) {
|
|
2356
|
-
await transport.require("revision.list", options);
|
|
2357
|
-
await transport.requireFeatures(entityFeatures(entity), options);
|
|
2358
2788
|
return {
|
|
2359
2789
|
data: await transport.requestParsed(RevisionApiList, `/api/revision/${entity}/${id}`, { ...options }),
|
|
2360
2790
|
total: null
|
|
@@ -2365,8 +2795,6 @@ function revisionResource(transport) {
|
|
|
2365
2795
|
* card the permission to run the query being restored.
|
|
2366
2796
|
*/
|
|
2367
2797
|
async function revert(params, options = {}) {
|
|
2368
|
-
await transport.require("revision.revert", options);
|
|
2369
|
-
await transport.requireFeatures(entityFeatures(params.entity), options);
|
|
2370
2798
|
return transport.requestParsed(RevisionApiRevert, "/api/revision/revert", {
|
|
2371
2799
|
...options,
|
|
2372
2800
|
method: "POST",
|
|
@@ -2374,8 +2802,8 @@ function revisionResource(transport) {
|
|
|
2374
2802
|
});
|
|
2375
2803
|
}
|
|
2376
2804
|
return {
|
|
2377
|
-
list,
|
|
2378
|
-
revert
|
|
2805
|
+
list: explain("list", list, entityFeatures),
|
|
2806
|
+
revert: explain("revert", revert, (params) => entityFeatures(params.entity))
|
|
2379
2807
|
};
|
|
2380
2808
|
}
|
|
2381
2809
|
//#endregion
|
|
@@ -2384,7 +2812,14 @@ const SearchApiResponse = z.object({
|
|
|
2384
2812
|
data: z.array(SearchResult),
|
|
2385
2813
|
total: z.number().int().nonnegative()
|
|
2386
2814
|
}).loose();
|
|
2815
|
+
function verifiedFeatures(params = {}) {
|
|
2816
|
+
return params.verified === true ? [{
|
|
2817
|
+
feature: "contentVerification",
|
|
2818
|
+
fields: ["verified"]
|
|
2819
|
+
}] : [];
|
|
2820
|
+
}
|
|
2387
2821
|
function searchResource(transport) {
|
|
2822
|
+
const { explain } = explainer(transport, "search");
|
|
2388
2823
|
/**
|
|
2389
2824
|
* Search over the instance's content, ranked against `q`. `models` narrows which kinds of entity
|
|
2390
2825
|
* may match, `archived` swaps the active set for the archived one, `table_db_id` restricts to
|
|
@@ -2394,9 +2829,12 @@ function searchResource(transport) {
|
|
|
2394
2829
|
* actions, transforms), `include_metadata` attaches each card's `result_metadata`,
|
|
2395
2830
|
* `include_dashboard_questions` also matches questions saved into a dashboard (excluded by
|
|
2396
2831
|
* default), and `limit`/`offset` are the window the server applies before ranking hydration.
|
|
2832
|
+
* `verified` needs the `content_verification` premium feature, and is refused before the wire
|
|
2833
|
+
* without it: a server granting neither it nor `official_collections` refuses the filter, and one
|
|
2834
|
+
* granting `official_collections` alone takes it without applying it.
|
|
2397
2835
|
*/
|
|
2398
2836
|
async function query(params = {}, options = {}) {
|
|
2399
|
-
await transport.
|
|
2837
|
+
await transport.requireFeatures(verifiedFeatures(params).map((requirement) => requirement.feature), options);
|
|
2400
2838
|
const response = await transport.requestParsed(SearchApiResponse, "/api/search", {
|
|
2401
2839
|
...options,
|
|
2402
2840
|
query: {
|
|
@@ -2419,7 +2857,7 @@ function searchResource(transport) {
|
|
|
2419
2857
|
total: response.total
|
|
2420
2858
|
};
|
|
2421
2859
|
}
|
|
2422
|
-
return { query };
|
|
2860
|
+
return { query: explain("query", query, verifiedFeatures) };
|
|
2423
2861
|
}
|
|
2424
2862
|
//#endregion
|
|
2425
2863
|
//#region ../client/src/resources/segment.ts
|
|
@@ -2427,7 +2865,6 @@ const SegmentApiList = z.array(Segment);
|
|
|
2427
2865
|
function segmentResource(transport) {
|
|
2428
2866
|
/** List every segment the caller can see. */
|
|
2429
2867
|
async function list(options = {}) {
|
|
2430
|
-
await transport.require("segment.list", options);
|
|
2431
2868
|
return {
|
|
2432
2869
|
data: await transport.requestParsed(SegmentApiList, "/api/segment", { ...options }),
|
|
2433
2870
|
total: null
|
|
@@ -2435,12 +2872,10 @@ function segmentResource(transport) {
|
|
|
2435
2872
|
}
|
|
2436
2873
|
/** Get one segment by id. */
|
|
2437
2874
|
async function get(id, options = {}) {
|
|
2438
|
-
await transport.require("segment.get", options);
|
|
2439
2875
|
return transport.requestParsed(Segment, `/api/segment/${id}`, { ...options });
|
|
2440
2876
|
}
|
|
2441
2877
|
/** Create a segment — a reusable row filter over one table — from a full segment body. */
|
|
2442
2878
|
async function create(params, options = {}) {
|
|
2443
|
-
await transport.require("segment.create", options);
|
|
2444
2879
|
return transport.requestParsed(Segment, "/api/segment", {
|
|
2445
2880
|
...options,
|
|
2446
2881
|
method: "POST",
|
|
@@ -2452,7 +2887,6 @@ function segmentResource(transport) {
|
|
|
2452
2887
|
* required: Metabase records every segment change in its revision history.
|
|
2453
2888
|
*/
|
|
2454
2889
|
async function update(id, params, options = {}) {
|
|
2455
|
-
await transport.require("segment.update", options);
|
|
2456
2890
|
return transport.requestParsed(Segment, `/api/segment/${id}`, {
|
|
2457
2891
|
...options,
|
|
2458
2892
|
method: "PUT",
|
|
@@ -2464,7 +2898,6 @@ function segmentResource(transport) {
|
|
|
2464
2898
|
* same required `revision_message`.
|
|
2465
2899
|
*/
|
|
2466
2900
|
async function archive(id, params, options = {}) {
|
|
2467
|
-
await transport.require("segment.archive", options);
|
|
2468
2901
|
return update(id, {
|
|
2469
2902
|
archived: true,
|
|
2470
2903
|
revision_message: params.revision_message
|
|
@@ -2489,7 +2922,6 @@ function rethrowSettingError(error, key) {
|
|
|
2489
2922
|
function settingResource(transport) {
|
|
2490
2923
|
/** Get all settings and their values, with descriptions and environment-variable names. */
|
|
2491
2924
|
async function list(options = {}) {
|
|
2492
|
-
await transport.require("setting.list", options);
|
|
2493
2925
|
return {
|
|
2494
2926
|
data: await transport.requestParsed(SettingApiList, "/api/setting", { ...options }),
|
|
2495
2927
|
total: null
|
|
@@ -2500,12 +2932,10 @@ function settingResource(transport) {
|
|
|
2500
2932
|
* reaches the caller as `null`; a string-typed setting answers bare text rather than JSON.
|
|
2501
2933
|
*/
|
|
2502
2934
|
async function get(key, options = {}) {
|
|
2503
|
-
await transport.require("setting.get", options);
|
|
2504
2935
|
return fetchOptionalParsed(transport, `/api/setting/${encodeURIComponent(key)}`, z.unknown(), options).catch((error) => rethrowSettingError(error, key));
|
|
2505
2936
|
}
|
|
2506
2937
|
/** Set a single setting's value by key. The endpoint answers 204 with no body. */
|
|
2507
2938
|
async function set(key, value, options = {}) {
|
|
2508
|
-
await transport.require("setting.set", options);
|
|
2509
2939
|
await transport.requestRaw(`/api/setting/${encodeURIComponent(key)}`, {
|
|
2510
2940
|
...options,
|
|
2511
2941
|
method: "PUT",
|
|
@@ -2527,7 +2957,6 @@ function setupResource(transport) {
|
|
|
2527
2957
|
* applying the instance preferences. The setup token is single-use.
|
|
2528
2958
|
*/
|
|
2529
2959
|
async function create(params, options = {}) {
|
|
2530
|
-
await transport.require("setup.create", options);
|
|
2531
2960
|
return transport.requestParsed(SetupResult, "/api/setup", {
|
|
2532
2961
|
...options,
|
|
2533
2962
|
method: "POST",
|
|
@@ -2542,7 +2971,6 @@ const SnippetApiList = z.array(Snippet);
|
|
|
2542
2971
|
function snippetResource(transport) {
|
|
2543
2972
|
/** List native query snippets. `archived` swaps the listing to archived snippets. */
|
|
2544
2973
|
async function list(params = {}, options = {}) {
|
|
2545
|
-
await transport.require("snippet.list", options);
|
|
2546
2974
|
return {
|
|
2547
2975
|
data: await transport.requestParsed(SnippetApiList, "/api/native-query-snippet", {
|
|
2548
2976
|
...options,
|
|
@@ -2553,12 +2981,10 @@ function snippetResource(transport) {
|
|
|
2553
2981
|
}
|
|
2554
2982
|
/** Get one native query snippet by id. */
|
|
2555
2983
|
async function get(id, options = {}) {
|
|
2556
|
-
await transport.require("snippet.get", options);
|
|
2557
2984
|
return transport.requestParsed(Snippet, `/api/native-query-snippet/${id}`, { ...options });
|
|
2558
2985
|
}
|
|
2559
2986
|
/** Create a native query snippet from a name, its SQL fragment, and an optional collection. */
|
|
2560
2987
|
async function create(params, options = {}) {
|
|
2561
|
-
await transport.require("snippet.create", options);
|
|
2562
2988
|
return transport.requestParsed(Snippet, "/api/native-query-snippet", {
|
|
2563
2989
|
...options,
|
|
2564
2990
|
method: "POST",
|
|
@@ -2567,7 +2993,6 @@ function snippetResource(transport) {
|
|
|
2567
2993
|
}
|
|
2568
2994
|
/** Update a native query snippet by id, patching only the fields the body carries. */
|
|
2569
2995
|
async function update(id, params, options = {}) {
|
|
2570
|
-
await transport.require("snippet.update", options);
|
|
2571
2996
|
return transport.requestParsed(Snippet, `/api/native-query-snippet/${id}`, {
|
|
2572
2997
|
...options,
|
|
2573
2998
|
method: "PUT",
|
|
@@ -2579,7 +3004,6 @@ function snippetResource(transport) {
|
|
|
2579
3004
|
* own endpoint.
|
|
2580
3005
|
*/
|
|
2581
3006
|
async function archive(id, options = {}) {
|
|
2582
|
-
await transport.require("snippet.archive", options);
|
|
2583
3007
|
return update(id, { archived: true }, options);
|
|
2584
3008
|
}
|
|
2585
3009
|
return {
|
|
@@ -2609,13 +3033,23 @@ const LIST_PARAM_FEATURES = [
|
|
|
2609
3033
|
["published-only", "tableListPublishedFilter"]
|
|
2610
3034
|
];
|
|
2611
3035
|
function listParamFeatures(params) {
|
|
2612
|
-
return LIST_PARAM_FEATURES.filter(([param]) => params[param]
|
|
3036
|
+
return LIST_PARAM_FEATURES.filter(([param]) => params[param] === true).map(([, feature]) => feature);
|
|
3037
|
+
}
|
|
3038
|
+
function dataLayerFeatures(value, field) {
|
|
3039
|
+
return isTierName(value) ? [{
|
|
3040
|
+
feature: "tableDataLayerTiers",
|
|
3041
|
+
fields: [field]
|
|
3042
|
+
}] : [];
|
|
3043
|
+
}
|
|
3044
|
+
function isTierName(value) {
|
|
3045
|
+
return value !== null && value !== void 0 && TableDataLayerTier.safeParse(value).success;
|
|
2613
3046
|
}
|
|
2614
3047
|
const UPLOAD_UPDATE_PATHS = {
|
|
2615
3048
|
append: "append-csv",
|
|
2616
3049
|
replace: "replace-csv"
|
|
2617
3050
|
};
|
|
2618
3051
|
function tableResource(transport) {
|
|
3052
|
+
const { explain } = explainer(transport, "table");
|
|
2619
3053
|
/**
|
|
2620
3054
|
* List every table the caller can see, across all databases. `term` matches names and display
|
|
2621
3055
|
* names; `can-query` and `can-write` keep only the tables the caller may query or edit the
|
|
@@ -2623,9 +3057,7 @@ function tableResource(transport) {
|
|
|
2623
3057
|
* tables nothing depends on; `published-only` keeps tables published to the library.
|
|
2624
3058
|
*/
|
|
2625
3059
|
async function list(params = {}, options = {}) {
|
|
2626
|
-
await transport.require("table.list", options);
|
|
2627
3060
|
await transport.requireFeatures(listParamFeatures(params), options);
|
|
2628
|
-
await requireDataLayer(params["data-layer"], options);
|
|
2629
3061
|
return {
|
|
2630
3062
|
data: await transport.requestParsed(TableApiList, "/api/table", {
|
|
2631
3063
|
...options,
|
|
@@ -2649,31 +3081,30 @@ function tableResource(transport) {
|
|
|
2649
3081
|
}
|
|
2650
3082
|
/** Get one table by id, without its fields. */
|
|
2651
3083
|
async function get(id, options = {}) {
|
|
2652
|
-
await transport.require("table.get", options);
|
|
2653
3084
|
return transport.requestParsed(Table, `/api/table/${id}`, { ...options });
|
|
2654
3085
|
}
|
|
2655
3086
|
/** Update a table by id, patching only the fields the body carries. */
|
|
2656
3087
|
async function update(id, params, options = {}) {
|
|
2657
|
-
await
|
|
2658
|
-
await
|
|
2659
|
-
|
|
3088
|
+
await refuseRewrittenDataLayer(params.data_layer, options);
|
|
3089
|
+
await requireDataAuthorityNull(params.data_authority, options);
|
|
3090
|
+
const path = `/api/table/${id}`;
|
|
3091
|
+
const updated = await transport.requestParsed(Table, path, {
|
|
2660
3092
|
...options,
|
|
2661
3093
|
method: "PUT",
|
|
2662
3094
|
body: params
|
|
2663
3095
|
});
|
|
3096
|
+
assertCollectionWritten(path, params.collection_id, updated);
|
|
3097
|
+
return updated;
|
|
2664
3098
|
}
|
|
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(", ")}`);
|
|
3099
|
+
async function refuseRewrittenDataLayer(value, options) {
|
|
3100
|
+
if (value === null || value === void 0 || isTierName(value)) return;
|
|
3101
|
+
const profile = await transport.preflightServer(options);
|
|
3102
|
+
if (profile === null) return;
|
|
3103
|
+
const failure = supersedingFeature("tableDataLayerTiers", profile, `data_layer "${value}" is a medallion name; this server names a table's layer ${TableDataLayerTier.options.join(", ")} and would map it onto one of those without a word`);
|
|
3104
|
+
if (failure !== null) transport.refuseBeforeSending(new CapabilityError(failure));
|
|
2673
3105
|
}
|
|
2674
3106
|
/** Get a table by id with its fields hydrated — the metadata the query builder runs on. */
|
|
2675
3107
|
async function queryMetadata(id, options = {}) {
|
|
2676
|
-
await transport.require("table.queryMetadata", options);
|
|
2677
3108
|
return transport.requestParsed(TableQueryMetadata, `/api/table/${id}/query_metadata`, { ...options });
|
|
2678
3109
|
}
|
|
2679
3110
|
/**
|
|
@@ -2681,7 +3112,6 @@ function tableResource(transport) {
|
|
|
2681
3112
|
* fields answers no content, which is the same empty list.
|
|
2682
3113
|
*/
|
|
2683
3114
|
async function fks(id, options = {}) {
|
|
2684
|
-
await transport.require("table.fks", options);
|
|
2685
3115
|
return {
|
|
2686
3116
|
data: await fetchOptionalParsed(transport, `/api/table/${id}/fks`, TableApiFks, { ...options }) ?? [],
|
|
2687
3117
|
total: null
|
|
@@ -2693,7 +3123,6 @@ function tableResource(transport) {
|
|
|
2693
3123
|
* connect to is a 422.
|
|
2694
3124
|
*/
|
|
2695
3125
|
async function syncSchema(id, options = {}) {
|
|
2696
|
-
await transport.require("table.syncSchema", options);
|
|
2697
3126
|
return {
|
|
2698
3127
|
id,
|
|
2699
3128
|
status: (await transport.requestParsed(SyncSchemaResponse, `/api/table/${id}/sync_schema`, {
|
|
@@ -2707,7 +3136,6 @@ function tableResource(transport) {
|
|
|
2707
3136
|
* cached set. The scan runs after the call returns.
|
|
2708
3137
|
*/
|
|
2709
3138
|
async function rescanValues(id, options = {}) {
|
|
2710
|
-
await transport.require("table.rescanValues", options);
|
|
2711
3139
|
return {
|
|
2712
3140
|
id,
|
|
2713
3141
|
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/rescan_values`, {
|
|
@@ -2722,7 +3150,6 @@ function tableResource(transport) {
|
|
|
2722
3150
|
* time the field's values are read.
|
|
2723
3151
|
*/
|
|
2724
3152
|
async function discardValues(id, options = {}) {
|
|
2725
|
-
await transport.require("table.discardValues", options);
|
|
2726
3153
|
return {
|
|
2727
3154
|
id,
|
|
2728
3155
|
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/discard_values`, {
|
|
@@ -2737,7 +3164,6 @@ function tableResource(transport) {
|
|
|
2737
3164
|
* selectors matched a table.
|
|
2738
3165
|
*/
|
|
2739
3166
|
async function bulkEdit(params, options = {}) {
|
|
2740
|
-
await transport.require("table.bulkEdit", options);
|
|
2741
3167
|
await requireDataAuthorityNull(params.data_authority, options);
|
|
2742
3168
|
await transport.requestParsed(BulkEditResponse, BULK_EDIT_PATH, {
|
|
2743
3169
|
...options,
|
|
@@ -2771,7 +3197,6 @@ function tableResource(transport) {
|
|
|
2771
3197
|
* call is a 422; the syncs run after it returns.
|
|
2772
3198
|
*/
|
|
2773
3199
|
async function bulkSyncSchema(selectors, options = {}) {
|
|
2774
|
-
await transport.require("table.bulkSyncSchema", options);
|
|
2775
3200
|
return postSelection(BULK_SYNC_SCHEMA_PATH, selectors, options);
|
|
2776
3201
|
}
|
|
2777
3202
|
/**
|
|
@@ -2779,7 +3204,6 @@ function tableResource(transport) {
|
|
|
2779
3204
|
* are already cached. The scans run after the call returns.
|
|
2780
3205
|
*/
|
|
2781
3206
|
async function bulkRescanValues(selectors, options = {}) {
|
|
2782
|
-
await transport.require("table.bulkRescanValues", options);
|
|
2783
3207
|
return postSelection(BULK_RESCAN_VALUES_PATH, selectors, options);
|
|
2784
3208
|
}
|
|
2785
3209
|
/**
|
|
@@ -2787,7 +3211,6 @@ function tableResource(transport) {
|
|
|
2787
3211
|
* included. No scan recreates a discarded set.
|
|
2788
3212
|
*/
|
|
2789
3213
|
async function bulkDiscardValues(selectors, options = {}) {
|
|
2790
|
-
await transport.require("table.bulkDiscardValues", options);
|
|
2791
3214
|
return postSelection(BULK_DISCARD_VALUES_PATH, selectors, options);
|
|
2792
3215
|
}
|
|
2793
3216
|
async function updateFromCsv(id, action, file, options) {
|
|
@@ -2809,7 +3232,6 @@ function tableResource(transport) {
|
|
|
2809
3232
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
2810
3233
|
*/
|
|
2811
3234
|
async function appendCsv(id, file, options = {}) {
|
|
2812
|
-
await transport.require("table.appendCsv", options);
|
|
2813
3235
|
return updateFromCsv(id, "append", file, options);
|
|
2814
3236
|
}
|
|
2815
3237
|
/**
|
|
@@ -2819,37 +3241,53 @@ function tableResource(transport) {
|
|
|
2819
3241
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
2820
3242
|
*/
|
|
2821
3243
|
async function replaceCsv(id, file, options = {}) {
|
|
2822
|
-
await transport.require("table.replaceCsv", options);
|
|
2823
3244
|
return updateFromCsv(id, "replace", file, options);
|
|
2824
3245
|
}
|
|
2825
3246
|
return {
|
|
2826
|
-
list,
|
|
3247
|
+
list: explain("list", list, (params) => dataLayerFeatures(params?.["data-layer"], "data-layer")),
|
|
2827
3248
|
get,
|
|
2828
|
-
update,
|
|
3249
|
+
update: explain("update", update, (_id, params) => dataLayerFeatures(params.data_layer, "data_layer")),
|
|
2829
3250
|
queryMetadata,
|
|
2830
3251
|
fks,
|
|
2831
3252
|
syncSchema,
|
|
2832
3253
|
rescanValues,
|
|
2833
3254
|
discardValues,
|
|
2834
|
-
bulkEdit,
|
|
2835
|
-
bulkSyncSchema,
|
|
2836
|
-
bulkRescanValues,
|
|
2837
|
-
bulkDiscardValues,
|
|
3255
|
+
bulkEdit: explain("bulkEdit", bulkEdit),
|
|
3256
|
+
bulkSyncSchema: explain("bulkSyncSchema", bulkSyncSchema),
|
|
3257
|
+
bulkRescanValues: explain("bulkRescanValues", bulkRescanValues),
|
|
3258
|
+
bulkDiscardValues: explain("bulkDiscardValues", bulkDiscardValues),
|
|
2838
3259
|
appendCsv,
|
|
2839
3260
|
replaceCsv
|
|
2840
3261
|
};
|
|
2841
3262
|
}
|
|
3263
|
+
function assertCollectionWritten(path, requested, table) {
|
|
3264
|
+
if (requested === void 0 || table.collection_id === requested) return;
|
|
3265
|
+
const answered = table.collection_id === void 0 ? { kind: "unreported" } : {
|
|
3266
|
+
kind: "reported",
|
|
3267
|
+
value: table.collection_id
|
|
3268
|
+
};
|
|
3269
|
+
throw new PartialWriteError(`the server applied the rest of the update to table ${table.id} but not collection_id: ${describeCollectionAnswer(answered)}, because this server does not move a table to a collection through an update`, {
|
|
3270
|
+
method: "PUT",
|
|
3271
|
+
path,
|
|
3272
|
+
field: "collection_id",
|
|
3273
|
+
requested,
|
|
3274
|
+
answered
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
function describeCollectionAnswer(answered) {
|
|
3278
|
+
if (answered.kind === "unreported") return "it answered the table without reporting a collection";
|
|
3279
|
+
if (answered.value === null) return "the table stays in no collection";
|
|
3280
|
+
return `the table stays in collection ${answered.value}`;
|
|
3281
|
+
}
|
|
2842
3282
|
//#endregion
|
|
2843
3283
|
//#region ../client/src/resources/timeline-event.ts
|
|
2844
3284
|
function timelineEventResource(transport) {
|
|
2845
3285
|
/** Get one timeline event by id. */
|
|
2846
3286
|
async function get(id, options = {}) {
|
|
2847
|
-
await transport.require("timelineEvent.get", options);
|
|
2848
3287
|
return transport.requestParsed(TimelineEvent, `/api/timeline-event/${id}`, { ...options });
|
|
2849
3288
|
}
|
|
2850
3289
|
/** Create a timeline event on an existing timeline. */
|
|
2851
3290
|
async function create(params, options = {}) {
|
|
2852
|
-
await transport.require("timelineEvent.create", options);
|
|
2853
3291
|
return transport.requestParsed(TimelineEvent, "/api/timeline-event", {
|
|
2854
3292
|
...options,
|
|
2855
3293
|
method: "POST",
|
|
@@ -2861,7 +3299,6 @@ function timelineEventResource(transport) {
|
|
|
2861
3299
|
* the event to another timeline.
|
|
2862
3300
|
*/
|
|
2863
3301
|
async function update(id, params, options = {}) {
|
|
2864
|
-
await transport.require("timelineEvent.update", options);
|
|
2865
3302
|
return transport.requestParsed(TimelineEvent, `/api/timeline-event/${id}`, {
|
|
2866
3303
|
...options,
|
|
2867
3304
|
method: "PUT",
|
|
@@ -2873,12 +3310,10 @@ function timelineEventResource(transport) {
|
|
|
2873
3310
|
* endpoint.
|
|
2874
3311
|
*/
|
|
2875
3312
|
async function archive(id, options = {}) {
|
|
2876
|
-
await transport.require("timelineEvent.archive", options);
|
|
2877
3313
|
return update(id, { archived: true }, options);
|
|
2878
3314
|
}
|
|
2879
3315
|
/** Permanently delete a timeline event by id. The server answers with no body. */
|
|
2880
3316
|
async function remove(id, options = {}) {
|
|
2881
|
-
await transport.require("timelineEvent.delete", options);
|
|
2882
3317
|
await transport.requestRaw(`/api/timeline-event/${id}`, {
|
|
2883
3318
|
...options,
|
|
2884
3319
|
method: "DELETE",
|
|
@@ -2900,7 +3335,6 @@ const TimelineWithEvents = Timeline.required({ events: true });
|
|
|
2900
3335
|
function timelineResource(transport) {
|
|
2901
3336
|
/** List timelines. `archived` swaps the active listing for the archived one. */
|
|
2902
3337
|
async function list(params = {}, options = {}) {
|
|
2903
|
-
await transport.require("timeline.list", options);
|
|
2904
3338
|
return {
|
|
2905
3339
|
data: await transport.requestParsed(TimelineApiList, "/api/timeline", {
|
|
2906
3340
|
...options,
|
|
@@ -2911,7 +3345,6 @@ function timelineResource(transport) {
|
|
|
2911
3345
|
}
|
|
2912
3346
|
/** Get one timeline by id, without its events. */
|
|
2913
3347
|
async function get(id, options = {}) {
|
|
2914
|
-
await transport.require("timeline.get", options);
|
|
2915
3348
|
return transport.requestParsed(Timeline, `/api/timeline/${id}`, { ...options });
|
|
2916
3349
|
}
|
|
2917
3350
|
/**
|
|
@@ -2919,7 +3352,6 @@ function timelineResource(transport) {
|
|
|
2919
3352
|
* timeline read hydrates them on request, and `archived` chooses which of them come back.
|
|
2920
3353
|
*/
|
|
2921
3354
|
async function events(id, params = {}, options = {}) {
|
|
2922
|
-
await transport.require("timeline.events", options);
|
|
2923
3355
|
return {
|
|
2924
3356
|
data: (await transport.requestParsed(TimelineWithEvents, `/api/timeline/${id}`, {
|
|
2925
3357
|
...options,
|
|
@@ -2933,7 +3365,6 @@ function timelineResource(transport) {
|
|
|
2933
3365
|
}
|
|
2934
3366
|
/** Create a timeline from a full timeline body. */
|
|
2935
3367
|
async function create(params, options = {}) {
|
|
2936
|
-
await transport.require("timeline.create", options);
|
|
2937
3368
|
return transport.requestParsed(Timeline, "/api/timeline", {
|
|
2938
3369
|
...options,
|
|
2939
3370
|
method: "POST",
|
|
@@ -2945,7 +3376,6 @@ function timelineResource(transport) {
|
|
|
2945
3376
|
* cascades to every event on the timeline.
|
|
2946
3377
|
*/
|
|
2947
3378
|
async function update(id, params, options = {}) {
|
|
2948
|
-
await transport.require("timeline.update", options);
|
|
2949
3379
|
return transport.requestParsed(Timeline, `/api/timeline/${id}`, {
|
|
2950
3380
|
...options,
|
|
2951
3381
|
method: "PUT",
|
|
@@ -2957,12 +3387,10 @@ function timelineResource(transport) {
|
|
|
2957
3387
|
* not its own endpoint.
|
|
2958
3388
|
*/
|
|
2959
3389
|
async function archive(id, options = {}) {
|
|
2960
|
-
await transport.require("timeline.archive", options);
|
|
2961
3390
|
return update(id, { archived: true }, options);
|
|
2962
3391
|
}
|
|
2963
3392
|
/** Permanently delete a timeline and all its events by id. The server answers with no body. */
|
|
2964
3393
|
async function remove(id, options = {}) {
|
|
2965
|
-
await transport.require("timeline.delete", options);
|
|
2966
3394
|
await transport.requestRaw(`/api/timeline/${id}`, {
|
|
2967
3395
|
...options,
|
|
2968
3396
|
method: "DELETE",
|
|
@@ -2999,12 +3427,12 @@ TransformMemberRun.pick({
|
|
|
2999
3427
|
//#region ../client/src/resources/transform-dag-run.ts
|
|
3000
3428
|
const TransformMemberRunList = z.array(TransformMemberRun);
|
|
3001
3429
|
function transformDagRunResource(transport) {
|
|
3430
|
+
const { explain } = explainer(transport, "transformDagRun");
|
|
3002
3431
|
/**
|
|
3003
3432
|
* List the transform runs a DAG run coordinated so far. A member run is inserted when its
|
|
3004
3433
|
* transform starts, so the list grows while the DAG run is in progress.
|
|
3005
3434
|
*/
|
|
3006
3435
|
async function transformRuns(runId, options = {}) {
|
|
3007
|
-
await transport.require("transformDagRun.transformRuns", options);
|
|
3008
3436
|
return {
|
|
3009
3437
|
data: await transport.requestParsed(TransformMemberRunList, `/api/transform-dag-run/${runId}/transform-runs`, { ...options }),
|
|
3010
3438
|
total: null
|
|
@@ -3012,7 +3440,6 @@ function transformDagRunResource(transport) {
|
|
|
3012
3440
|
}
|
|
3013
3441
|
/** Cancel an in-progress DAG run and request cancellation of its still-running transforms. */
|
|
3014
3442
|
async function cancel(runId, options = {}) {
|
|
3015
|
-
await transport.require("transformDagRun.cancel", options);
|
|
3016
3443
|
await transport.requestRaw(`/api/transform-dag-run/${runId}/cancel`, {
|
|
3017
3444
|
...options,
|
|
3018
3445
|
method: "POST",
|
|
@@ -3020,8 +3447,8 @@ function transformDagRunResource(transport) {
|
|
|
3020
3447
|
});
|
|
3021
3448
|
}
|
|
3022
3449
|
return {
|
|
3023
|
-
transformRuns,
|
|
3024
|
-
cancel
|
|
3450
|
+
transformRuns: explain("transformRuns", transformRuns),
|
|
3451
|
+
cancel: explain("cancel", cancel)
|
|
3025
3452
|
};
|
|
3026
3453
|
}
|
|
3027
3454
|
//#endregion
|
|
@@ -3169,12 +3596,12 @@ function lensPath(id, lensId) {
|
|
|
3169
3596
|
return `/api/ee/transforms/${id}/inspect/${encodeURIComponent(lensId)}`;
|
|
3170
3597
|
}
|
|
3171
3598
|
function transformInspectorResource(transport) {
|
|
3599
|
+
const { explain } = explainer(transport, "transformInspector");
|
|
3172
3600
|
/**
|
|
3173
3601
|
* Discover what the inspector can show for a transform: its source and target tables with field
|
|
3174
3602
|
* statistics, the fields its query touches, and the lenses available on it.
|
|
3175
3603
|
*/
|
|
3176
3604
|
async function discover(id, options = {}) {
|
|
3177
|
-
await transport.require("transformInspector.discover", options);
|
|
3178
3605
|
return transport.requestParsed(TransformInspection, `/api/ee/transforms/${id}/inspect`, { ...options });
|
|
3179
3606
|
}
|
|
3180
3607
|
/**
|
|
@@ -3183,7 +3610,6 @@ function transformInspectorResource(transport) {
|
|
|
3183
3610
|
* the `params` its trigger emitted.
|
|
3184
3611
|
*/
|
|
3185
3612
|
async function lens(id, lensId, params = {}, options = {}) {
|
|
3186
|
-
await transport.require("transformInspector.lens", options);
|
|
3187
3613
|
return transport.requestParsed(TransformLens, lensPath(id, lensId), {
|
|
3188
3614
|
...options,
|
|
3189
3615
|
query: { join_step: params.join_step }
|
|
@@ -3195,7 +3621,6 @@ function transformInspectorResource(transport) {
|
|
|
3195
3621
|
* opened with.
|
|
3196
3622
|
*/
|
|
3197
3623
|
async function query(id, lensId, params, options = {}) {
|
|
3198
|
-
await transport.require("transformInspector.query", options);
|
|
3199
3624
|
return transport.requestParsed(CardQueryResult, `${lensPath(id, lensId)}/query`, {
|
|
3200
3625
|
...options,
|
|
3201
3626
|
method: "POST",
|
|
@@ -3203,17 +3628,17 @@ function transformInspectorResource(transport) {
|
|
|
3203
3628
|
});
|
|
3204
3629
|
}
|
|
3205
3630
|
return {
|
|
3206
|
-
discover,
|
|
3207
|
-
lens,
|
|
3208
|
-
query
|
|
3631
|
+
discover: explain("discover", discover),
|
|
3632
|
+
lens: explain("lens", lens),
|
|
3633
|
+
query: explain("query", query)
|
|
3209
3634
|
};
|
|
3210
3635
|
}
|
|
3211
3636
|
//#endregion
|
|
3212
3637
|
//#region ../client/src/resources/transform-job.ts
|
|
3213
3638
|
function transformJobResource(transport) {
|
|
3639
|
+
const { explain } = explainer(transport, "transformJob");
|
|
3214
3640
|
/** List every transform job the caller can see. */
|
|
3215
3641
|
async function list(options = {}) {
|
|
3216
|
-
await transport.require("transformJob.list", options);
|
|
3217
3642
|
const { features } = await transport.server(options);
|
|
3218
3643
|
return {
|
|
3219
3644
|
data: await transport.requestParsed(z.array(transformJobSchema(features)), "/api/transform-job", { ...options }),
|
|
@@ -3222,13 +3647,11 @@ function transformJobResource(transport) {
|
|
|
3222
3647
|
}
|
|
3223
3648
|
/** Get one transform job by id. */
|
|
3224
3649
|
async function get(id, options = {}) {
|
|
3225
|
-
await transport.require("transformJob.get", options);
|
|
3226
3650
|
const { features } = await transport.server(options);
|
|
3227
3651
|
return transport.requestParsed(transformJobSchema(features), `/api/transform-job/${id}`, { ...options });
|
|
3228
3652
|
}
|
|
3229
3653
|
/** Create a transform job — a schedule plus the tags it runs — from a full body. */
|
|
3230
3654
|
async function create(params, options = {}) {
|
|
3231
|
-
await transport.require("transformJob.create", options);
|
|
3232
3655
|
const { features } = await transport.server(options);
|
|
3233
3656
|
return transport.requestParsed(transformJobSchema(features), "/api/transform-job", {
|
|
3234
3657
|
...options,
|
|
@@ -3238,7 +3661,6 @@ function transformJobResource(transport) {
|
|
|
3238
3661
|
}
|
|
3239
3662
|
/** Update a transform job by id, patching only the fields the body carries. */
|
|
3240
3663
|
async function update(id, params, options = {}) {
|
|
3241
|
-
await transport.require("transformJob.update", options);
|
|
3242
3664
|
const { features } = await transport.server(options);
|
|
3243
3665
|
return transport.requestParsed(transformJobSchema(features), `/api/transform-job/${id}`, {
|
|
3244
3666
|
...options,
|
|
@@ -3248,7 +3670,6 @@ function transformJobResource(transport) {
|
|
|
3248
3670
|
}
|
|
3249
3671
|
/** Delete a transform job by id, leaving the transforms it ran untouched. */
|
|
3250
3672
|
async function remove(id, options = {}) {
|
|
3251
|
-
await transport.require("transformJob.delete", options);
|
|
3252
3673
|
await transport.requestRaw(`/api/transform-job/${id}`, {
|
|
3253
3674
|
...options,
|
|
3254
3675
|
method: "DELETE",
|
|
@@ -3260,7 +3681,6 @@ function transformJobResource(transport) {
|
|
|
3260
3681
|
* plus those transforms' dependencies; `run_all` re-runs dependencies that are already fresh.
|
|
3261
3682
|
*/
|
|
3262
3683
|
async function run(id, params = {}, options = {}) {
|
|
3263
|
-
await transport.require("transformJob.run", options);
|
|
3264
3684
|
const { features } = await transport.server(options);
|
|
3265
3685
|
return transport.requestParsed(transformJobRunResultSchema(features), `/api/transform-job/${id}/run`, {
|
|
3266
3686
|
...options,
|
|
@@ -3270,7 +3690,6 @@ function transformJobResource(transport) {
|
|
|
3270
3690
|
}
|
|
3271
3691
|
/** List the transforms a job would execute, resolved from the job's tags. */
|
|
3272
3692
|
async function transforms(id, options = {}) {
|
|
3273
|
-
await transport.require("transformJob.transforms", options);
|
|
3274
3693
|
const { features } = await transport.server(options);
|
|
3275
3694
|
return {
|
|
3276
3695
|
data: await transport.requestParsed(z.array(transformRowSchema(features)), `/api/transform-job/${id}/transforms`, { ...options }),
|
|
@@ -3282,7 +3701,6 @@ function transformJobResource(transport) {
|
|
|
3282
3701
|
* manual runs ignore the flag. The path carries no id and is a different endpoint from `update`.
|
|
3283
3702
|
*/
|
|
3284
3703
|
async function setActive(active, options = {}) {
|
|
3285
|
-
await transport.require("transformJob.setActive", options);
|
|
3286
3704
|
return transport.requestParsed(TransformJobActiveResult, "/api/transform-job/active", {
|
|
3287
3705
|
...options,
|
|
3288
3706
|
method: "PUT",
|
|
@@ -3290,14 +3708,14 @@ function transformJobResource(transport) {
|
|
|
3290
3708
|
});
|
|
3291
3709
|
}
|
|
3292
3710
|
return {
|
|
3293
|
-
list,
|
|
3294
|
-
get,
|
|
3295
|
-
create,
|
|
3296
|
-
update,
|
|
3297
|
-
delete: remove,
|
|
3298
|
-
run,
|
|
3299
|
-
transforms,
|
|
3300
|
-
setActive
|
|
3711
|
+
list: explain("list", list),
|
|
3712
|
+
get: explain("get", get),
|
|
3713
|
+
create: explain("create", create),
|
|
3714
|
+
update: explain("update", update),
|
|
3715
|
+
delete: explain("delete", remove),
|
|
3716
|
+
run: explain("run", run),
|
|
3717
|
+
transforms: explain("transforms", transforms),
|
|
3718
|
+
setActive: explain("setActive", setActive)
|
|
3301
3719
|
};
|
|
3302
3720
|
}
|
|
3303
3721
|
//#endregion
|
|
@@ -3357,14 +3775,13 @@ function libraryPath(path) {
|
|
|
3357
3775
|
return `/api/ee/transforms-python/library/${encodeURIComponent(path)}`;
|
|
3358
3776
|
}
|
|
3359
3777
|
function transformPythonResource(transport) {
|
|
3778
|
+
const { explain } = explainer(transport, "transformPython");
|
|
3360
3779
|
/** Get the Python library at a path, the user module every Python transform can import. */
|
|
3361
3780
|
async function getLibrary(path, options = {}) {
|
|
3362
|
-
await transport.require("transformPython.getLibrary", options);
|
|
3363
3781
|
return transport.requestParsed(PythonLibrary, libraryPath(path), { ...options });
|
|
3364
3782
|
}
|
|
3365
3783
|
/** Replace the source of the Python library at a path, creating the library when none exists. */
|
|
3366
3784
|
async function updateLibrary(path, params, options = {}) {
|
|
3367
|
-
await transport.require("transformPython.updateLibrary", options);
|
|
3368
3785
|
return transport.requestParsed(PythonLibrary, libraryPath(path), {
|
|
3369
3786
|
...options,
|
|
3370
3787
|
method: "PUT",
|
|
@@ -3377,7 +3794,6 @@ function transformPythonResource(transport) {
|
|
|
3377
3794
|
* sizes are capped at 100 rows.
|
|
3378
3795
|
*/
|
|
3379
3796
|
async function testRun(params, options = {}) {
|
|
3380
|
-
await transport.require("transformPython.testRun", options);
|
|
3381
3797
|
return transport.requestParsed(PythonApiTestRun, "/api/ee/transforms-python/test-run", {
|
|
3382
3798
|
...options,
|
|
3383
3799
|
method: "POST",
|
|
@@ -3385,9 +3801,9 @@ function transformPythonResource(transport) {
|
|
|
3385
3801
|
});
|
|
3386
3802
|
}
|
|
3387
3803
|
return {
|
|
3388
|
-
getLibrary,
|
|
3389
|
-
updateLibrary,
|
|
3390
|
-
testRun
|
|
3804
|
+
getLibrary: explain("getLibrary", getLibrary),
|
|
3805
|
+
updateLibrary: explain("updateLibrary", updateLibrary),
|
|
3806
|
+
testRun: explain("testRun", testRun)
|
|
3391
3807
|
};
|
|
3392
3808
|
}
|
|
3393
3809
|
//#endregion
|
|
@@ -3401,6 +3817,7 @@ const TransformRunKickoff = z.object({
|
|
|
3401
3817
|
});
|
|
3402
3818
|
const TransformDagTransformList = z.array(TransformDagTransform);
|
|
3403
3819
|
function transformResource(transport) {
|
|
3820
|
+
const { explain, explainWalk } = explainer(transport, "transform");
|
|
3404
3821
|
async function requestTransformList(path, opts) {
|
|
3405
3822
|
const { features } = await transport.server(opts);
|
|
3406
3823
|
return {
|
|
@@ -3414,18 +3831,15 @@ function transformResource(transport) {
|
|
|
3414
3831
|
}
|
|
3415
3832
|
/** List every transform the caller can see. */
|
|
3416
3833
|
async function list(options = {}) {
|
|
3417
|
-
await transport.require("transform.list", options);
|
|
3418
3834
|
return requestTransformList("/api/transform", { ...options });
|
|
3419
3835
|
}
|
|
3420
3836
|
/** Get one transform by id. */
|
|
3421
3837
|
async function get(id, options = {}) {
|
|
3422
|
-
await transport.require("transform.get", options);
|
|
3423
3838
|
const { features } = await transport.server(options);
|
|
3424
3839
|
return transport.requestParsed(transformDetailSchema(features), `/api/transform/${id}`, { ...options });
|
|
3425
3840
|
}
|
|
3426
3841
|
/** Create a transform — a query plus the warehouse table it writes — from a full body. */
|
|
3427
3842
|
async function create(params, options = {}) {
|
|
3428
|
-
await transport.require("transform.create", options);
|
|
3429
3843
|
return requestTransform("/api/transform", {
|
|
3430
3844
|
...options,
|
|
3431
3845
|
method: "POST",
|
|
@@ -3434,7 +3848,6 @@ function transformResource(transport) {
|
|
|
3434
3848
|
}
|
|
3435
3849
|
/** Update a transform by id, patching only the fields the body carries. */
|
|
3436
3850
|
async function update(id, params, options = {}) {
|
|
3437
|
-
await transport.require("transform.update", options);
|
|
3438
3851
|
return requestTransform(`/api/transform/${id}`, {
|
|
3439
3852
|
...options,
|
|
3440
3853
|
method: "PUT",
|
|
@@ -3443,7 +3856,6 @@ function transformResource(transport) {
|
|
|
3443
3856
|
}
|
|
3444
3857
|
/** Delete a transform by id, leaving any table it already materialized in place. */
|
|
3445
3858
|
async function remove(id, options = {}) {
|
|
3446
|
-
await transport.require("transform.delete", options);
|
|
3447
3859
|
await transport.requestRaw(`/api/transform/${id}`, {
|
|
3448
3860
|
...options,
|
|
3449
3861
|
method: "DELETE",
|
|
@@ -3452,7 +3864,6 @@ function transformResource(transport) {
|
|
|
3452
3864
|
}
|
|
3453
3865
|
/** Drop a transform's materialized output table, keeping the transform definition. */
|
|
3454
3866
|
async function deleteTable(id, options = {}) {
|
|
3455
|
-
await transport.require("transform.deleteTable", options);
|
|
3456
3867
|
await transport.requestRaw(`/api/transform/${id}/table`, {
|
|
3457
3868
|
...options,
|
|
3458
3869
|
method: "DELETE",
|
|
@@ -3461,12 +3872,10 @@ function transformResource(transport) {
|
|
|
3461
3872
|
}
|
|
3462
3873
|
/** List the upstream transforms a transform depends on. */
|
|
3463
3874
|
async function dependencies(id, options = {}) {
|
|
3464
|
-
await transport.require("transform.dependencies", options);
|
|
3465
3875
|
return requestTransformList(`/api/transform/${id}/dependencies`, { ...options });
|
|
3466
3876
|
}
|
|
3467
3877
|
/** Request cancellation of a transform's current run. */
|
|
3468
3878
|
async function cancel(id, options = {}) {
|
|
3469
|
-
await transport.require("transform.cancel", options);
|
|
3470
3879
|
await transport.requestRaw(`/api/transform/${id}/cancel`, {
|
|
3471
3880
|
...options,
|
|
3472
3881
|
method: "POST",
|
|
@@ -3475,12 +3884,10 @@ function transformResource(transport) {
|
|
|
3475
3884
|
}
|
|
3476
3885
|
/** Get one transform run by run id — not by the id of the transform that produced it. */
|
|
3477
3886
|
async function getRun(runId, options = {}) {
|
|
3478
|
-
await transport.require("transform.getRun", options);
|
|
3479
3887
|
return transport.requestParsed(TransformRun, `/api/transform/run/${runId}`, { ...options });
|
|
3480
3888
|
}
|
|
3481
3889
|
/** Walk the run history, newest first, optionally narrowed to a single transform. */
|
|
3482
3890
|
async function* runPages(params = {}, options = {}) {
|
|
3483
|
-
await transport.require("transform.runPages", options);
|
|
3484
3891
|
yield* paginatePages(transport, "/api/transform/run", TransformRun, {
|
|
3485
3892
|
query: { "transform-ids": params["transform-ids"] },
|
|
3486
3893
|
...options.offset !== void 0 && { offset: options.offset },
|
|
@@ -3494,7 +3901,6 @@ function transformResource(transport) {
|
|
|
3494
3901
|
* DAG reprocess run, or a standalone transform run — and never a member run of a job or DAG.
|
|
3495
3902
|
*/
|
|
3496
3903
|
async function* runSummaryPages(params = {}, options = {}) {
|
|
3497
|
-
await transport.require("transform.runSummaryPages", options);
|
|
3498
3904
|
yield* paginatePages(transport, "/api/transform/runs", TransformRunSummary, {
|
|
3499
3905
|
query: {
|
|
3500
3906
|
types: params.types,
|
|
@@ -3517,7 +3923,6 @@ function transformResource(transport) {
|
|
|
3517
3923
|
* source rather than the rows past the last checkpoint.
|
|
3518
3924
|
*/
|
|
3519
3925
|
async function resetCheckpoint(id, options = {}) {
|
|
3520
|
-
await transport.require("transform.resetCheckpoint", options);
|
|
3521
3926
|
await transport.requestRaw(`/api/transform/${id}/reset-checkpoint`, {
|
|
3522
3927
|
...options,
|
|
3523
3928
|
method: "POST",
|
|
@@ -3532,7 +3937,6 @@ function transformResource(transport) {
|
|
|
3532
3937
|
* `runSummaryPages` with `types: ["dag"]`, and the member runs off `transformDagRun.transformRuns`.
|
|
3533
3938
|
*/
|
|
3534
3939
|
async function runDag(id, params, options = {}) {
|
|
3535
|
-
await transport.require("transform.runDag", options);
|
|
3536
3940
|
return transport.requestParsed(TransformDagRunResult, `/api/transform/${id}/run-dag`, {
|
|
3537
3941
|
...options,
|
|
3538
3942
|
method: "POST",
|
|
@@ -3541,7 +3945,6 @@ function transformResource(transport) {
|
|
|
3541
3945
|
}
|
|
3542
3946
|
/** Preview the transforms a DAG reprocess seeded at a transform would run, in execution order. */
|
|
3543
3947
|
async function dagTransforms(id, params, options = {}) {
|
|
3544
|
-
await transport.require("transform.dagTransforms", options);
|
|
3545
3948
|
return {
|
|
3546
3949
|
data: await transport.requestParsed(TransformDagTransformList, `/api/transform/${id}/dag-transforms`, {
|
|
3547
3950
|
...options,
|
|
@@ -3556,7 +3959,6 @@ function transformResource(transport) {
|
|
|
3556
3959
|
* A server that answers no run id started nothing, and there is then nothing to poll.
|
|
3557
3960
|
*/
|
|
3558
3961
|
async function run(id, params = {}, options = {}) {
|
|
3559
|
-
await transport.require("transform.run", options);
|
|
3560
3962
|
const kickoff = await transport.requestParsed(TransformRunKickoff, `/api/transform/${id}/run`, {
|
|
3561
3963
|
...options,
|
|
3562
3964
|
method: "POST"
|
|
@@ -3597,30 +3999,30 @@ function transformResource(transport) {
|
|
|
3597
3999
|
}
|
|
3598
4000
|
}
|
|
3599
4001
|
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
|
|
4002
|
+
list: explain("list", list),
|
|
4003
|
+
get: explain("get", get),
|
|
4004
|
+
create: explain("create", create),
|
|
4005
|
+
update: explain("update", update),
|
|
4006
|
+
delete: explain("delete", remove),
|
|
4007
|
+
deleteTable: explain("deleteTable", deleteTable),
|
|
4008
|
+
dependencies: explain("dependencies", dependencies),
|
|
4009
|
+
cancel: explain("cancel", cancel),
|
|
4010
|
+
getRun: explain("getRun", getRun),
|
|
4011
|
+
runPages: explainWalk("runPages", runPages),
|
|
4012
|
+
runSummaryPages: explainWalk("runSummaryPages", runSummaryPages),
|
|
4013
|
+
resetCheckpoint: explain("resetCheckpoint", resetCheckpoint),
|
|
4014
|
+
runDag: explain("runDag", runDag),
|
|
4015
|
+
dagTransforms: explain("dagTransforms", dagTransforms),
|
|
4016
|
+
run: explain("run", run)
|
|
3615
4017
|
};
|
|
3616
4018
|
}
|
|
3617
4019
|
//#endregion
|
|
3618
4020
|
//#region ../client/src/resources/transform-tag.ts
|
|
3619
4021
|
const TransformTagApiList = z.array(TransformTag);
|
|
3620
4022
|
function transformTagResource(transport) {
|
|
4023
|
+
const { explain } = explainer(transport, "transformTag");
|
|
3621
4024
|
/** List every transform tag the caller can see, built-in tags included. */
|
|
3622
4025
|
async function list(options = {}) {
|
|
3623
|
-
await transport.require("transformTag.list", options);
|
|
3624
4026
|
return {
|
|
3625
4027
|
data: await transport.requestParsed(TransformTagApiList, "/api/transform-tag", { ...options }),
|
|
3626
4028
|
total: null
|
|
@@ -3628,7 +4030,6 @@ function transformTagResource(transport) {
|
|
|
3628
4030
|
}
|
|
3629
4031
|
/** Create a transform tag, the label a transform job matches transforms on. */
|
|
3630
4032
|
async function create(params, options = {}) {
|
|
3631
|
-
await transport.require("transformTag.create", options);
|
|
3632
4033
|
return transport.requestParsed(TransformTag, "/api/transform-tag", {
|
|
3633
4034
|
...options,
|
|
3634
4035
|
method: "POST",
|
|
@@ -3637,7 +4038,6 @@ function transformTagResource(transport) {
|
|
|
3637
4038
|
}
|
|
3638
4039
|
/** Rename a transform tag by id. */
|
|
3639
4040
|
async function update(id, params, options = {}) {
|
|
3640
|
-
await transport.require("transformTag.update", options);
|
|
3641
4041
|
return transport.requestParsed(TransformTag, `/api/transform-tag/${id}`, {
|
|
3642
4042
|
...options,
|
|
3643
4043
|
method: "PUT",
|
|
@@ -3646,7 +4046,6 @@ function transformTagResource(transport) {
|
|
|
3646
4046
|
}
|
|
3647
4047
|
/** Delete a transform tag by id, detaching it from every transform and job carrying it. */
|
|
3648
4048
|
async function remove(id, options = {}) {
|
|
3649
|
-
await transport.require("transformTag.delete", options);
|
|
3650
4049
|
await transport.requestRaw(`/api/transform-tag/${id}`, {
|
|
3651
4050
|
...options,
|
|
3652
4051
|
method: "DELETE",
|
|
@@ -3654,19 +4053,180 @@ function transformTagResource(transport) {
|
|
|
3654
4053
|
});
|
|
3655
4054
|
}
|
|
3656
4055
|
return {
|
|
3657
|
-
list,
|
|
3658
|
-
create,
|
|
3659
|
-
update,
|
|
3660
|
-
delete: remove
|
|
4056
|
+
list: explain("list", list),
|
|
4057
|
+
create: explain("create", create),
|
|
4058
|
+
update: explain("update", update),
|
|
4059
|
+
delete: explain("delete", remove)
|
|
3661
4060
|
};
|
|
3662
4061
|
}
|
|
3663
4062
|
//#endregion
|
|
4063
|
+
//#region ../client/src/domain/transform-test.ts
|
|
4064
|
+
const TransformTestTable = z.object({
|
|
4065
|
+
schema: z.string().min(1).nullable().optional(),
|
|
4066
|
+
name: z.string().min(1)
|
|
4067
|
+
}).loose();
|
|
4068
|
+
const TransformTestColumn = z.object({
|
|
4069
|
+
name: z.string().min(1),
|
|
4070
|
+
cast_type: z.string().min(1)
|
|
4071
|
+
}).loose();
|
|
4072
|
+
const TransformTestCell = z.union([
|
|
4073
|
+
z.boolean(),
|
|
4074
|
+
z.number(),
|
|
4075
|
+
z.string(),
|
|
4076
|
+
z.null()
|
|
4077
|
+
]);
|
|
4078
|
+
const TransformTestRow = z.record(z.string(), TransformTestCell);
|
|
4079
|
+
const TransformTestSqlInput = z.object({
|
|
4080
|
+
table: TransformTestTable,
|
|
4081
|
+
format: z.literal("sql"),
|
|
4082
|
+
sql: z.string().min(1)
|
|
4083
|
+
}).loose();
|
|
4084
|
+
const TransformTestRowsInput = z.object({
|
|
4085
|
+
table: TransformTestTable,
|
|
4086
|
+
format: z.literal("rows"),
|
|
4087
|
+
columns: z.array(TransformTestColumn).min(1),
|
|
4088
|
+
rows: z.array(TransformTestRow)
|
|
4089
|
+
}).loose();
|
|
4090
|
+
const TransformTestInput = z.discriminatedUnion("format", [TransformTestSqlInput, TransformTestRowsInput]);
|
|
4091
|
+
const TransformTestEqualsSqlExpectation = z.object({
|
|
4092
|
+
type: z.literal("equals"),
|
|
4093
|
+
name: z.string().min(1),
|
|
4094
|
+
format: z.literal("sql"),
|
|
4095
|
+
sql: z.string().min(1)
|
|
4096
|
+
}).loose();
|
|
4097
|
+
const TransformTestEqualsRowsExpectation = z.object({
|
|
4098
|
+
type: z.literal("equals"),
|
|
4099
|
+
name: z.string().min(1),
|
|
4100
|
+
format: z.literal("rows"),
|
|
4101
|
+
columns: z.array(TransformTestColumn).min(1),
|
|
4102
|
+
rows: z.array(TransformTestRow)
|
|
4103
|
+
}).loose();
|
|
4104
|
+
const TransformTestEqualsExpectation = z.discriminatedUnion("format", [TransformTestEqualsSqlExpectation, TransformTestEqualsRowsExpectation]);
|
|
4105
|
+
const TransformTestEmptyExpectation = z.object({
|
|
4106
|
+
type: z.literal("empty"),
|
|
4107
|
+
name: z.string().min(1),
|
|
4108
|
+
sql: z.string().min(1)
|
|
4109
|
+
}).loose();
|
|
4110
|
+
const TransformTestExpectation = z.discriminatedUnion("type", [TransformTestEqualsExpectation, TransformTestEmptyExpectation]);
|
|
4111
|
+
const TransformTest = z.object({
|
|
4112
|
+
id: z.number().int(),
|
|
4113
|
+
entity_id: z.string(),
|
|
4114
|
+
transform_id: z.number().int(),
|
|
4115
|
+
creator_id: z.number().int(),
|
|
4116
|
+
name: z.string(),
|
|
4117
|
+
description: z.string().nullable(),
|
|
4118
|
+
inputs: z.array(TransformTestInput),
|
|
4119
|
+
expectations: z.array(TransformTestExpectation),
|
|
4120
|
+
created_at: z.string(),
|
|
4121
|
+
updated_at: z.string()
|
|
4122
|
+
}).loose();
|
|
4123
|
+
TransformTest.pick({
|
|
4124
|
+
id: true,
|
|
4125
|
+
transform_id: true,
|
|
4126
|
+
name: true,
|
|
4127
|
+
description: true
|
|
4128
|
+
}).strip();
|
|
4129
|
+
z.object({
|
|
4130
|
+
transform_id: z.number().int().positive(),
|
|
4131
|
+
name: z.string().min(1),
|
|
4132
|
+
description: z.string().nullable().optional(),
|
|
4133
|
+
inputs: z.array(TransformTestInput),
|
|
4134
|
+
expectations: z.array(TransformTestExpectation)
|
|
4135
|
+
}).loose();
|
|
4136
|
+
z.object({
|
|
4137
|
+
transform_id: z.number().int().positive().optional(),
|
|
4138
|
+
name: z.string().min(1).optional(),
|
|
4139
|
+
description: z.string().nullable().optional(),
|
|
4140
|
+
inputs: z.array(TransformTestInput).optional(),
|
|
4141
|
+
expectations: z.array(TransformTestExpectation).optional()
|
|
4142
|
+
}).loose();
|
|
4143
|
+
const TransformTestResultColumn = z.object({
|
|
4144
|
+
name: z.string(),
|
|
4145
|
+
database_type: z.string()
|
|
4146
|
+
}).loose();
|
|
4147
|
+
const TransformTestExpectationError = z.object({
|
|
4148
|
+
type: z.string(),
|
|
4149
|
+
message: z.string()
|
|
4150
|
+
}).loose();
|
|
4151
|
+
const TransformTestCellMismatch = z.object({
|
|
4152
|
+
column: z.string(),
|
|
4153
|
+
expected: TransformTestCell,
|
|
4154
|
+
actual: TransformTestCell
|
|
4155
|
+
}).loose();
|
|
4156
|
+
const TransformTestRowCounts = z.object({
|
|
4157
|
+
actual: z.number().int(),
|
|
4158
|
+
expected: z.number().int()
|
|
4159
|
+
}).loose();
|
|
4160
|
+
const TransformTestEqualsFindings = z.object({
|
|
4161
|
+
name: z.string(),
|
|
4162
|
+
type: z.literal("equals"),
|
|
4163
|
+
status: z.enum(["passed", "failed"]),
|
|
4164
|
+
columns: z.array(TransformTestResultColumn),
|
|
4165
|
+
"row-counts": TransformTestRowCounts,
|
|
4166
|
+
"extra-rows": z.array(TransformTestRow),
|
|
4167
|
+
"missing-rows": z.array(TransformTestRow),
|
|
4168
|
+
"cell-mismatches": z.array(TransformTestCellMismatch),
|
|
4169
|
+
truncated: z.number().int()
|
|
4170
|
+
}).loose();
|
|
4171
|
+
const TransformTestEqualsError = z.object({
|
|
4172
|
+
name: z.string(),
|
|
4173
|
+
type: z.literal("equals"),
|
|
4174
|
+
status: z.literal("error"),
|
|
4175
|
+
error: TransformTestExpectationError
|
|
4176
|
+
}).loose();
|
|
4177
|
+
const TransformTestEqualsResult = z.discriminatedUnion("status", [TransformTestEqualsFindings, TransformTestEqualsError]);
|
|
4178
|
+
const TransformTestEmptyPassed = z.object({
|
|
4179
|
+
name: z.string(),
|
|
4180
|
+
type: z.literal("empty"),
|
|
4181
|
+
status: z.literal("passed")
|
|
4182
|
+
}).loose();
|
|
4183
|
+
const TransformTestEmptyFailed = z.object({
|
|
4184
|
+
name: z.string(),
|
|
4185
|
+
type: z.literal("empty"),
|
|
4186
|
+
status: z.literal("failed"),
|
|
4187
|
+
columns: z.array(TransformTestResultColumn),
|
|
4188
|
+
sample: z.array(TransformTestRow),
|
|
4189
|
+
truncated: z.number().int()
|
|
4190
|
+
}).loose();
|
|
4191
|
+
const TransformTestEmptyError = z.object({
|
|
4192
|
+
name: z.string(),
|
|
4193
|
+
type: z.literal("empty"),
|
|
4194
|
+
status: z.literal("error"),
|
|
4195
|
+
error: TransformTestExpectationError
|
|
4196
|
+
}).loose();
|
|
4197
|
+
const TransformTestEmptyResult = z.discriminatedUnion("status", [
|
|
4198
|
+
TransformTestEmptyPassed,
|
|
4199
|
+
TransformTestEmptyFailed,
|
|
4200
|
+
TransformTestEmptyError
|
|
4201
|
+
]);
|
|
4202
|
+
const TransformTestExpectationResult = z.discriminatedUnion("type", [TransformTestEqualsResult, TransformTestEmptyResult]);
|
|
4203
|
+
const TransformTestRunResult = z.object({
|
|
4204
|
+
status: z.enum(["passed", "failed"]),
|
|
4205
|
+
expectations: z.array(TransformTestExpectationResult),
|
|
4206
|
+
tables: z.record(z.string(), z.string())
|
|
4207
|
+
}).loose();
|
|
4208
|
+
z.enum([
|
|
4209
|
+
"transform-test.unknown-column",
|
|
4210
|
+
"transform-test.ambiguous-column",
|
|
4211
|
+
"transform-test.missing-inputs",
|
|
4212
|
+
"transform-test.unused-inputs",
|
|
4213
|
+
"transform-test.duplicate-input-table",
|
|
4214
|
+
"transform-test.unparseable-source",
|
|
4215
|
+
"transform-test.unremapped-reference",
|
|
4216
|
+
"transform-test.unsupported-transform",
|
|
4217
|
+
"transform-test.unsupported-driver",
|
|
4218
|
+
"transform-test.transform-failed",
|
|
4219
|
+
"transform-test.setup-failed",
|
|
4220
|
+
"transform-test.expectation-failed",
|
|
4221
|
+
"transform-test.unsupported-format"
|
|
4222
|
+
]);
|
|
4223
|
+
//#endregion
|
|
3664
4224
|
//#region ../client/src/resources/transform-test.ts
|
|
3665
4225
|
const TransformTestApiList = z.array(TransformTest);
|
|
3666
4226
|
function transformTestResource(transport) {
|
|
4227
|
+
const { explain } = explainer(transport, "transformTest");
|
|
3667
4228
|
/** List the transform tests, optionally only those of one transform. */
|
|
3668
4229
|
async function list(params = {}, options = {}) {
|
|
3669
|
-
await transport.require("transformTest.list", options);
|
|
3670
4230
|
return {
|
|
3671
4231
|
data: await transport.requestParsed(TransformTestApiList, "/api/ee/transform-test", {
|
|
3672
4232
|
...options,
|
|
@@ -3677,7 +4237,6 @@ function transformTestResource(transport) {
|
|
|
3677
4237
|
}
|
|
3678
4238
|
/** Get a transform test by id. */
|
|
3679
4239
|
async function get(id, options = {}) {
|
|
3680
|
-
await transport.require("transformTest.get", options);
|
|
3681
4240
|
return transport.requestParsed(TransformTest, `/api/ee/transform-test/${id}`, { ...options });
|
|
3682
4241
|
}
|
|
3683
4242
|
/**
|
|
@@ -3686,7 +4245,6 @@ function transformTestResource(transport) {
|
|
|
3686
4245
|
* `TransformTestRefusalCode` when they cannot run.
|
|
3687
4246
|
*/
|
|
3688
4247
|
async function create(params, options = {}) {
|
|
3689
|
-
await transport.require("transformTest.create", options);
|
|
3690
4248
|
return transport.requestParsed(TransformTest, "/api/ee/transform-test", {
|
|
3691
4249
|
...options,
|
|
3692
4250
|
method: "POST",
|
|
@@ -3698,7 +4256,6 @@ function transformTestResource(transport) {
|
|
|
3698
4256
|
* transform, inputs or expectations is validated as on create, with the same refusal.
|
|
3699
4257
|
*/
|
|
3700
4258
|
async function update(id, params, options = {}) {
|
|
3701
|
-
await transport.require("transformTest.update", options);
|
|
3702
4259
|
return transport.requestParsed(TransformTest, `/api/ee/transform-test/${id}`, {
|
|
3703
4260
|
...options,
|
|
3704
4261
|
method: "PUT",
|
|
@@ -3707,7 +4264,6 @@ function transformTestResource(transport) {
|
|
|
3707
4264
|
}
|
|
3708
4265
|
/** Delete a transform test by id. */
|
|
3709
4266
|
async function remove(id, options = {}) {
|
|
3710
|
-
await transport.require("transformTest.delete", options);
|
|
3711
4267
|
await transport.requestRaw(`/api/ee/transform-test/${id}`, {
|
|
3712
4268
|
...options,
|
|
3713
4269
|
method: "DELETE",
|
|
@@ -3721,19 +4277,18 @@ function transformTestResource(transport) {
|
|
|
3721
4277
|
* refused and nothing meaningful ran.
|
|
3722
4278
|
*/
|
|
3723
4279
|
async function run(id, options = {}) {
|
|
3724
|
-
await transport.require("transformTest.run", options);
|
|
3725
4280
|
return transport.requestParsed(TransformTestRunResult, `/api/ee/transform-test/${id}/run`, {
|
|
3726
4281
|
...options,
|
|
3727
4282
|
method: "POST"
|
|
3728
4283
|
});
|
|
3729
4284
|
}
|
|
3730
4285
|
return {
|
|
3731
|
-
list,
|
|
3732
|
-
get,
|
|
3733
|
-
create,
|
|
3734
|
-
update,
|
|
3735
|
-
delete: remove,
|
|
3736
|
-
run
|
|
4286
|
+
list: explain("list", list),
|
|
4287
|
+
get: explain("get", get),
|
|
4288
|
+
create: explain("create", create),
|
|
4289
|
+
update: explain("update", update),
|
|
4290
|
+
delete: explain("delete", remove),
|
|
4291
|
+
run: explain("run", run)
|
|
3737
4292
|
};
|
|
3738
4293
|
}
|
|
3739
4294
|
//#endregion
|
|
@@ -3747,7 +4302,6 @@ function uploadResource(transport) {
|
|
|
3747
4302
|
* The file may be at most 50 MB; larger uploads are rejected with a 413 response.
|
|
3748
4303
|
*/
|
|
3749
4304
|
async function createFromCsv(file, params, options = {}) {
|
|
3750
|
-
await transport.require("upload.createFromCsv", options);
|
|
3751
4305
|
const form = buildCsvFormData(file);
|
|
3752
4306
|
form.append("collection_id", params.collection_id);
|
|
3753
4307
|
const response = await transport.requestRaw("/api/upload/csv", {
|
|
@@ -3800,7 +4354,6 @@ CurrentUser.pick({
|
|
|
3800
4354
|
function userResource(transport) {
|
|
3801
4355
|
/** Get the user the request's credentials authenticate as. */
|
|
3802
4356
|
async function current(options = {}) {
|
|
3803
|
-
await transport.require("user.current", options);
|
|
3804
4357
|
return transport.requestParsed(CurrentUser, "/api/user/current", { ...options });
|
|
3805
4358
|
}
|
|
3806
4359
|
return { current };
|
|
@@ -3815,7 +4368,6 @@ function createClient(config, options) {
|
|
|
3815
4368
|
collection: collectionResource(transport),
|
|
3816
4369
|
contentTranslation: contentTranslationResource(transport),
|
|
3817
4370
|
dashboard: dashboardResource(transport),
|
|
3818
|
-
dataAction: dataActionResource(transport),
|
|
3819
4371
|
database: databaseResource(transport),
|
|
3820
4372
|
dataset: datasetResource(transport),
|
|
3821
4373
|
dependency: dependencyResource(transport),
|
|
@@ -3852,6 +4404,8 @@ function createClient(config, options) {
|
|
|
3852
4404
|
upload: uploadResource(transport),
|
|
3853
4405
|
user: userResource(transport),
|
|
3854
4406
|
server: transport.server,
|
|
4407
|
+
verifiedServer: transport.verifiedServer,
|
|
4408
|
+
probesSettled: transport.probesSettled,
|
|
3855
4409
|
requestParsed: transport.requestParsed,
|
|
3856
4410
|
requestRaw: transport.requestRaw,
|
|
3857
4411
|
requestStream: transport.requestStream
|