dataiku-sdk 0.12.7 → 1.0.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.
Files changed (51) hide show
  1. package/README.md +60 -14
  2. package/bin/dss.js +1 -1
  3. package/dist/packages/types/src/index.d.ts +128 -3
  4. package/dist/packages/types/src/index.js +106 -3
  5. package/dist/src/cli/coerce.d.ts +2 -1
  6. package/dist/src/cli/coerce.js +84 -26
  7. package/dist/src/cli/commands/app.js +6 -6
  8. package/dist/src/cli/commands/code.js +2 -2
  9. package/dist/src/cli/commands/data-quality.js +8 -8
  10. package/dist/src/cli/commands/dataset.js +11 -7
  11. package/dist/src/cli/commands/folder.js +5 -4
  12. package/dist/src/cli/commands/future.js +2 -2
  13. package/dist/src/cli/commands/index.js +2 -0
  14. package/dist/src/cli/commands/job.js +11 -11
  15. package/dist/src/cli/commands/notebook.js +1 -1
  16. package/dist/src/cli/commands/project-git.d.ts +2 -0
  17. package/dist/src/cli/commands/project-git.js +563 -0
  18. package/dist/src/cli/commands/project.js +2 -2
  19. package/dist/src/cli/commands/recipe.js +14 -17
  20. package/dist/src/cli/commands/scenario.js +4 -4
  21. package/dist/src/cli/contract.d.ts +5 -4
  22. package/dist/src/cli/contract.js +413 -55
  23. package/dist/src/cli/doctor.js +6 -6
  24. package/dist/src/cli/flags.js +21 -2
  25. package/dist/src/cli/helpers/dataset.js +1 -1
  26. package/dist/src/cli/output.d.ts +2 -1
  27. package/dist/src/cli/output.js +2 -6
  28. package/dist/src/cli/usage.d.ts +1 -1
  29. package/dist/src/cli/usage.js +2 -1
  30. package/dist/src/cli/version.d.ts +2 -2
  31. package/dist/src/cli/version.js +2 -2
  32. package/dist/src/cli.js +43 -26
  33. package/dist/src/client.d.ts +3 -0
  34. package/dist/src/client.js +5 -0
  35. package/dist/src/errors.d.ts +1 -1
  36. package/dist/src/index.d.ts +3 -2
  37. package/dist/src/index.js +2 -1
  38. package/dist/src/resources/connections.d.ts +3 -1
  39. package/dist/src/resources/connections.js +82 -21
  40. package/dist/src/resources/datasets.js +42 -2
  41. package/dist/src/resources/folders.js +7 -3
  42. package/dist/src/resources/project-git.d.ts +154 -0
  43. package/dist/src/resources/project-git.js +427 -0
  44. package/dist/src/resources/recipes.js +4 -4
  45. package/dist/src/schemas.d.ts +2 -2
  46. package/dist/src/schemas.js +1 -1
  47. package/package.json +1 -1
  48. package/packages/types/dist/index.d.ts +128 -3
  49. package/packages/types/dist/index.js +106 -3
  50. package/plugin.json +1 -1
  51. package/skills/dataiku-dss/SKILL.md +26 -19
@@ -9,27 +9,89 @@ function normalizeConnectionNames(value) {
9
9
  .filter((v) => typeof v === "string" && v.length > 0)
10
10
  .sort();
11
11
  }
12
- async function inferRichConnectionsFromDatasets(client, projectEnc) {
13
- const datasets = await client.get(`/public/api/projects/${projectEnc}/datasets/`);
12
+ export async function resolveAdminManagedStorageConnection(client, capability, preferredName) {
13
+ try {
14
+ const raw = await client.get("/public/api/admin/connections/");
15
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
16
+ return undefined;
17
+ const candidates = [];
18
+ for (const [name, value,] of Object.entries(raw)) {
19
+ if (!value || typeof value !== "object" || Array.isArray(value))
20
+ continue;
21
+ const details = value;
22
+ if (details["allowWrite"] === true && details[capability] === true) {
23
+ candidates.push(name);
24
+ }
25
+ }
26
+ candidates.sort();
27
+ return preferredName && candidates.includes(preferredName) ? preferredName : candidates[0];
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ async function inferRichConnections(client, projectEnc) {
34
+ const datasetsRaw = await client.get(`/public/api/projects/${projectEnc}/datasets/`);
35
+ const datasets = Array.isArray(datasetsRaw) ? datasetsRaw : [];
14
36
  const map = new Map();
15
- for (const ds of datasets) {
16
- const d = ds;
17
- const params = d["params"];
18
- const connection = params?.["connection"];
37
+ const recordConnection = (connection, type, managed, schema) => {
19
38
  if (typeof connection !== "string" || connection.length === 0)
20
- continue;
21
- if (!map.has(connection)) {
22
- map.set(connection, { types: new Set(), managed: false, dbSchemas: new Set(), });
39
+ return;
40
+ let entry = map.get(connection);
41
+ if (!entry) {
42
+ entry = { types: new Set(), managed: false, dbSchemas: new Set(), };
43
+ map.set(connection, entry);
23
44
  }
24
- const entry = map.get(connection);
25
- const dsType = d["type"];
26
- if (typeof dsType === "string" && dsType.length > 0)
27
- entry.types.add(dsType);
28
- if (d["managed"] === true)
45
+ if (typeof type === "string" && type.length > 0)
46
+ entry.types.add(type);
47
+ if (managed)
29
48
  entry.managed = true;
30
- const schema = params?.["schema"];
31
49
  if (typeof schema === "string" && schema.length > 0)
32
50
  entry.dbSchemas.add(schema);
51
+ };
52
+ for (const dataset of datasets) {
53
+ if (!dataset || typeof dataset !== "object" || Array.isArray(dataset))
54
+ continue;
55
+ const details = dataset;
56
+ const params = details["params"];
57
+ if (!params || typeof params !== "object" || Array.isArray(params))
58
+ continue;
59
+ const datasetParams = params;
60
+ recordConnection(datasetParams["connection"], details["type"], details["managed"] === true, datasetParams["schema"]);
61
+ recordConnection(datasetParams["uploadConnection"], details["type"], false);
62
+ }
63
+ try {
64
+ const foldersRaw = await client.get(`/public/api/projects/${projectEnc}/managedfolders/`);
65
+ const folders = Array.isArray(foldersRaw) ? foldersRaw : [];
66
+ const folderDetails = await Promise.all(folders.map(async (folder) => {
67
+ if (!folder || typeof folder !== "object" || Array.isArray(folder))
68
+ return folder;
69
+ const summary = folder;
70
+ const params = summary["params"];
71
+ if (params && typeof params === "object" && !Array.isArray(params))
72
+ return summary;
73
+ const id = summary["id"];
74
+ if (typeof id !== "string" || id.length === 0)
75
+ return summary;
76
+ try {
77
+ return await client.get(`/public/api/projects/${projectEnc}/managedfolders/${encodeURIComponent(id)}`);
78
+ }
79
+ catch {
80
+ return summary;
81
+ }
82
+ }));
83
+ for (const folder of folderDetails) {
84
+ if (!folder || typeof folder !== "object" || Array.isArray(folder))
85
+ continue;
86
+ const details = folder;
87
+ const params = details["params"];
88
+ if (!params || typeof params !== "object" || Array.isArray(params))
89
+ continue;
90
+ recordConnection(params["connection"], details["type"], true);
91
+ }
92
+ }
93
+ catch {
94
+ // Dataset-derived inference remains useful when managed folders are inaccessible.
33
95
  }
34
96
  return [...map.entries(),]
35
97
  .sort(([a,], [b,]) => a.localeCompare(b))
@@ -48,9 +110,8 @@ export class ConnectionsResource extends BaseResource {
48
110
  * Returns sorted list of all connection names visible to the current user.
49
111
  */
50
112
  async list(opts) {
51
- const type = opts?.type?.trim();
52
- const query = type ? `?type=${encodeURIComponent(type)}` : "";
53
- const raw = await this.client.get(`/public/api/connections/get-names/${query}`);
113
+ const type = opts?.type?.trim() || "all";
114
+ const raw = await this.client.get(`/public/api/connections/get-names/?type=${encodeURIComponent(type)}`);
54
115
  return normalizeConnectionNames(raw);
55
116
  }
56
117
  /**
@@ -58,14 +119,14 @@ export class ConnectionsResource extends BaseResource {
58
119
  *
59
120
  * - fast (default): fetches the connection name list and maps to ConnectionSummary.
60
121
  * Falls back to rich mode on any failure or empty result set.
61
- * - rich: inspects project datasets to derive connection metadata
122
+ * - rich: inspects project datasets and managed folders to derive connection metadata
62
123
  * (types, managed flag, db schemas).
63
124
  */
64
125
  async infer(opts) {
65
126
  const mode = opts?.mode ?? "fast";
66
127
  const projectEnc = this.enc(opts?.projectKey);
67
128
  if (mode === "rich") {
68
- return inferRichConnectionsFromDatasets(this.client, projectEnc);
129
+ return inferRichConnections(this.client, projectEnc);
69
130
  }
70
131
  // fast — attempt name list, fall back to rich on any error or empty result
71
132
  try {
@@ -77,7 +138,7 @@ export class ConnectionsResource extends BaseResource {
77
138
  catch {
78
139
  // Fall through to rich inference.
79
140
  }
80
- return inferRichConnectionsFromDatasets(this.client, projectEnc);
141
+ return inferRichConnections(this.client, projectEnc);
81
142
  }
82
143
  async schemas(opts) {
83
144
  const pk = this.resolveProjectKey(opts.projectKey);
@@ -3,11 +3,12 @@ import * as nodePath from "node:path";
3
3
  import { Readable, Transform, } from "node:stream";
4
4
  import { pipeline, } from "node:stream/promises";
5
5
  import { createGzip, } from "node:zlib";
6
- import { DataikuError, } from "../errors.js";
6
+ import { ClientValidationError, DataikuError, } from "../errors.js";
7
7
  import { DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, } from "../schemas.js";
8
8
  import { deepMerge, } from "../utils/deep-merge.js";
9
9
  import { sanitizeFileName, } from "../utils/sanitize.js";
10
10
  import { BaseResource, } from "./base.js";
11
+ import { resolveAdminManagedStorageConnection, } from "./connections.js";
11
12
  // ---------------------------------------------------------------------------
12
13
  // Helpers: TSV → CSV streaming conversion
13
14
  // ---------------------------------------------------------------------------
@@ -253,15 +254,25 @@ function shouldRetryWithConnectionInferredType(error) {
253
254
  || detail.includes("invalid type")
254
255
  || detail.includes("illegal argument"));
255
256
  }
257
+ function isMissingUploadedFilesTargetConnection(error) {
258
+ if (!(error instanceof DataikuError))
259
+ return false;
260
+ const detail = error.body.toLowerCase();
261
+ return (detail.includes("without a target connection")
262
+ || detail.includes("target connection is required"));
263
+ }
256
264
  function buildDatasetCreateBody(opts) {
257
265
  if (opts.dsType.toLowerCase() === "uploadedfiles") {
258
266
  return {
259
267
  projectKey: opts.projectKey,
260
268
  name: opts.datasetName,
261
269
  type: opts.dsType,
262
- params: { uploadConnection: opts.connection, },
270
+ params: opts.connection ? { uploadConnection: opts.connection, } : {},
263
271
  };
264
272
  }
273
+ if (!opts.connection) {
274
+ throw new ClientValidationError("connection is required unless dsType is UploadedFiles.");
275
+ }
265
276
  if (opts.table) {
266
277
  const params = {
267
278
  connection: opts.connection,
@@ -481,6 +492,35 @@ export class DatasetsResource extends BaseResource {
481
492
  return await this.client.post(`/public/api/projects/${enc}/datasets/`, body);
482
493
  }
483
494
  catch (error) {
495
+ if (explicitType?.toLowerCase() === "uploadedfiles"
496
+ && !opts.connection
497
+ && isMissingUploadedFilesTargetConnection(error)) {
498
+ let uploadConnection = await resolveAdminManagedStorageConnection(this.client, "allowManagedDatasets", "filesystem_managed");
499
+ if (!uploadConnection) {
500
+ const existing = await this.list(pk);
501
+ uploadConnection = [
502
+ ...new Set(existing
503
+ .filter((dataset) => dataset.managed === true)
504
+ .map((dataset) => dataset.params?.connection)
505
+ .filter((connection) => typeof connection === "string" && connection.length > 0)),
506
+ ].sort()[0];
507
+ }
508
+ if (!uploadConnection)
509
+ throw error;
510
+ body = buildDatasetCreateBody({
511
+ projectKey: pk,
512
+ datasetName: opts.datasetName,
513
+ connection: uploadConnection,
514
+ dsType,
515
+ table: opts.table,
516
+ dbSchema: opts.dbSchema,
517
+ catalog: opts.catalog,
518
+ formatType: opts.formatType,
519
+ formatParams: opts.formatParams,
520
+ managed: opts.managed,
521
+ });
522
+ return this.client.post(`/public/api/projects/${enc}/datasets/`, body);
523
+ }
484
524
  if (explicitType || !shouldRetryWithConnectionInferredType(error)) {
485
525
  throw error;
486
526
  }
@@ -6,6 +6,7 @@ import { FolderDetailsSchema, FolderItemArraySchema, FolderSummaryArraySchema, }
6
6
  import { deepMerge, } from "../utils/deep-merge.js";
7
7
  import { sanitizeFileName, } from "../utils/sanitize.js";
8
8
  import { BaseResource, } from "./base.js";
9
+ import { resolveAdminManagedStorageConnection, } from "./connections.js";
9
10
  function normalizeRemotePath(path) {
10
11
  return path.replace(/\\/g, "/");
11
12
  }
@@ -20,14 +21,17 @@ function inferDownloadFileName(remotePath) {
20
21
  export class FoldersResource extends BaseResource {
21
22
  async create(opts) {
22
23
  const pk = this.resolveProjectKey(opts.projectKey);
23
- const path = opts.path?.trim() || `/dataiku/${pk}/${opts.name}`;
24
+ const path = opts.path?.trim() || "/${projectKey}/${odbId}";
25
+ const connection = opts.connection
26
+ ?? await resolveAdminManagedStorageConnection(this.client, "allowManagedFolders", "filesystem_folders")
27
+ ?? "filesystem_folders";
24
28
  const raw = await this.client.post(`/public/api/projects/${encodeURIComponent(pk)}/managedfolders/`, {
25
29
  name: opts.name,
26
30
  projectKey: pk,
27
- type: opts.type,
31
+ type: opts.type ?? null,
28
32
  params: {
29
33
  ...opts.params,
30
- connection: opts.connection,
34
+ connection: connection,
31
35
  path,
32
36
  },
33
37
  });
@@ -0,0 +1,154 @@
1
+ import type { ProjectGitActionResult, ProjectGitDiffResult, ProjectGitFutureResponse, ProjectGitFutureState, ProjectGitLibraries, ProjectGitLogResult, ProjectGitRemote, ProjectGitStatus, ProjectGitTags } from "../schemas.js";
2
+ import { BaseResource } from "./base.js";
3
+ export interface ProjectGitSetRemoteOptions {
4
+ url: string;
5
+ name?: string;
6
+ }
7
+ export interface ProjectGitCreateBranchOptions {
8
+ commit?: string;
9
+ duplicateProject?: boolean;
10
+ targetProjectKey?: string;
11
+ targetProjectFolderId?: string;
12
+ }
13
+ export interface ProjectGitDeleteBranchOptions {
14
+ remote?: boolean;
15
+ deleteRemotely?: boolean;
16
+ forceDelete?: boolean;
17
+ }
18
+ export interface ProjectGitCreateTagOptions {
19
+ reference?: string;
20
+ message?: string;
21
+ }
22
+ export interface ProjectGitPullOptions {
23
+ branchName?: string;
24
+ }
25
+ export interface ProjectGitPushOptions {
26
+ branchName?: string;
27
+ }
28
+ export interface ProjectGitLogOptions {
29
+ path?: string;
30
+ startCommit?: string;
31
+ count?: number;
32
+ }
33
+ export interface ProjectGitDiffOptions {
34
+ commitFrom?: string;
35
+ commitTo?: string;
36
+ }
37
+ export interface ProjectGitCommitOptions {
38
+ message: string;
39
+ }
40
+ /**
41
+ * `dropAndRebuild` permanently destroys the project's Git history, so the
42
+ * caller must acknowledge it explicitly before the request is issued.
43
+ */
44
+ export interface ProjectGitDropAndRebuildOptions {
45
+ confirmed?: boolean;
46
+ }
47
+ export interface ProjectGitAddLibraryOptions {
48
+ repository: string;
49
+ localTargetPath: string;
50
+ checkout: string;
51
+ pathInGitRepository?: string;
52
+ addToPythonPath?: boolean;
53
+ login?: string;
54
+ password?: string;
55
+ }
56
+ export interface ProjectGitSetLibraryOptions {
57
+ repository: string;
58
+ pathInGitRepository?: string;
59
+ checkout: string;
60
+ login?: string;
61
+ password?: string;
62
+ }
63
+ export interface ProjectGitFutureStateOptions {
64
+ peek?: boolean;
65
+ }
66
+ export interface ProjectGitFutureWaitOptions {
67
+ pollIntervalMs?: number;
68
+ timeoutMs?: number;
69
+ }
70
+ export declare class ProjectGitResource extends BaseResource {
71
+ /** Working-copy status: current branch, remotes, tracking counts, changes. */
72
+ status(projectKey: string): Promise<ProjectGitStatus>;
73
+ /**
74
+ * Read a Git remote. DSS answers `{}` when the remote is not configured.
75
+ * The remote name defaults to `origin` like the Python client.
76
+ */
77
+ getRemote(projectKey: string, name?: string): Promise<ProjectGitRemote>;
78
+ /** Create or replace a Git remote. */
79
+ setRemote(projectKey: string, options: ProjectGitSetRemoteOptions): Promise<ProjectGitActionResult>;
80
+ /** Delete a Git remote. */
81
+ removeRemote(projectKey: string, name?: string): Promise<void>;
82
+ /** Plain list of branch names, local by default. */
83
+ listBranches(projectKey: string, remote?: boolean): Promise<string[]>;
84
+ /**
85
+ * Create a branch and switch to it. With `duplicateProject`, DSS creates a
86
+ * duplicate project for the branch instead of moving this one.
87
+ */
88
+ createBranch(projectKey: string, name: string, options?: ProjectGitCreateBranchOptions): Promise<ProjectGitActionResult>;
89
+ /** Delete a branch locally and, with `deleteRemotely`, on the remote too. */
90
+ deleteBranch(projectKey: string, name: string, options?: ProjectGitDeleteBranchOptions): Promise<ProjectGitActionResult>;
91
+ /** Current branch name, or null when DSS reports none. */
92
+ currentBranch(projectKey: string): Promise<string | null>;
93
+ /** Switch the project to another branch. */
94
+ switchBranch(projectKey: string, branchName: string): Promise<ProjectGitActionResult>;
95
+ listTags(projectKey: string): Promise<ProjectGitTags>;
96
+ createTag(projectKey: string, name: string, options?: ProjectGitCreateTagOptions): Promise<ProjectGitActionResult>;
97
+ deleteTag(projectKey: string, name: string): Promise<ProjectGitActionResult>;
98
+ /** Fetch from the remote. Failures arrive as `{success: false}` with HTTP 200. */
99
+ fetch(projectKey: string): Promise<ProjectGitActionResult>;
100
+ /** Pull. DSS only ever exposes a rebase pull; there is no merge variant. */
101
+ pull(projectKey: string, options?: ProjectGitPullOptions): Promise<ProjectGitActionResult>;
102
+ push(projectKey: string, options?: ProjectGitPushOptions): Promise<ProjectGitActionResult>;
103
+ /** Commit log page. Paginate by passing the previous `nextCommit` as `startCommit`. */
104
+ log(projectKey: string, options?: ProjectGitLogOptions): Promise<ProjectGitLogResult>;
105
+ diff(projectKey: string, options?: ProjectGitDiffOptions): Promise<ProjectGitDiffResult>;
106
+ /** Commit the project. DSS stages untracked files before committing. */
107
+ commit(projectKey: string, options: ProjectGitCommitOptions): Promise<ProjectGitActionResult>;
108
+ /** Restore the project to the state of a past commit. */
109
+ revertToRevision(projectKey: string, commit: string): Promise<ProjectGitActionResult>;
110
+ /** Revert one commit, keeping later history. */
111
+ revertCommit(projectKey: string, commit: string): Promise<ProjectGitActionResult>;
112
+ /** Hard reset to local HEAD, dropping uncommitted changes. */
113
+ resetToHead(projectKey: string): Promise<ProjectGitActionResult>;
114
+ /** Hard reset to the tracked upstream branch, dropping local commits. */
115
+ resetToUpstream(projectKey: string): Promise<ProjectGitActionResult>;
116
+ /**
117
+ * Wipe the project's entire Git history and rebuild a fresh repository.
118
+ * Irreversible, so nothing is sent unless the caller acknowledges it.
119
+ */
120
+ dropAndRebuild(projectKey: string, options?: ProjectGitDropAndRebuildOptions): Promise<ProjectGitActionResult>;
121
+ listLibraries(projectKey: string): Promise<ProjectGitLibraries>;
122
+ /** Attach an external Git library. Runs as a DSS future. */
123
+ addLibrary(projectKey: string, options: ProjectGitAddLibraryOptions): Promise<ProjectGitFutureResponse>;
124
+ /** Update an attached library's repository, sub-path, or checkout. */
125
+ setLibrary(projectKey: string, gitReferencePath: string, options: ProjectGitSetLibraryOptions): Promise<string>;
126
+ /** Detach a library, optionally deleting its local directory. */
127
+ removeLibrary(projectKey: string, gitReferencePath: string, deleteDirectory?: boolean): Promise<void>;
128
+ /** Reset one library to its remote state. Runs as a DSS future. */
129
+ resetLibrary(projectKey: string, gitReferencePath: string): Promise<ProjectGitFutureResponse>;
130
+ /** Commit and push one library. Runs as a DSS future. */
131
+ pushLibrary(projectKey: string, gitReferencePath: string, commitMessage: string): Promise<ProjectGitFutureResponse>;
132
+ /** Commit and push every attached library. Runs as a DSS future. */
133
+ pushAllLibraries(projectKey: string, commitMessage: string): Promise<ProjectGitFutureResponse>;
134
+ /** Reset every attached library to its remote state. Runs as a DSS future. */
135
+ resetAllLibraries(projectKey: string): Promise<ProjectGitFutureResponse>;
136
+ /**
137
+ * Read a future's state. `peek` leaves the future's log unconsumed, which is
138
+ * what polling loops want; the default consumes it like the Python client.
139
+ */
140
+ getFutureState(jobId: string, options?: ProjectGitFutureStateOptions): Promise<ProjectGitFutureState>;
141
+ /** Abort a running future. */
142
+ abortFuture(jobId: string): Promise<void>;
143
+ /**
144
+ * Poll until the future carries a result and return that result. Throws on
145
+ * an aborted or failed future and on an exhausted budget, so a caller never
146
+ * mistakes an unfinished future for a successful one.
147
+ */
148
+ waitForFuture(jobId: string, options?: ProjectGitFutureWaitOptions): Promise<unknown>;
149
+ private gitPath;
150
+ private actionPath;
151
+ private libraryPath;
152
+ private query;
153
+ private request;
154
+ }