dataiku-sdk 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/bin/dss.js +2 -0
- package/dist/packages/types/src/index.d.ts +458 -0
- package/dist/packages/types/src/index.js +384 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +689 -0
- package/dist/src/client.d.ts +89 -0
- package/dist/src/client.js +301 -0
- package/dist/src/errors.d.ts +29 -0
- package/dist/src/errors.js +141 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +24 -0
- package/dist/src/resources/base.d.ts +7 -0
- package/dist/src/resources/base.js +12 -0
- package/dist/src/resources/code-envs.d.ts +8 -0
- package/dist/src/resources/code-envs.js +36 -0
- package/dist/src/resources/connections.d.ts +20 -0
- package/dist/src/resources/connections.js +77 -0
- package/dist/src/resources/datasets.d.ts +57 -0
- package/dist/src/resources/datasets.js +423 -0
- package/dist/src/resources/folders.d.ts +15 -0
- package/dist/src/resources/folders.js +58 -0
- package/dist/src/resources/jobs.d.ts +72 -0
- package/dist/src/resources/jobs.js +184 -0
- package/dist/src/resources/notebooks.d.ts +34 -0
- package/dist/src/resources/notebooks.js +75 -0
- package/dist/src/resources/projects.d.ts +38 -0
- package/dist/src/resources/projects.js +185 -0
- package/dist/src/resources/recipes.d.ts +35 -0
- package/dist/src/resources/recipes.js +281 -0
- package/dist/src/resources/scenarios.d.ts +26 -0
- package/dist/src/resources/scenarios.js +57 -0
- package/dist/src/resources/sql.d.ts +40 -0
- package/dist/src/resources/sql.js +40 -0
- package/dist/src/resources/variables.d.ts +10 -0
- package/dist/src/resources/variables.js +22 -0
- package/dist/src/schemas.d.ts +7 -0
- package/dist/src/schemas.js +6 -0
- package/dist/src/utils/deep-merge.d.ts +1 -0
- package/dist/src/utils/deep-merge.js +15 -0
- package/dist/src/utils/flow-map.d.ts +37 -0
- package/dist/src/utils/flow-map.js +296 -0
- package/dist/src/utils/pagination.d.ts +8 -0
- package/dist/src/utils/pagination.js +23 -0
- package/dist/src/utils/sanitize.d.ts +2 -0
- package/dist/src/utils/sanitize.js +16 -0
- package/package.json +47 -0
- package/packages/types/dist/index.d.ts +458 -0
- package/packages/types/dist/index.js +384 -0
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, } from "node:fs";
|
|
3
|
+
import { dirname, resolve, } from "node:path";
|
|
4
|
+
import { fileURLToPath, } from "node:url";
|
|
5
|
+
import { DataikuClient, } from "./client.js";
|
|
6
|
+
import { DataikuError, } from "./errors.js";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Utility helpers
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
function num(v) {
|
|
11
|
+
if (typeof v !== "string")
|
|
12
|
+
return undefined;
|
|
13
|
+
const n = Number(v);
|
|
14
|
+
return Number.isFinite(n) ? n : undefined;
|
|
15
|
+
}
|
|
16
|
+
function json(v) {
|
|
17
|
+
if (typeof v !== "string")
|
|
18
|
+
return undefined;
|
|
19
|
+
return JSON.parse(v);
|
|
20
|
+
}
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const positional = [];
|
|
23
|
+
const flags = {};
|
|
24
|
+
let i = 0;
|
|
25
|
+
while (i < argv.length) {
|
|
26
|
+
const arg = argv[i];
|
|
27
|
+
if (arg === "--") {
|
|
28
|
+
positional.push(...argv.slice(i + 1));
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
if (arg.startsWith("--")) {
|
|
32
|
+
const eqIdx = arg.indexOf("=");
|
|
33
|
+
if (eqIdx !== -1) {
|
|
34
|
+
flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const next = argv[i + 1];
|
|
38
|
+
if (next !== undefined && !next.startsWith("--")) {
|
|
39
|
+
flags[arg.slice(2)] = next;
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
flags[arg.slice(2)] = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
positional.push(arg);
|
|
49
|
+
}
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
52
|
+
return { positional, flags, };
|
|
53
|
+
}
|
|
54
|
+
const commands = {
|
|
55
|
+
project: {
|
|
56
|
+
list: {
|
|
57
|
+
handler: (c) => c.projects.list(),
|
|
58
|
+
usage: "dss project list",
|
|
59
|
+
},
|
|
60
|
+
get: {
|
|
61
|
+
handler: (c, _a, f) => c.projects.get(f["project-key"]),
|
|
62
|
+
usage: "dss project get [--project-key KEY]",
|
|
63
|
+
},
|
|
64
|
+
metadata: {
|
|
65
|
+
handler: (c, _a, f) => c.projects.metadata(f["project-key"]),
|
|
66
|
+
usage: "dss project metadata [--project-key KEY]",
|
|
67
|
+
},
|
|
68
|
+
flow: {
|
|
69
|
+
handler: (c, _a, f) => c.projects.flow(f["project-key"]),
|
|
70
|
+
usage: "dss project flow [--project-key KEY]",
|
|
71
|
+
},
|
|
72
|
+
map: {
|
|
73
|
+
handler: (c, _a, f) => c.projects.map({
|
|
74
|
+
maxNodes: num(f["max-nodes"]),
|
|
75
|
+
maxEdges: num(f["max-edges"]),
|
|
76
|
+
includeRaw: f["include-raw"] === true,
|
|
77
|
+
}),
|
|
78
|
+
usage: "dss project map [--max-nodes N] [--max-edges N] [--include-raw]",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
dataset: {
|
|
82
|
+
list: {
|
|
83
|
+
handler: (c, _a, f) => c.datasets.list(f["project-key"]),
|
|
84
|
+
usage: "dss dataset list [--project-key KEY]",
|
|
85
|
+
},
|
|
86
|
+
get: {
|
|
87
|
+
handler: (c, a, f) => {
|
|
88
|
+
requireArgs(a, 1, "dss dataset get <name>");
|
|
89
|
+
return c.datasets.get(a[0], f["project-key"]);
|
|
90
|
+
},
|
|
91
|
+
usage: "dss dataset get <name> [--project-key KEY]",
|
|
92
|
+
},
|
|
93
|
+
schema: {
|
|
94
|
+
handler: (c, a, f) => {
|
|
95
|
+
requireArgs(a, 1, "dss dataset schema <name>");
|
|
96
|
+
return c.datasets.schema(a[0], f["project-key"]);
|
|
97
|
+
},
|
|
98
|
+
usage: "dss dataset schema <name> [--project-key KEY]",
|
|
99
|
+
},
|
|
100
|
+
preview: {
|
|
101
|
+
handler: (c, a, f) => {
|
|
102
|
+
requireArgs(a, 1, "dss dataset preview <name>");
|
|
103
|
+
return c.datasets.preview(a[0], {
|
|
104
|
+
maxRows: num(f["max-rows"]),
|
|
105
|
+
projectKey: f["project-key"],
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
usage: "dss dataset preview <name> [--max-rows N] [--project-key KEY]",
|
|
109
|
+
},
|
|
110
|
+
metadata: {
|
|
111
|
+
handler: (c, a, f) => {
|
|
112
|
+
requireArgs(a, 1, "dss dataset metadata <name>");
|
|
113
|
+
return c.datasets.metadata(a[0], f["project-key"]);
|
|
114
|
+
},
|
|
115
|
+
usage: "dss dataset metadata <name> [--project-key KEY]",
|
|
116
|
+
},
|
|
117
|
+
download: {
|
|
118
|
+
handler: (c, a, f) => {
|
|
119
|
+
requireArgs(a, 1, "dss dataset download <name>");
|
|
120
|
+
return c.datasets.download(a[0], {
|
|
121
|
+
outputPath: f["output"],
|
|
122
|
+
projectKey: f["project-key"],
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
usage: "dss dataset download <name> [--output PATH] [--project-key KEY]",
|
|
126
|
+
},
|
|
127
|
+
create: {
|
|
128
|
+
handler: (c, _a, f) => c.datasets.create({
|
|
129
|
+
datasetName: f["name"],
|
|
130
|
+
connection: f["connection"],
|
|
131
|
+
dsType: f["type"],
|
|
132
|
+
projectKey: f["project-key"],
|
|
133
|
+
}),
|
|
134
|
+
usage: "dss dataset create --name NAME --connection CONN --type TYPE [--project-key KEY]",
|
|
135
|
+
},
|
|
136
|
+
delete: {
|
|
137
|
+
handler: (c, a, f) => {
|
|
138
|
+
requireArgs(a, 1, "dss dataset delete <name>");
|
|
139
|
+
return c.datasets.delete(a[0], f["project-key"]);
|
|
140
|
+
},
|
|
141
|
+
usage: "dss dataset delete <name> [--project-key KEY]",
|
|
142
|
+
},
|
|
143
|
+
update: {
|
|
144
|
+
handler: (c, a, f) => {
|
|
145
|
+
requireArgs(a, 1, "dss dataset update <name> --data '{...}'");
|
|
146
|
+
const data = json(f["data"]);
|
|
147
|
+
if (!data) {
|
|
148
|
+
throw new UsageError("--data is required. Usage: dss dataset update <name> --data '{...}'");
|
|
149
|
+
}
|
|
150
|
+
return c.datasets.update(a[0], data, f["project-key"]);
|
|
151
|
+
},
|
|
152
|
+
usage: "dss dataset update <name> --data '{...}' [--project-key KEY]",
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
recipe: {
|
|
156
|
+
list: {
|
|
157
|
+
handler: (c, _a, f) => c.recipes.list(f["project-key"]),
|
|
158
|
+
usage: "dss recipe list [--project-key KEY]",
|
|
159
|
+
},
|
|
160
|
+
get: {
|
|
161
|
+
handler: (c, a, f) => {
|
|
162
|
+
requireArgs(a, 1, "dss recipe get <name>");
|
|
163
|
+
return c.recipes.get(a[0], {
|
|
164
|
+
includePayload: f["include-payload"] === true,
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
usage: "dss recipe get <name> [--include-payload]",
|
|
168
|
+
},
|
|
169
|
+
delete: {
|
|
170
|
+
handler: (c, a, f) => {
|
|
171
|
+
requireArgs(a, 1, "dss recipe delete <name>");
|
|
172
|
+
return c.recipes.delete(a[0], f["project-key"]);
|
|
173
|
+
},
|
|
174
|
+
usage: "dss recipe delete <name> [--project-key KEY]",
|
|
175
|
+
},
|
|
176
|
+
download: {
|
|
177
|
+
handler: (c, a, f) => {
|
|
178
|
+
requireArgs(a, 1, "dss recipe download <name>");
|
|
179
|
+
return c.recipes.download(a[0], {
|
|
180
|
+
outputPath: f["output"],
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
usage: "dss recipe download <name> [--output PATH]",
|
|
184
|
+
},
|
|
185
|
+
create: {
|
|
186
|
+
handler: (c, _a, f) => {
|
|
187
|
+
const type = f["type"];
|
|
188
|
+
if (!type) {
|
|
189
|
+
throw new UsageError("--type is required. Usage: dss recipe create --type TYPE --input DS");
|
|
190
|
+
}
|
|
191
|
+
return c.recipes.create({
|
|
192
|
+
type,
|
|
193
|
+
name: f["name"],
|
|
194
|
+
inputDatasets: f["input"] ? [f["input"],] : undefined,
|
|
195
|
+
outputDataset: f["output"],
|
|
196
|
+
outputConnection: f["output-connection"],
|
|
197
|
+
projectKey: f["project-key"],
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
usage: "dss recipe create --type TYPE --input DS [--output DS] [--output-connection CONN] [--project-key KEY]",
|
|
201
|
+
},
|
|
202
|
+
update: {
|
|
203
|
+
handler: (c, a, f) => {
|
|
204
|
+
requireArgs(a, 1, "dss recipe update <name> --data '{...}'");
|
|
205
|
+
const data = json(f["data"]);
|
|
206
|
+
if (!data) {
|
|
207
|
+
throw new UsageError("--data is required. Usage: dss recipe update <name> --data '{...}'");
|
|
208
|
+
}
|
|
209
|
+
return c.recipes.update(a[0], data, f["project-key"]);
|
|
210
|
+
},
|
|
211
|
+
usage: "dss recipe update <name> --data '{...}' [--project-key KEY]",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
job: {
|
|
215
|
+
list: {
|
|
216
|
+
handler: (c, _a, f) => c.jobs.list(f["project-key"]),
|
|
217
|
+
usage: "dss job list [--project-key KEY]",
|
|
218
|
+
},
|
|
219
|
+
get: {
|
|
220
|
+
handler: (c, a, f) => {
|
|
221
|
+
requireArgs(a, 1, "dss job get <id>");
|
|
222
|
+
return c.jobs.get(a[0], f["project-key"]);
|
|
223
|
+
},
|
|
224
|
+
usage: "dss job get <id> [--project-key KEY]",
|
|
225
|
+
},
|
|
226
|
+
log: {
|
|
227
|
+
handler: (c, a, f) => {
|
|
228
|
+
requireArgs(a, 1, "dss job log <id>");
|
|
229
|
+
return c.jobs.log(a[0], {
|
|
230
|
+
activity: f["activity"],
|
|
231
|
+
maxLogLines: num(f["max-lines"]),
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
usage: "dss job log <id> [--activity NAME] [--max-lines N]",
|
|
235
|
+
},
|
|
236
|
+
build: {
|
|
237
|
+
handler: (c, a, f) => {
|
|
238
|
+
requireArgs(a, 1, "dss job build <dataset>");
|
|
239
|
+
return c.jobs.build(a[0], {
|
|
240
|
+
buildMode: f["build-mode"],
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
usage: "dss job build <dataset> [--build-mode MODE]",
|
|
244
|
+
},
|
|
245
|
+
"build-and-wait": {
|
|
246
|
+
handler: (c, a, f) => {
|
|
247
|
+
requireArgs(a, 1, "dss job build-and-wait <dataset>");
|
|
248
|
+
return c.jobs.buildAndWait(a[0], {
|
|
249
|
+
buildMode: f["build-mode"],
|
|
250
|
+
includeLogs: f["include-logs"] === true,
|
|
251
|
+
timeoutMs: num(f["timeout"]),
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
usage: "dss job build-and-wait <dataset> [--build-mode MODE] [--include-logs] [--timeout MS]",
|
|
255
|
+
},
|
|
256
|
+
wait: {
|
|
257
|
+
handler: (c, a, f) => {
|
|
258
|
+
requireArgs(a, 1, "dss job wait <id>");
|
|
259
|
+
return c.jobs.wait(a[0], {
|
|
260
|
+
includeLogs: f["include-logs"] === true,
|
|
261
|
+
timeoutMs: num(f["timeout"]),
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
usage: "dss job wait <id> [--include-logs] [--timeout MS]",
|
|
265
|
+
},
|
|
266
|
+
abort: {
|
|
267
|
+
handler: (c, a, f) => {
|
|
268
|
+
requireArgs(a, 1, "dss job abort <id>");
|
|
269
|
+
return c.jobs.abort(a[0], f["project-key"]);
|
|
270
|
+
},
|
|
271
|
+
usage: "dss job abort <id> [--project-key KEY]",
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
scenario: {
|
|
275
|
+
list: {
|
|
276
|
+
handler: (c, _a, f) => c.scenarios.list(f["project-key"]),
|
|
277
|
+
usage: "dss scenario list [--project-key KEY]",
|
|
278
|
+
},
|
|
279
|
+
get: {
|
|
280
|
+
handler: (c, a, _f) => {
|
|
281
|
+
requireArgs(a, 1, "dss scenario get <id>");
|
|
282
|
+
return c.scenarios.get(a[0]);
|
|
283
|
+
},
|
|
284
|
+
usage: "dss scenario get <id>",
|
|
285
|
+
},
|
|
286
|
+
run: {
|
|
287
|
+
handler: (c, a, f) => {
|
|
288
|
+
requireArgs(a, 1, "dss scenario run <id>");
|
|
289
|
+
return c.scenarios.run(a[0], f["project-key"]);
|
|
290
|
+
},
|
|
291
|
+
usage: "dss scenario run <id> [--project-key KEY]",
|
|
292
|
+
},
|
|
293
|
+
status: {
|
|
294
|
+
handler: (c, a, f) => {
|
|
295
|
+
requireArgs(a, 1, "dss scenario status <id>");
|
|
296
|
+
return c.scenarios.status(a[0], f["project-key"]);
|
|
297
|
+
},
|
|
298
|
+
usage: "dss scenario status <id> [--project-key KEY]",
|
|
299
|
+
},
|
|
300
|
+
delete: {
|
|
301
|
+
handler: (c, a, f) => {
|
|
302
|
+
requireArgs(a, 1, "dss scenario delete <id>");
|
|
303
|
+
return c.scenarios.delete(a[0], f["project-key"]);
|
|
304
|
+
},
|
|
305
|
+
usage: "dss scenario delete <id> [--project-key KEY]",
|
|
306
|
+
},
|
|
307
|
+
create: {
|
|
308
|
+
handler: (c, a, f) => {
|
|
309
|
+
requireArgs(a, 2, "dss scenario create <id> <name>");
|
|
310
|
+
return c.scenarios.create(a[0], a[1], {
|
|
311
|
+
scenarioType: f["type"],
|
|
312
|
+
projectKey: f["project-key"],
|
|
313
|
+
});
|
|
314
|
+
},
|
|
315
|
+
usage: "dss scenario create <id> <name> [--type step_based|custom_python] [--project-key KEY]",
|
|
316
|
+
},
|
|
317
|
+
update: {
|
|
318
|
+
handler: (c, a, f) => {
|
|
319
|
+
requireArgs(a, 1, "dss scenario update <id> --data '{...}'");
|
|
320
|
+
const data = json(f["data"]);
|
|
321
|
+
if (!data) {
|
|
322
|
+
throw new UsageError("--data is required. Usage: dss scenario update <id> --data '{...}'");
|
|
323
|
+
}
|
|
324
|
+
return c.scenarios.update(a[0], data, f["project-key"]);
|
|
325
|
+
},
|
|
326
|
+
usage: "dss scenario update <id> --data '{...}' [--project-key KEY]",
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
folder: {
|
|
330
|
+
list: {
|
|
331
|
+
handler: (c, _a, f) => c.folders.list(f["project-key"]),
|
|
332
|
+
usage: "dss folder list [--project-key KEY]",
|
|
333
|
+
},
|
|
334
|
+
get: {
|
|
335
|
+
handler: (c, a, f) => {
|
|
336
|
+
requireArgs(a, 1, "dss folder get <id>");
|
|
337
|
+
return c.folders.get(a[0], f["project-key"]);
|
|
338
|
+
},
|
|
339
|
+
usage: "dss folder get <id> [--project-key KEY]",
|
|
340
|
+
},
|
|
341
|
+
contents: {
|
|
342
|
+
handler: (c, a, _f) => {
|
|
343
|
+
requireArgs(a, 1, "dss folder contents <id>");
|
|
344
|
+
return c.folders.contents(a[0]);
|
|
345
|
+
},
|
|
346
|
+
usage: "dss folder contents <id>",
|
|
347
|
+
},
|
|
348
|
+
download: {
|
|
349
|
+
handler: (c, a, f) => {
|
|
350
|
+
requireArgs(a, 2, "dss folder download <id> <path>");
|
|
351
|
+
return c.folders.download(a[0], a[1], {
|
|
352
|
+
localPath: f["output"],
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
usage: "dss folder download <id> <path> [--output PATH]",
|
|
356
|
+
},
|
|
357
|
+
upload: {
|
|
358
|
+
handler: (c, a, f) => {
|
|
359
|
+
requireArgs(a, 3, "dss folder upload <id> <path> <localPath>");
|
|
360
|
+
return c.folders.upload(a[0], a[1], a[2], f["project-key"]);
|
|
361
|
+
},
|
|
362
|
+
usage: "dss folder upload <id> <path> <localPath> [--project-key KEY]",
|
|
363
|
+
},
|
|
364
|
+
"delete-file": {
|
|
365
|
+
handler: (c, a, f) => {
|
|
366
|
+
requireArgs(a, 2, "dss folder delete-file <id> <path>");
|
|
367
|
+
return c.folders.deleteFile(a[0], a[1], f["project-key"]);
|
|
368
|
+
},
|
|
369
|
+
usage: "dss folder delete-file <id> <path> [--project-key KEY]",
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
variable: {
|
|
373
|
+
get: {
|
|
374
|
+
handler: (c, _a, f) => c.variables.get(f["project-key"]),
|
|
375
|
+
usage: "dss variable get [--project-key KEY]",
|
|
376
|
+
},
|
|
377
|
+
set: {
|
|
378
|
+
handler: (c, _a, f) => c.variables.set({
|
|
379
|
+
standard: json(f["standard"]),
|
|
380
|
+
local: json(f["local"]),
|
|
381
|
+
}),
|
|
382
|
+
usage: 'dss variable set --standard \'{"k":"v"}\' --local \'{"k":"v"}\'',
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
connection: {
|
|
386
|
+
list: {
|
|
387
|
+
handler: (c) => c.connections.list(),
|
|
388
|
+
usage: "dss connection list",
|
|
389
|
+
},
|
|
390
|
+
infer: {
|
|
391
|
+
handler: (c, _a, f) => c.connections.infer({
|
|
392
|
+
mode: f["mode"],
|
|
393
|
+
}),
|
|
394
|
+
usage: "dss connection infer [--mode fast|rich]",
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
"code-env": {
|
|
398
|
+
list: {
|
|
399
|
+
handler: (c, _a, f) => c.codeEnvs.list({
|
|
400
|
+
envLang: f["lang"],
|
|
401
|
+
}),
|
|
402
|
+
usage: "dss code-env list [--lang LANG]",
|
|
403
|
+
},
|
|
404
|
+
get: {
|
|
405
|
+
handler: (c, a) => {
|
|
406
|
+
requireArgs(a, 2, "dss code-env get <lang> <name>");
|
|
407
|
+
return c.codeEnvs.get(a[0], a[1]);
|
|
408
|
+
},
|
|
409
|
+
usage: "dss code-env get <lang> <name>",
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
sql: {
|
|
413
|
+
query: {
|
|
414
|
+
handler: (c, _a, f) => {
|
|
415
|
+
const query = f["sql"];
|
|
416
|
+
if (!query)
|
|
417
|
+
throw new UsageError("--sql is required. Usage: dss sql query --sql 'SELECT ...'");
|
|
418
|
+
return c.sql.query({
|
|
419
|
+
query,
|
|
420
|
+
connection: f["connection"],
|
|
421
|
+
datasetFullName: f["dataset"],
|
|
422
|
+
database: f["database"],
|
|
423
|
+
});
|
|
424
|
+
},
|
|
425
|
+
usage: "dss sql query --sql 'SELECT ...' [--connection CONN] [--dataset FULL_NAME] [--database DB]",
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
notebook: {
|
|
429
|
+
"list-jupyter": {
|
|
430
|
+
handler: (c, _a, f) => c.notebooks.listJupyter(f["project-key"]),
|
|
431
|
+
usage: "dss notebook list-jupyter [--project-key KEY]",
|
|
432
|
+
},
|
|
433
|
+
"get-jupyter": {
|
|
434
|
+
handler: (c, a, _f) => {
|
|
435
|
+
requireArgs(a, 1, "dss notebook get-jupyter <name>");
|
|
436
|
+
return c.notebooks.getJupyter(a[0]);
|
|
437
|
+
},
|
|
438
|
+
usage: "dss notebook get-jupyter <name>",
|
|
439
|
+
},
|
|
440
|
+
"delete-jupyter": {
|
|
441
|
+
handler: (c, a, _f) => {
|
|
442
|
+
requireArgs(a, 1, "dss notebook delete-jupyter <name>");
|
|
443
|
+
return c.notebooks.deleteJupyter(a[0]);
|
|
444
|
+
},
|
|
445
|
+
usage: "dss notebook delete-jupyter <name>",
|
|
446
|
+
},
|
|
447
|
+
"clear-jupyter-outputs": {
|
|
448
|
+
handler: (c, a, _f) => {
|
|
449
|
+
requireArgs(a, 1, "dss notebook clear-jupyter-outputs <name>");
|
|
450
|
+
return c.notebooks.clearJupyterOutputs(a[0]);
|
|
451
|
+
},
|
|
452
|
+
usage: "dss notebook clear-jupyter-outputs <name>",
|
|
453
|
+
},
|
|
454
|
+
"sessions-jupyter": {
|
|
455
|
+
handler: (c, a, _f) => {
|
|
456
|
+
requireArgs(a, 1, "dss notebook sessions-jupyter <name>");
|
|
457
|
+
return c.notebooks.listJupyterSessions(a[0]);
|
|
458
|
+
},
|
|
459
|
+
usage: "dss notebook sessions-jupyter <name>",
|
|
460
|
+
},
|
|
461
|
+
"unload-jupyter": {
|
|
462
|
+
handler: (c, a, _f) => {
|
|
463
|
+
requireArgs(a, 2, "dss notebook unload-jupyter <name> <sessionId>");
|
|
464
|
+
return c.notebooks.unloadJupyter(a[0], a[1]);
|
|
465
|
+
},
|
|
466
|
+
usage: "dss notebook unload-jupyter <name> <sessionId>",
|
|
467
|
+
},
|
|
468
|
+
"list-sql": {
|
|
469
|
+
handler: (c, _a, f) => c.notebooks.listSql(f["project-key"]),
|
|
470
|
+
usage: "dss notebook list-sql [--project-key KEY]",
|
|
471
|
+
},
|
|
472
|
+
"get-sql": {
|
|
473
|
+
handler: (c, a, _f) => {
|
|
474
|
+
requireArgs(a, 1, "dss notebook get-sql <id>");
|
|
475
|
+
return c.notebooks.getSql(a[0]);
|
|
476
|
+
},
|
|
477
|
+
usage: "dss notebook get-sql <id>",
|
|
478
|
+
},
|
|
479
|
+
"delete-sql": {
|
|
480
|
+
handler: (c, a, _f) => {
|
|
481
|
+
requireArgs(a, 1, "dss notebook delete-sql <id>");
|
|
482
|
+
return c.notebooks.deleteSql(a[0]);
|
|
483
|
+
},
|
|
484
|
+
usage: "dss notebook delete-sql <id>",
|
|
485
|
+
},
|
|
486
|
+
"history-sql": {
|
|
487
|
+
handler: (c, a, _f) => {
|
|
488
|
+
requireArgs(a, 1, "dss notebook history-sql <id>");
|
|
489
|
+
return c.notebooks.getSqlHistory(a[0]);
|
|
490
|
+
},
|
|
491
|
+
usage: "dss notebook history-sql <id>",
|
|
492
|
+
},
|
|
493
|
+
"save-jupyter": {
|
|
494
|
+
handler: (c, a, f) => {
|
|
495
|
+
requireArgs(a, 1, "dss notebook save-jupyter <name> --data '{...}'");
|
|
496
|
+
const data = json(f["data"]);
|
|
497
|
+
if (!data)
|
|
498
|
+
throw new UsageError("--data is required (notebook JSON content)");
|
|
499
|
+
return c.notebooks.saveJupyter(a[0], data, f["project-key"]);
|
|
500
|
+
},
|
|
501
|
+
usage: "dss notebook save-jupyter <name> --data '{...}' [--project-key KEY]",
|
|
502
|
+
},
|
|
503
|
+
"save-sql": {
|
|
504
|
+
handler: (c, a, f) => {
|
|
505
|
+
requireArgs(a, 1, "dss notebook save-sql <id> --data '{...}'");
|
|
506
|
+
const data = json(f["data"]);
|
|
507
|
+
if (!data)
|
|
508
|
+
throw new UsageError("--data is required (SQL notebook content JSON)");
|
|
509
|
+
return c.notebooks.saveSql(a[0], data, f["project-key"]);
|
|
510
|
+
},
|
|
511
|
+
usage: "dss notebook save-sql <id> --data '{...}' [--project-key KEY]",
|
|
512
|
+
},
|
|
513
|
+
"clear-sql-history": {
|
|
514
|
+
handler: (c, a, f) => {
|
|
515
|
+
requireArgs(a, 1, "dss notebook clear-sql-history <id>");
|
|
516
|
+
return c.notebooks.clearSqlHistory(a[0], {
|
|
517
|
+
cellId: f["cell-id"],
|
|
518
|
+
numRunsToRetain: num(f["retain"]),
|
|
519
|
+
projectKey: f["project-key"],
|
|
520
|
+
});
|
|
521
|
+
},
|
|
522
|
+
usage: "dss notebook clear-sql-history <id> [--cell-id CID] [--retain N] [--project-key KEY]",
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
// ---------------------------------------------------------------------------
|
|
527
|
+
// Help
|
|
528
|
+
// ---------------------------------------------------------------------------
|
|
529
|
+
const RESOURCE_NAMES = Object.keys(commands).sort();
|
|
530
|
+
function printTopLevelHelp() {
|
|
531
|
+
const lines = [
|
|
532
|
+
"Usage: dss <resource> <action> [args...] [--flags]",
|
|
533
|
+
"",
|
|
534
|
+
"Global flags:",
|
|
535
|
+
" --url URL Dataiku DSS base URL (env: DATAIKU_URL)",
|
|
536
|
+
" --api-key KEY API key (env: DATAIKU_API_KEY)",
|
|
537
|
+
" --project-key KEY Default project key (env: DATAIKU_PROJECT_KEY)",
|
|
538
|
+
" --help Show help",
|
|
539
|
+
"",
|
|
540
|
+
"Resources:",
|
|
541
|
+
...RESOURCE_NAMES.map((r) => ` ${r}`),
|
|
542
|
+
];
|
|
543
|
+
process.stderr.write(`${lines.join("\n")}\n`);
|
|
544
|
+
}
|
|
545
|
+
function printResourceHelp(resource) {
|
|
546
|
+
const actions = commands[resource];
|
|
547
|
+
if (!actions)
|
|
548
|
+
return;
|
|
549
|
+
const lines = [
|
|
550
|
+
`Usage: dss ${resource} <action> [args...] [--flags]`,
|
|
551
|
+
"",
|
|
552
|
+
"Actions:",
|
|
553
|
+
...Object.entries(actions).map(([name, meta,]) => ` ${name} → ${meta.usage}`),
|
|
554
|
+
];
|
|
555
|
+
process.stderr.write(`${lines.join("\n")}\n`);
|
|
556
|
+
}
|
|
557
|
+
function printActionHelp(resource, action) {
|
|
558
|
+
const meta = commands[resource]?.[action];
|
|
559
|
+
if (!meta)
|
|
560
|
+
return;
|
|
561
|
+
process.stderr.write(`Usage: ${meta.usage}\n`);
|
|
562
|
+
}
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
// Validation
|
|
565
|
+
// ---------------------------------------------------------------------------
|
|
566
|
+
class UsageError extends Error {
|
|
567
|
+
constructor(message) {
|
|
568
|
+
super(message);
|
|
569
|
+
this.name = "UsageError";
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function requireArgs(args, count, usage) {
|
|
573
|
+
if (args.length < count) {
|
|
574
|
+
throw new UsageError(`Expected ${count} argument(s), got ${args.length}.\nUsage: ${usage}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// ---------------------------------------------------------------------------
|
|
578
|
+
// .env auto-loading
|
|
579
|
+
// ---------------------------------------------------------------------------
|
|
580
|
+
function loadEnvFile() {
|
|
581
|
+
const dirs = [
|
|
582
|
+
resolve(dirname(fileURLToPath(import.meta.url)), ".."),
|
|
583
|
+
process.cwd(),
|
|
584
|
+
];
|
|
585
|
+
for (const dir of dirs) {
|
|
586
|
+
try {
|
|
587
|
+
const content = readFileSync(resolve(dir, ".env"), "utf-8");
|
|
588
|
+
for (const line of content.split("\n")) {
|
|
589
|
+
const trimmed = line.trim();
|
|
590
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
591
|
+
continue;
|
|
592
|
+
const eq = trimmed.indexOf("=");
|
|
593
|
+
if (eq === -1)
|
|
594
|
+
continue;
|
|
595
|
+
const key = trimmed.slice(0, eq).trim();
|
|
596
|
+
const val = trimmed.slice(eq + 1).trim().replace(/^['"]|['"]$/g, "");
|
|
597
|
+
if (!process.env[key])
|
|
598
|
+
process.env[key] = val;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
catch {
|
|
602
|
+
// no .env file — fine
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// ---------------------------------------------------------------------------
|
|
607
|
+
// Main
|
|
608
|
+
// ---------------------------------------------------------------------------
|
|
609
|
+
async function main() {
|
|
610
|
+
loadEnvFile();
|
|
611
|
+
const { positional, flags, } = parseArgs(process.argv.slice(2));
|
|
612
|
+
// Top-level help
|
|
613
|
+
if (positional.length === 0 || (positional.length === 0 && flags["help"])) {
|
|
614
|
+
printTopLevelHelp();
|
|
615
|
+
if (flags["help"])
|
|
616
|
+
process.exit(0);
|
|
617
|
+
process.exit(1);
|
|
618
|
+
}
|
|
619
|
+
const resource = positional[0];
|
|
620
|
+
// Unknown resource
|
|
621
|
+
if (!commands[resource]) {
|
|
622
|
+
if (flags["help"]) {
|
|
623
|
+
printTopLevelHelp();
|
|
624
|
+
process.exit(0);
|
|
625
|
+
}
|
|
626
|
+
process.stderr.write(`Unknown resource: ${resource}\nAvailable: ${RESOURCE_NAMES.join(", ")}\n`);
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
// Resource-level help
|
|
630
|
+
if (positional.length === 1 || flags["help"] === true) {
|
|
631
|
+
if (positional.length === 1) {
|
|
632
|
+
printResourceHelp(resource);
|
|
633
|
+
if (flags["help"])
|
|
634
|
+
process.exit(0);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const action = positional[1];
|
|
639
|
+
const actionMeta = commands[resource][action];
|
|
640
|
+
// Unknown action
|
|
641
|
+
if (!actionMeta) {
|
|
642
|
+
process.stderr.write(`Unknown action: ${resource} ${action}\nAvailable actions for ${resource}: ${Object.keys(commands[resource]).join(", ")}\n`);
|
|
643
|
+
process.exit(1);
|
|
644
|
+
}
|
|
645
|
+
// Action-level help
|
|
646
|
+
if (flags["help"] === true) {
|
|
647
|
+
printActionHelp(resource, action);
|
|
648
|
+
process.exit(0);
|
|
649
|
+
}
|
|
650
|
+
// Validate config
|
|
651
|
+
const url = flags["url"] ?? process.env.DATAIKU_URL ?? "";
|
|
652
|
+
const apiKey = flags["api-key"] ?? process.env.DATAIKU_API_KEY ?? "";
|
|
653
|
+
if (!url) {
|
|
654
|
+
process.stderr.write(`${JSON.stringify({ error: "Missing Dataiku URL. Set DATAIKU_URL or pass --url.", }, null, 2)}\n`);
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
if (!apiKey) {
|
|
658
|
+
process.stderr.write(`${JSON.stringify({ error: "Missing API key. Set DATAIKU_API_KEY or pass --api-key.", }, null, 2)}\n`);
|
|
659
|
+
process.exit(1);
|
|
660
|
+
}
|
|
661
|
+
const client = new DataikuClient({
|
|
662
|
+
url,
|
|
663
|
+
apiKey,
|
|
664
|
+
projectKey: flags["project-key"] ?? process.env.DATAIKU_PROJECT_KEY,
|
|
665
|
+
});
|
|
666
|
+
const args = positional.slice(2);
|
|
667
|
+
const result = await actionMeta.handler(client, args, flags);
|
|
668
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
669
|
+
}
|
|
670
|
+
main().catch((err) => {
|
|
671
|
+
if (err instanceof UsageError) {
|
|
672
|
+
process.stderr.write(`${err.message}\n`);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
if (err instanceof DataikuError) {
|
|
676
|
+
const payload = {
|
|
677
|
+
error: err.message,
|
|
678
|
+
category: err.category,
|
|
679
|
+
retryable: err.retryable,
|
|
680
|
+
};
|
|
681
|
+
if (err.retryHint)
|
|
682
|
+
payload.retryHint = err.retryHint;
|
|
683
|
+
process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
684
|
+
process.exit(1);
|
|
685
|
+
}
|
|
686
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
687
|
+
process.stderr.write(`${JSON.stringify({ error: message, }, null, 2)}\n`);
|
|
688
|
+
process.exit(1);
|
|
689
|
+
});
|