@metabase/cli 0.3.2-alpha.fix-transform-test-ee-path.8ac2a7a → 0.4.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-B_-eMU_k.mjs → add-collection-CC6KSwjX.mjs} +5 -5
- package/dist/{alert-Dalin_DD.mjs → alert-Dt6La9tK.mjs} +7 -7
- package/dist/{alerts-DpBgrjPI.mjs → alerts-ClKnJqgB.mjs} +6 -6
- package/dist/{append-C1JNVALm.mjs → append-pnaQWP_A.mjs} +4 -4
- package/dist/{archive-B51prFgS.mjs → archive-By1pLYtO.mjs} +3 -3
- package/dist/{archive-CwITzj_j.mjs → archive-CLu2TGW5.mjs} +5 -5
- package/dist/{archive-52ZHbFDm2.mjs → archive-COP9joWU.mjs} +4 -4
- package/dist/{archive-cAdUa1-2.mjs → archive-CSnBFcQ_.mjs} +3 -3
- package/dist/{archive-DA3OiLEE.mjs → archive-Ct1eyolN.mjs} +3 -3
- package/dist/{archive-CdKf_Eqq.mjs → archive-D7nAQcCz.mjs} +3 -3
- package/dist/{archive-CISXgZAI.mjs → archive-DEDA48w3.mjs} +5 -5
- package/dist/{archive-DVHxeDKS.mjs → archive-DJAsSzCz.mjs} +5 -5
- package/dist/{archive-BkITQyWW.mjs → archive-DtabbPUk.mjs} +4 -4
- package/dist/{archive-DckxlWBt.mjs → archive-YNpa8daf.mjs} +3 -3
- package/dist/{archive-CicPYX27.mjs → archive-ewitrKdx.mjs} +5 -5
- package/dist/{auth-A96JOCQx.mjs → auth-DdLhXCn-.mjs} +5 -5
- package/dist/body-Dd-TwYYL.mjs +17 -0
- package/dist/branch-flag-pFQPNYKr.mjs +9 -0
- package/dist/{branches-CbcwWYL9.mjs → branches-ZvGNhed6.mjs} +3 -3
- package/dist/breaking-BLswgvkA.mjs +36 -0
- package/dist/broken-BN0nL2hf.mjs +37 -0
- package/dist/bulk-discard-values-DFJUjSnE.mjs +49 -0
- package/dist/bulk-edit-Brqr67bu.mjs +53 -0
- package/dist/bulk-rescan-values-DndXXi2N.mjs +28 -0
- package/dist/bulk-sync-schema-mO-t2QlL.mjs +28 -0
- package/dist/{cancel-DpDcVY7M.mjs → cancel-DP03dXBf.mjs} +3 -3
- package/dist/{cancel-task-B4RDvIYb.mjs → cancel-task-CELBfBPP.mjs} +4 -4
- package/dist/{card-voh1k4hl.mjs → card-Bv4ZN0oV.mjs} +5 -3
- package/dist/card-CFd-GUEM.mjs +25 -0
- package/dist/{card-CEkp1mcI.mjs → card-DSrgU7lT.mjs} +1 -1
- package/dist/{cards-BC0JX8Yg.mjs → cards-Cn2ywFTJ.mjs} +6 -6
- package/dist/cli.mjs +410 -111
- package/dist/{client-ClL6gf7G.mjs → client-ToLiPu9q.mjs} +785 -316
- package/dist/{collection-DYM0p3Xl.mjs → collection-BG3Ksea-.mjs} +1 -1
- package/dist/collection-BKNShKcY.mjs +21 -0
- package/dist/{collection-BxdPxD38.mjs → collection-D3KXhvnc.mjs} +7 -5
- package/dist/{collection-namespace-Dv0l-V9K.mjs → collection-namespace-CWW6hZxK.mjs} +2 -2
- package/dist/{content-translation-BXIXL4eW.mjs → content-translation-C19SqZWp.mjs} +3 -3
- package/dist/copy-BGK8Wz6d.mjs +55 -0
- package/dist/copy-COLUuiuF.mjs +36 -0
- package/dist/copy-flags-eU8aa0zj.mjs +32 -0
- package/dist/{create-BbjSOTN-.mjs → create-B2tuInV9.mjs} +4 -4
- package/dist/{create-Za1EskpV.mjs → create-B3TKri1E.mjs} +4 -4
- package/dist/{create--fiwuTWc.mjs → create-B4gXKDRr.mjs} +3 -3
- package/dist/create-B63JphYe.mjs +34 -0
- package/dist/{create-NCKHHdov.mjs → create-BH6kLjHS.mjs} +4 -4
- package/dist/{create-Dhai9zAt.mjs → create-BwifyhyL.mjs} +3 -3
- package/dist/{create-DWFJdyIr.mjs → create-Colz5bOO.mjs} +3 -3
- package/dist/{create-BcsVwXgS2.mjs → create-CulUZgN1.mjs} +4 -4
- package/dist/{create-D--Z1S3p2.mjs → create-Cxuqr3kT.mjs} +6 -6
- package/dist/{create-Bryrkb9g.mjs → create-CzW_UTE7.mjs} +6 -6
- package/dist/{create-kZIIEN2Y.mjs → create-DNR8RMvV.mjs} +6 -6
- package/dist/{create-DmN2gFc4.mjs → create-DSHeoV7e.mjs} +4 -4
- package/dist/{create-BVNhtAgF.mjs → create-DWbW2WSc.mjs} +6 -6
- package/dist/{create-CHr8MPPq.mjs → create-Dx8nfjGs.mjs} +3 -3
- package/dist/{create-B9qXQ_wV.mjs → create-XGomrTz5.mjs} +5 -5
- package/dist/{create-branch-CseVn3lp.mjs → create-branch-DxL7KUZp.mjs} +4 -4
- package/dist/{create-YetX69Re.mjs → create-g1f2yJ4s.mjs} +3 -3
- package/dist/{csv-DNzesu8y.mjs → csv-C6TEfLMi.mjs} +4 -4
- package/dist/{current-task-DRTKZcP9.mjs → current-task-CHs0zNFM.mjs} +4 -4
- package/dist/dashboard-AWrXqQ7a.mjs +35 -0
- package/dist/{dashboard-D0hwTC-f.mjs → dashboard-Phz3XpRM.mjs} +2 -1
- package/dist/{dashboard-DgK81azj.mjs → dashboard-XSTr9QCr.mjs} +6 -2
- package/dist/database-D-CRyRIv.mjs +45 -0
- package/dist/{database-C3E9PZSx.mjs → database-MlUPpitM.mjs} +1 -1
- package/dist/dataset-p5oNPQgL.mjs +66 -0
- package/dist/{db-BDijt81U.mjs → db-BFSeETMO.mjs} +7 -7
- package/dist/{delete-Dc8E5YwZ.mjs → delete-BUKgSJhh.mjs} +4 -4
- package/dist/{delete-BxjJ66BS.mjs → delete-CPBByycV.mjs} +4 -4
- package/dist/{delete-wFsy80db.mjs → delete-Cucqn4-V.mjs} +4 -4
- package/dist/{delete--15it-A8.mjs → delete-D79TlpcE.mjs} +4 -4
- package/dist/delete-DZJxBmdU.mjs +44 -0
- package/dist/{delete-BudvZkvK.mjs → delete-DndykJn2.mjs} +4 -4
- package/dist/delete-runtime-BbmRg_in.mjs +55 -0
- package/dist/{delete-table-5AQpn8OB.mjs → delete-table-CbsWcjh5.mjs} +4 -4
- package/dist/{dependencies-C1ogC3BV.mjs → dependencies-B-880phi.mjs} +5 -5
- package/dist/dependency-DBUJwXqv.mjs +15 -0
- package/dist/dependency-DKSjKT8P.mjs +165 -0
- package/dist/dependency-DSxKm9oS.mjs +111 -0
- package/dist/dependents-C_FDnpZs.mjs +52 -0
- package/dist/{dirty-CSLDYO-K.mjs → dirty-B-Is68--.mjs} +5 -5
- package/dist/discard-values-yzH-kIsr.mjs +55 -0
- package/dist/document-BRaba87v.mjs +20 -0
- package/dist/{document-CA0Q6lqu.mjs → document-C18b5rM6.mjs} +1 -1
- package/dist/{document-BHpLJbKU.mjs → document-DFCj1Az_.mjs} +2 -1
- package/dist/{download-Ddap1OAo.mjs → download-DluF-vnU.mjs} +2 -2
- package/dist/{eid-HnrWhPoe.mjs → eid-Dz-mP40j.mjs} +11 -10
- package/dist/entity-CA4zfRt4.mjs +20 -0
- package/dist/entry-flags-7x7bIyOQ.mjs +42 -0
- package/dist/{events-CXqetjcS.mjs → events-2sOk6H09.mjs} +4 -4
- package/dist/export-BKkAJOFb.mjs +74 -0
- package/dist/export-flags-NiZ7C3Gm.mjs +81 -0
- package/dist/export-preflight-CwJ_3-iX.mjs +35 -0
- package/dist/{field-CcN4oZy6.mjs → field-5nivEfd6.mjs} +33 -3
- package/dist/field-BARgq5I9.mjs +21 -0
- package/dist/field-wmN6raOG.mjs +128 -0
- package/dist/{fields-DxHxuxwO.mjs → fields-Bod0s_uV.mjs} +6 -6
- package/dist/filter-flags-BYcbB85j.mjs +71 -0
- package/dist/fks-IUw66K_Q.mjs +36 -0
- package/dist/{get-eJilYnHk.mjs → get-B8DUMrCw.mjs} +5 -5
- package/dist/{get-C3cuZNeW.mjs → get-BGqvoGQM.mjs} +3 -3
- package/dist/{get-DmKtPNOy.mjs → get-BSUsfysk.mjs} +3 -3
- package/dist/{get-DW7mCaGe.mjs → get-BXsLkoit.mjs} +5 -5
- package/dist/{get-Dtre5jzh.mjs → get-BXssHyJ1.mjs} +5 -5
- package/dist/get-Bct52yPw.mjs +36 -0
- package/dist/{get-BiPclcIP.mjs → get-Besqwy32.mjs} +3 -3
- package/dist/{get-BcoJ9Aha.mjs → get-BfSX-OeS.mjs} +3 -3
- package/dist/{get-BF2vdF3W.mjs → get-Biy4gOzu2.mjs} +3 -3
- package/dist/{get-C_tdSMwW2.mjs → get-CYIeiTZ9.mjs} +4 -4
- package/dist/{get-b0CoPz5B.mjs → get-CaEiQnSw.mjs} +4 -4
- package/dist/{get-CxUQyhte.mjs → get-Cl81y0Md.mjs} +5 -5
- package/dist/{get-B2pj2q3X.mjs → get-D1NjMXAH.mjs} +4 -4
- package/dist/{get-D_x-ENFe.mjs → get-D2cPuOPR.mjs} +5 -5
- package/dist/{get-DjlQhgD4.mjs → get-DITlshJH.mjs} +3 -3
- package/dist/{get-CE9_4Q7E.mjs → get-DLyg6Al9.mjs} +3 -3
- package/dist/{get-CDnmpfHQ.mjs → get-DQZfaVNq.mjs} +4 -4
- package/dist/{get-BGLvyOO3.mjs → get-DtBqoGTa.mjs} +5 -5
- package/dist/{get-H6kInHIx.mjs → get-_ik_5_lQ.mjs} +5 -5
- package/dist/{get-run-BE1HtJDW.mjs → get-run-CEc_tr5b.mjs} +4 -4
- package/dist/{git-sync-BwBRmEhE.mjs → git-sync-COokpYgc.mjs} +12 -3
- package/dist/git-sync-DKcaddkV.mjs +29 -0
- package/dist/git-sync-DspFahwV.mjs +165 -0
- package/dist/glossary-BlNCrYbw.mjs +30 -0
- package/dist/glossary-CWIX8Zh6.mjs +14 -0
- package/dist/glossary-LmZWwCNP.mjs +21 -0
- package/dist/graph-f6Bp1e8U.mjs +34 -0
- package/dist/{group-YQQ5GIcP.mjs → group-bpD_KcjA.mjs} +1 -1
- package/dist/{has-remote-changes-Cj1KhBOT.mjs → has-remote-changes-B8MmN6yh.mjs} +4 -4
- package/dist/{import-DLb9VN4l.mjs → import-Bhq15KgP.mjs} +19 -8
- package/dist/{input-D5tQEfqN.mjs → input-D8qW-KtR.mjs} +6 -2
- package/dist/{is-dirty-BxtgV2xF.mjs → is-dirty-CAsVuVx8.mjs} +2 -2
- package/dist/{items-DLUYc5-z.mjs → items-BZJomiDE.mjs} +8 -8
- package/dist/{key-PYetuQ13.mjs → key-BynJSK9u.mjs} +1 -1
- package/dist/{library-BNSxXOpc.mjs → library-B7wDH-Nc.mjs} +5 -5
- package/dist/{library-PGCfm0UA.mjs → library-BOv5SY24.mjs} +1 -1
- package/dist/{library-BPIzb3K_.mjs → library-Ck0EK5bi.mjs} +4 -2
- package/dist/{list-BUz6KOfw.mjs → list-B2uUl26x.mjs} +6 -6
- package/dist/{list-CERbeLbQ.mjs → list-B4luDSYj.mjs} +6 -6
- package/dist/{list-jhQSgh6i.mjs → list-B8ND9t2h.mjs} +3 -3
- package/dist/{list-M8J10Ozk.mjs → list-BT5t8ZOl.mjs} +4 -4
- package/dist/{list-Dat8QNcf.mjs → list-BT7xiUQe.mjs} +3 -3
- package/dist/{list-cIMww2Me.mjs → list-BWFCv_zk.mjs} +5 -5
- package/dist/{list-B1soS8QW.mjs → list-BtZVe1Nq.mjs} +6 -6
- package/dist/{list-C5mS_ehX.mjs → list-BwdSG-P5.mjs} +5 -5
- package/dist/{list-BQnOEB3W.mjs → list-C-A-2HFq.mjs} +3 -3
- package/dist/list-C9x--9Jq.mjs +101 -0
- package/dist/{list-CX9qZ-bx.mjs → list-CCdXig7b.mjs} +4 -4
- package/dist/{list-Sd7jGZoX.mjs → list-CJDFoOHb.mjs} +3 -3
- package/dist/{list-CzRzCbCY.mjs → list-CLFu3mQB.mjs} +3 -3
- package/dist/{list-CBLxhaNZ.mjs → list-Cd40Kr0R.mjs} +3 -3
- package/dist/{list-COLpwXW4.mjs → list-Cj_mTxbY.mjs} +4 -4
- package/dist/{list-CQbiSx26.mjs → list-CtS50R7m.mjs} +5 -5
- package/dist/{list-CehE6KBV.mjs → list-CvZyTwmV.mjs} +5 -5
- package/dist/list-DcnNlYQA.mjs +35 -0
- package/dist/{list-BcP1DTJH.mjs → list-DtgbtE3_.mjs} +3 -3
- package/dist/{login-BfLqBfCd.mjs → login-Bi_nGMJ9.mjs} +6 -6
- package/dist/{logout-BpRXB3Ze.mjs → logout-CiHQJWTu.mjs} +2 -2
- package/dist/{measure-Ds8O5Evr.mjs → measure-D2qq5e8f.mjs} +6 -6
- package/dist/moderation-review-BdqEH6N1.mjs +31 -0
- package/dist/{notification-DVrLkjFT.mjs → notification-DOYdl0ao.mjs} +1 -1
- package/dist/{parameter-values-DhrkLXoN.mjs → parameter-values-CZow98nb.mjs} +3 -3
- package/dist/{parse-enum-Dc0dMZ3P.mjs → parse-enum-CLIXMsd8.mjs} +1 -1
- package/dist/parse-id-BW09NysN.mjs +13 -0
- package/dist/{parse-ref-DTDld_LJ.mjs → parse-ref-Cw5fa4FI.mjs} +1 -1
- package/dist/parse-text-T0lTXzTZ.mjs +11 -0
- package/dist/{path-_GHFrTLx.mjs → path-K6LPLYPb.mjs} +4 -4
- package/dist/{poll-KLl4Eq8Y.mjs → poll-Dz4tUm2f.mjs} +2 -2
- package/dist/{preflight-BpYtF0IU.mjs → preflight-CX6cqB90.mjs} +3 -3
- package/dist/{process-CLtOWKBn.mjs → process-D0HdqZ3r.mjs} +1 -1
- package/dist/{publish-D-7Zlmuk.mjs → publish-COatW4Un.mjs} +4 -4
- package/dist/{pulse-x0gFxWfG.mjs → pulse-D1uh8UIU.mjs} +1 -1
- package/dist/query-BJXTIDhy.mjs +328 -0
- package/dist/{query-B1CZztxs.mjs → query-DmvkyDfo.mjs} +14 -33
- package/dist/remapping-CPAvDKHV.mjs +53 -0
- package/dist/{remove-collection-C4saCFxz.mjs → remove-collection-Bg-sNX3U.mjs} +5 -5
- package/dist/{render-6O_eF-P1.mjs → render-BSfDD6ip.mjs} +2 -2
- package/dist/{replace-BuR4f2Ai.mjs → replace-RCHeZLUT.mjs} +4 -4
- package/dist/{rescan-values-CNOiiWQ5.mjs → rescan-values-CpDGYZvf.mjs} +5 -5
- package/dist/rescan-values-PeMnpaFY.mjs +32 -0
- package/dist/{run-DijA_BXt.mjs → run-CaQ7kubO.mjs} +3 -3
- package/dist/{run-QEUbWNm7.mjs → run-DkRkZRgf.mjs} +7 -7
- package/dist/{runs-BSlhVeDO.mjs → runs-BDjoVJ0P.mjs} +5 -5
- package/dist/{runtime-Dtb93zKy.mjs → runtime-CZ5zh32z.mjs} +21 -82
- package/dist/{schema-tables-BE3rEb6I.mjs → schema-tables-C2qjLcPq.mjs} +6 -6
- package/dist/{schemas-yDjBiwlH.mjs → schemas-D8hfNduk.mjs} +4 -4
- package/dist/search-BrQQuNmh.mjs +131 -0
- package/dist/search-CnTXKJgx.mjs +85 -0
- package/dist/{segment-Cm6zQjJW.mjs → segment-Bi6eqj3e.mjs} +6 -6
- package/dist/{send-kC8oerkT.mjs → send-CAjgVNlA.mjs} +3 -3
- package/dist/{set-tFhPkxrL.mjs → set-BXTkYWid.mjs} +4 -4
- package/dist/{set-active-lDKhAlij.mjs → set-active-CBQVCePU.mjs} +3 -3
- package/dist/set-sensitivity-D39kGTob.mjs +57 -0
- package/dist/{setting-DrJXCaFE.mjs → setting-CPkvkc4H.mjs} +4 -4
- package/dist/{setup-BWY9M0iR.mjs → setup-Be3xDXHi.mjs} +3 -3
- package/dist/{signal-Csom0mt5.mjs → signal-CxWeLszu.mjs} +1 -1
- package/dist/{skill-list-CCes8pKe.mjs → skill-list-CBGq30sI.mjs} +2 -2
- package/dist/{skills-DE9ej5Rg.mjs → skills-CJ9eXouJ.mjs} +2 -2
- package/dist/{skills-DPRN2384.mjs → skills-DyiLycMY.mjs} +3 -3
- package/dist/snippet-QirK70R_.mjs +19 -0
- package/dist/{stash-C9oDvQ9P.mjs → stash-BVN3s5N_.mjs} +6 -6
- package/dist/{status-CkpujA26.mjs → status-BtbMmIWo.mjs} +7 -7
- package/dist/{status-DSdXmNC3.mjs → status-CxP6w3aL.mjs} +3 -3
- package/dist/{subscription-DKOKoHRG.mjs → subscription-Dr-YHARg.mjs} +6 -6
- package/dist/{subscriptions-C5SyJL4B.mjs → subscriptions-8tM8TTeQ.mjs} +6 -6
- package/dist/{summary-DTPgXsc0.mjs → summary-DKx6ptQe.mjs} +5 -5
- package/dist/sync-schema-B8RY0Ra4.mjs +32 -0
- package/dist/{sync-schema-DKKkNSR7.mjs → sync-schema-kNriXZjG.mjs} +6 -6
- package/dist/sync-task-CXFoqlpF.mjs +53 -0
- package/dist/table-CZnT6d9x.mjs +26 -0
- package/dist/{table-DfuY0N8e.mjs → table-Ci6bhZkb.mjs} +35 -12
- package/dist/table-CxvaDiIZ.mjs +155 -0
- package/dist/table-selector-flags-1bYkAKld.mjs +42 -0
- package/dist/text-DaNkPRwt.mjs +6 -0
- package/dist/timeline-CUzx6uXi.mjs +21 -0
- package/dist/{timeline-event-n4Lzet7l.mjs → timeline-event-DpNm4dkn.mjs} +6 -6
- package/dist/{transform-CtqB1WYo.mjs → transform-CNBIV7No.mjs} +1 -1
- package/dist/transform-g4-HHzDp.mjs +28 -0
- package/dist/transform-job-DqBtnTrG.mjs +22 -0
- package/dist/{transform-tag-D6NXqWaX.mjs → transform-tag-nTb0Ivpj.mjs} +5 -5
- package/dist/{transforms-O4NtXqFz.mjs → transforms-BSsAfwAg.mjs} +5 -5
- package/dist/tree-lMvw1jnZ.mjs +34 -0
- package/dist/{unpublish-DvWefKJi.mjs → unpublish-CYFDd828.mjs} +3 -3
- package/dist/unreferenced-8Xdq76Kw.mjs +36 -0
- package/dist/{update-AJ2JmwDl.mjs → update-6P_GYCLT.mjs} +5 -5
- package/dist/{update-76nzOc71.mjs → update-B28EJzrx.mjs} +13 -8
- package/dist/{update-To6A1Ugu.mjs → update-BDunZa7G.mjs} +4 -4
- package/dist/{update-2KIpYgyj.mjs → update-BT2yNB_g.mjs} +6 -6
- package/dist/{update-DmgRIp7o.mjs → update-BnMUSFYZ.mjs} +4 -4
- package/dist/update-BwrYeKso.mjs +41 -0
- package/dist/{update-DOdcflLH.mjs → update-C7DEMiu-.mjs} +7 -7
- package/dist/{update-CGJvIS-l.mjs → update-CITzAFG8.mjs} +4 -4
- package/dist/update-CLl8tE20.mjs +181 -0
- package/dist/{update-BB6Go_RX.mjs → update-CM5uuhjB.mjs} +4 -4
- package/dist/{update-xEegxIyH.mjs → update-CQXcsvaa.mjs} +5 -5
- package/dist/{update-DV54H3L5.mjs → update-Cs2zv_L2.mjs} +7 -7
- package/dist/{update-Bah7N8QX.mjs → update-D--Fw7-_.mjs} +5 -5
- package/dist/{update-H_fdjugs.mjs → update-D9p0N8ky.mjs} +4 -4
- package/dist/{update-7oKvP1HC.mjs → update-DAzRx0of.mjs} +5 -5
- package/dist/{update-DXmZuJho.mjs → update-D_DCOOAB.mjs} +6 -6
- package/dist/{update-D7dG7H-z.mjs → update-_sYM1GuU.mjs} +7 -7
- package/dist/{update-dashcard-DjT5QOkL.mjs → update-dashcard-VNnIqHDI.mjs} +6 -6
- package/dist/{upgrade-CuJx9fX4.mjs → upgrade-Tj_reACv.mjs} +4 -4
- package/dist/upload-BKxOpSz3.mjs +13 -0
- package/dist/{upload-DOO94rCQ.mjs → upload-C2fhpKkP.mjs} +2 -2
- package/dist/{upload-CboARPZm.mjs → upload-Dg91ldGg.mjs} +3 -3
- package/dist/{uuid-InwZlrpQ.mjs → uuid-Blpv_e0S.mjs} +2 -2
- package/dist/{validate-D6-Vg_kn.mjs → validate-D7e110oU.mjs} +1 -1
- package/dist/{validate-query-B90KYPQr.mjs → validate-query-C7LuKiUT.mjs} +2 -2
- package/dist/{values-CsZLk1Ls.mjs → values-PonkbYAK.mjs} +5 -5
- package/dist/verify-DixUBbd2.mjs +38 -0
- package/dist/verify-Dv7ER11c.mjs +38 -0
- package/dist/{verify-DrGjZwBM.mjs → verify-Mpb65a5v.mjs} +3 -3
- package/dist/verify-flags-CMpRkMPa.mjs +58 -0
- package/dist/{wait-BmzdarP9.mjs → wait-CaH6XHQV.mjs} +6 -6
- package/dist/{wait-flags-CiJGrTCT.mjs → wait-flags-rUU4NUrv.mjs} +2 -2
- package/dist/{window-DSb1tqSe.mjs → window-CI_QD04s.mjs} +1 -1
- package/package.json +1 -1
- package/skill-data/core/SKILL.md +24 -23
- package/skill-data/dashboard/SKILL.md +1 -1
- package/skill-data/document/SKILL.md +12 -1
- package/skill-data/git-sync/SKILL.md +61 -9
- package/skill-data/mbql/SKILL.md +5 -0
- package/skill-data/metadata/SKILL.md +36 -6
- package/skill-data/transform/SKILL.md +1 -63
- package/dist/body-CshgcV6U.mjs +0 -17
- package/dist/card-CefVYtF7.mjs +0 -24
- package/dist/collection-Be5AHVWP.mjs +0 -20
- package/dist/create-DiJ8bUb-.mjs +0 -34
- package/dist/dashboard-CMw13KXM.mjs +0 -33
- package/dist/database-JtHTqSpq.mjs +0 -26
- package/dist/delete-Bsdwqh7H.mjs +0 -44
- package/dist/delete-runtime-BKEzJn22.mjs +0 -49
- package/dist/document-BGzVip5_.mjs +0 -19
- package/dist/export-IFZdgYZ2.mjs +0 -63
- package/dist/field-BJ6HoLRd.mjs +0 -18
- package/dist/field-fDtIrY6D.mjs +0 -80
- package/dist/get-i0KInCw9.mjs +0 -32
- package/dist/get-jofVjU6e.mjs +0 -31
- package/dist/git-sync-TjX1r_wF.mjs +0 -136
- package/dist/git-sync-xKC1sqC4.mjs +0 -28
- package/dist/list-Cuhf2lKL.mjs +0 -35
- package/dist/list-D-SyhKFw.mjs +0 -34
- package/dist/parse-id-DtM6q9HU.mjs +0 -10
- package/dist/query-CxESKMve.mjs +0 -114
- package/dist/query-result-E4Q-Gra6.mjs +0 -15
- package/dist/run-FmRruANb.mjs +0 -64
- package/dist/search-CR_xNArA.mjs +0 -97
- package/dist/selectors-C-vgMcGK.mjs +0 -35
- package/dist/snippet-DWHTSo9c.mjs +0 -19
- package/dist/sync-task-BtDnTXCP.mjs +0 -38
- package/dist/table-C6h4tIRw.mjs +0 -37
- package/dist/table-I8NXSdUC.mjs +0 -18
- package/dist/timeline-B_ESH-6W.mjs +0 -21
- package/dist/transform-BNuNiH9d.mjs +0 -28
- package/dist/transform-job-B7yc6dfH.mjs +0 -22
- package/dist/transform-test-BjjCQ2xm.mjs +0 -169
- package/dist/transform-test-C1Oqtfb-.mjs +0 -16
- package/dist/transform-test-gzDcbypy.mjs +0 -25
- package/dist/transport-CyVo29u7.mjs +0 -313
- package/dist/tree-CuyF116R.mjs +0 -26
- package/dist/update-l3PyNQKG.mjs +0 -41
- package/dist/upload-2YFFBSln.mjs +0 -13
- package/skill-data/transform-test-plan/SKILL.md +0 -171
- package/skill-data/transform-test-plan/references/checklist.md +0 -81
- package/skill-data/transform-test-plan/references/checks.md +0 -263
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { at as ConfigError, ct as NetworkError, ft as ValidationError, lt as ResponseShapeError, mt as formatZodIssue, n as methodRequirements, ot as InternalError, pt as errorMessage, ut as TimeoutError } from "./cli.mjs";
|
|
2
2
|
import { n as Notification, r as NotificationCardPayload, t as CARD_PAYLOAD_TYPE } from "./notification-DaA4bBHW.mjs";
|
|
3
|
-
import { H as
|
|
4
|
-
import { n as
|
|
5
|
-
import {
|
|
3
|
+
import { $ as CapabilityError, B as assertCredentialHeaderSafe, G as normalizeUrl, H as credentialSecrets, I as isRetryableStatus, J as checkFeatures, L as JSON_CONTENT_TYPE, M as buildNetworkError, N as isConnectionClosed, P as HttpError, Q as probeServer, R as parseJson, V as credentialAuthHeader, X as createServerProfile, Z as PROBE_PATH, z as parseJsonOrPlain } from "./runtime-CZ5zh32z.mjs";
|
|
4
|
+
import { n as throwIfAborted, r as untilAborted, t as combineAborts } from "./signal-CxWeLszu.mjs";
|
|
5
|
+
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
6
|
import { a as TemporalUnit, r as ParameterValues } from "./parameter-Celg0wZa.mjs";
|
|
7
|
-
import { a as CardQueryResult,
|
|
8
|
-
import {
|
|
9
|
-
import { t as Database } from "./database-
|
|
7
|
+
import { a as CardQueryResult, d as PivotExportFormat, l as DatasetQuery, t as Card } from "./card-Bv4ZN0oV.mjs";
|
|
8
|
+
import { b as TableVisibilityType, p as TableQueryMetadata, s as TableDataLayerTier, t as Table, u as TableForeignKey, v as TableSelectors } from "./table-Ci6bhZkb.mjs";
|
|
9
|
+
import { t as Database } from "./database-D-CRyRIv.mjs";
|
|
10
10
|
import { t as Snippet } from "./snippet-CyKAgIZl.mjs";
|
|
11
|
-
import {
|
|
11
|
+
import { o as compiledQuerySchema, r as QueryMetadata } from "./dataset-p5oNPQgL.mjs";
|
|
12
|
+
import { c as CollectionItem, m as CollectionTreeNode, r as Collection } from "./collection-D3KXhvnc.mjs";
|
|
12
13
|
import { t as ContentTranslationUploadResult } from "./content-translation-BrHOrmj9.mjs";
|
|
13
|
-
import {
|
|
14
|
-
import { r as pollUntil } from "./poll-
|
|
15
|
-
import { t as
|
|
14
|
+
import { l as Dashcard, o as DashboardDetail, r as DashboardCopy, t as Dashboard } from "./dashboard-Phz3XpRM.mjs";
|
|
15
|
+
import { r as pollUntil } from "./poll-Dz4tUm2f.mjs";
|
|
16
|
+
import { i as DependencyEntity, s as DependencyGraph, t as BreakingSource, u as DependencyNode } from "./dependency-DKSjKT8P.mjs";
|
|
17
|
+
import { t as Document } from "./document-DFCj1Az_.mjs";
|
|
16
18
|
import { i as EidTranslateResult } from "./eid-translation-zEHtr5Oc.mjs";
|
|
17
|
-
import { c as
|
|
18
|
-
import {
|
|
19
|
+
import { c as SyncRemoteChanges, d as SyncTask, i as SyncExportPreflight, l as SyncSettingsUpdateResult, m as isSyncTaskTerminal, n as SyncDirtyItem, t as SyncBranchCreated } from "./git-sync-COokpYgc.mjs";
|
|
20
|
+
import { t as Glossary } from "./glossary-BlNCrYbw.mjs";
|
|
21
|
+
import { a as libraryWireSchema, i as LibraryPublishTablesInput, n as LibraryCollectionInfo, o as toLibrary } from "./library-Ck0EK5bi.mjs";
|
|
19
22
|
import { t as Measure } from "./measure-CtfjqJFP.mjs";
|
|
23
|
+
import { r as ModerationReviewCreateInput, t as ModerationReview } from "./moderation-review-BdqEH6N1.mjs";
|
|
20
24
|
import { t as Pulse } from "./pulse-Dz0ocv0l.mjs";
|
|
21
25
|
import { a as TransformDagTransform, d as TransformTarget, g as transformRowSchema, h as transformDetailSchema, i as TransformDagRunResult, l as TransformRunSummary, m as isTransformRunTerminal, o as TransformRun, p as isTransformRunFailed, u as TransformSourceTableEntry } from "./transform-DiW7bREn.mjs";
|
|
22
26
|
import { r as SearchResult } from "./search-BAXg-EJw.mjs";
|
|
@@ -26,9 +30,9 @@ import { n as SetupResult } from "./setup-CO7QV-ao.mjs";
|
|
|
26
30
|
import { i as TimelineEvent, t as Timeline } from "./timeline-CCSU2qOS.mjs";
|
|
27
31
|
import { c as transformJobSchema, n as TransformJobActiveResult, s as transformJobRunResultSchema } from "./transform-job-CCguL6LP.mjs";
|
|
28
32
|
import { t as TransformTag } from "./transform-tag-CcElGp3C.mjs";
|
|
29
|
-
import { i as TransformTestRunResult, t as TransformTest } from "./transform-test-BjjCQ2xm.mjs";
|
|
30
33
|
import { t as UploadResult } from "./upload-DAtxBBms.mjs";
|
|
31
34
|
import { z } from "zod";
|
|
35
|
+
import { setTimeout } from "node:timers/promises";
|
|
32
36
|
//#region \0rolldown/runtime.js
|
|
33
37
|
var __defProp = Object.defineProperty;
|
|
34
38
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -41,43 +45,324 @@ var __exportAll = (all, no_symbols) => {
|
|
|
41
45
|
return target;
|
|
42
46
|
};
|
|
43
47
|
//#endregion
|
|
44
|
-
//#region ../client/src/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
//#region ../client/src/http/response-shape.ts
|
|
49
|
+
async function parseJsonResponse(text, schema, context) {
|
|
50
|
+
try {
|
|
51
|
+
return parseJson(text, schema, { source: context.url });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof ValidationError) throw ResponseShapeError.fromZodIssues({
|
|
54
|
+
kind: "zod",
|
|
55
|
+
method: context.method,
|
|
56
|
+
url: context.url,
|
|
57
|
+
status: context.status,
|
|
58
|
+
zodIssues: error.developerDetail.zodIssues,
|
|
59
|
+
serverTag: await context.getServerTag(),
|
|
60
|
+
serverSkew: context.serverSkew
|
|
61
|
+
});
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region ../client/src/http/retry.ts
|
|
67
|
+
const BASE_DELAY_MS = 250;
|
|
68
|
+
const MAX_DELAY_MS = 8e3;
|
|
69
|
+
const MS_PER_SECOND = 1e3;
|
|
70
|
+
function backoffDelay({ attempt, retryAfterHeader }) {
|
|
71
|
+
const fromHeader = parseRetryAfter(retryAfterHeader);
|
|
72
|
+
if (fromHeader !== null) return fromHeader;
|
|
73
|
+
return Math.min(MAX_DELAY_MS, BASE_DELAY_MS * 2 ** attempt);
|
|
74
|
+
}
|
|
75
|
+
function parseRetryAfter(header) {
|
|
76
|
+
if (!header) return null;
|
|
77
|
+
const numeric = Number(header);
|
|
78
|
+
if (Number.isFinite(numeric) && numeric >= 0) return Math.round(numeric * MS_PER_SECOND);
|
|
79
|
+
const dateMs = Date.parse(header);
|
|
80
|
+
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function sleep(ms, signal) {
|
|
84
|
+
return setTimeout(ms, void 0, { signal });
|
|
85
|
+
}
|
|
86
|
+
async function runWithRetries(attempt, signal) {
|
|
87
|
+
let attemptIndex = 0;
|
|
88
|
+
while (true) {
|
|
89
|
+
const outcome = await attempt(attemptIndex);
|
|
90
|
+
if (outcome.kind === "success") return outcome.response;
|
|
91
|
+
await sleep(outcome.delayMs, signal);
|
|
92
|
+
attemptIndex += 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region ../client/src/http/transport.ts
|
|
97
|
+
const UNAUTHORIZED_STATUS = 401;
|
|
98
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
99
|
+
const OCTET_STREAM_CONTENT_TYPE = "application/octet-stream";
|
|
100
|
+
const TEXT_CONTENT_TYPE_PREFIX = "text/";
|
|
101
|
+
const ERROR_BODY_BYTE_CAP = 64 * 1024;
|
|
102
|
+
const IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
|
|
103
|
+
"GET",
|
|
104
|
+
"HEAD",
|
|
105
|
+
"OPTIONS"
|
|
54
106
|
]);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
107
|
+
function createTransport(config, options) {
|
|
108
|
+
const baseUrl = normalizeUrl(config.url);
|
|
109
|
+
assertCredentialHeaderSafe(config.credential);
|
|
110
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
111
|
+
let settledProfile = options.server ?? null;
|
|
112
|
+
const getServerTag = options.getServerTag ?? (async () => tagOf(settledProfile));
|
|
113
|
+
const serverSkew = () => settledProfile === null ? null : settledProfile.skew;
|
|
114
|
+
const refreshCredential = options.refreshCredential;
|
|
115
|
+
let credential = config.credential;
|
|
116
|
+
const knownSecrets = new Set(credentialSecrets(credential));
|
|
117
|
+
const redactionContext = { knownSecrets };
|
|
118
|
+
let refreshInFlight = null;
|
|
119
|
+
function tryRefreshCredential() {
|
|
120
|
+
const refresh = refreshCredential;
|
|
121
|
+
if (credential.kind !== "oauth" || refresh === void 0) return Promise.resolve(false);
|
|
122
|
+
refreshInFlight ??= refreshOnce(refresh).finally(() => {
|
|
123
|
+
refreshInFlight = null;
|
|
124
|
+
});
|
|
125
|
+
return refreshInFlight;
|
|
126
|
+
}
|
|
127
|
+
async function refreshOnce(refresh) {
|
|
128
|
+
const next = await refresh();
|
|
129
|
+
if (next === null) return false;
|
|
130
|
+
credential = next;
|
|
131
|
+
for (const secret of credentialSecrets(next)) knownSecrets.add(secret);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
async function attemptOnce(prepared, attempt) {
|
|
135
|
+
const hasRetriesLeft = attempt < prepared.retries;
|
|
136
|
+
const timeoutSignal = AbortSignal.timeout(prepared.timeoutMs);
|
|
137
|
+
const combined = combineAborts(timeoutSignal, prepared.cancelSignal);
|
|
138
|
+
let response;
|
|
139
|
+
try {
|
|
140
|
+
response = await fetchImpl(prepared.url, {
|
|
141
|
+
method: prepared.method,
|
|
142
|
+
headers: prepared.headers,
|
|
143
|
+
body: prepared.body,
|
|
144
|
+
signal: combined
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
throwIfAborted(prepared.cancelSignal);
|
|
148
|
+
const isStaleSocketFirstTry = attempt === 0 && isConnectionClosed(error);
|
|
149
|
+
if (prepared.idempotent && (hasRetriesLeft || isStaleSocketFirstTry)) return {
|
|
150
|
+
kind: "retry",
|
|
151
|
+
delayMs: backoffDelay({ attempt })
|
|
152
|
+
};
|
|
153
|
+
if (timeoutSignal.aborted) throw new TimeoutError(`Request timed out after ${prepared.timeoutMs}ms`, {
|
|
154
|
+
kind: "http",
|
|
155
|
+
method: prepared.method,
|
|
156
|
+
url: prepared.url,
|
|
157
|
+
timeoutMs: prepared.timeoutMs
|
|
158
|
+
});
|
|
159
|
+
throw buildNetworkError(error, prepared.method, prepared.url);
|
|
160
|
+
}
|
|
161
|
+
const canRetryStatus = hasRetriesLeft && prepared.idempotent;
|
|
162
|
+
if (!response.ok && isRetryableStatus(response.status) && canRetryStatus) {
|
|
163
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
164
|
+
response.body?.cancel().catch(() => void 0);
|
|
165
|
+
return {
|
|
166
|
+
kind: "retry",
|
|
167
|
+
delayMs: backoffDelay({
|
|
168
|
+
attempt,
|
|
169
|
+
retryAfterHeader: retryAfter
|
|
170
|
+
})
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (!response.ok) {
|
|
174
|
+
const rawBody = await readBodyForError(response);
|
|
175
|
+
const serverTag = await getServerTag();
|
|
176
|
+
throw new HttpError({
|
|
177
|
+
status: response.status,
|
|
178
|
+
statusText: response.statusText,
|
|
179
|
+
method: prepared.method,
|
|
180
|
+
url: prepared.url,
|
|
181
|
+
responseHeaders: response.headers,
|
|
182
|
+
rawBody,
|
|
183
|
+
serverTag,
|
|
184
|
+
redactionContext
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
assertContentType(response, prepared);
|
|
188
|
+
return {
|
|
189
|
+
kind: "success",
|
|
190
|
+
response
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async function executeRaw(prepared) {
|
|
194
|
+
try {
|
|
195
|
+
return await runWithRetries((attempt) => attemptOnce(prepared, attempt), prepared.cancelSignal);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
throwIfAborted(prepared.cancelSignal);
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async function executeWithAuthRefresh(path, opts) {
|
|
202
|
+
const prepared = prepare(path, opts);
|
|
203
|
+
try {
|
|
204
|
+
return {
|
|
205
|
+
response: await executeRaw(prepared),
|
|
206
|
+
prepared
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (error instanceof HttpError && error.status === UNAUTHORIZED_STATUS) {
|
|
210
|
+
if (await tryRefreshCredential()) {
|
|
211
|
+
const retried = prepare(path, opts);
|
|
212
|
+
return {
|
|
213
|
+
response: await executeRaw(retried),
|
|
214
|
+
prepared: retried
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function prepare(path, opts = {}) {
|
|
222
|
+
const method = opts.method ?? "GET";
|
|
223
|
+
const expectContentType = opts.expectContentType ?? "json";
|
|
224
|
+
const url = buildUrl(baseUrl, path, opts.query);
|
|
225
|
+
const headers = new Headers();
|
|
226
|
+
const auth = credentialAuthHeader(credential);
|
|
227
|
+
headers.set(auth.name, auth.value);
|
|
228
|
+
headers.set("accept", acceptHeader(expectContentType));
|
|
229
|
+
headers.set("user-agent", options.userAgent);
|
|
230
|
+
let body = null;
|
|
231
|
+
if (opts.body !== void 0 && opts.body !== null) if (typeof opts.body === "string" || opts.body instanceof URLSearchParams) body = opts.body;
|
|
232
|
+
else if (opts.body instanceof FormData || opts.body instanceof ReadableStream) body = opts.body;
|
|
233
|
+
else if (opts.body instanceof Uint8Array) {
|
|
234
|
+
body = opts.body;
|
|
235
|
+
headers.set("content-type", OCTET_STREAM_CONTENT_TYPE);
|
|
236
|
+
} else {
|
|
237
|
+
body = JSON.stringify(opts.body);
|
|
238
|
+
headers.set("content-type", JSON_CONTENT_TYPE);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
url,
|
|
242
|
+
method,
|
|
243
|
+
headers,
|
|
244
|
+
body,
|
|
245
|
+
expectContentType,
|
|
246
|
+
retries: opts.retries ?? 3,
|
|
247
|
+
idempotent: opts.idempotent ?? IDEMPOTENT_METHODS.has(method),
|
|
248
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
249
|
+
cancelSignal: combineAborts(opts.signal, options.signal)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
let serverProfile = options.server === void 0 ? null : Promise.resolve(options.server);
|
|
253
|
+
async function server(wait = {}) {
|
|
254
|
+
throwIfAborted(wait.signal);
|
|
255
|
+
if (serverProfile === null) {
|
|
256
|
+
const probe = probeServer(transport).then((info) => {
|
|
257
|
+
settledProfile = createServerProfile(info);
|
|
258
|
+
return settledProfile;
|
|
259
|
+
});
|
|
260
|
+
probe.catch(() => {
|
|
261
|
+
serverProfile = null;
|
|
262
|
+
});
|
|
263
|
+
serverProfile = probe;
|
|
264
|
+
}
|
|
265
|
+
return untilAborted(serverProfile, wait.signal);
|
|
266
|
+
}
|
|
267
|
+
const enforceRequirements = options.enforceRequirements ?? true;
|
|
268
|
+
async function requireFeatures(features, wait = {}) {
|
|
269
|
+
if (!enforceRequirements || features.length === 0) return;
|
|
270
|
+
const failure = checkFeatures(features, await server(wait));
|
|
271
|
+
if (failure !== null) throw new CapabilityError(failure);
|
|
272
|
+
}
|
|
273
|
+
async function requireMethod(key, wait = {}) {
|
|
274
|
+
return requireFeatures(methodRequirements(key), wait);
|
|
275
|
+
}
|
|
276
|
+
const transport = {
|
|
277
|
+
server,
|
|
278
|
+
require: requireMethod,
|
|
279
|
+
requireFeatures,
|
|
280
|
+
async requestRaw(path, opts) {
|
|
281
|
+
return (await executeWithAuthRefresh(path, opts ?? {})).response;
|
|
282
|
+
},
|
|
283
|
+
async requestParsed(schema, path, opts) {
|
|
284
|
+
const { response, prepared } = await executeWithAuthRefresh(path, {
|
|
285
|
+
...opts,
|
|
286
|
+
expectContentType: "json"
|
|
287
|
+
});
|
|
288
|
+
return parseJsonResponse(await response.text(), schema, {
|
|
289
|
+
method: prepared.method,
|
|
290
|
+
url: prepared.url,
|
|
291
|
+
status: response.status,
|
|
292
|
+
getServerTag,
|
|
293
|
+
serverSkew: serverSkew()
|
|
294
|
+
});
|
|
295
|
+
},
|
|
296
|
+
async requestStream(path, opts) {
|
|
297
|
+
const { response, prepared } = await executeWithAuthRefresh(path, {
|
|
298
|
+
...opts,
|
|
299
|
+
expectContentType: opts?.expectContentType ?? "binary"
|
|
300
|
+
});
|
|
301
|
+
if (!response.body) throw new NetworkError("Response had no body to stream", {
|
|
302
|
+
method: prepared.method,
|
|
303
|
+
url: prepared.url,
|
|
304
|
+
cause: "missing body"
|
|
305
|
+
});
|
|
306
|
+
return response.body;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
return transport;
|
|
310
|
+
}
|
|
311
|
+
function tagOf(profile) {
|
|
312
|
+
return profile === null ? null : profile.version.tag;
|
|
313
|
+
}
|
|
314
|
+
function buildUrl(baseUrl, path, query) {
|
|
315
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
316
|
+
const target = new URL(baseUrl + normalizedPath);
|
|
317
|
+
if (query) for (const [key, value] of Object.entries(query)) {
|
|
318
|
+
if (value === void 0) continue;
|
|
319
|
+
if (Array.isArray(value)) for (const entry of value) target.searchParams.append(key, String(entry));
|
|
320
|
+
else target.searchParams.append(key, String(value));
|
|
321
|
+
}
|
|
322
|
+
return target.toString();
|
|
323
|
+
}
|
|
324
|
+
function acceptHeader(expected) {
|
|
325
|
+
if (expected === "json") return JSON_CONTENT_TYPE;
|
|
326
|
+
if (expected === "text") return "text/*";
|
|
327
|
+
return "*/*";
|
|
328
|
+
}
|
|
329
|
+
function assertContentType(response, prepared) {
|
|
330
|
+
if (prepared.expectContentType === "binary") return;
|
|
331
|
+
const contentType = response.headers.get("content-type");
|
|
332
|
+
if (contentType === null) throwContentTypeMismatch(response, prepared, prepared.expectContentType);
|
|
333
|
+
if (prepared.expectContentType === "json" && !contentType.includes("application/json")) throwContentTypeMismatch(response, prepared, "json");
|
|
334
|
+
if (prepared.expectContentType === "text" && !contentType.startsWith(TEXT_CONTENT_TYPE_PREFIX)) throwContentTypeMismatch(response, prepared, "text");
|
|
335
|
+
}
|
|
336
|
+
function throwContentTypeMismatch(response, prepared, expected) {
|
|
337
|
+
const actual = response.headers.get("content-type") ?? "no content-type";
|
|
338
|
+
throw new HttpError({
|
|
339
|
+
status: response.status,
|
|
340
|
+
statusText: response.statusText,
|
|
341
|
+
method: prepared.method,
|
|
342
|
+
url: prepared.url,
|
|
343
|
+
responseHeaders: response.headers,
|
|
344
|
+
rawBody: null,
|
|
345
|
+
overrideUserMessage: `Expected ${expected} response but got ${actual}`
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async function readBodyForError(response) {
|
|
349
|
+
try {
|
|
350
|
+
return Buffer.from(await response.arrayBuffer()).subarray(0, ERROR_BODY_BYTE_CAP).toString("utf8");
|
|
351
|
+
} catch (error) {
|
|
352
|
+
return `[body read failed: ${errorMessage(error)}]`;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region ../client/src/resources/pivot-export.ts
|
|
357
|
+
const PivotedExportsSetting = z.object({ "enable-pivoted-exports": z.boolean() });
|
|
358
|
+
/**
|
|
359
|
+
* Refuse a pivoted export the server would answer with plain rows: a JSON export keeps only the
|
|
360
|
+
* ungrouped rows, and an admin can turn pivoted CSV and XLSX exports off.
|
|
361
|
+
*/
|
|
362
|
+
async function assertPivotedExport(transport, format, options) {
|
|
363
|
+
if (!PivotExportFormat.safeParse(format).success) throw new ConfigError(`a ${format} export cannot be pivoted; only ${PivotExportFormat.options.join(" and ")} can`);
|
|
364
|
+
if (!(await transport.requestParsed(PivotedExportsSetting, "/api/session/properties", { ...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");
|
|
365
|
+
}
|
|
81
366
|
//#endregion
|
|
82
367
|
//#region ../client/src/resources/card.ts
|
|
83
368
|
const CardApiList = z.array(Card);
|
|
@@ -134,15 +419,22 @@ function cardResource(transport) {
|
|
|
134
419
|
});
|
|
135
420
|
}
|
|
136
421
|
/**
|
|
137
|
-
* Run a
|
|
138
|
-
* form-encoded body and answers bytes, so a caller
|
|
422
|
+
* Run the query associated with a Card, and return its results as a file in the specified
|
|
423
|
+
* format. Unlike `query`, this endpoint takes a form-encoded body and answers bytes, so a caller
|
|
424
|
+
* consumes the stream rather than a value. A pivot the server would answer with plain rows (a
|
|
425
|
+
* JSON export, or pivoted exports turned off) is refused. `csv_include_bom` opens a CSV with a
|
|
426
|
+
* UTF-8 byte order mark; a server without it drops the key silently, so asking for one is refused
|
|
427
|
+
* there before the wire.
|
|
139
428
|
*/
|
|
140
429
|
async function exportQuery(id, format, params, options = {}) {
|
|
141
430
|
await transport.require("card.exportQuery", options);
|
|
431
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
432
|
+
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
142
433
|
const body = new URLSearchParams({
|
|
143
434
|
parameters: JSON.stringify(params.parameters),
|
|
144
435
|
format_rows: String(params.format_rows),
|
|
145
|
-
pivot_results: String(params.pivot_results)
|
|
436
|
+
pivot_results: String(params.pivot_results),
|
|
437
|
+
csv_include_bom: String(params.csv_include_bom)
|
|
146
438
|
});
|
|
147
439
|
return transport.requestStream(`/api/card/${id}/query/${format}`, {
|
|
148
440
|
...options,
|
|
@@ -226,20 +518,20 @@ function refPath(ref) {
|
|
|
226
518
|
return encodeURIComponent(ref);
|
|
227
519
|
}
|
|
228
520
|
/**
|
|
229
|
-
* List
|
|
230
|
-
* projection. `Collection` pins `type`, `namespace` and `authority_level`
|
|
231
|
-
* consumer reading a few fields off every collection on the instance narrows
|
|
232
|
-
* carrying a server value outside those sets then costs nothing to a caller
|
|
233
|
-
* field.
|
|
521
|
+
* List every collection the caller can read, the Library and its children among them, parsing each
|
|
522
|
+
* through the caller's own projection. `Collection` pins `type`, `namespace` and `authority_level`
|
|
523
|
+
* to closed enums, so a consumer reading a few fields off every collection on the instance narrows
|
|
524
|
+
* here: one collection carrying a server value outside those sets then costs nothing to a caller
|
|
525
|
+
* that never reads the field.
|
|
234
526
|
*/
|
|
235
|
-
async function
|
|
236
|
-
return transport.requestParsed(z.array(schema), "/api/collection", {
|
|
237
|
-
...options,
|
|
238
|
-
query: { "include-library": true }
|
|
239
|
-
});
|
|
527
|
+
async function listCollectionsAs(transport, schema, options = {}) {
|
|
528
|
+
return transport.requestParsed(z.array(schema), "/api/collection", { ...options });
|
|
240
529
|
}
|
|
241
530
|
function collectionResource(transport) {
|
|
242
|
-
/**
|
|
531
|
+
/**
|
|
532
|
+
* List collections. `filter` picks a server-side preset: everything, archived, or personal. Every
|
|
533
|
+
* preset includes the Library collections that match it.
|
|
534
|
+
*/
|
|
243
535
|
async function list(params = {}, options = {}) {
|
|
244
536
|
await transport.require("collection.list", options);
|
|
245
537
|
return {
|
|
@@ -250,18 +542,6 @@ function collectionResource(transport) {
|
|
|
250
542
|
total: null
|
|
251
543
|
};
|
|
252
544
|
}
|
|
253
|
-
/**
|
|
254
|
-
* List collections including the Library and its children, which the plain listing omits.
|
|
255
|
-
* The Library's own children carry neither `type` nor `is_remote_synced`, so this is where a
|
|
256
|
-
* caller resolves both.
|
|
257
|
-
*/
|
|
258
|
-
async function listWithLibrary(options = {}) {
|
|
259
|
-
await transport.require("collection.listWithLibrary", options);
|
|
260
|
-
return {
|
|
261
|
-
data: await listCollectionsWithLibrary(transport, Collection, options),
|
|
262
|
-
total: null
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
545
|
/** Get one collection by id, entity id, or the `root`/`trash` alias. */
|
|
266
546
|
async function get(ref, options = {}) {
|
|
267
547
|
await transport.require("collection.get", options);
|
|
@@ -276,7 +556,11 @@ function collectionResource(transport) {
|
|
|
276
556
|
body: params
|
|
277
557
|
});
|
|
278
558
|
}
|
|
279
|
-
/**
|
|
559
|
+
/**
|
|
560
|
+
* Update a collection, patching the fields the body carries. The server reads an absent
|
|
561
|
+
* `archived` as `false`, so a patch without it restores an archived collection, and a patch that
|
|
562
|
+
* moves a collection into the trash ignores `parent_id`.
|
|
563
|
+
*/
|
|
280
564
|
async function update(ref, params, options = {}) {
|
|
281
565
|
await transport.require("collection.update", options);
|
|
282
566
|
return transport.requestParsed(Collection, `/api/collection/${refPath(ref)}`, {
|
|
@@ -309,16 +593,18 @@ function collectionResource(transport) {
|
|
|
309
593
|
});
|
|
310
594
|
}
|
|
311
595
|
/** Fetch the collection hierarchy as a forest of nested nodes. */
|
|
312
|
-
async function tree(options = {}) {
|
|
596
|
+
async function tree(params = {}, options = {}) {
|
|
313
597
|
await transport.require("collection.tree", options);
|
|
314
598
|
return {
|
|
315
|
-
data: await transport.requestParsed(CollectionTreeApiList, "/api/collection/tree", {
|
|
599
|
+
data: await transport.requestParsed(CollectionTreeApiList, "/api/collection/tree", {
|
|
600
|
+
...options,
|
|
601
|
+
query: { "include-library": params["include-library"] }
|
|
602
|
+
}),
|
|
316
603
|
total: null
|
|
317
604
|
};
|
|
318
605
|
}
|
|
319
606
|
return {
|
|
320
607
|
list,
|
|
321
|
-
listWithLibrary,
|
|
322
608
|
get,
|
|
323
609
|
create,
|
|
324
610
|
update,
|
|
@@ -407,9 +693,13 @@ function dashboardResource(transport) {
|
|
|
407
693
|
}
|
|
408
694
|
/**
|
|
409
695
|
* Copy a dashboard, with its tabs and dashcards, into `collection_id` (root when absent). A deep
|
|
410
|
-
* copy duplicates
|
|
411
|
-
*
|
|
412
|
-
*
|
|
696
|
+
* copy duplicates its questions and metrics and references its models. With `is_deep_copy`
|
|
697
|
+
* false or absent the copy references every card, and a dashboard holding unarchived dashboard
|
|
698
|
+
* questions is refused. Archived cards, cards the caller cannot read, and every card on a
|
|
699
|
+
* dashcard whose main card the caller cannot read are left out and reported as `uncopied`, once
|
|
700
|
+
* per dashcard they sat on. A left-out main card takes its dashcard with it; a left-out series
|
|
701
|
+
* card stays on a dashcard whose main card is referenced and is dropped from one whose main card
|
|
702
|
+
* is duplicated. Action, link and placeholder dashcards are not copied.
|
|
413
703
|
*/
|
|
414
704
|
async function copy(id, params = {}, options = {}) {
|
|
415
705
|
await transport.require("dashboard.copy", options);
|
|
@@ -663,7 +953,8 @@ function datasetResource(transport) {
|
|
|
663
953
|
*/
|
|
664
954
|
async function native(datasetQuery, params = {}, options = {}) {
|
|
665
955
|
await transport.require("dataset.native", options);
|
|
666
|
-
|
|
956
|
+
const { features } = await transport.server(options);
|
|
957
|
+
return transport.requestParsed(compiledQuerySchema(features), "/api/dataset/native", {
|
|
667
958
|
...options,
|
|
668
959
|
method: "POST",
|
|
669
960
|
body: {
|
|
@@ -686,18 +977,33 @@ function datasetResource(transport) {
|
|
|
686
977
|
});
|
|
687
978
|
}
|
|
688
979
|
/**
|
|
689
|
-
*
|
|
690
|
-
* bytes, so a caller consumes the stream rather than a value.
|
|
980
|
+
* Execute a query and download the result data as a file in the specified format. Unlike
|
|
981
|
+
* `query`, this endpoint answers bytes, so a caller consumes the stream rather than a value.
|
|
982
|
+
* `visualization_settings` drive `format_rows` column formatting and, through
|
|
983
|
+
* `pivot_table.column_split`, the layout `pivot_results` lays the rows out in. The server only
|
|
984
|
+
* computes that layout for a query it runs as a pivot, which it reads from the query's own
|
|
985
|
+
* `was-pivot`, so `pivot_results` sets it. A pivot the server would answer with plain rows (a
|
|
986
|
+
* JSON export, or pivoted exports turned off) is refused. `csv_include_bom` opens a CSV with a
|
|
987
|
+
* UTF-8 byte order mark; a server without it drops the key silently, so asking for one is refused
|
|
988
|
+
* there before the wire.
|
|
691
989
|
*/
|
|
692
990
|
async function exportQuery(format, params, options = {}) {
|
|
693
991
|
await transport.require("dataset.exportQuery", options);
|
|
992
|
+
await transport.requireFeatures(params.csv_include_bom ? ["exportCsvByteOrderMark"] : [], options);
|
|
993
|
+
if (params.pivot_results) await assertPivotedExport(transport, format, options);
|
|
994
|
+
const exported = params.pivot_results ? {
|
|
995
|
+
...params.query,
|
|
996
|
+
"was-pivot": true
|
|
997
|
+
} : params.query;
|
|
694
998
|
return transport.requestStream(`/api/dataset/${format}`, {
|
|
695
999
|
...options,
|
|
696
1000
|
method: "POST",
|
|
697
1001
|
body: {
|
|
698
|
-
query:
|
|
1002
|
+
query: exported,
|
|
1003
|
+
visualization_settings: params.visualization_settings,
|
|
699
1004
|
format_rows: params.format_rows,
|
|
700
|
-
pivot_results: params.pivot_results
|
|
1005
|
+
pivot_results: params.pivot_results,
|
|
1006
|
+
csv_include_bom: params.csv_include_bom
|
|
701
1007
|
}
|
|
702
1008
|
});
|
|
703
1009
|
}
|
|
@@ -709,133 +1015,18 @@ function datasetResource(transport) {
|
|
|
709
1015
|
};
|
|
710
1016
|
}
|
|
711
1017
|
//#endregion
|
|
712
|
-
//#region ../client/src/domain/dependency.ts
|
|
713
|
-
const DependencyType = z.enum([
|
|
714
|
-
"table",
|
|
715
|
-
"card",
|
|
716
|
-
"snippet",
|
|
717
|
-
"transform",
|
|
718
|
-
"dashboard",
|
|
719
|
-
"document",
|
|
720
|
-
"sandbox",
|
|
721
|
-
"segment",
|
|
722
|
-
"measure"
|
|
723
|
-
]);
|
|
724
|
-
const DependencyUsageType = z.enum([
|
|
725
|
-
"table",
|
|
726
|
-
"snippet",
|
|
727
|
-
"transform",
|
|
728
|
-
"dashboard",
|
|
729
|
-
"document",
|
|
730
|
-
"sandbox",
|
|
731
|
-
"segment",
|
|
732
|
-
"question",
|
|
733
|
-
"model",
|
|
734
|
-
"metric",
|
|
735
|
-
"measure"
|
|
736
|
-
]);
|
|
737
|
-
const DependencyFindingErrorType = z.enum([
|
|
738
|
-
"missing-column",
|
|
739
|
-
"missing-table-alias",
|
|
740
|
-
"missing-table",
|
|
741
|
-
"missing-card",
|
|
742
|
-
"duplicate-column",
|
|
743
|
-
"syntax-error",
|
|
744
|
-
"validation-exception-error"
|
|
745
|
-
]);
|
|
746
|
-
const DependencyErrorSourceType = z.enum([
|
|
747
|
-
"table",
|
|
748
|
-
"card",
|
|
749
|
-
"transform"
|
|
750
|
-
]);
|
|
751
|
-
z.enum([
|
|
752
|
-
"name",
|
|
753
|
-
"location",
|
|
754
|
-
"view-count"
|
|
755
|
-
]);
|
|
756
|
-
z.enum([
|
|
757
|
-
"name",
|
|
758
|
-
"location",
|
|
759
|
-
"dependents-with-errors",
|
|
760
|
-
"dependents-errors"
|
|
761
|
-
]);
|
|
762
|
-
const DependencyCollectionRef = z.object({
|
|
763
|
-
id: CollectionId,
|
|
764
|
-
name: z.string(),
|
|
765
|
-
authority_level: CollectionAuthorityLevel.nullable(),
|
|
766
|
-
is_personal: z.boolean().optional()
|
|
767
|
-
}).loose();
|
|
768
|
-
const DependencyContainerRef = z.object({
|
|
769
|
-
id: z.number().int(),
|
|
770
|
-
name: z.string()
|
|
771
|
-
}).loose();
|
|
772
|
-
const DependencyEntityData = z.object({
|
|
773
|
-
name: z.string().nullable().optional(),
|
|
774
|
-
display_name: z.string().nullable().optional(),
|
|
775
|
-
description: z.string().nullable().optional(),
|
|
776
|
-
type: CardType.nullable().optional(),
|
|
777
|
-
display: z.string().nullable().optional(),
|
|
778
|
-
database_id: z.number().int().nullable().optional(),
|
|
779
|
-
db_id: z.number().int().nullable().optional(),
|
|
780
|
-
schema: z.string().nullable().optional(),
|
|
781
|
-
collection_id: CollectionId.nullable().optional(),
|
|
782
|
-
collection: DependencyCollectionRef.nullable().optional(),
|
|
783
|
-
dashboard_id: z.number().int().nullable().optional(),
|
|
784
|
-
dashboard: DependencyContainerRef.nullable().optional(),
|
|
785
|
-
document_id: z.number().int().nullable().optional(),
|
|
786
|
-
document: DependencyContainerRef.nullable().optional(),
|
|
787
|
-
table_id: z.number().int().nullable().optional(),
|
|
788
|
-
view_count: z.number().int().nullable().optional()
|
|
789
|
-
}).loose();
|
|
790
|
-
const DependencyEntity = z.object({
|
|
791
|
-
id: z.number().int(),
|
|
792
|
-
type: DependencyType,
|
|
793
|
-
data: DependencyEntityData
|
|
794
|
-
}).loose();
|
|
795
|
-
const DependencyNode = DependencyEntity.extend({ dependents_count: z.partialRecord(DependencyUsageType, z.number().int()).nullable() });
|
|
796
|
-
const DependencyFindingError = z.object({
|
|
797
|
-
id: z.number().int(),
|
|
798
|
-
analyzed_entity_type: DependencyType,
|
|
799
|
-
analyzed_entity_id: z.number().int(),
|
|
800
|
-
error_type: DependencyFindingErrorType,
|
|
801
|
-
error_detail: z.string().nullable().optional(),
|
|
802
|
-
source_entity_type: DependencyErrorSourceType.nullable().optional(),
|
|
803
|
-
source_entity_id: z.number().int().nullable().optional()
|
|
804
|
-
}).loose();
|
|
805
|
-
const BreakingSource = DependencyNode.extend({ dependents_errors: z.array(DependencyFindingError).nullable().optional() });
|
|
806
|
-
const DependencyEdge = z.object({
|
|
807
|
-
from_entity_type: DependencyType,
|
|
808
|
-
from_entity_id: z.number().int(),
|
|
809
|
-
to_entity_type: DependencyType,
|
|
810
|
-
to_entity_id: z.number().int()
|
|
811
|
-
}).loose();
|
|
812
|
-
const DependencyGraph = z.object({
|
|
813
|
-
nodes: z.array(DependencyNode),
|
|
814
|
-
edges: z.array(DependencyEdge)
|
|
815
|
-
}).loose();
|
|
816
|
-
//#endregion
|
|
817
1018
|
//#region ../client/src/resources/dependency.ts
|
|
818
1019
|
const DependencyNodeApiList = z.array(DependencyNode);
|
|
819
1020
|
const DependencyEntityApiList = z.array(DependencyEntity);
|
|
820
|
-
const RENAMED_FILTERS = [
|
|
821
|
-
"dependent-types",
|
|
822
|
-
"dependent-card-types",
|
|
823
|
-
"card-types",
|
|
824
|
-
"include-personal-collections",
|
|
825
|
-
"sort-column",
|
|
826
|
-
"sort-direction"
|
|
827
|
-
];
|
|
828
|
-
function filterFeatures(params) {
|
|
829
|
-
return RENAMED_FILTERS.some((key) => params[key] !== void 0) ? ["dependencyKebabCaseFilters"] : [];
|
|
830
|
-
}
|
|
831
1021
|
function dependencyResource(transport) {
|
|
832
1022
|
/**
|
|
833
1023
|
* The upstream dependency graph of one entity. `nodes` holds the starting entity plus every
|
|
834
1024
|
* entity it depends on, directly or transitively; each edge runs from the dependent to what it
|
|
835
|
-
* depends on.
|
|
1025
|
+
* depends on. A measure starts a graph only on a server with `measureDependencyGraph`.
|
|
836
1026
|
*/
|
|
837
1027
|
async function graph(type, id, options = {}) {
|
|
838
1028
|
await transport.require("dependency.graph", options);
|
|
1029
|
+
if (type === "measure") await transport.requireFeatures(["measureDependencyGraph"], options);
|
|
839
1030
|
return transport.requestParsed(DependencyGraph, "/api/ee/dependencies/graph", {
|
|
840
1031
|
...options,
|
|
841
1032
|
query: {
|
|
@@ -852,7 +1043,6 @@ function dependencyResource(transport) {
|
|
|
852
1043
|
*/
|
|
853
1044
|
async function dependents(type, id, params = {}, options = {}) {
|
|
854
1045
|
await transport.require("dependency.dependents", options);
|
|
855
|
-
await transport.requireFeatures(filterFeatures(params), options);
|
|
856
1046
|
return {
|
|
857
1047
|
data: await transport.requestParsed(DependencyNodeApiList, "/api/ee/dependencies/graph/dependents", {
|
|
858
1048
|
...options,
|
|
@@ -879,7 +1069,6 @@ function dependencyResource(transport) {
|
|
|
879
1069
|
*/
|
|
880
1070
|
async function broken(type, id, params = {}, options = {}) {
|
|
881
1071
|
await transport.require("dependency.broken", options);
|
|
882
|
-
await transport.requireFeatures(filterFeatures(params), options);
|
|
883
1072
|
return {
|
|
884
1073
|
data: await transport.requestParsed(DependencyEntityApiList, "/api/ee/dependencies/graph/broken", {
|
|
885
1074
|
...options,
|
|
@@ -897,15 +1086,23 @@ function dependencyResource(transport) {
|
|
|
897
1086
|
};
|
|
898
1087
|
}
|
|
899
1088
|
/**
|
|
900
|
-
* Walk the entities
|
|
901
|
-
*
|
|
1089
|
+
* Walk the entities that no readable, unarchived dependent depends on, one page at a time: an
|
|
1090
|
+
* entity used only by archived content, or by content the caller cannot read, is listed. `types`
|
|
1091
|
+
* and `card-types` narrow which kinds are listed, `query` matches against names and locations,
|
|
902
1092
|
* `include-personal-collections` admits content in personal collections, and `sort-column` /
|
|
903
|
-
* `sort-direction` order the result.
|
|
904
|
-
*
|
|
1093
|
+
* `sort-direction` order the result. A `query` leaves sandboxes out, since they have no name or
|
|
1094
|
+
* location to match, so sandboxes alone with a `query` answer one empty page without a request.
|
|
1095
|
+
* This endpoint pages on the server, so the caller consumes pages and decides how far to pull.
|
|
905
1096
|
*/
|
|
906
1097
|
async function* unreferencedPages(params = {}, options = {}) {
|
|
907
1098
|
await transport.require("dependency.unreferencedPages", options);
|
|
908
|
-
|
|
1099
|
+
if (queryLeavesNoKind(params)) {
|
|
1100
|
+
yield {
|
|
1101
|
+
items: [],
|
|
1102
|
+
total: 0
|
|
1103
|
+
};
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
909
1106
|
yield* paginatePages(transport, "/api/ee/dependencies/graph/unreferenced", DependencyNode, {
|
|
910
1107
|
query: itemListQuery(params),
|
|
911
1108
|
...options.offset !== void 0 && { offset: options.offset },
|
|
@@ -923,7 +1120,13 @@ function dependencyResource(transport) {
|
|
|
923
1120
|
*/
|
|
924
1121
|
async function* breakingPages(params = {}, options = {}) {
|
|
925
1122
|
await transport.require("dependency.breakingPages", options);
|
|
926
|
-
|
|
1123
|
+
if (queryLeavesNoKind(params)) {
|
|
1124
|
+
yield {
|
|
1125
|
+
items: [],
|
|
1126
|
+
total: 0
|
|
1127
|
+
};
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
927
1130
|
yield* paginatePages(transport, "/api/ee/dependencies/graph/breaking", BreakingSource, {
|
|
928
1131
|
query: itemListQuery(params),
|
|
929
1132
|
...options.offset !== void 0 && { offset: options.offset },
|
|
@@ -940,9 +1143,14 @@ function dependencyResource(transport) {
|
|
|
940
1143
|
breakingPages
|
|
941
1144
|
};
|
|
942
1145
|
}
|
|
1146
|
+
function queryLeavesNoKind(params) {
|
|
1147
|
+
const { query, types } = params;
|
|
1148
|
+
if (query === void 0 || types === void 0 || types.length === 0) return false;
|
|
1149
|
+
return types.every((type) => type === "sandbox");
|
|
1150
|
+
}
|
|
943
1151
|
function itemListQuery(params) {
|
|
944
1152
|
return {
|
|
945
|
-
types: params.types,
|
|
1153
|
+
types: params.types === void 0 ? void 0 : [...new Set(params.types)],
|
|
946
1154
|
"card-types": params["card-types"],
|
|
947
1155
|
query: params.query,
|
|
948
1156
|
"include-personal-collections": params["include-personal-collections"],
|
|
@@ -1109,21 +1317,39 @@ async function fetchOptionalParsed(client, path, schema, options = {}) {
|
|
|
1109
1317
|
//#region ../client/src/resources/field.ts
|
|
1110
1318
|
const FieldApiSummary = z.tuple([z.tuple([z.literal("count"), z.number().int()]), z.tuple([z.literal("distincts"), z.number().int()])]);
|
|
1111
1319
|
function fieldResource(transport) {
|
|
1112
|
-
/**
|
|
1320
|
+
/**
|
|
1321
|
+
* Get one field by id. A server that labels data sensitivity answers `data_sensitivity`, `null`
|
|
1322
|
+
* for an unlabelled field; any other answers the field without the key (`hasDataSensitivity`).
|
|
1323
|
+
*/
|
|
1113
1324
|
async function get(id, options = {}) {
|
|
1114
1325
|
await transport.require("field.get", options);
|
|
1115
|
-
|
|
1326
|
+
const { features } = await transport.server(options);
|
|
1327
|
+
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, { ...options });
|
|
1116
1328
|
}
|
|
1117
1329
|
/**
|
|
1118
1330
|
* Update a field by id, patching only the fields the body carries. A `data_sensitivity` label is
|
|
1119
1331
|
* a person's call the server's classifier never overwrites, and `null` withdraws it so the
|
|
1120
1332
|
* classifier's own applies again; a server without the column would drop the key silently, so
|
|
1121
|
-
* the label is refused there before the wire.
|
|
1333
|
+
* the label is refused there before the wire. Answers the field the way `get` does.
|
|
1122
1334
|
*/
|
|
1123
1335
|
async function update(id, params, options = {}) {
|
|
1124
1336
|
await transport.require("field.update", options);
|
|
1125
1337
|
await transport.requireFeatures(params.data_sensitivity === void 0 ? [] : ["fieldDataSensitivity"], options);
|
|
1126
|
-
|
|
1338
|
+
const { features } = await transport.server(options);
|
|
1339
|
+
return transport.requestParsed(fieldDetailSchema(features), `/api/field/${id}`, {
|
|
1340
|
+
...options,
|
|
1341
|
+
method: "PUT",
|
|
1342
|
+
body: params
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Label a field's data sensitivity by hand. A person's label is never overwritten by the server's
|
|
1347
|
+
* classifier; `null` withdraws it, so whatever label the classifier wrote shows again. Answers
|
|
1348
|
+
* the field with the label it now carries.
|
|
1349
|
+
*/
|
|
1350
|
+
async function setDataSensitivity(id, params, options = {}) {
|
|
1351
|
+
await transport.require("field.setDataSensitivity", options);
|
|
1352
|
+
return transport.requestParsed(FieldWithDataSensitivity, `/api/field/${id}`, {
|
|
1127
1353
|
...options,
|
|
1128
1354
|
method: "PUT",
|
|
1129
1355
|
body: params
|
|
@@ -1131,8 +1357,11 @@ function fieldResource(transport) {
|
|
|
1131
1357
|
}
|
|
1132
1358
|
/**
|
|
1133
1359
|
* Search the values of `searchId` that contain `value`, case-insensitively, answering the
|
|
1134
|
-
* matching values of `id` paired with them
|
|
1135
|
-
* points at
|
|
1360
|
+
* matching values of `id` paired with them, ordered by value, at most `limit` of them. An FK on
|
|
1361
|
+
* either side is followed to the PK it points at, and a field that resolves to the same field as
|
|
1362
|
+
* `searchId` answers each value alone. Without `value`, the first `limit` values. A field with
|
|
1363
|
+
* custom display values answers every mapped value with its display value instead, matching
|
|
1364
|
+
* `value` against the display value, ignoring both `searchId` and `limit`.
|
|
1136
1365
|
*/
|
|
1137
1366
|
async function search(id, searchId, params, options = {}) {
|
|
1138
1367
|
await transport.require("field.search", options);
|
|
@@ -1146,11 +1375,13 @@ function fieldResource(transport) {
|
|
|
1146
1375
|
}
|
|
1147
1376
|
/**
|
|
1148
1377
|
* The value of `remappedId` on the one row where `id` equals `value`, as `[value, remapped]`,
|
|
1149
|
-
* or `null` when no row matches.
|
|
1378
|
+
* or `null` when no row matches. For a numeric field the server reads the leading number of
|
|
1379
|
+
* `value` and ignores any text after it, refusing only a `value` that has none.
|
|
1150
1380
|
*/
|
|
1151
1381
|
async function remapping(id, remappedId, params, options = {}) {
|
|
1152
1382
|
await transport.require("field.remapping", options);
|
|
1153
|
-
|
|
1383
|
+
const { features } = await transport.server(options);
|
|
1384
|
+
return fetchOptionalParsed(transport, `/api/field/${id}/remapping/${remappedId}`, fieldRemappedValueSchema(features, id === remappedId), {
|
|
1154
1385
|
...options,
|
|
1155
1386
|
query: { value: params.value }
|
|
1156
1387
|
});
|
|
@@ -1173,6 +1404,7 @@ function fieldResource(transport) {
|
|
|
1173
1404
|
return {
|
|
1174
1405
|
get,
|
|
1175
1406
|
update,
|
|
1407
|
+
setDataSensitivity,
|
|
1176
1408
|
search,
|
|
1177
1409
|
remapping,
|
|
1178
1410
|
summary,
|
|
@@ -1204,6 +1436,7 @@ const SyncStashStarted = z.object({
|
|
|
1204
1436
|
task_id: z.number().int().positive()
|
|
1205
1437
|
});
|
|
1206
1438
|
const RemoteSyncSetting = z.string().nullable();
|
|
1439
|
+
const RemoteSyncBranchProperty = z.object({ "remote-sync-branch": z.string().nullable().optional() });
|
|
1207
1440
|
const FORBIDDEN_STATUS = 403;
|
|
1208
1441
|
const UNREGISTERED_STATUS = 404;
|
|
1209
1442
|
function isRemoteUnreadable(error) {
|
|
@@ -1251,16 +1484,24 @@ function gitSyncResource(transport) {
|
|
|
1251
1484
|
/**
|
|
1252
1485
|
* Import content from the remote into Metabase. The endpoint queues a task and returns at once;
|
|
1253
1486
|
* pass `wait` to poll that task until it reaches a terminal status. A server already up to date
|
|
1254
|
-
* answers no task id, and there is then nothing to poll.
|
|
1487
|
+
* answers no task id, and there is then nothing to poll. A server that guards the tracked branch
|
|
1488
|
+
* refuses with 409 when `expected_branch` disagrees with the setting; left out, it is the tracked
|
|
1489
|
+
* branch as read just before the request. `merge` folds the remote's changes into local content
|
|
1490
|
+
* by a three-way merge and keeps un-pushed local changes, where a plain import refuses on a dirty
|
|
1491
|
+
* instance.
|
|
1255
1492
|
*/
|
|
1256
1493
|
async function importFromRemote(params = {}, options = {}) {
|
|
1257
1494
|
await transport.require("gitSync.import", options);
|
|
1495
|
+
await requireMerge(params.merge, options);
|
|
1496
|
+
const expectedBranch = await guardedBranch(params.expected_branch, options);
|
|
1258
1497
|
const started = await transport.requestParsed(SyncImportStarted, "/api/ee/remote-sync/import", {
|
|
1259
1498
|
...options,
|
|
1260
1499
|
method: "POST",
|
|
1261
1500
|
body: {
|
|
1262
1501
|
branch: params.branch,
|
|
1263
|
-
force: params.force
|
|
1502
|
+
force: params.force,
|
|
1503
|
+
merge: params.merge,
|
|
1504
|
+
expected_branch: expectedBranch
|
|
1264
1505
|
}
|
|
1265
1506
|
});
|
|
1266
1507
|
const message = started.message ?? null;
|
|
@@ -1274,19 +1515,36 @@ function gitSyncResource(transport) {
|
|
|
1274
1515
|
final: await settle(params.wait, options)
|
|
1275
1516
|
};
|
|
1276
1517
|
}
|
|
1518
|
+
async function requireMerge(merge, options) {
|
|
1519
|
+
if (merge === true) await transport.requireFeatures(["remoteSyncMerge"], options);
|
|
1520
|
+
}
|
|
1521
|
+
async function guardedBranch(given, options) {
|
|
1522
|
+
if (given !== void 0) return given;
|
|
1523
|
+
const { features } = await transport.server(options);
|
|
1524
|
+
if (!features.remoteSyncBranchGuard) return;
|
|
1525
|
+
return trackedBranch(options);
|
|
1526
|
+
}
|
|
1277
1527
|
/**
|
|
1278
1528
|
* Export Metabase's content to the remote. The endpoint queues a task and returns at once; pass
|
|
1279
|
-
* `wait` to poll that task until it reaches a terminal status.
|
|
1529
|
+
* `wait` to poll that task until it reaches a terminal status. A server that guards the tracked
|
|
1530
|
+
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked branch
|
|
1531
|
+
* as read just before the request there, and the server's own default elsewhere. When the remote
|
|
1532
|
+
* has moved past the last sync, a plain export ends in a `conflict` task where `merge` is
|
|
1533
|
+
* available, and is refused with 400 before any task elsewhere; `merge` folds the remote's changes
|
|
1534
|
+
* in by a three-way merge instead, and `force` overwrites them.
|
|
1280
1535
|
*/
|
|
1281
1536
|
async function exportToRemote(params = {}, options = {}) {
|
|
1282
1537
|
await transport.require("gitSync.export", options);
|
|
1538
|
+
await requireMerge(params.merge, options);
|
|
1539
|
+
const branchName = await guardedBranch(params.branch, options);
|
|
1283
1540
|
const started = await transport.requestParsed(SyncExportStarted, "/api/ee/remote-sync/export", {
|
|
1284
1541
|
...options,
|
|
1285
1542
|
method: "POST",
|
|
1286
1543
|
body: {
|
|
1287
|
-
branch:
|
|
1544
|
+
branch: branchName,
|
|
1288
1545
|
message: params.message,
|
|
1289
|
-
force: params.force
|
|
1546
|
+
force: params.force,
|
|
1547
|
+
merge: params.merge
|
|
1290
1548
|
}
|
|
1291
1549
|
});
|
|
1292
1550
|
if (params.wait === void 0) return {
|
|
@@ -1371,7 +1629,7 @@ function gitSyncResource(transport) {
|
|
|
1371
1629
|
async function syncedCollections(options = {}) {
|
|
1372
1630
|
await transport.require("gitSync.syncedCollections", options);
|
|
1373
1631
|
return {
|
|
1374
|
-
data: (await
|
|
1632
|
+
data: (await listCollectionsAs(transport, SyncScopeCollection, options)).filter((entry) => entry.is_remote_synced === true),
|
|
1375
1633
|
total: null
|
|
1376
1634
|
};
|
|
1377
1635
|
}
|
|
@@ -1387,17 +1645,22 @@ function gitSyncResource(transport) {
|
|
|
1387
1645
|
}
|
|
1388
1646
|
}
|
|
1389
1647
|
/**
|
|
1390
|
-
* The branch git-sync tracks, or null when none is configured
|
|
1391
|
-
* the server has no remote-sync module
|
|
1648
|
+
* The branch git-sync tracks, or null when none is configured. Refuses when the caller may not
|
|
1649
|
+
* read the setting (it is admin-only) or the server has no remote-sync module, rather than
|
|
1650
|
+
* reading either as unset.
|
|
1392
1651
|
*/
|
|
1393
1652
|
async function branch(options = {}) {
|
|
1394
1653
|
await transport.require("gitSync.branch", options);
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1654
|
+
const tracked = (await transport.requestParsed(RemoteSyncBranchProperty, PROBE_PATH, { ...options }))["remote-sync-branch"];
|
|
1655
|
+
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");
|
|
1656
|
+
return tracked;
|
|
1657
|
+
}
|
|
1658
|
+
/** The branch git-sync tracks, refused when none is configured or the caller may not read it. */
|
|
1659
|
+
async function trackedBranch(options = {}) {
|
|
1660
|
+
await transport.require("gitSync.trackedBranch", options);
|
|
1661
|
+
const tracked = await branch(options);
|
|
1662
|
+
if (tracked === null) throw new ConfigError("git-sync tracks no branch: the remote-sync-branch setting is unset");
|
|
1663
|
+
return tracked;
|
|
1401
1664
|
}
|
|
1402
1665
|
/**
|
|
1403
1666
|
* Poll the current task until it reaches a terminal status. A server with no task at all
|
|
@@ -1429,36 +1692,11 @@ function gitSyncResource(transport) {
|
|
|
1429
1692
|
syncedCollections,
|
|
1430
1693
|
remoteUrl,
|
|
1431
1694
|
branch,
|
|
1695
|
+
trackedBranch,
|
|
1432
1696
|
waitForTask
|
|
1433
1697
|
};
|
|
1434
1698
|
}
|
|
1435
1699
|
//#endregion
|
|
1436
|
-
//#region ../client/src/domain/glossary.ts
|
|
1437
|
-
const GlossaryCreator = z.object({
|
|
1438
|
-
id: z.number().int(),
|
|
1439
|
-
email: z.string(),
|
|
1440
|
-
first_name: z.string().nullable(),
|
|
1441
|
-
last_name: z.string().nullable()
|
|
1442
|
-
}).loose();
|
|
1443
|
-
const Glossary = z.object({
|
|
1444
|
-
id: z.number().int(),
|
|
1445
|
-
term: z.string(),
|
|
1446
|
-
definition: z.string(),
|
|
1447
|
-
creator_id: z.number().int(),
|
|
1448
|
-
creator: GlossaryCreator,
|
|
1449
|
-
created_at: z.string(),
|
|
1450
|
-
updated_at: z.string()
|
|
1451
|
-
}).loose();
|
|
1452
|
-
Glossary.pick({
|
|
1453
|
-
id: true,
|
|
1454
|
-
term: true,
|
|
1455
|
-
definition: true
|
|
1456
|
-
}).strip();
|
|
1457
|
-
z.object({
|
|
1458
|
-
term: z.string().min(1),
|
|
1459
|
-
definition: z.string().min(1)
|
|
1460
|
-
}).strict();
|
|
1461
|
-
//#endregion
|
|
1462
1700
|
//#region ../client/src/resources/glossary.ts
|
|
1463
1701
|
const GlossaryApiList = z.object({ data: z.array(Glossary) }).loose();
|
|
1464
1702
|
function glossaryResource(transport) {
|
|
@@ -1511,6 +1749,17 @@ function glossaryResource(transport) {
|
|
|
1511
1749
|
};
|
|
1512
1750
|
}
|
|
1513
1751
|
//#endregion
|
|
1752
|
+
//#region ../client/src/resources/request-body.ts
|
|
1753
|
+
/**
|
|
1754
|
+
* Parse the body a caller hands a method, refusing one that does not match `schema` before any
|
|
1755
|
+
* request. `what` names the body in the refusal.
|
|
1756
|
+
*/
|
|
1757
|
+
function parseRequestBody(schema, value, what) {
|
|
1758
|
+
const parsed = schema.safeParse(value);
|
|
1759
|
+
if (!parsed.success) throw new ConfigError(`invalid ${what}: ${parsed.error.issues.map(formatZodIssue).join("; ")}`);
|
|
1760
|
+
return parsed.data;
|
|
1761
|
+
}
|
|
1762
|
+
//#endregion
|
|
1514
1763
|
//#region ../client/src/resources/library.ts
|
|
1515
1764
|
const LIBRARY_ROOT_PATH = "/api/ee/library/";
|
|
1516
1765
|
const PUBLISH_TABLES_PATH = "/api/ee/data-studio/table/publish-tables";
|
|
@@ -1527,7 +1776,7 @@ function libraryResource(transport) {
|
|
|
1527
1776
|
return toLibrary(wire, await libraryListing(options));
|
|
1528
1777
|
}
|
|
1529
1778
|
async function libraryListing(options) {
|
|
1530
|
-
const data = await
|
|
1779
|
+
const data = await listCollectionsAs(transport, LibraryCollectionInfo, options);
|
|
1531
1780
|
const byId = /* @__PURE__ */ new Map();
|
|
1532
1781
|
for (const collection of data) if (typeof collection.id === "number") byId.set(collection.id, collection);
|
|
1533
1782
|
return byId;
|
|
@@ -1560,26 +1809,31 @@ function libraryResource(transport) {
|
|
|
1560
1809
|
}
|
|
1561
1810
|
/**
|
|
1562
1811
|
* Publish tables — and every upstream table they depend on — into a collection, so they lead the
|
|
1563
|
-
* data pickers and rank up in search. Answers the collection they landed in.
|
|
1812
|
+
* data pickers and rank up in search. Answers the collection they landed in. The endpoint
|
|
1813
|
+
* answers the same whether or not the selectors matched a table, so a body that does not match
|
|
1814
|
+
* `LibraryPublishTablesInput` is refused before any request.
|
|
1564
1815
|
*/
|
|
1565
1816
|
async function publishTables(params, options = {}) {
|
|
1566
1817
|
await transport.require("library.publishTables", options);
|
|
1818
|
+
const body = parseRequestBody(LibraryPublishTablesInput, params, "tables to publish");
|
|
1567
1819
|
return (await transport.requestParsed(PublishTablesResponse, PUBLISH_TABLES_PATH, {
|
|
1568
1820
|
...options,
|
|
1569
1821
|
method: "POST",
|
|
1570
|
-
body
|
|
1822
|
+
body
|
|
1571
1823
|
})).target_collection;
|
|
1572
1824
|
}
|
|
1573
1825
|
/**
|
|
1574
1826
|
* Clear the Library collection from tables, and recursively from every downstream table that
|
|
1575
|
-
* depends on them. The endpoint answers no JSON body
|
|
1827
|
+
* depends on them. The endpoint answers no JSON body, whether or not the selectors matched a
|
|
1828
|
+
* table, so selectors that do not match `TableSelectors` are refused before any request.
|
|
1576
1829
|
*/
|
|
1577
1830
|
async function unpublishTables(params, options = {}) {
|
|
1578
1831
|
await transport.require("library.unpublishTables", options);
|
|
1832
|
+
const body = parseRequestBody(TableSelectors, params, "table selectors");
|
|
1579
1833
|
await transport.requestRaw(UNPUBLISH_TABLES_PATH, {
|
|
1580
1834
|
...options,
|
|
1581
1835
|
method: "POST",
|
|
1582
|
-
body
|
|
1836
|
+
body,
|
|
1583
1837
|
expectContentType: "binary"
|
|
1584
1838
|
});
|
|
1585
1839
|
}
|
|
@@ -1832,48 +2086,21 @@ function metricResource(transport) {
|
|
|
1832
2086
|
};
|
|
1833
2087
|
}
|
|
1834
2088
|
//#endregion
|
|
1835
|
-
//#region ../client/src/domain/moderation-review.ts
|
|
1836
|
-
const ModeratedItemType = z.enum(["card", "dashboard"]);
|
|
1837
|
-
const ModerationStatus = z.enum(["verified"]);
|
|
1838
|
-
const ModerationReview = z.object({
|
|
1839
|
-
id: z.number().int(),
|
|
1840
|
-
moderated_item_id: z.number().int(),
|
|
1841
|
-
moderated_item_type: ModeratedItemType,
|
|
1842
|
-
moderator_id: z.number().int(),
|
|
1843
|
-
status: ModerationStatus.nullable(),
|
|
1844
|
-
text: z.string().nullable(),
|
|
1845
|
-
most_recent: z.boolean(),
|
|
1846
|
-
created_at: z.string(),
|
|
1847
|
-
updated_at: z.string()
|
|
1848
|
-
}).loose();
|
|
1849
|
-
ModerationReview.pick({
|
|
1850
|
-
id: true,
|
|
1851
|
-
moderated_item_id: true,
|
|
1852
|
-
moderated_item_type: true,
|
|
1853
|
-
status: true,
|
|
1854
|
-
text: true,
|
|
1855
|
-
most_recent: true
|
|
1856
|
-
}).strip();
|
|
1857
|
-
z.object({
|
|
1858
|
-
moderated_item_id: z.number().int().positive(),
|
|
1859
|
-
moderated_item_type: ModeratedItemType,
|
|
1860
|
-
status: ModerationStatus.nullable().optional(),
|
|
1861
|
-
text: z.string().nullable().optional()
|
|
1862
|
-
}).strict();
|
|
1863
|
-
//#endregion
|
|
1864
2089
|
//#region ../client/src/resources/moderation-review.ts
|
|
1865
2090
|
function moderationReviewResource(transport) {
|
|
1866
2091
|
/**
|
|
1867
2092
|
* Create a moderation review on a card or dashboard. `status: "verified"` marks the item
|
|
1868
2093
|
* verified, `null` or an absent status records a note that leaves it unverified. The new review
|
|
1869
2094
|
* becomes the item's most recent one; the server keeps the ten newest per item. Admins only.
|
|
2095
|
+
* A body that does not match `ModerationReviewCreateInput` is refused before any request.
|
|
1870
2096
|
*/
|
|
1871
2097
|
async function create(params, options = {}) {
|
|
1872
2098
|
await transport.require("moderationReview.create", options);
|
|
2099
|
+
const body = parseRequestBody(ModerationReviewCreateInput, params, "moderation review");
|
|
1873
2100
|
return transport.requestParsed(ModerationReview, "/api/moderation-review", {
|
|
1874
2101
|
...options,
|
|
1875
2102
|
method: "POST",
|
|
1876
|
-
body
|
|
2103
|
+
body
|
|
1877
2104
|
});
|
|
1878
2105
|
}
|
|
1879
2106
|
return { create };
|
|
@@ -2394,8 +2621,10 @@ function searchResource(transport) {
|
|
|
2394
2621
|
* may match, `archived` swaps the active set for the archived one, `table_db_id` restricts to
|
|
2395
2622
|
* items on one database, `verified` to verified content, `collection` to one collection and its
|
|
2396
2623
|
* descendants, `created_by` to items created by any of those users, `search_native_query` also
|
|
2397
|
-
* matches
|
|
2398
|
-
*
|
|
2624
|
+
* matches native query text but narrows to the models that carry one (cards, models, metrics,
|
|
2625
|
+
* actions, transforms), `include_metadata` attaches each card's `result_metadata`,
|
|
2626
|
+
* `include_dashboard_questions` also matches questions saved into a dashboard (excluded by
|
|
2627
|
+
* default), and `limit`/`offset` are the window the server applies before ranking hydration.
|
|
2399
2628
|
*/
|
|
2400
2629
|
async function query(params = {}, options = {}) {
|
|
2401
2630
|
await transport.require("search.query", options);
|
|
@@ -2412,7 +2641,8 @@ function searchResource(transport) {
|
|
|
2412
2641
|
collection: params.collection,
|
|
2413
2642
|
created_by: params.created_by,
|
|
2414
2643
|
search_native_query: params.search_native_query,
|
|
2415
|
-
include_metadata: params.include_metadata
|
|
2644
|
+
include_metadata: params.include_metadata,
|
|
2645
|
+
include_dashboard_questions: params.include_dashboard_questions
|
|
2416
2646
|
}
|
|
2417
2647
|
});
|
|
2418
2648
|
return {
|
|
@@ -2596,14 +2826,18 @@ function snippetResource(transport) {
|
|
|
2596
2826
|
const TableApiList = z.array(Table);
|
|
2597
2827
|
const TableApiFks = z.array(TableForeignKey);
|
|
2598
2828
|
const SyncSchemaResponse = z.object({ status: z.literal("ok") });
|
|
2599
|
-
const
|
|
2829
|
+
const FieldValuesResponse = z.object({ status: z.literal("success") });
|
|
2600
2830
|
const BulkEditResponse = z.object({});
|
|
2601
2831
|
const BULK_EDIT_PATH = "/api/data-studio/table/edit";
|
|
2832
|
+
const BULK_SYNC_SCHEMA_PATH = "/api/data-studio/table/sync-schema";
|
|
2833
|
+
const BULK_RESCAN_VALUES_PATH = "/api/data-studio/table/rescan-values";
|
|
2834
|
+
const BULK_DISCARD_VALUES_PATH = "/api/data-studio/table/discard-values";
|
|
2602
2835
|
const LIST_PARAM_FEATURES = [
|
|
2603
2836
|
["can-query", "tableListAccessFilters"],
|
|
2604
2837
|
["can-write", "tableListAccessFilters"],
|
|
2605
2838
|
["include-transform-targets", "tableListTransformTargets"],
|
|
2606
|
-
["unused-only", "tableUnusedFilter"]
|
|
2839
|
+
["unused-only", "tableUnusedFilter"],
|
|
2840
|
+
["published-only", "tableListPublishedFilter"]
|
|
2607
2841
|
];
|
|
2608
2842
|
function listParamFeatures(params) {
|
|
2609
2843
|
return LIST_PARAM_FEATURES.filter(([param]) => params[param] !== void 0).map(([, feature]) => feature);
|
|
@@ -2617,7 +2851,7 @@ function tableResource(transport) {
|
|
|
2617
2851
|
* List every table the caller can see, across all databases. `term` matches names and display
|
|
2618
2852
|
* names; `can-query` and `can-write` keep only the tables the caller may query or edit the
|
|
2619
2853
|
* metadata of; `include-transform-targets` adds tables a transform writes to; `unused-only` keeps
|
|
2620
|
-
* tables nothing depends on.
|
|
2854
|
+
* tables nothing depends on; `published-only` keeps tables published to the library.
|
|
2621
2855
|
*/
|
|
2622
2856
|
async function list(params = {}, options = {}) {
|
|
2623
2857
|
await transport.require("table.list", options);
|
|
@@ -2635,6 +2869,7 @@ function tableResource(transport) {
|
|
|
2635
2869
|
"owner-email": params["owner-email"],
|
|
2636
2870
|
"orphan-only": params["orphan-only"],
|
|
2637
2871
|
"unused-only": params["unused-only"],
|
|
2872
|
+
"published-only": params["published-only"],
|
|
2638
2873
|
"can-query": params["can-query"],
|
|
2639
2874
|
"can-write": params["can-write"],
|
|
2640
2875
|
"include-transform-targets": params["include-transform-targets"]
|
|
@@ -2684,38 +2919,107 @@ function tableResource(transport) {
|
|
|
2684
2919
|
};
|
|
2685
2920
|
}
|
|
2686
2921
|
/**
|
|
2687
|
-
* Trigger a
|
|
2688
|
-
*
|
|
2922
|
+
* Trigger a sync of this table: its columns, their fingerprints and its cached field values. It
|
|
2923
|
+
* never discovers new tables. The sync runs after the call returns; a warehouse the server cannot
|
|
2924
|
+
* connect to is a 422.
|
|
2689
2925
|
*/
|
|
2690
2926
|
async function syncSchema(id, options = {}) {
|
|
2691
2927
|
await transport.require("table.syncSchema", options);
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2928
|
+
return {
|
|
2929
|
+
id,
|
|
2930
|
+
status: (await transport.requestParsed(SyncSchemaResponse, `/api/table/${id}/sync_schema`, {
|
|
2931
|
+
...options,
|
|
2932
|
+
method: "POST"
|
|
2933
|
+
})).status
|
|
2934
|
+
};
|
|
2696
2935
|
}
|
|
2697
2936
|
/**
|
|
2698
|
-
* Trigger an update of the field values of every eligible field of this table
|
|
2699
|
-
* after the call returns.
|
|
2937
|
+
* Trigger an update of the field values of every eligible field of this table that already has a
|
|
2938
|
+
* cached set. The scan runs after the call returns.
|
|
2700
2939
|
*/
|
|
2701
2940
|
async function rescanValues(id, options = {}) {
|
|
2702
2941
|
await transport.require("table.rescanValues", options);
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2942
|
+
return {
|
|
2943
|
+
id,
|
|
2944
|
+
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/rescan_values`, {
|
|
2945
|
+
...options,
|
|
2946
|
+
method: "POST"
|
|
2947
|
+
})).status
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2950
|
+
/**
|
|
2951
|
+
* Discard the cached field values of every field of this table, custom display values included.
|
|
2952
|
+
* No scan recreates a discarded set; the server rebuilds it, without the display values, the next
|
|
2953
|
+
* time the field's values are read.
|
|
2954
|
+
*/
|
|
2955
|
+
async function discardValues(id, options = {}) {
|
|
2956
|
+
await transport.require("table.discardValues", options);
|
|
2957
|
+
return {
|
|
2958
|
+
id,
|
|
2959
|
+
status: (await transport.requestParsed(FieldValuesResponse, `/api/table/${id}/discard_values`, {
|
|
2960
|
+
...options,
|
|
2961
|
+
method: "POST"
|
|
2962
|
+
})).status
|
|
2963
|
+
};
|
|
2707
2964
|
}
|
|
2708
2965
|
/**
|
|
2709
2966
|
* Set the same metadata on every table the selectors pick out. A table leaving the `hidden` data
|
|
2710
|
-
* layer is re-synced after the call returns.
|
|
2967
|
+
* layer is re-synced after the call returns. The server answers the same whether or not the
|
|
2968
|
+
* selectors matched a table.
|
|
2711
2969
|
*/
|
|
2712
2970
|
async function bulkEdit(params, options = {}) {
|
|
2713
2971
|
await transport.require("table.bulkEdit", options);
|
|
2972
|
+
await requireDataAuthorityNull(params.data_authority, options);
|
|
2714
2973
|
await transport.requestParsed(BulkEditResponse, BULK_EDIT_PATH, {
|
|
2715
2974
|
...options,
|
|
2716
2975
|
method: "POST",
|
|
2717
2976
|
body: params
|
|
2718
2977
|
});
|
|
2978
|
+
return {
|
|
2979
|
+
accepted: true,
|
|
2980
|
+
...params
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
async function requireDataAuthorityNull(value, options) {
|
|
2984
|
+
if (value === null) await transport.requireFeatures(["tableUserValueWithdrawal"], options);
|
|
2985
|
+
}
|
|
2986
|
+
async function postSelection(path, selectors, options) {
|
|
2987
|
+
const body = parseRequestBody(TableSelectors, selectors, "table selectors");
|
|
2988
|
+
await transport.requestRaw(path, {
|
|
2989
|
+
...options,
|
|
2990
|
+
method: "POST",
|
|
2991
|
+
body,
|
|
2992
|
+
expectContentType: "binary"
|
|
2993
|
+
});
|
|
2994
|
+
return {
|
|
2995
|
+
accepted: true,
|
|
2996
|
+
...body
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Trigger a sync of every table the selectors pick out: its columns, their fingerprints and its
|
|
3001
|
+
* cached field values. Every database behind them must answer a connection test first, or the
|
|
3002
|
+
* call is a 422; the syncs run after it returns.
|
|
3003
|
+
*/
|
|
3004
|
+
async function bulkSyncSchema(selectors, options = {}) {
|
|
3005
|
+
await transport.require("table.bulkSyncSchema", options);
|
|
3006
|
+
return postSelection(BULK_SYNC_SCHEMA_PATH, selectors, options);
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* Trigger a field-values rescan of every table the selectors pick out, refreshing the sets that
|
|
3010
|
+
* are already cached. The scans run after the call returns.
|
|
3011
|
+
*/
|
|
3012
|
+
async function bulkRescanValues(selectors, options = {}) {
|
|
3013
|
+
await transport.require("table.bulkRescanValues", options);
|
|
3014
|
+
return postSelection(BULK_RESCAN_VALUES_PATH, selectors, options);
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Discard the cached field values of every table the selectors pick out, custom display values
|
|
3018
|
+
* included. No scan recreates a discarded set.
|
|
3019
|
+
*/
|
|
3020
|
+
async function bulkDiscardValues(selectors, options = {}) {
|
|
3021
|
+
await transport.require("table.bulkDiscardValues", options);
|
|
3022
|
+
return postSelection(BULK_DISCARD_VALUES_PATH, selectors, options);
|
|
2719
3023
|
}
|
|
2720
3024
|
async function updateFromCsv(id, action, file, options) {
|
|
2721
3025
|
await transport.requestRaw(`/api/table/${id}/${UPLOAD_UPDATE_PATHS[action]}`, {
|
|
@@ -2757,7 +3061,11 @@ function tableResource(transport) {
|
|
|
2757
3061
|
fks,
|
|
2758
3062
|
syncSchema,
|
|
2759
3063
|
rescanValues,
|
|
3064
|
+
discardValues,
|
|
2760
3065
|
bulkEdit,
|
|
3066
|
+
bulkSyncSchema,
|
|
3067
|
+
bulkRescanValues,
|
|
3068
|
+
bulkDiscardValues,
|
|
2761
3069
|
appendCsv,
|
|
2762
3070
|
replaceCsv
|
|
2763
3071
|
};
|
|
@@ -3584,6 +3892,167 @@ function transformTagResource(transport) {
|
|
|
3584
3892
|
};
|
|
3585
3893
|
}
|
|
3586
3894
|
//#endregion
|
|
3895
|
+
//#region ../client/src/domain/transform-test.ts
|
|
3896
|
+
const TransformTestTable = z.object({
|
|
3897
|
+
schema: z.string().min(1).nullable().optional(),
|
|
3898
|
+
name: z.string().min(1)
|
|
3899
|
+
}).loose();
|
|
3900
|
+
const TransformTestColumn = z.object({
|
|
3901
|
+
name: z.string().min(1),
|
|
3902
|
+
cast_type: z.string().min(1)
|
|
3903
|
+
}).loose();
|
|
3904
|
+
const TransformTestCell = z.union([
|
|
3905
|
+
z.boolean(),
|
|
3906
|
+
z.number(),
|
|
3907
|
+
z.string(),
|
|
3908
|
+
z.null()
|
|
3909
|
+
]);
|
|
3910
|
+
const TransformTestRow = z.record(z.string(), TransformTestCell);
|
|
3911
|
+
const TransformTestSqlInput = z.object({
|
|
3912
|
+
table: TransformTestTable,
|
|
3913
|
+
format: z.literal("sql"),
|
|
3914
|
+
sql: z.string().min(1)
|
|
3915
|
+
}).loose();
|
|
3916
|
+
const TransformTestRowsInput = z.object({
|
|
3917
|
+
table: TransformTestTable,
|
|
3918
|
+
format: z.literal("rows"),
|
|
3919
|
+
columns: z.array(TransformTestColumn).min(1),
|
|
3920
|
+
rows: z.array(TransformTestRow)
|
|
3921
|
+
}).loose();
|
|
3922
|
+
const TransformTestInput = z.discriminatedUnion("format", [TransformTestSqlInput, TransformTestRowsInput]);
|
|
3923
|
+
const TransformTestEqualsSqlExpectation = z.object({
|
|
3924
|
+
type: z.literal("equals"),
|
|
3925
|
+
name: z.string().min(1),
|
|
3926
|
+
format: z.literal("sql"),
|
|
3927
|
+
sql: z.string().min(1)
|
|
3928
|
+
}).loose();
|
|
3929
|
+
const TransformTestEqualsRowsExpectation = z.object({
|
|
3930
|
+
type: z.literal("equals"),
|
|
3931
|
+
name: z.string().min(1),
|
|
3932
|
+
format: z.literal("rows"),
|
|
3933
|
+
columns: z.array(TransformTestColumn).min(1),
|
|
3934
|
+
rows: z.array(TransformTestRow)
|
|
3935
|
+
}).loose();
|
|
3936
|
+
const TransformTestEqualsExpectation = z.discriminatedUnion("format", [TransformTestEqualsSqlExpectation, TransformTestEqualsRowsExpectation]);
|
|
3937
|
+
const TransformTestEmptyExpectation = z.object({
|
|
3938
|
+
type: z.literal("empty"),
|
|
3939
|
+
name: z.string().min(1),
|
|
3940
|
+
sql: z.string().min(1)
|
|
3941
|
+
}).loose();
|
|
3942
|
+
const TransformTestExpectation = z.discriminatedUnion("type", [TransformTestEqualsExpectation, TransformTestEmptyExpectation]);
|
|
3943
|
+
const TransformTest = z.object({
|
|
3944
|
+
id: z.number().int(),
|
|
3945
|
+
entity_id: z.string(),
|
|
3946
|
+
transform_id: z.number().int(),
|
|
3947
|
+
creator_id: z.number().int(),
|
|
3948
|
+
name: z.string(),
|
|
3949
|
+
description: z.string().nullable(),
|
|
3950
|
+
inputs: z.array(TransformTestInput),
|
|
3951
|
+
expectations: z.array(TransformTestExpectation),
|
|
3952
|
+
created_at: z.string(),
|
|
3953
|
+
updated_at: z.string()
|
|
3954
|
+
}).loose();
|
|
3955
|
+
TransformTest.pick({
|
|
3956
|
+
id: true,
|
|
3957
|
+
transform_id: true,
|
|
3958
|
+
name: true,
|
|
3959
|
+
description: true
|
|
3960
|
+
}).strip();
|
|
3961
|
+
z.object({
|
|
3962
|
+
transform_id: z.number().int().positive(),
|
|
3963
|
+
name: z.string().min(1),
|
|
3964
|
+
description: z.string().nullable().optional(),
|
|
3965
|
+
inputs: z.array(TransformTestInput),
|
|
3966
|
+
expectations: z.array(TransformTestExpectation)
|
|
3967
|
+
}).loose();
|
|
3968
|
+
z.object({
|
|
3969
|
+
transform_id: z.number().int().positive().optional(),
|
|
3970
|
+
name: z.string().min(1).optional(),
|
|
3971
|
+
description: z.string().nullable().optional(),
|
|
3972
|
+
inputs: z.array(TransformTestInput).optional(),
|
|
3973
|
+
expectations: z.array(TransformTestExpectation).optional()
|
|
3974
|
+
}).loose();
|
|
3975
|
+
const TransformTestResultColumn = z.object({
|
|
3976
|
+
name: z.string(),
|
|
3977
|
+
database_type: z.string()
|
|
3978
|
+
}).loose();
|
|
3979
|
+
const TransformTestExpectationError = z.object({
|
|
3980
|
+
type: z.string(),
|
|
3981
|
+
message: z.string()
|
|
3982
|
+
}).loose();
|
|
3983
|
+
const TransformTestCellMismatch = z.object({
|
|
3984
|
+
column: z.string(),
|
|
3985
|
+
expected: TransformTestCell,
|
|
3986
|
+
actual: TransformTestCell
|
|
3987
|
+
}).loose();
|
|
3988
|
+
const TransformTestRowCounts = z.object({
|
|
3989
|
+
actual: z.number().int(),
|
|
3990
|
+
expected: z.number().int()
|
|
3991
|
+
}).loose();
|
|
3992
|
+
const TransformTestEqualsFindings = z.object({
|
|
3993
|
+
name: z.string(),
|
|
3994
|
+
type: z.literal("equals"),
|
|
3995
|
+
status: z.enum(["passed", "failed"]),
|
|
3996
|
+
columns: z.array(TransformTestResultColumn),
|
|
3997
|
+
"row-counts": TransformTestRowCounts,
|
|
3998
|
+
"extra-rows": z.array(TransformTestRow),
|
|
3999
|
+
"missing-rows": z.array(TransformTestRow),
|
|
4000
|
+
"cell-mismatches": z.array(TransformTestCellMismatch),
|
|
4001
|
+
truncated: z.number().int()
|
|
4002
|
+
}).loose();
|
|
4003
|
+
const TransformTestEqualsError = z.object({
|
|
4004
|
+
name: z.string(),
|
|
4005
|
+
type: z.literal("equals"),
|
|
4006
|
+
status: z.literal("error"),
|
|
4007
|
+
error: TransformTestExpectationError
|
|
4008
|
+
}).loose();
|
|
4009
|
+
const TransformTestEqualsResult = z.discriminatedUnion("status", [TransformTestEqualsFindings, TransformTestEqualsError]);
|
|
4010
|
+
const TransformTestEmptyPassed = z.object({
|
|
4011
|
+
name: z.string(),
|
|
4012
|
+
type: z.literal("empty"),
|
|
4013
|
+
status: z.literal("passed")
|
|
4014
|
+
}).loose();
|
|
4015
|
+
const TransformTestEmptyFailed = z.object({
|
|
4016
|
+
name: z.string(),
|
|
4017
|
+
type: z.literal("empty"),
|
|
4018
|
+
status: z.literal("failed"),
|
|
4019
|
+
columns: z.array(TransformTestResultColumn),
|
|
4020
|
+
sample: z.array(TransformTestRow),
|
|
4021
|
+
truncated: z.number().int()
|
|
4022
|
+
}).loose();
|
|
4023
|
+
const TransformTestEmptyError = z.object({
|
|
4024
|
+
name: z.string(),
|
|
4025
|
+
type: z.literal("empty"),
|
|
4026
|
+
status: z.literal("error"),
|
|
4027
|
+
error: TransformTestExpectationError
|
|
4028
|
+
}).loose();
|
|
4029
|
+
const TransformTestEmptyResult = z.discriminatedUnion("status", [
|
|
4030
|
+
TransformTestEmptyPassed,
|
|
4031
|
+
TransformTestEmptyFailed,
|
|
4032
|
+
TransformTestEmptyError
|
|
4033
|
+
]);
|
|
4034
|
+
const TransformTestExpectationResult = z.discriminatedUnion("type", [TransformTestEqualsResult, TransformTestEmptyResult]);
|
|
4035
|
+
const TransformTestRunResult = z.object({
|
|
4036
|
+
status: z.enum(["passed", "failed"]),
|
|
4037
|
+
expectations: z.array(TransformTestExpectationResult),
|
|
4038
|
+
tables: z.record(z.string(), z.string())
|
|
4039
|
+
}).loose();
|
|
4040
|
+
z.enum([
|
|
4041
|
+
"transform-test.unknown-column",
|
|
4042
|
+
"transform-test.ambiguous-column",
|
|
4043
|
+
"transform-test.missing-inputs",
|
|
4044
|
+
"transform-test.unused-inputs",
|
|
4045
|
+
"transform-test.duplicate-input-table",
|
|
4046
|
+
"transform-test.unparseable-source",
|
|
4047
|
+
"transform-test.unremapped-reference",
|
|
4048
|
+
"transform-test.unsupported-transform",
|
|
4049
|
+
"transform-test.unsupported-driver",
|
|
4050
|
+
"transform-test.transform-failed",
|
|
4051
|
+
"transform-test.setup-failed",
|
|
4052
|
+
"transform-test.expectation-failed",
|
|
4053
|
+
"transform-test.unsupported-format"
|
|
4054
|
+
]);
|
|
4055
|
+
//#endregion
|
|
3587
4056
|
//#region ../client/src/resources/transform-test.ts
|
|
3588
4057
|
const TransformTestApiList = z.array(TransformTest);
|
|
3589
4058
|
function transformTestResource(transport) {
|