@metabase/client 0.0.0-stage → 0.1.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/CHANGELOG.md +7 -0
- package/LICENSE +661 -0
- package/README.md +488 -2
- package/dist/auth/credential.d.ts +2 -0
- package/dist/auth/credential.js +61 -0
- package/dist/auth/oauth-login.d.ts +2 -0
- package/dist/auth/oauth-login.js +2 -0
- package/dist/auth/oauth-session.d.ts +2 -0
- package/dist/auth/oauth-session.js +31 -0
- package/dist/card-B6merEUN.d.ts +1392 -0
- package/dist/card-BZx4NSr-.d.ts +33 -0
- package/dist/client-DNRvXFRm.d.ts +3276 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +95 -0
- package/dist/collection-Bib61WiV.d.ts +294 -0
- package/dist/collection-CqtGPi4W.d.ts +39 -0
- package/dist/content-translation-zfUkE_OG.d.ts +8 -0
- package/dist/credential-6aOEGqMH.d.ts +30 -0
- package/dist/cron-DThzerR6.d.ts +9 -0
- package/dist/csv-upload--K35QedP.d.ts +8 -0
- package/dist/dashboard-XTe46250.d.ts +38 -0
- package/dist/dashboard-z9i1VUZC.d.ts +970 -0
- package/dist/data-action-rS27f2Ji.d.ts +95 -0
- package/dist/database-BLUMlywE.d.ts +997 -0
- package/dist/database-DqTespfa.d.ts +27 -0
- package/dist/dataset-C6lX1-cr.d.ts +4144 -0
- package/dist/dataset-Cj3XuKLe.d.ts +23 -0
- package/dist/dependency-D8Qs5Xdx.d.ts +827 -0
- package/dist/dependency-fN5_7a22.d.ts +299 -0
- package/dist/document-oZNDbDfx.d.ts +115 -0
- package/dist/domain/card.d.ts +2 -0
- package/dist/domain/card.js +140 -0
- package/dist/domain/collection.d.ts +2 -0
- package/dist/domain/collection.js +137 -0
- package/dist/domain/content-translation.d.ts +2 -0
- package/dist/domain/content-translation.js +5 -0
- package/dist/domain/cron.d.ts +2 -0
- package/dist/domain/cron.js +5 -0
- package/dist/domain/dashboard.d.ts +2 -0
- package/dist/domain/dashboard.js +194 -0
- package/dist/domain/data-action.d.ts +2 -0
- package/dist/domain/data-action.js +85 -0
- package/dist/domain/database.d.ts +2 -0
- package/dist/domain/database.js +56 -0
- package/dist/domain/dataset.d.ts +2 -0
- package/dist/domain/dataset.js +56 -0
- package/dist/domain/dependency.d.ts +2 -0
- package/dist/domain/dependency.js +165 -0
- package/dist/domain/document.d.ts +2 -0
- package/dist/domain/document.js +94 -0
- package/dist/domain/eid-translation.d.ts +2 -0
- package/dist/domain/eid-translation.js +36 -0
- package/dist/domain/embedding.d.ts +2 -0
- package/dist/domain/embedding.js +9 -0
- package/dist/domain/erd.d.ts +2 -0
- package/dist/domain/erd.js +45 -0
- package/dist/domain/field.d.ts +2 -0
- package/dist/domain/field.js +268 -0
- package/dist/domain/git-sync.d.ts +2 -0
- package/dist/domain/git-sync.js +147 -0
- package/dist/domain/glossary.d.ts +2 -0
- package/dist/domain/glossary.js +30 -0
- package/dist/domain/library.d.ts +2 -0
- package/dist/domain/library.js +83 -0
- package/dist/domain/measure.d.ts +2 -0
- package/dist/domain/measure.js +41 -0
- package/dist/domain/metric.d.ts +2 -0
- package/dist/domain/metric.js +139 -0
- package/dist/domain/moderation-review.d.ts +2 -0
- package/dist/domain/moderation-review.js +31 -0
- package/dist/domain/notification.d.ts +2 -0
- package/dist/domain/notification.js +138 -0
- package/dist/domain/parameter.d.ts +2 -0
- package/dist/domain/parameter.js +109 -0
- package/dist/domain/permission.d.ts +2 -0
- package/dist/domain/permission.js +36 -0
- package/dist/domain/pulse.d.ts +2 -0
- package/dist/domain/pulse.js +133 -0
- package/dist/domain/query.d.ts +2 -0
- package/dist/domain/query.js +13 -0
- package/dist/domain/replacement.d.ts +2 -0
- package/dist/domain/replacement.js +93 -0
- package/dist/domain/revision.d.ts +2 -0
- package/dist/domain/revision.js +63 -0
- package/dist/domain/search.d.ts +2 -0
- package/dist/domain/search.js +63 -0
- package/dist/domain/segment.d.ts +2 -0
- package/dist/domain/segment.js +44 -0
- package/dist/domain/session-properties.d.ts +2 -0
- package/dist/domain/session-properties.js +14 -0
- package/dist/domain/setting.d.ts +2 -0
- package/dist/domain/setting.js +22 -0
- package/dist/domain/setup.d.ts +2 -0
- package/dist/domain/setup.js +21 -0
- package/dist/domain/snippet.d.ts +2 -0
- package/dist/domain/snippet.js +38 -0
- package/dist/domain/table.d.ts +2 -0
- package/dist/domain/table.js +156 -0
- package/dist/domain/text.d.ts +5 -0
- package/dist/domain/text.js +6 -0
- package/dist/domain/timeline.d.ts +2 -0
- package/dist/domain/timeline.js +97 -0
- package/dist/domain/transform-dag-run.d.ts +2 -0
- package/dist/domain/transform-dag-run.js +19 -0
- package/dist/domain/transform-inspector.d.ts +2 -0
- package/dist/domain/transform-inspector.js +142 -0
- package/dist/domain/transform-job.d.ts +2 -0
- package/dist/domain/transform-job.js +109 -0
- package/dist/domain/transform-python.d.ts +2 -0
- package/dist/domain/transform-python.js +39 -0
- package/dist/domain/transform-tag.d.ts +2 -0
- package/dist/domain/transform-tag.js +17 -0
- package/dist/domain/transform-test.d.ts +2 -0
- package/dist/domain/transform-test.js +176 -0
- package/dist/domain/transform.d.ts +2 -0
- package/dist/domain/transform.js +235 -0
- package/dist/domain/upload.d.ts +2 -0
- package/dist/domain/upload.js +13 -0
- package/dist/domain/user.d.ts +2 -0
- package/dist/domain/user.js +16 -0
- package/dist/eid-translation-nuhr8St2.d.ts +143 -0
- package/dist/embedding-Cz3RUJYf.d.ts +10 -0
- package/dist/erd-BdeFEeSy.d.ts +842 -0
- package/dist/erd-BujD227Y.d.ts +51 -0
- package/dist/errors-DdMlaXqq.js +330 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +189 -0
- package/dist/features-CnFkUCj2.d.ts +261 -0
- package/dist/field-BCRO19mk.d.ts +2547 -0
- package/dist/field-Bqn10Fq_.d.ts +50 -0
- package/dist/git-sync-DNXrNVCW.d.ts +258 -0
- package/dist/git-sync-gAMKsZgN.d.ts +205 -0
- package/dist/glossary-CHK3elPj.d.ts +35 -0
- package/dist/glossary-CqFlzW33.d.ts +15 -0
- package/dist/http/errors.d.ts +2 -0
- package/dist/http/errors.js +2 -0
- package/dist/http/oauth.d.ts +2 -0
- package/dist/http/oauth.js +201 -0
- package/dist/http/retry.d.ts +19 -0
- package/dist/http/retry.js +33 -0
- package/dist/http/transport.d.ts +2 -0
- package/dist/http/transport.js +2 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +62 -0
- package/dist/json-CZvjc3--.d.ts +19 -0
- package/dist/json-pointer.d.ts +4 -0
- package/dist/json-pointer.js +7 -0
- package/dist/json.d.ts +2 -0
- package/dist/json.js +44 -0
- package/dist/known-range-DTOBgTEC.d.ts +7 -0
- package/dist/library-Cx9h_RkU.d.ts +228 -0
- package/dist/list-BT_bBm9Z.d.ts +7 -0
- package/dist/list.d.ts +2 -0
- package/dist/list.js +1 -0
- package/dist/measure-CuMqPbQX.d.ts +108 -0
- package/dist/measure-KuXdjZYJ.d.ts +44 -0
- package/dist/metric-yvehaVCS.d.ts +2655 -0
- package/dist/moderation-review-B4RUTZ6_.d.ts +56 -0
- package/dist/network-error-5g4U1iul.js +62 -0
- package/dist/notification-BT2TDAd_.d.ts +379 -0
- package/dist/notification-BiegPoSD.d.ts +22 -0
- package/dist/oauth-C4ordeoJ.d.ts +59 -0
- package/dist/oauth-login-CJsT_3EE.d.ts +19 -0
- package/dist/oauth-login-D734Jvbq.js +179 -0
- package/dist/oauth-session-BlIlcd8Q.d.ts +6 -0
- package/dist/paginate-DZWvWvbm.d.ts +24 -0
- package/dist/paginate.d.ts +2 -0
- package/dist/paginate.js +49 -0
- package/dist/parameter-ak-J-IOj.d.ts +190 -0
- package/dist/permission-BnCQw5Kp.d.ts +149 -0
- package/dist/poll-CIwnee7h.d.ts +15 -0
- package/dist/poll.d.ts +2 -0
- package/dist/poll.js +47 -0
- package/dist/predicates.d.ts +10 -0
- package/dist/predicates.js +18 -0
- package/dist/pulse-CMc1Rwdj.d.ts +596 -0
- package/dist/pulse-dylocLd9.d.ts +18 -0
- package/dist/query-Xsin530u.d.ts +23 -0
- package/dist/refusal-B9aeHMc9.js +130 -0
- package/dist/replacement-CyvtbxGg.d.ts +2467 -0
- package/dist/replacement-S9U3gApY.d.ts +128 -0
- package/dist/requirement-check-CIA5IHC1.d.ts +17 -0
- package/dist/requirements-C0txASN8.d.ts +217 -0
- package/dist/resources/card.d.ts +2 -0
- package/dist/resources/card.js +103 -0
- package/dist/resources/collection.d.ts +2 -0
- package/dist/resources/collection.js +110 -0
- package/dist/resources/content-translation.d.ts +11 -0
- package/dist/resources/content-translation.js +27 -0
- package/dist/resources/csv-upload.d.ts +2 -0
- package/dist/resources/csv-upload.js +9 -0
- package/dist/resources/dashboard.d.ts +2 -0
- package/dist/resources/dashboard.js +218 -0
- package/dist/resources/data-action.d.ts +105 -0
- package/dist/resources/data-action.js +79 -0
- package/dist/resources/database.d.ts +2 -0
- package/dist/resources/database.js +103 -0
- package/dist/resources/dataset.d.ts +2 -0
- package/dist/resources/dataset.js +81 -0
- package/dist/resources/dependency.d.ts +2 -0
- package/dist/resources/dependency.js +150 -0
- package/dist/resources/document.d.ts +55 -0
- package/dist/resources/document.js +60 -0
- package/dist/resources/eid-translation.d.ts +8 -0
- package/dist/resources/eid-translation.js +19 -0
- package/dist/resources/erd.d.ts +2 -0
- package/dist/resources/erd.js +26 -0
- package/dist/resources/field.d.ts +2 -0
- package/dist/resources/field.js +98 -0
- package/dist/resources/git-sync.d.ts +2 -0
- package/dist/resources/git-sync.js +362 -0
- package/dist/resources/glossary.d.ts +2 -0
- package/dist/resources/glossary.js +51 -0
- package/dist/resources/library.d.ts +85 -0
- package/dist/resources/library.js +90 -0
- package/dist/resources/measure.d.ts +2 -0
- package/dist/resources/measure.js +57 -0
- package/dist/resources/metric.d.ts +2 -0
- package/dist/resources/metric.js +50 -0
- package/dist/resources/moderation-review.d.ts +23 -0
- package/dist/resources/moderation-review.js +24 -0
- package/dist/resources/notification.d.ts +2 -0
- package/dist/resources/notification.js +93 -0
- package/dist/resources/optional-parsed.d.ts +7 -0
- package/dist/resources/optional-parsed.js +14 -0
- package/dist/resources/permission.d.ts +10 -0
- package/dist/resources/permission.js +31 -0
- package/dist/resources/pivot-export.d.ts +10 -0
- package/dist/resources/pivot-export.js +16 -0
- package/dist/resources/pulse.d.ts +2 -0
- package/dist/resources/pulse.js +75 -0
- package/dist/resources/replacement.d.ts +2 -0
- package/dist/resources/replacement.js +82 -0
- package/dist/resources/request-body.d.ts +9 -0
- package/dist/resources/request-body.js +13 -0
- package/dist/resources/revision.d.ts +70 -0
- package/dist/resources/revision.js +51 -0
- package/dist/resources/search.d.ts +2 -0
- package/dist/resources/search.js +57 -0
- package/dist/resources/segment.d.ts +2 -0
- package/dist/resources/segment.js +55 -0
- package/dist/resources/setting.d.ts +11 -0
- package/dist/resources/setting.js +43 -0
- package/dist/resources/setup.d.ts +8 -0
- package/dist/resources/setup.js +18 -0
- package/dist/resources/snippet.d.ts +2 -0
- package/dist/resources/snippet.js +52 -0
- package/dist/resources/table.d.ts +2 -0
- package/dist/resources/table.js +275 -0
- package/dist/resources/timeline-event.d.ts +12 -0
- package/dist/resources/timeline-event.js +51 -0
- package/dist/resources/timeline.d.ts +2 -0
- package/dist/resources/timeline.js +82 -0
- package/dist/resources/transform-dag-run.d.ts +22 -0
- package/dist/resources/transform-dag-run.js +32 -0
- package/dist/resources/transform-inspector.d.ts +214 -0
- package/dist/resources/transform-inspector.js +47 -0
- package/dist/resources/transform-job.d.ts +2 -0
- package/dist/resources/transform-job.js +90 -0
- package/dist/resources/transform-python.d.ts +52 -0
- package/dist/resources/transform-python.js +56 -0
- package/dist/resources/transform-tag.d.ts +35 -0
- package/dist/resources/transform-tag.js +47 -0
- package/dist/resources/transform-test.d.ts +2 -0
- package/dist/resources/transform-test.js +75 -0
- package/dist/resources/transform.d.ts +2 -0
- package/dist/resources/transform.js +217 -0
- package/dist/resources/upload.d.ts +2 -0
- package/dist/resources/upload.js +48 -0
- package/dist/resources/user.d.ts +8 -0
- package/dist/resources/user.js +11 -0
- package/dist/revision-CQBPhohA.d.ts +104 -0
- package/dist/search-BxJipLN6.d.ts +26 -0
- package/dist/search-NZR6iRBs.d.ts +92 -0
- package/dist/segment-D2i34X9l.d.ts +16 -0
- package/dist/segment-pj3wklGe.d.ts +47 -0
- package/dist/session-properties-COtlxAcK.d.ts +15 -0
- package/dist/setting-DXuQGXGF.d.ts +25 -0
- package/dist/setup-Ez_U7GmM.d.ts +26 -0
- package/dist/signal.d.ts +8 -0
- package/dist/signal.js +25 -0
- package/dist/snippet-RnnWa4TL.d.ts +16 -0
- package/dist/snippet-hEV429Pv.d.ts +41 -0
- package/dist/table-B3-UaPHa.d.ts +3229 -0
- package/dist/table-B_lfuaHc.d.ts +176 -0
- package/dist/tag-qzjGGci8.d.ts +48 -0
- package/dist/timeline-C2IEcRlP.d.ts +21 -0
- package/dist/timeline-folD3mQ_.d.ts +218 -0
- package/dist/transform-CDurgE1D.d.ts +443 -0
- package/dist/transform-CXOT5kFe.d.ts +638 -0
- package/dist/transform-dag-run-BgZwv1yx.d.ts +49 -0
- package/dist/transform-inspector-BUYBt6--.d.ts +374 -0
- package/dist/transform-job-29P6BULe.d.ts +237 -0
- package/dist/transform-job-CTJImaOd.d.ts +96 -0
- package/dist/transform-python-DdDnW6yK.d.ts +56 -0
- package/dist/transform-tag-DVUIZTEM.d.ts +25 -0
- package/dist/transform-test-A-CJYT6B.d.ts +412 -0
- package/dist/transform-test-BwuyrHpT.d.ts +346 -0
- package/dist/transport-E0ZaPNWJ.d.ts +462 -0
- package/dist/transport-HP7cyo9N.js +397 -0
- package/dist/upload-B7PG3ePk.d.ts +12 -0
- package/dist/upload-UPfZFdbH.d.ts +22 -0
- package/dist/url-CI0pxHOc.d.ts +7 -0
- package/dist/url.d.ts +2 -0
- package/dist/url.js +34 -0
- package/dist/user-2r0OBoO6.d.ts +18 -0
- package/dist/version/capability-error.d.ts +2 -0
- package/dist/version/capability-error.js +42 -0
- package/dist/version/features.d.ts +2 -0
- package/dist/version/features.js +143 -0
- package/dist/version/known-range.d.ts +2 -0
- package/dist/version/known-range.js +7 -0
- package/dist/version/probe.d.ts +2 -0
- package/dist/version/probe.js +30 -0
- package/dist/version/profile.d.ts +2 -0
- package/dist/version/profile.js +64 -0
- package/dist/version/requirement-check.d.ts +2 -0
- package/dist/version/requirement-check.js +62 -0
- package/dist/version/requirements.d.ts +2 -0
- package/dist/version/requirements.js +219 -0
- package/dist/version/tag.d.ts +2 -0
- package/dist/version/tag.js +52 -0
- package/package.json +65 -4
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { SessionProperties } from "../domain/session-properties.js";
|
|
2
|
+
import { ConfigError } from "../errors.js";
|
|
3
|
+
import { a as HttpError } from "../errors-DdMlaXqq.js";
|
|
4
|
+
import { profileFromProperties } from "../version/probe.js";
|
|
5
|
+
import { Collection } from "../domain/collection.js";
|
|
6
|
+
import { listCollectionsAs } from "./collection.js";
|
|
7
|
+
import { t as explainer } from "../refusal-B9aeHMc9.js";
|
|
8
|
+
import { pollUntil } from "../poll.js";
|
|
9
|
+
import { fetchOptionalParsed } from "./optional-parsed.js";
|
|
10
|
+
import { RemoteSyncState, SyncBranchCreated, SyncDirtyItem, SyncExportPreflight, SyncRemoteChanges, SyncSettingsUpdateResult, SyncTask, isSyncTaskTerminal } from "../domain/git-sync.js";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
//#region src/resources/git-sync.ts
|
|
13
|
+
const SyncScopeCollection = Collection.pick({
|
|
14
|
+
id: true,
|
|
15
|
+
name: true,
|
|
16
|
+
is_remote_synced: true
|
|
17
|
+
}).strip();
|
|
18
|
+
const SyncDirtyFlag = z.object({ is_dirty: z.boolean() });
|
|
19
|
+
const SyncDirtyList = z.object({ dirty: z.array(SyncDirtyItem) });
|
|
20
|
+
const SyncBranchList = z.object({ items: z.array(z.string()) });
|
|
21
|
+
const SyncImportStarted = z.object({
|
|
22
|
+
status: z.literal("success"),
|
|
23
|
+
task_id: z.number().int().positive().nullable(),
|
|
24
|
+
message: z.string().nullable().optional()
|
|
25
|
+
});
|
|
26
|
+
const SyncExportStarted = z.object({
|
|
27
|
+
message: z.string(),
|
|
28
|
+
task_id: z.number().int().positive()
|
|
29
|
+
});
|
|
30
|
+
const SyncStashStarted = z.object({
|
|
31
|
+
status: z.literal("success"),
|
|
32
|
+
message: z.string(),
|
|
33
|
+
task_id: z.number().int().positive()
|
|
34
|
+
});
|
|
35
|
+
const RemoteSyncSetting = z.string().nullable();
|
|
36
|
+
const RemoteSyncBranchProperty = SessionProperties.extend({ "remote-sync-branch": z.string().nullable().optional() });
|
|
37
|
+
const RemoteSyncUrlProperty = SessionProperties.extend({ "remote-sync-url": z.string().nullable().optional() });
|
|
38
|
+
const BRANCH_UNREADABLE_MESSAGE = "the remote-sync-branch setting is not readable: only admins see it, and a server without remote sync has none";
|
|
39
|
+
const BRANCH_UNSET_MESSAGE = "git-sync tracks no branch: the remote-sync-branch setting is unset";
|
|
40
|
+
const RemoteSyncStateProperty = RemoteSyncBranchProperty.extend({
|
|
41
|
+
"remote-sync-enabled": z.boolean().optional(),
|
|
42
|
+
"remote-sync-type": RemoteSyncState.shape.sync_type.optional()
|
|
43
|
+
});
|
|
44
|
+
const REMOTE_SYNC_STATE_ABSENT_MESSAGE = "the remote-sync-enabled and remote-sync-type settings are absent: the server has no remote-sync module";
|
|
45
|
+
function isRemoteUnreadable(error) {
|
|
46
|
+
if (!(error instanceof HttpError)) return false;
|
|
47
|
+
return error.status === 403 || error.status === 404;
|
|
48
|
+
}
|
|
49
|
+
function gitSyncResource(transport) {
|
|
50
|
+
const { explain, refuse, refuseAfterReading } = explainer(transport, "gitSync");
|
|
51
|
+
/** Get the running or most recently finished sync task, or null when the server has none. */
|
|
52
|
+
async function currentTask(options = {}) {
|
|
53
|
+
return fetchOptionalParsed(transport, "/api/ee/remote-sync/current-task", SyncTask, options);
|
|
54
|
+
}
|
|
55
|
+
/** Request cancellation of the running sync task, and answer it in the state that left it. */
|
|
56
|
+
async function cancelTask(options = {}) {
|
|
57
|
+
return transport.requestParsed(SyncTask, "/api/ee/remote-sync/current-task/cancel", {
|
|
58
|
+
...options,
|
|
59
|
+
method: "POST"
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Whether Metabase holds content changes the remote has not been told about. */
|
|
63
|
+
async function isDirty(options = {}) {
|
|
64
|
+
return (await transport.requestParsed(SyncDirtyFlag, "/api/ee/remote-sync/is-dirty", { ...options })).is_dirty;
|
|
65
|
+
}
|
|
66
|
+
/** List the objects whose local state differs from the remote. */
|
|
67
|
+
async function dirty(options = {}) {
|
|
68
|
+
return {
|
|
69
|
+
data: (await transport.requestParsed(SyncDirtyList, "/api/ee/remote-sync/dirty", { ...options })).dirty,
|
|
70
|
+
total: null
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compare the tracked remote branch against what Metabase last imported. The server caches the
|
|
75
|
+
* comparison; `force-refresh` re-reads the remote instead.
|
|
76
|
+
*/
|
|
77
|
+
async function hasRemoteChanges(params = {}, options = {}) {
|
|
78
|
+
return transport.requestParsed(SyncRemoteChanges, "/api/ee/remote-sync/has-remote-changes", {
|
|
79
|
+
...options,
|
|
80
|
+
query: { "force-refresh": params["force-refresh"] }
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Import content from the remote into Metabase. The endpoint queues a task and returns at once;
|
|
85
|
+
* pass `wait` to poll that task until it reaches a terminal status. A server already up to date
|
|
86
|
+
* answers no task id, and there is then nothing to poll. A server that guards the tracked branch
|
|
87
|
+
* refuses with 409 when `expected_branch` disagrees with the setting; left out, it is the tracked
|
|
88
|
+
* branch as read just before the request, on every server with remote sync. A server that guards
|
|
89
|
+
* the branch also requires it, so there the request is refused before the wire when the setting
|
|
90
|
+
* is unset or hidden from the caller; a server without the guard ignores the assertion, and gets
|
|
91
|
+
* none when there is none to read. `merge` folds the remote's changes into local content by a
|
|
92
|
+
* three-way merge and keeps un-pushed local changes, where a plain import refuses on a dirty
|
|
93
|
+
* instance.
|
|
94
|
+
*/
|
|
95
|
+
async function importFromRemote(params = {}, options = {}) {
|
|
96
|
+
const expectedBranch = await expectedBranchFor(params.expected_branch, options);
|
|
97
|
+
await requireMerge(params.merge, options);
|
|
98
|
+
const started = await transport.requestParsed(SyncImportStarted, "/api/ee/remote-sync/import", {
|
|
99
|
+
...options,
|
|
100
|
+
method: "POST",
|
|
101
|
+
body: {
|
|
102
|
+
branch: params.branch,
|
|
103
|
+
force: params.force,
|
|
104
|
+
merge: params.merge,
|
|
105
|
+
expected_branch: expectedBranch
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const message = started.message ?? null;
|
|
109
|
+
if (params.wait === void 0 || started.task_id === null) return {
|
|
110
|
+
message,
|
|
111
|
+
task_id: started.task_id
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
message,
|
|
115
|
+
task_id: started.task_id,
|
|
116
|
+
final: await settle(params.wait, options)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async function expectedBranchFor(given, options) {
|
|
120
|
+
if (given !== void 0) return given;
|
|
121
|
+
return importAssertion(await readTrackedBranch(options));
|
|
122
|
+
}
|
|
123
|
+
async function exportBranchFor(given, options) {
|
|
124
|
+
if (given !== void 0) return given;
|
|
125
|
+
return exportBranch(await readTrackedBranch(options));
|
|
126
|
+
}
|
|
127
|
+
async function readTrackedBranch(options) {
|
|
128
|
+
const properties = await transport.probe(RemoteSyncBranchProperty, options);
|
|
129
|
+
return {
|
|
130
|
+
tracked: properties["remote-sync-branch"],
|
|
131
|
+
features: profileFromProperties(properties).features
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function requireMerge(merge, options) {
|
|
135
|
+
if (merge === true) await transport.requireFeatures(["remoteSyncMerge"], options);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Export Metabase's content to the remote. The endpoint queues a task and returns at once; pass
|
|
139
|
+
* `wait` to poll that task until it reaches a terminal status. A server that guards the tracked
|
|
140
|
+
* branch refuses with 409 when `branch` is not the tracked one; left out, it is the tracked
|
|
141
|
+
* branch as read just before the request, and the request is refused before the wire when the
|
|
142
|
+
* setting is unset or hidden from the caller. A server without the guard exports to a named
|
|
143
|
+
* `branch` and tracks it from then on; left out, it gets none and exports to its own. When the
|
|
144
|
+
* remote has moved past the last sync, a plain export ends in a `conflict` task where `merge` is
|
|
145
|
+
* available, and is refused with 400 before any task elsewhere; `merge` folds the remote's
|
|
146
|
+
* changes in by a three-way merge instead, and `force` overwrites them.
|
|
147
|
+
*/
|
|
148
|
+
async function exportToRemote(params = {}, options = {}) {
|
|
149
|
+
const branchName = await exportBranchFor(params.branch, options);
|
|
150
|
+
await requireMerge(params.merge, options);
|
|
151
|
+
const started = await transport.requestParsed(SyncExportStarted, "/api/ee/remote-sync/export", {
|
|
152
|
+
...options,
|
|
153
|
+
method: "POST",
|
|
154
|
+
body: {
|
|
155
|
+
branch: branchName,
|
|
156
|
+
message: params.message,
|
|
157
|
+
force: params.force,
|
|
158
|
+
merge: params.merge
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const accepted = {
|
|
162
|
+
message: started.message,
|
|
163
|
+
task_id: started.task_id,
|
|
164
|
+
branch: branchName ?? null
|
|
165
|
+
};
|
|
166
|
+
if (params.wait === void 0) return accepted;
|
|
167
|
+
return {
|
|
168
|
+
...accepted,
|
|
169
|
+
final: await settle(params.wait, options)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Preview what exporting to `branch` would do against the live remote, without writing: whether
|
|
174
|
+
* the remote has moved on, whether a merge would apply cleanly, which entities conflict, and what
|
|
175
|
+
* a force push would discard. `branch` must be the one git-sync tracks; the server answers 409
|
|
176
|
+
* otherwise.
|
|
177
|
+
*/
|
|
178
|
+
async function exportPreflight(params, options = {}) {
|
|
179
|
+
return transport.requestParsed(SyncExportPreflight, "/api/ee/remote-sync/export-preflight", {
|
|
180
|
+
...options,
|
|
181
|
+
query: { branch: params.branch }
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Export Metabase's current content to a branch the remote does not have yet. The endpoint
|
|
186
|
+
* queues a task and returns at once; pass `wait` to poll that task to a terminal status.
|
|
187
|
+
*/
|
|
188
|
+
async function stash(params, options = {}) {
|
|
189
|
+
const started = await transport.requestParsed(SyncStashStarted, "/api/ee/remote-sync/stash", {
|
|
190
|
+
...options,
|
|
191
|
+
method: "POST",
|
|
192
|
+
body: {
|
|
193
|
+
new_branch: params.new_branch,
|
|
194
|
+
message: params.message
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
if (params.wait === void 0) return {
|
|
198
|
+
status: started.status,
|
|
199
|
+
message: started.message,
|
|
200
|
+
task_id: started.task_id
|
|
201
|
+
};
|
|
202
|
+
return {
|
|
203
|
+
status: started.status,
|
|
204
|
+
message: started.message,
|
|
205
|
+
task_id: started.task_id,
|
|
206
|
+
final: await settle(params.wait, options)
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** List the branches the configured remote carries. */
|
|
210
|
+
async function branches(options = {}) {
|
|
211
|
+
return {
|
|
212
|
+
data: (await transport.requestParsed(SyncBranchList, "/api/ee/remote-sync/branches", { ...options })).items,
|
|
213
|
+
total: null
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** Create a branch on the remote and point git-sync at it. */
|
|
217
|
+
async function createBranch(params, options = {}) {
|
|
218
|
+
return transport.requestParsed(SyncBranchCreated, "/api/ee/remote-sync/create-branch", {
|
|
219
|
+
...options,
|
|
220
|
+
method: "POST",
|
|
221
|
+
body: { name: params.name }
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Mark one collection as git-synced, or unmark it. The server cascades the flag to descendants
|
|
226
|
+
* by location prefix, and may queue an import task to bring the instance in line with the
|
|
227
|
+
* remote. A server without `remoteSyncCollectionsOnlyUpdate` validates every settings update as
|
|
228
|
+
* a remote configuration and fails one naming no remote, after it has applied the flag, so it is
|
|
229
|
+
* sent the remote's URL and its token as the server shows it, which it checks against the remote
|
|
230
|
+
* and keeps as they are, or a blank URL when no remote is configured, which clears the remote
|
|
231
|
+
* settings that are already empty.
|
|
232
|
+
*/
|
|
233
|
+
async function setCollectionSynced(collectionId, synced, options = {}) {
|
|
234
|
+
return transport.requestParsed(SyncSettingsUpdateResult, "/api/ee/remote-sync/settings", {
|
|
235
|
+
...options,
|
|
236
|
+
method: "PUT",
|
|
237
|
+
body: await settingsUpdateBody({ [collectionId]: synced }, options)
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function settingsUpdateBody(collections, options) {
|
|
241
|
+
const { features } = await transport.server(options);
|
|
242
|
+
if (features.remoteSyncCollectionsOnlyUpdate) return { collections };
|
|
243
|
+
const url = (await transport.probe(RemoteSyncUrlProperty, options))["remote-sync-url"];
|
|
244
|
+
if (url === void 0) return { collections };
|
|
245
|
+
if (url === null) return {
|
|
246
|
+
collections,
|
|
247
|
+
"remote-sync-url": ""
|
|
248
|
+
};
|
|
249
|
+
const token = await fetchOptionalParsed(transport, "/api/setting/remote-sync-token", RemoteSyncSetting, options);
|
|
250
|
+
if (token === null) return {
|
|
251
|
+
collections,
|
|
252
|
+
"remote-sync-url": url
|
|
253
|
+
};
|
|
254
|
+
return {
|
|
255
|
+
collections,
|
|
256
|
+
"remote-sync-url": url,
|
|
257
|
+
"remote-sync-token": token
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/** The collections currently in git-sync's scope. */
|
|
261
|
+
async function syncedCollections(options = {}) {
|
|
262
|
+
return {
|
|
263
|
+
data: (await listCollectionsAs(transport, SyncScopeCollection, options)).filter((entry) => entry.is_remote_synced === true),
|
|
264
|
+
total: null
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/** The remote's URL, or null when none is configured or the caller may not read it. */
|
|
268
|
+
async function remoteUrl(options = {}) {
|
|
269
|
+
try {
|
|
270
|
+
const url = await fetchOptionalParsed(transport, "/api/setting/remote-sync-url", RemoteSyncSetting, options);
|
|
271
|
+
return url === "" ? null : url;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (isRemoteUnreadable(error)) return null;
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The branch git-sync tracks, or null when none is configured. Refuses when the server has no
|
|
279
|
+
* remote sync or the caller may not read the setting (it is admin-only), rather than reading
|
|
280
|
+
* either as unset.
|
|
281
|
+
*/
|
|
282
|
+
function branch(properties) {
|
|
283
|
+
const tracked = properties["remote-sync-branch"];
|
|
284
|
+
if (tracked === void 0) throw new ConfigError(BRANCH_UNREADABLE_MESSAGE);
|
|
285
|
+
return tracked;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Whether a remote is configured, through the settings or the environment, the direction the
|
|
289
|
+
* instance syncs in, which is `read-only` until an admin sets it, and the branch it tracks, all
|
|
290
|
+
* from one read. The remote's URL is visible to admins only and absent when the environment sets
|
|
291
|
+
* it, so `configured` is read from its own setting. Refused like `branch` when the caller may not
|
|
292
|
+
* read the branch, and on a server without the remote-sync module.
|
|
293
|
+
*/
|
|
294
|
+
function remote(properties) {
|
|
295
|
+
const tracked = branch(properties);
|
|
296
|
+
const configured = properties["remote-sync-enabled"];
|
|
297
|
+
const syncType = properties["remote-sync-type"];
|
|
298
|
+
if (configured === void 0 || syncType === void 0) throw new ConfigError(REMOTE_SYNC_STATE_ABSENT_MESSAGE);
|
|
299
|
+
return {
|
|
300
|
+
configured,
|
|
301
|
+
sync_type: syncType,
|
|
302
|
+
branch: tracked
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* The branch git-sync tracks, refused when none is configured, the server has no remote sync, or
|
|
307
|
+
* the caller may not read it.
|
|
308
|
+
*/
|
|
309
|
+
function trackedBranch(properties) {
|
|
310
|
+
const tracked = branch(properties);
|
|
311
|
+
if (tracked === null) throw new ConfigError(BRANCH_UNSET_MESSAGE);
|
|
312
|
+
return tracked;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Poll the current task until it reaches a terminal status. A server with no task at all
|
|
316
|
+
* answers null, which is terminal in the same sense: nothing further will happen.
|
|
317
|
+
*/
|
|
318
|
+
async function waitForTask(wait, options = {}) {
|
|
319
|
+
return settle(wait, options);
|
|
320
|
+
}
|
|
321
|
+
async function settle(wait, options) {
|
|
322
|
+
return pollUntil(async (signal) => currentTask({
|
|
323
|
+
...options,
|
|
324
|
+
signal
|
|
325
|
+
}), (task) => task === null || isSyncTaskTerminal(task.status), wait);
|
|
326
|
+
}
|
|
327
|
+
return {
|
|
328
|
+
currentTask: explain("currentTask", currentTask),
|
|
329
|
+
cancelTask: explain("cancelTask", cancelTask),
|
|
330
|
+
isDirty: explain("isDirty", isDirty),
|
|
331
|
+
dirty: explain("dirty", dirty),
|
|
332
|
+
hasRemoteChanges: explain("hasRemoteChanges", hasRemoteChanges),
|
|
333
|
+
import: explain("import", importFromRemote),
|
|
334
|
+
export: explain("export", exportToRemote),
|
|
335
|
+
exportPreflight: explain("exportPreflight", exportPreflight),
|
|
336
|
+
stash: explain("stash", stash),
|
|
337
|
+
branches: explain("branches", branches),
|
|
338
|
+
createBranch: explain("createBranch", createBranch),
|
|
339
|
+
setCollectionSynced: explain("setCollectionSynced", setCollectionSynced),
|
|
340
|
+
syncedCollections: refuse("syncedCollections", syncedCollections),
|
|
341
|
+
remoteUrl,
|
|
342
|
+
branch: refuseAfterReading("branch", RemoteSyncBranchProperty, branch),
|
|
343
|
+
remote: refuseAfterReading("remote", RemoteSyncStateProperty, remote),
|
|
344
|
+
trackedBranch: refuseAfterReading("trackedBranch", RemoteSyncBranchProperty, trackedBranch),
|
|
345
|
+
waitForTask: explain("waitForTask", waitForTask)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function importAssertion(read) {
|
|
349
|
+
if (!read.features.remoteSync) return;
|
|
350
|
+
if (read.features.remoteSyncBranchGuard) return requiredBranch(read.tracked);
|
|
351
|
+
return typeof read.tracked === "string" ? read.tracked : void 0;
|
|
352
|
+
}
|
|
353
|
+
function exportBranch(read) {
|
|
354
|
+
if (!read.features.remoteSync || !read.features.remoteSyncBranchGuard) return;
|
|
355
|
+
return requiredBranch(read.tracked);
|
|
356
|
+
}
|
|
357
|
+
function requiredBranch(tracked) {
|
|
358
|
+
if (typeof tracked === "string") return tracked;
|
|
359
|
+
throw new ConfigError(tracked === null ? BRANCH_UNSET_MESSAGE : BRANCH_UNREADABLE_MESSAGE);
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
export { SyncScopeCollection, gitSyncResource };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Glossary } from "../domain/glossary.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/resources/glossary.ts
|
|
4
|
+
const GlossaryApiList = z.object({ data: z.array(Glossary) }).loose();
|
|
5
|
+
function glossaryResource(transport) {
|
|
6
|
+
/**
|
|
7
|
+
* List glossary entries in term order. `search` keeps the entries whose term or definition
|
|
8
|
+
* contains it, case-insensitively.
|
|
9
|
+
*/
|
|
10
|
+
async function list(params = {}, options = {}) {
|
|
11
|
+
return {
|
|
12
|
+
data: (await transport.requestParsed(GlossaryApiList, "/api/glossary", {
|
|
13
|
+
...options,
|
|
14
|
+
query: { search: params.search }
|
|
15
|
+
})).data,
|
|
16
|
+
total: null
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Create a glossary entry, a term and its definition. Terms are unique. */
|
|
20
|
+
async function create(params, options = {}) {
|
|
21
|
+
return transport.requestParsed(Glossary, "/api/glossary", {
|
|
22
|
+
...options,
|
|
23
|
+
method: "POST",
|
|
24
|
+
body: params
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Replace the term and definition of a glossary entry by id. */
|
|
28
|
+
async function update(id, params, options = {}) {
|
|
29
|
+
return transport.requestParsed(Glossary, `/api/glossary/${id}`, {
|
|
30
|
+
...options,
|
|
31
|
+
method: "PUT",
|
|
32
|
+
body: params
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** Delete a glossary entry by id. */
|
|
36
|
+
async function remove(id, options = {}) {
|
|
37
|
+
await transport.requestRaw(`/api/glossary/${id}`, {
|
|
38
|
+
...options,
|
|
39
|
+
method: "DELETE",
|
|
40
|
+
expectContentType: "binary"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
list,
|
|
45
|
+
create,
|
|
46
|
+
update,
|
|
47
|
+
delete: remove
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { glossaryResource };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { f as RequestOptions, h as Transport } from "../transport-E0ZaPNWJ.js";
|
|
2
|
+
//#region src/resources/library.d.ts
|
|
3
|
+
declare function libraryResource(transport: Transport): {
|
|
4
|
+
get: (options?: RequestOptions | undefined) => Promise<{
|
|
5
|
+
[x: string]: unknown;
|
|
6
|
+
id: string | number;
|
|
7
|
+
name: string;
|
|
8
|
+
effective_children: {
|
|
9
|
+
id: string | number;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string | null;
|
|
12
|
+
type: "library" | "instance-analytics" | "trash" | "library-data" | "library-metrics" | "library-dashboards" | "tenant-specific-root-collection" | null;
|
|
13
|
+
is_remote_synced: boolean | null;
|
|
14
|
+
}[];
|
|
15
|
+
description?: string | null | undefined;
|
|
16
|
+
archived?: boolean | undefined;
|
|
17
|
+
location?: string | null | undefined;
|
|
18
|
+
parent_id?: string | number | null | undefined;
|
|
19
|
+
personal_owner_id?: number | null | undefined;
|
|
20
|
+
is_personal?: boolean | undefined;
|
|
21
|
+
authority_level?: "official" | null | undefined;
|
|
22
|
+
type?: "library" | "instance-analytics" | "trash" | "library-data" | "library-metrics" | "library-dashboards" | "tenant-specific-root-collection" | null | undefined;
|
|
23
|
+
namespace?: "transforms" | "snippets" | "analytics" | "shared-tenant-collection" | "tenant-specific" | "data-apps" | "data-actions" | null | undefined;
|
|
24
|
+
entity_id?: string | null | undefined;
|
|
25
|
+
slug?: string | undefined;
|
|
26
|
+
can_write?: boolean | undefined;
|
|
27
|
+
is_remote_synced?: boolean | null | undefined;
|
|
28
|
+
} | null>;
|
|
29
|
+
create: (options?: RequestOptions | undefined) => Promise<{
|
|
30
|
+
[x: string]: unknown;
|
|
31
|
+
id: string | number;
|
|
32
|
+
name: string;
|
|
33
|
+
effective_children: {
|
|
34
|
+
id: string | number;
|
|
35
|
+
name: string;
|
|
36
|
+
description: string | null;
|
|
37
|
+
type: "library" | "instance-analytics" | "trash" | "library-data" | "library-metrics" | "library-dashboards" | "tenant-specific-root-collection" | null;
|
|
38
|
+
is_remote_synced: boolean | null;
|
|
39
|
+
}[];
|
|
40
|
+
description?: string | null | undefined;
|
|
41
|
+
archived?: boolean | undefined;
|
|
42
|
+
location?: string | null | undefined;
|
|
43
|
+
parent_id?: string | number | null | undefined;
|
|
44
|
+
personal_owner_id?: number | null | undefined;
|
|
45
|
+
is_personal?: boolean | undefined;
|
|
46
|
+
authority_level?: "official" | null | undefined;
|
|
47
|
+
type?: "library" | "instance-analytics" | "trash" | "library-data" | "library-metrics" | "library-dashboards" | "tenant-specific-root-collection" | null | undefined;
|
|
48
|
+
namespace?: "transforms" | "snippets" | "analytics" | "shared-tenant-collection" | "tenant-specific" | "data-apps" | "data-actions" | null | undefined;
|
|
49
|
+
entity_id?: string | null | undefined;
|
|
50
|
+
slug?: string | undefined;
|
|
51
|
+
can_write?: boolean | undefined;
|
|
52
|
+
is_remote_synced?: boolean | null | undefined;
|
|
53
|
+
}>;
|
|
54
|
+
ensureDataCollectionId: (options?: RequestOptions | undefined) => Promise<number>;
|
|
55
|
+
publishTables: (params: {
|
|
56
|
+
collection_id: number;
|
|
57
|
+
database_ids?: number[] | undefined;
|
|
58
|
+
schema_ids?: string[] | undefined;
|
|
59
|
+
table_ids?: number[] | undefined;
|
|
60
|
+
}, options?: RequestOptions | undefined) => Promise<{
|
|
61
|
+
[x: string]: unknown;
|
|
62
|
+
id: string | number;
|
|
63
|
+
name: string;
|
|
64
|
+
description?: string | null | undefined;
|
|
65
|
+
archived?: boolean | undefined;
|
|
66
|
+
location?: string | null | undefined;
|
|
67
|
+
parent_id?: string | number | null | undefined;
|
|
68
|
+
personal_owner_id?: number | null | undefined;
|
|
69
|
+
is_personal?: boolean | undefined;
|
|
70
|
+
authority_level?: "official" | null | undefined;
|
|
71
|
+
type?: "library" | "instance-analytics" | "trash" | "library-data" | "library-metrics" | "library-dashboards" | "tenant-specific-root-collection" | null | undefined;
|
|
72
|
+
namespace?: "transforms" | "snippets" | "analytics" | "shared-tenant-collection" | "tenant-specific" | "data-apps" | "data-actions" | null | undefined;
|
|
73
|
+
entity_id?: string | null | undefined;
|
|
74
|
+
slug?: string | undefined;
|
|
75
|
+
can_write?: boolean | undefined;
|
|
76
|
+
is_remote_synced?: boolean | null | undefined;
|
|
77
|
+
} | null>;
|
|
78
|
+
unpublishTables: (params: {
|
|
79
|
+
database_ids?: number[] | undefined;
|
|
80
|
+
schema_ids?: string[] | undefined;
|
|
81
|
+
table_ids?: number[] | undefined;
|
|
82
|
+
}, options?: RequestOptions | undefined) => Promise<void>;
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { libraryResource };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { TableSelectors } from "../domain/table.js";
|
|
2
|
+
import { Collection } from "../domain/collection.js";
|
|
3
|
+
import { listCollectionsAs } from "./collection.js";
|
|
4
|
+
import { t as explainer } from "../refusal-B9aeHMc9.js";
|
|
5
|
+
import { LibraryCollectionInfo, LibraryPublishTablesInput, libraryWireSchema, toLibrary } from "../domain/library.js";
|
|
6
|
+
import { parseRequestBody } from "./request-body.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
//#region src/resources/library.ts
|
|
9
|
+
const LIBRARY_ROOT_PATH = "/api/ee/library/";
|
|
10
|
+
const PUBLISH_TABLES_PATH = "/api/ee/data-studio/table/publish-tables";
|
|
11
|
+
const UNPUBLISH_TABLES_PATH = "/api/ee/data-studio/table/unpublish-tables";
|
|
12
|
+
const LIBRARY_DATA_TYPE = "library-data";
|
|
13
|
+
const PublishTablesResponse = z.object({ target_collection: Collection.nullable() });
|
|
14
|
+
function libraryResource(transport) {
|
|
15
|
+
const { explain } = explainer(transport, "library");
|
|
16
|
+
/** Get the Library root and its child collections, or `null` on an instance that has none. */
|
|
17
|
+
async function get(options = {}) {
|
|
18
|
+
const { features } = await transport.server(options);
|
|
19
|
+
const wire = await transport.requestParsed(libraryWireSchema(features), LIBRARY_ROOT_PATH, { ...options });
|
|
20
|
+
if (wire === null) return null;
|
|
21
|
+
return toLibrary(wire, await libraryListing(options));
|
|
22
|
+
}
|
|
23
|
+
async function libraryListing(options) {
|
|
24
|
+
const data = await listCollectionsAs(transport, LibraryCollectionInfo, options);
|
|
25
|
+
const byId = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const collection of data) if (typeof collection.id === "number") byId.set(collection.id, collection);
|
|
27
|
+
return byId;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create the Library subtree (its Data and Metrics collections). Metabase takes no body, rejects
|
|
31
|
+
* a second create with a 400, and answers the bare root collection without its
|
|
32
|
+
* `effective_children` — so an existing Library short-circuits the call and a fresh one comes
|
|
33
|
+
* back from a refetch, which together make this idempotent.
|
|
34
|
+
*/
|
|
35
|
+
async function create(options = {}) {
|
|
36
|
+
const existing = await get(options);
|
|
37
|
+
if (existing !== null) return existing;
|
|
38
|
+
await transport.requestRaw(LIBRARY_ROOT_PATH, {
|
|
39
|
+
...options,
|
|
40
|
+
method: "POST"
|
|
41
|
+
});
|
|
42
|
+
const created = await get(options);
|
|
43
|
+
if (created === null) throw new Error(`Library was not created after POST ${LIBRARY_ROOT_PATH}`);
|
|
44
|
+
return created;
|
|
45
|
+
}
|
|
46
|
+
/** The id of the Library's Data collection, creating the Library first when it does not exist. */
|
|
47
|
+
async function ensureDataCollectionId(options = {}) {
|
|
48
|
+
const data = (await create(options)).effective_children.find((child) => child.type === LIBRARY_DATA_TYPE);
|
|
49
|
+
if (data === void 0) throw new Error("Library has no Data collection to publish into");
|
|
50
|
+
if (typeof data.id !== "number") throw new Error(`Library Data collection has a non-numeric id ${String(data.id)}`);
|
|
51
|
+
return data.id;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Publish tables — and every upstream table they depend on — into a collection, so they lead the
|
|
55
|
+
* data pickers and rank up in search. Answers the collection they landed in. The endpoint
|
|
56
|
+
* answers the same whether or not the selectors matched a table, so a body that does not match
|
|
57
|
+
* `LibraryPublishTablesInput` is refused before any request.
|
|
58
|
+
*/
|
|
59
|
+
async function publishTables(params, options = {}) {
|
|
60
|
+
const body = parseRequestBody(LibraryPublishTablesInput, params, "tables to publish");
|
|
61
|
+
return (await transport.requestParsed(PublishTablesResponse, PUBLISH_TABLES_PATH, {
|
|
62
|
+
...options,
|
|
63
|
+
method: "POST",
|
|
64
|
+
body
|
|
65
|
+
})).target_collection;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Clear the Library collection from tables, and recursively from every downstream table that
|
|
69
|
+
* depends on them. The endpoint answers no JSON body, whether or not the selectors matched a
|
|
70
|
+
* table, so selectors that do not match `TableSelectors` are refused before any request.
|
|
71
|
+
*/
|
|
72
|
+
async function unpublishTables(params, options = {}) {
|
|
73
|
+
const body = parseRequestBody(TableSelectors, params, "table selectors");
|
|
74
|
+
await transport.requestRaw(UNPUBLISH_TABLES_PATH, {
|
|
75
|
+
...options,
|
|
76
|
+
method: "POST",
|
|
77
|
+
body,
|
|
78
|
+
expectContentType: "binary"
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
get: explain("get", get),
|
|
83
|
+
create: explain("create", create),
|
|
84
|
+
ensureDataCollectionId: explain("ensureDataCollectionId", ensureDataCollectionId),
|
|
85
|
+
publishTables: explain("publishTables", publishTables),
|
|
86
|
+
unpublishTables: explain("unpublishTables", unpublishTables)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { libraryResource };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { t as explainer } from "../refusal-B9aeHMc9.js";
|
|
2
|
+
import { Measure } from "../domain/measure.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/resources/measure.ts
|
|
5
|
+
const MeasureApiList = z.array(Measure);
|
|
6
|
+
function measureResource(transport) {
|
|
7
|
+
const { explain } = explainer(transport, "measure");
|
|
8
|
+
/** List measures. */
|
|
9
|
+
async function list(options = {}) {
|
|
10
|
+
return {
|
|
11
|
+
data: await transport.requestParsed(MeasureApiList, "/api/measure", { ...options }),
|
|
12
|
+
total: null
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Get one measure by id. */
|
|
16
|
+
async function get(id, options = {}) {
|
|
17
|
+
return transport.requestParsed(Measure, `/api/measure/${id}`, { ...options });
|
|
18
|
+
}
|
|
19
|
+
/** Create a measure — a saved aggregation tied to a table — from a full measure body. */
|
|
20
|
+
async function create(params, options = {}) {
|
|
21
|
+
return transport.requestParsed(Measure, "/api/measure", {
|
|
22
|
+
...options,
|
|
23
|
+
method: "POST",
|
|
24
|
+
body: params
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Update a measure by id, patching only the fields the body carries. `revision_message` is
|
|
29
|
+
* required and lands in the audit log.
|
|
30
|
+
*/
|
|
31
|
+
async function update(id, params, options = {}) {
|
|
32
|
+
return transport.requestParsed(Measure, `/api/measure/${id}`, {
|
|
33
|
+
...options,
|
|
34
|
+
method: "PUT",
|
|
35
|
+
body: params
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Archive (soft-delete) a measure by id. Metabase models this as an update, not its own endpoint,
|
|
40
|
+
* so it carries the same required `revision_message`.
|
|
41
|
+
*/
|
|
42
|
+
async function archive(id, params, options = {}) {
|
|
43
|
+
return update(id, {
|
|
44
|
+
archived: true,
|
|
45
|
+
revision_message: params.revision_message
|
|
46
|
+
}, options);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
list: explain("list", list),
|
|
50
|
+
get: explain("get", get),
|
|
51
|
+
create: explain("create", create),
|
|
52
|
+
update: explain("update", update),
|
|
53
|
+
archive: explain("archive", archive)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { measureResource };
|