dataiku-sdk 0.12.2 → 0.12.3

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.
@@ -59,7 +59,6 @@ export const apiServiceCommands = {
59
59
  handler: async (c, a, f) => {
60
60
  requireArgs(a, 1, "dss api-service list-packages <serviceId> [--project-key KEY]");
61
61
  const projectKey = f["project-key"];
62
- await c.apiServices.getSettings(a[0], projectKey);
63
62
  return c.apiServices.listPackages(a[0], projectKey);
64
63
  },
65
64
  usage: "dss api-service list-packages <serviceId> [--project-key KEY]",
@@ -44,7 +44,11 @@ export const notebookCommands = {
44
44
  const current = await c.notebooks.getJupyter(a[0], pk);
45
45
  const next = {
46
46
  ...current,
47
- cells: current.cells.map((cell) => ({ ...cell, outputs: [], execution_count: null, })),
47
+ cells: current.cells.map((cell) => {
48
+ if (cell.cell_type !== "code")
49
+ return cell;
50
+ return { ...cell, outputs: [], execution_count: 0, };
51
+ }),
48
52
  };
49
53
  return {
50
54
  dryRun: true,
@@ -73,7 +73,7 @@ export function transientBodyWithTargetContext(body, target, elapsedMs) {
73
73
  }
74
74
  export function addTransientTargetContext(error, target, elapsedMs) {
75
75
  if (error instanceof DataikuError && error.category === "transient") {
76
- throw new DataikuError(error.status, error.statusText, transientBodyWithTargetContext(error.body, target, elapsedMs), error.retry, error.requestId);
76
+ throw new DataikuError(error.status, error.statusText, transientBodyWithTargetContext(error.body, target, elapsedMs), error.retry, error.requestId, { target, elapsedMs, });
77
77
  }
78
78
  throw error;
79
79
  }
package/dist/src/cli.js CHANGED
@@ -12,7 +12,7 @@ import { COMMANDS_RUN_HINT, missingActionError, noCommandError, requireArgs, unk
12
12
  import { cliVersionResult, } from "./cli/version.js";
13
13
  import { DataikuClient, } from "./client.js";
14
14
  import { getCredentialsPath, } from "./config.js";
15
- import { ClientValidationError, DataikuError, dataikuErrorCode, } from "./errors.js";
15
+ import { canonicalStatusText, ClientValidationError, DataikuError, dataikuErrorCode, } from "./errors.js";
16
16
  import { AGENTS, detectAgents, findWorkspaceRoot, installSkill, planSkillInstalls, } from "./skill.js";
17
17
  import { appendCleanupLedgerEntry, readCleanupLedger, } from "./utils/cleanup-ledger.js";
18
18
  // ---------------------------------------------------------------------------
@@ -833,16 +833,41 @@ function rawCommandContext() {
833
833
  : undefined),
834
834
  };
835
835
  }
836
- function requestIdFromBody(body) {
836
+ const SAFE_RESPONSE_METADATA_KEYS = [
837
+ "requestId",
838
+ "request_id",
839
+ "errorId",
840
+ "elapsedMs",
841
+ ];
842
+ function safeResponseMetadata(body) {
837
843
  try {
838
844
  const parsed = JSON.parse(body);
839
- const value = parsed.requestId ?? parsed.request_id ?? parsed.errorId;
840
- return typeof value === "string" && value.length > 0 ? value : undefined;
845
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
846
+ return {};
847
+ const metadata = {};
848
+ for (const key of SAFE_RESPONSE_METADATA_KEYS) {
849
+ const value = parsed[key];
850
+ if ((typeof value === "string" && value.length > 0)
851
+ || (typeof value === "number" && Number.isFinite(value))
852
+ || typeof value === "boolean") {
853
+ metadata[key] = value;
854
+ }
855
+ }
856
+ return metadata;
841
857
  }
842
858
  catch {
843
- return undefined;
859
+ return {};
844
860
  }
845
861
  }
862
+ function requestIdFromBody(body) {
863
+ const metadata = safeResponseMetadata(body);
864
+ for (const key of ["requestId", "request_id", "errorId",]) {
865
+ const value = metadata[key];
866
+ if (typeof value === "string")
867
+ return value;
868
+ }
869
+ return undefined;
870
+ }
846
871
  const MISSING_PROJECT_KEY_ERROR_PREFIX = "projectKey is required";
847
872
  function errorExitCode(err) {
848
873
  if (err instanceof CommandResultFailure)
@@ -884,13 +909,18 @@ function buildErrorReport(err) {
884
909
  };
885
910
  }
886
911
  if (err instanceof DataikuError) {
887
- const errorMessage = err.category === "not_found"
888
- && err.message.includes("Dataiku instance not found")
889
- && context.resource
912
+ const errorMessage = err.category === "not_found" && context.resource
890
913
  ? `Not found: ${context.resource}${context.action ? ` ${context.action}` : ""}`
891
914
  + `${context.projectKey ? ` in project ${context.projectKey}` : ""}`
892
- + ` — verify the object identifier and project key (DSS: ${err.message.split("\n")[0]}).`
893
- : err.message;
915
+ + " — verify the object identifier and project key."
916
+ + `\nHint: ${err.retryHint}`
917
+ : err.safeMessage;
918
+ const safeMetadata = safeResponseMetadata(err.body);
919
+ if (err.trustedTarget)
920
+ safeMetadata.target = err.trustedTarget;
921
+ if (err.trustedElapsedMs !== undefined)
922
+ safeMetadata.elapsedMs = err.trustedElapsedMs;
923
+ const safeBody = JSON.stringify(safeMetadata);
894
924
  return {
895
925
  type: "error",
896
926
  ok: false,
@@ -904,8 +934,8 @@ function buildErrorReport(err) {
904
934
  requestId: err.requestId ?? requestIdFromBody(err.body),
905
935
  details: {
906
936
  dssCategory: err.category,
907
- statusText: err.statusText,
908
- body: err.body,
937
+ statusText: canonicalStatusText(err.status),
938
+ body: safeBody,
909
939
  ...(err.retry ? { retry: err.retry, } : {}),
910
940
  },
911
941
  ...context,
@@ -15,6 +15,11 @@ export interface DataikuRetryMetadata {
15
15
  delaysMs: number[];
16
16
  timedOut: boolean;
17
17
  }
18
+ export interface DataikuErrorTrustedMetadata {
19
+ target?: string;
20
+ elapsedMs?: number;
21
+ }
22
+ export declare function canonicalStatusText(status: number): string;
18
23
  export declare class ClientValidationError extends Error {
19
24
  readonly code: StableErrorCode;
20
25
  readonly hint?: string;
@@ -31,7 +36,10 @@ export declare class DataikuError extends Error {
31
36
  retryHint: string;
32
37
  retry?: DataikuRetryMetadata;
33
38
  requestId?: string;
34
- constructor(status: number, statusText: string, body: string, retry?: DataikuRetryMetadata, requestId?: string);
39
+ readonly trustedTarget?: string;
40
+ readonly trustedElapsedMs?: number;
41
+ constructor(status: number, statusText: string, body: string, retry?: DataikuRetryMetadata, requestId?: string, trustedMetadata?: DataikuErrorTrustedMetadata);
42
+ get safeMessage(): string;
35
43
  private static extractSummary;
36
44
  private static formatRetryMetadata;
37
45
  private static buildDetails;
@@ -12,6 +12,25 @@ export function dataikuErrorCode(category) {
12
12
  return "internal_error";
13
13
  }
14
14
  }
15
+ const CANONICAL_STATUS_TEXT = {
16
+ 400: "Bad Request",
17
+ 401: "Unauthorized",
18
+ 403: "Forbidden",
19
+ 404: "Not Found",
20
+ 408: "Request Timeout",
21
+ 409: "Conflict",
22
+ 422: "Unprocessable Entity",
23
+ 425: "Too Early",
24
+ 429: "Too Many Requests",
25
+ 500: "Internal Server Error",
26
+ 501: "Not Implemented",
27
+ 502: "Bad Gateway",
28
+ 503: "Service Unavailable",
29
+ 504: "Gateway Timeout",
30
+ };
31
+ export function canonicalStatusText(status) {
32
+ return CANONICAL_STATUS_TEXT[status] ?? (status === 0 ? "Network Error" : "HTTP Error");
33
+ }
15
34
  export class ClientValidationError extends Error {
16
35
  code;
17
36
  hint;
@@ -276,8 +295,10 @@ export class DataikuError extends Error {
276
295
  retryHint;
277
296
  retry;
278
297
  requestId;
279
- constructor(status, statusText, body, retry, requestId) {
280
- const details = DataikuError.buildDetails(status, statusText, body, retry);
298
+ trustedTarget;
299
+ trustedElapsedMs;
300
+ constructor(status, statusText, body, retry, requestId, trustedMetadata) {
301
+ const details = DataikuError.buildDetails(status, body, retry);
281
302
  super(details.message);
282
303
  this.status = status;
283
304
  this.statusText = statusText;
@@ -288,8 +309,20 @@ export class DataikuError extends Error {
288
309
  this.retryHint = details.retryHint;
289
310
  this.retry = retry;
290
311
  this.requestId = requestId;
312
+ this.trustedTarget = trustedMetadata?.target;
313
+ this.trustedElapsedMs = trustedMetadata?.elapsedMs;
314
+ }
315
+ get safeMessage() {
316
+ const retrySummary = DataikuError.formatRetryMetadata(this.retry);
317
+ return [
318
+ `${this.status} ${canonicalStatusText(this.status)}`,
319
+ `Error type: ${this.category}`,
320
+ `Retryable: ${this.retryable ? "yes" : "no"}`,
321
+ `Hint: ${this.retryHint}`,
322
+ ...(retrySummary ? [retrySummary,] : []),
323
+ ].join("\n");
291
324
  }
292
- static extractSummary(_status, _statusText, body) {
325
+ static extractSummary(_status, body) {
293
326
  try {
294
327
  const parsed = JSON.parse(body);
295
328
  if (parsed.message)
@@ -316,14 +349,14 @@ export class DataikuError extends Error {
316
349
  `Timed out: ${retry.timedOut ? "yes" : "no"}`,
317
350
  ].join(" | ");
318
351
  }
319
- static buildDetails(status, statusText, body, retry) {
320
- const summary = DataikuError.extractSummary(status, statusText, body);
352
+ static buildDetails(status, body, retry) {
353
+ const summary = DataikuError.extractSummary(status, body);
321
354
  const taxonomy = classifyDataikuError(status, body);
322
355
  const retrySummary = DataikuError.formatRetryMetadata(retry);
323
356
  return {
324
357
  ...taxonomy,
325
358
  message: [
326
- `${status} ${statusText}: ${summary}`,
359
+ `${status} ${canonicalStatusText(status)}: ${summary}`,
327
360
  `Error type: ${taxonomy.category}`,
328
361
  `Retryable: ${taxonomy.retryable ? "yes" : "no"}`,
329
362
  `Hint: ${taxonomy.retryHint}`,
@@ -28,6 +28,7 @@ export class ApiServicesResource extends BaseResource {
28
28
  }
29
29
  /** List deployable packages for an API service. */
30
30
  async listPackages(serviceId, projectKey) {
31
+ await this.getSettings(serviceId, projectKey);
31
32
  return this.client.get(`${this.servicePath(serviceId, projectKey)}/packages`);
32
33
  }
33
34
  /** Get an API service package summary. */
@@ -36,11 +36,15 @@ export class NotebooksResource extends BaseResource {
36
36
  */
37
37
  async clearJupyterOutputs(name, projectKey) {
38
38
  const notebook = await this.getJupyter(name, projectKey);
39
- const clearedCells = notebook.cells.map((cell) => ({
40
- ...cell,
41
- outputs: [],
42
- execution_count: null,
43
- }));
39
+ const clearedCells = notebook.cells.map((cell) => {
40
+ if (cell.cell_type !== "code")
41
+ return cell;
42
+ return {
43
+ ...cell,
44
+ outputs: [],
45
+ execution_count: 0,
46
+ };
47
+ });
44
48
  await this.saveJupyter(name, { ...notebook, cells: clearedCells, }, projectKey);
45
49
  }
46
50
  /** List running kernel sessions for a Jupyter notebook. */
@@ -1,4 +1,4 @@
1
- import { DataikuError, } from "../errors.js";
1
+ import { ClientValidationError, DataikuError, } from "../errors.js";
2
2
  import { BaseResource, } from "./base.js";
3
3
  const UNSUPPORTED_SQL_DATASET_CONNECTION_DETAIL = "neither of sql nor hdfs type";
4
4
  function isUnsupportedSqlDatasetConnectionError(error) {
@@ -161,28 +161,23 @@ export class SqlResource extends BaseResource {
161
161
  const datasetFullName = opts.datasetFullName;
162
162
  if (!datasetFullName)
163
163
  return null;
164
- try {
165
- const identifier = splitDatasetIdentifier(datasetFullName, opts.projectKey);
166
- const projectKey = identifier.projectKey
167
- ? identifier.projectKey
168
- : this.resolveProjectKey(opts.projectKey);
169
- const dsEnc = encodeURIComponent(identifier.datasetName);
170
- const raw = await this.client.get(`/public/api/projects/${encodeURIComponent(projectKey)}/datasets/${dsEnc}`);
171
- const params = asRecord(raw.params);
172
- const connection = asString(params?.connection);
173
- if (!connection)
174
- return null;
175
- return {
176
- ...opts,
177
- connection,
178
- datasetFullName: undefined,
179
- database: opts.database ?? asString(params?.schema) ?? asString(params?.catalog),
180
- projectKey,
181
- };
182
- }
183
- catch {
164
+ const identifier = splitDatasetIdentifier(datasetFullName, opts.projectKey);
165
+ const projectKey = identifier.projectKey
166
+ ? identifier.projectKey
167
+ : this.resolveProjectKey(opts.projectKey);
168
+ const dsEnc = encodeURIComponent(identifier.datasetName);
169
+ const raw = await this.client.get(`/public/api/projects/${encodeURIComponent(projectKey)}/datasets/${dsEnc}`);
170
+ const params = asRecord(raw.params);
171
+ const connection = asString(params?.connection);
172
+ if (!connection)
184
173
  return null;
185
- }
174
+ return {
175
+ ...opts,
176
+ connection,
177
+ datasetFullName: undefined,
178
+ database: opts.database ?? asString(params?.schema) ?? asString(params?.catalog),
179
+ projectKey,
180
+ };
186
181
  }
187
182
  /**
188
183
  * Execute a SQL query end-to-end: start, stream all rows, verify, return combined result.
@@ -198,9 +193,7 @@ export class SqlResource extends BaseResource {
198
193
  withSqlErrorContext(error);
199
194
  const retryOpts = await this.resolveDatasetQueryFallback(queryOpts);
200
195
  if (!retryOpts) {
201
- throw new Error(buildUnsupportedSqlDatasetConnectionMessage(queryOpts.datasetFullName), {
202
- cause: error,
203
- });
196
+ throw new ClientValidationError(buildUnsupportedSqlDatasetConnectionMessage(queryOpts.datasetFullName));
204
197
  }
205
198
  try {
206
199
  return await this.executeQuery(retryOpts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataiku-sdk",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "Dataiku DSS SDK and CLI for programmatic access to DSS REST APIs",
5
5
  "type": "module",
6
6
  "workspaces": [