dataiku-sdk 3.7.0 → 3.9.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 (62) hide show
  1. package/README.md +4 -2
  2. package/dist/build-metadata.json +1 -1
  3. package/dist/packages/types/src/index.d.ts +40 -4
  4. package/dist/packages/types/src/index.js +22 -1
  5. package/dist/src/cli/coerce.d.ts +1 -0
  6. package/dist/src/cli/coerce.js +1 -1
  7. package/dist/src/cli/command-syntax.json +156 -0
  8. package/dist/src/cli/commands/dataset.js +44 -8
  9. package/dist/src/cli/commands/flow-zone.js +241 -46
  10. package/dist/src/cli/commands/job.js +52 -9
  11. package/dist/src/cli/commands/project.js +4 -3
  12. package/dist/src/cli/commands/recipe.d.ts +4 -0
  13. package/dist/src/cli/commands/recipe.js +38 -1
  14. package/dist/src/cli/commands/wiki.js +56 -0
  15. package/dist/src/cli/contract.js +13 -8
  16. package/dist/src/cli/doctor.js +2 -0
  17. package/dist/src/cli/flags.d.ts +7 -0
  18. package/dist/src/cli/flags.js +28 -2
  19. package/dist/src/cli/helpers/dataset.d.ts +12 -0
  20. package/dist/src/cli/helpers/dataset.js +36 -0
  21. package/dist/src/cli/helpers/flow-zone.d.ts +21 -0
  22. package/dist/src/cli/helpers/flow-zone.js +144 -4
  23. package/dist/src/cli/output-schemas.js +27 -3
  24. package/dist/src/cli/output.js +6 -0
  25. package/dist/src/cli/plans.js +58 -2
  26. package/dist/src/cli/project-map-output.d.ts +24 -0
  27. package/dist/src/cli/project-map-output.js +21 -0
  28. package/dist/src/cli/read-shapes.d.ts +14 -0
  29. package/dist/src/cli/read-shapes.js +114 -0
  30. package/dist/src/cli/result-shapes.d.ts +6 -0
  31. package/dist/src/cli/result-shapes.js +235 -0
  32. package/dist/src/cli/types.d.ts +2 -0
  33. package/dist/src/cli/version.d.ts +2 -2
  34. package/dist/src/cli/version.js +2 -2
  35. package/dist/src/cli.js +23 -20
  36. package/dist/src/errors.d.ts +11 -1
  37. package/dist/src/errors.js +18 -3
  38. package/dist/src/generated/action-output-schemas.json +224 -35
  39. package/dist/src/resources/dataset-create.d.ts +19 -0
  40. package/dist/src/resources/dataset-create.js +69 -0
  41. package/dist/src/resources/datasets.d.ts +21 -0
  42. package/dist/src/resources/datasets.js +61 -1
  43. package/dist/src/resources/flow-zones.d.ts +29 -3
  44. package/dist/src/resources/flow-zones.js +53 -3
  45. package/dist/src/resources/jobs.d.ts +36 -1
  46. package/dist/src/resources/jobs.js +111 -5
  47. package/dist/src/resources/projects.d.ts +9 -0
  48. package/dist/src/resources/projects.js +10 -3
  49. package/dist/src/resources/recipe-create.d.ts +17 -3
  50. package/dist/src/resources/recipe-create.js +149 -33
  51. package/dist/src/resources/recipes.js +76 -23
  52. package/dist/src/resources/sql.js +52 -13
  53. package/dist/src/resources/wiki.d.ts +47 -1
  54. package/dist/src/resources/wiki.js +89 -0
  55. package/dist/src/utils/flow-analysis.js +4 -0
  56. package/dist/src/utils/secret-sanitize.d.ts +11 -2
  57. package/dist/src/utils/secret-sanitize.js +23 -6
  58. package/package.json +4 -4
  59. package/plugin.json +1 -1
  60. package/skills/dataiku-dss/references/discovery.md +2 -2
  61. package/skills/dataiku-dss/references/flow-maps.md +2 -2
  62. package/skills/dataiku-dss/references/troubleshooting.md +3 -3
package/dist/src/cli.js CHANGED
@@ -5,10 +5,10 @@ import { resolve, } from "node:path";
5
5
  import { num, seenJsonInputCredentials, unknownJsonInput, } from "./cli/coerce.js";
6
6
  import { commands, } from "./cli/commands/index.js";
7
7
  import { ambientProjectKey, loadEnvFile, } from "./cli/env.js";
8
- import { executionMode, FLAG_ALIASES, isNegativeNumberToken, parseArgs, rawPositionals, } from "./cli/flags.js";
8
+ import { executionMode, FLAG_ALIASES, isFlagValueToken, parseArgs, rawPositionals, } from "./cli/flags.js";
9
9
  import { cleanupLedgerEntry, } from "./cli/helpers/cleanup.js";
10
- import { shapeListResult, } from "./cli/list-output.js";
11
10
  import { commandFailureExitCode, CommandResultFailure, enqueueCliWarning, flushCliWarnings, isFailedWaitResult, planResult, projectResultFields, setOutputFieldProjection, writeCommandResult, } from "./cli/output.js";
11
+ import { shapeCommandResult, } from "./cli/result-shapes.js";
12
12
  import { currentCommandContext, resolveCredentials, resolveLoginCredentials, } from "./cli/runtime.js";
13
13
  import { commandSyntaxTree, commandUsage, } from "./cli/syntax.js";
14
14
  import { COMMANDS_RUN_HINT, inferRequiresProject, missingActionError, noCommandError, requireArgs, RESOURCE_NAMES, unknownActionError, unknownFlagError, unknownResourceError, UsageError, } from "./cli/usage.js";
@@ -533,8 +533,7 @@ function redactArgv(argv) {
533
533
  else {
534
534
  redacted.push(arg);
535
535
  const next = argv[index + 1];
536
- if (next !== undefined
537
- && (next === "-" || !next.startsWith("-") || isNegativeNumberToken(next))) {
536
+ if (next !== undefined && isFlagValueToken(next)) {
538
537
  redacted.push("***");
539
538
  index++;
540
539
  }
@@ -1023,8 +1022,7 @@ async function runBatch(flags) {
1023
1022
  await preflightCleanupLedgerForFlags(client, stepFlags);
1024
1023
  const stepArgs = positional.slice(2);
1025
1024
  result = await meta.handler(client, stepArgs, stepFlags);
1026
- if (action === "list")
1027
- result = shapeListResult(resource, result, stepFlags);
1025
+ result = shapeCommandResult(resource, action, result, stepFlags);
1028
1026
  await recordCleanupLedgerEntry(client, resource, action, stepArgs, stepFlags, result, projectKey);
1029
1027
  }
1030
1028
  const failureExitCode = commandFailureExitCode(result);
@@ -1247,6 +1245,10 @@ function dssDiagnosticDetails(err) {
1247
1245
  function isAmbiguousMutationFailure(err, context) {
1248
1246
  if (!(err instanceof DataikuError))
1249
1247
  return false;
1248
+ // DSS definitively refused the operation (e.g. the database rejected the SQL):
1249
+ // nothing took effect, so there is no outcome to verify.
1250
+ if (err.definiteFailure)
1251
+ return false;
1250
1252
  const method = err.retry?.method.toUpperCase();
1251
1253
  if (!method || method === "GET" || method === "HEAD")
1252
1254
  return false;
@@ -1298,13 +1300,14 @@ function errorExitCode(err) {
1298
1300
  }
1299
1301
  function buildErrorReport(err, contextOverride) {
1300
1302
  const context = contextOverride ?? rawCommandContext();
1303
+ // The envelope echoes only the resolved project; resource/action are known to the caller.
1304
+ const scope = context.projectKey ? { projectKey: context.projectKey, } : {};
1301
1305
  const exitCode = errorExitCode(err);
1302
1306
  if (err instanceof ClientValidationError
1303
1307
  && (err.code === "ambiguous_outcome" || err.code === "assertion_failed"
1304
1308
  || err.code === "long_running_failure")) {
1305
1309
  return {
1306
1310
  type: "error",
1307
- ok: false,
1308
1311
  error: err.message,
1309
1312
  code: err.code,
1310
1313
  category: "dss",
@@ -1312,13 +1315,12 @@ function buildErrorReport(err, contextOverride) {
1312
1315
  retryable: false,
1313
1316
  ...(err.hint ? { hint: err.hint, } : {}),
1314
1317
  ...(err.details ? { details: err.details, } : {}),
1315
- ...context,
1318
+ ...scope,
1316
1319
  };
1317
1320
  }
1318
1321
  if (err instanceof UsageError || err instanceof ClientValidationError) {
1319
1322
  return {
1320
1323
  type: "error",
1321
- ok: false,
1322
1324
  error: err.message,
1323
1325
  code: err.code,
1324
1326
  category: err.code === "target_absence_unverifiable"
@@ -1330,26 +1332,24 @@ function buildErrorReport(err, contextOverride) {
1330
1332
  ...(err.code === "target_absence_unverifiable" ? { retryable: false, } : {}),
1331
1333
  ...(err.hint ? { hint: err.hint, } : {}),
1332
1334
  ...(err.details ? { details: err.details, } : {}),
1333
- ...context,
1335
+ ...scope,
1334
1336
  };
1335
1337
  }
1336
1338
  if (err instanceof CommandResultFailure) {
1337
1339
  return {
1338
1340
  type: "error",
1339
- ok: false,
1340
1341
  error: err.message,
1341
1342
  code: err.code,
1342
1343
  category: "dss",
1343
1344
  exitCode: err.exitCode,
1344
1345
  details: boundedFailureResult(err.result),
1345
- ...context,
1346
+ ...scope,
1346
1347
  };
1347
1348
  }
1348
1349
  if (isAmbiguousMutationFailure(err, context)) {
1349
1350
  const method = err.retry?.method.toUpperCase() ?? "mutation";
1350
1351
  return {
1351
1352
  type: "error",
1352
- ok: false,
1353
1353
  error: `The ${method} request failed after dispatch; the mutation outcome is unknown.`,
1354
1354
  code: "ambiguous_outcome",
1355
1355
  category: "dss",
@@ -1363,14 +1363,19 @@ function buildErrorReport(err, contextOverride) {
1363
1363
  idempotency: "none",
1364
1364
  ...dssErrorDetails(err),
1365
1365
  ...dssDiagnosticDetails(err),
1366
+ ...err.details,
1366
1367
  ...(err.retry ? { retry: err.retry, } : {}),
1367
1368
  },
1368
- ...context,
1369
+ ...scope,
1369
1370
  };
1370
1371
  }
1371
1372
  if (err instanceof DataikuError) {
1372
1373
  const { dssMessage, dssErrorType, } = dssDiagnosticDetails(err);
1373
- const details = { ...dssErrorDetails(err), ...(dssErrorType ? { dssErrorType, } : {}), };
1374
+ const details = {
1375
+ ...dssErrorDetails(err),
1376
+ ...(dssErrorType ? { dssErrorType, } : {}),
1377
+ ...err.details,
1378
+ };
1374
1379
  // Generic hints name what the command targeted. DSS answers 403 (not
1375
1380
  // 404) for a project that does not exist: "Failed to read project
1376
1381
  // permissions". A 403 about something else keeps the generic hint.
@@ -1383,7 +1388,6 @@ function buildErrorReport(err, contextOverride) {
1383
1388
  : err.retryHint;
1384
1389
  return {
1385
1390
  type: "error",
1386
- ok: false,
1387
1391
  error: dssMessage ? `${err.safeMessage}: ${dssMessage}` : err.safeMessage,
1388
1392
  code: dataikuErrorCode(err.category),
1389
1393
  category: "dss",
@@ -1393,18 +1397,17 @@ function buildErrorReport(err, contextOverride) {
1393
1397
  retryable: err.retryable,
1394
1398
  requestId: err.requestId ?? requestIdFromBody(err.body),
1395
1399
  ...(Object.keys(details).length > 0 ? { details, } : {}),
1396
- ...context,
1400
+ ...scope,
1397
1401
  };
1398
1402
  }
1399
1403
  const message = err instanceof Error ? err.message : String(err);
1400
1404
  return {
1401
1405
  type: "error",
1402
- ok: false,
1403
1406
  error: message,
1404
1407
  code: "internal_error",
1405
1408
  category: "internal",
1406
1409
  exitCode,
1407
- ...context,
1410
+ ...scope,
1408
1411
  };
1409
1412
  }
1410
1413
  function writeErrorReport(err) {
@@ -1516,7 +1519,7 @@ async function main() {
1516
1519
  assertCleanupLedgerSupported(resource, action, flags);
1517
1520
  await preflightCleanupLedgerForFlags(client, flags);
1518
1521
  const handled = await actionMeta.handler(client, args, flags);
1519
- const result = action === "list" ? shapeListResult(resource, handled, flags) : handled;
1522
+ const result = shapeCommandResult(resource, action, handled, flags);
1520
1523
  await recordCleanupLedgerEntry(client, resource, action, args, flags, result, projectKey);
1521
1524
  const failureExitCode = commandFailureExitCode(result);
1522
1525
  if (failureExitCode !== undefined)
@@ -20,12 +20,18 @@ export interface DataikuErrorTrustedMetadata {
20
20
  elapsedMs?: number;
21
21
  /** Set by the transport when the retained response body is only a bounded prefix. */
22
22
  bodyTruncated?: boolean;
23
+ /**
24
+ * Set by a resource that recognized DSS's answer as a definite failure of the
25
+ * whole operation: nothing took effect, so a non-idempotent mutation is not
26
+ * ambiguous.
27
+ */
28
+ definiteFailure?: boolean;
23
29
  }
24
30
  export declare function canonicalStatusText(status: number): string;
25
31
  export declare class ClientValidationError extends Error {
26
32
  readonly code: StableErrorCode;
27
33
  readonly hint?: string;
28
- readonly details?: Record<string, unknown>;
34
+ details?: Record<string, unknown>;
29
35
  constructor(message: string, code?: StableErrorCode, hint?: string, details?: Record<string, unknown>, options?: ErrorOptions);
30
36
  }
31
37
  /**
@@ -62,6 +68,10 @@ export declare class DataikuError extends Error {
62
68
  readonly trustedTarget?: string;
63
69
  readonly trustedElapsedMs?: number;
64
70
  readonly bodyTruncated?: boolean;
71
+ /** DSS definitively failed the whole operation; see `DataikuErrorTrustedMetadata.definiteFailure`. */
72
+ readonly definiteFailure?: boolean;
73
+ /** Client-side outcome context (e.g. rollback of partial mutations), surfaced in CLI `details`. */
74
+ details?: Record<string, unknown>;
65
75
  constructor(status: number, statusText: string, body: string, retry?: DataikuRetryMetadata, requestId?: string, trustedMetadata?: DataikuErrorTrustedMetadata);
66
76
  /** Status line only: never echoes response body text. */
67
77
  get safeMessage(): string;
@@ -74,6 +74,8 @@ export const UNEXPECTED_RESPONSE_MARKER = "Unexpected DSS response";
74
74
  export function unexpectedResponseError(detail, status = 200) {
75
75
  return new DataikuError(status, "Unexpected Response", `${UNEXPECTED_RESPONSE_MARKER}: ${detail}`);
76
76
  }
77
+ /** Hint for a DSS answer that definitively failed the request (e.g. the database rejected the SQL). */
78
+ const DEFINITE_FAILURE_HINT = "DSS reported that the request failed (for SQL: syntax error, missing object, or missing privileges of the connection's role). Fix the request or grants; do not retry unchanged.";
77
79
  /** Prefix for a SQL query failure reported by DSS on a 2xx finish-streaming response. */
78
80
  export const SQL_QUERY_FAILED_MARKER = "SQL query failed in DSS";
79
81
  const TLS_CERTIFICATE_HINT = "TLS certificate verification failed. Trust the DSS/corporate CA with --ca-cert PATH or NODE_EXTRA_CA_CERTS, or save it for every process and directory with `dss auth login --ca-cert PATH` (a project .env applies only when dss runs in that directory); use --insecure only for temporary troubleshooting.";
@@ -140,6 +142,8 @@ function isCertificateTrustFailure(lowerBody) {
140
142
  || lowerBody.includes("unable to get local issuer certificate")
141
143
  || lowerBody.includes("unable to verify leaf signature")
142
144
  || lowerBody.includes("self-signed certificate")
145
+ || lowerBody.includes("self signed certificate")
146
+ || lowerBody.includes("depth_zero_self_signed_cert")
143
147
  || lowerBody.includes("certificate has expired")
144
148
  || lowerBody.includes("cert_has_expired")
145
149
  || lowerBody.includes("self_signed_cert_in_chain")
@@ -372,6 +376,10 @@ export class DataikuError extends Error {
372
376
  trustedTarget;
373
377
  trustedElapsedMs;
374
378
  bodyTruncated;
379
+ /** DSS definitively failed the whole operation; see `DataikuErrorTrustedMetadata.definiteFailure`. */
380
+ definiteFailure;
381
+ /** Client-side outcome context (e.g. rollback of partial mutations), surfaced in CLI `details`. */
382
+ details;
375
383
  constructor(status, statusText, body, retry, requestId, trustedMetadata) {
376
384
  const details = DataikuError.buildDetails(status, body);
377
385
  super(details.message);
@@ -379,14 +387,21 @@ export class DataikuError extends Error {
379
387
  this.statusText = statusText;
380
388
  this.body = body;
381
389
  this.name = "DataikuError";
382
- this.category = details.category;
383
- this.retryable = details.retryable;
384
- this.retryHint = details.retryHint;
390
+ const definite = trustedMetadata?.definiteFailure === true;
391
+ // A definite DSS refusal is not a transport/server fault: replaying it
392
+ // unchanged cannot succeed. Keep a more specific forbidden/not_found class.
393
+ const reclassify = definite
394
+ && (details.category === "transient" || details.category === "unknown"
395
+ || details.category === "unexpected_response");
396
+ this.category = reclassify ? "validation" : details.category;
397
+ this.retryable = reclassify ? false : details.retryable;
398
+ this.retryHint = reclassify ? DEFINITE_FAILURE_HINT : details.retryHint;
385
399
  this.retry = retry;
386
400
  this.requestId = requestId;
387
401
  this.trustedTarget = trustedMetadata?.target;
388
402
  this.trustedElapsedMs = trustedMetadata?.elapsedMs;
389
403
  this.bodyTruncated = trustedMetadata?.bodyTruncated === true ? true : undefined;
404
+ this.definiteFailure = definite ? true : undefined;
390
405
  }
391
406
  /** Status line only: never echoes response body text. */
392
407
  get safeMessage() {
@@ -363,6 +363,107 @@
363
363
  }
364
364
  }
365
365
  },
366
+ "dataset.info": {
367
+ "type": "object",
368
+ "additionalProperties": false,
369
+ "required": [
370
+ "name"
371
+ ],
372
+ "properties": {
373
+ "name": {
374
+ "type": "string"
375
+ },
376
+ "type": {
377
+ "type": "string"
378
+ },
379
+ "managed": {
380
+ "type": "boolean"
381
+ },
382
+ "connection": {
383
+ "type": "string"
384
+ },
385
+ "formatType": {
386
+ "type": "string"
387
+ },
388
+ "partitioned": {
389
+ "type": "boolean"
390
+ },
391
+ "schema": {
392
+ "type": "array",
393
+ "items": {
394
+ "type": "object",
395
+ "additionalProperties": false,
396
+ "required": [
397
+ "name",
398
+ "type"
399
+ ],
400
+ "properties": {
401
+ "name": {
402
+ "type": "string"
403
+ },
404
+ "type": {
405
+ "type": "string"
406
+ },
407
+ "meaning": {
408
+ "type": "string"
409
+ },
410
+ "comment": {
411
+ "type": "string"
412
+ }
413
+ }
414
+ }
415
+ },
416
+ "recipes": {
417
+ "type": "array",
418
+ "items": {
419
+ "type": "string"
420
+ }
421
+ },
422
+ "buildable": {
423
+ "type": "boolean"
424
+ },
425
+ "upstreamBuildable": {
426
+ "type": "boolean"
427
+ },
428
+ "downstreamBuildable": {
429
+ "type": "boolean"
430
+ },
431
+ "tags": {
432
+ "type": "array",
433
+ "items": {
434
+ "type": "string"
435
+ }
436
+ },
437
+ "dataQuality": {
438
+ "type": "object",
439
+ "additionalProperties": true
440
+ },
441
+ "lastBuild": {
442
+ "type": "object",
443
+ "additionalProperties": false,
444
+ "properties": {
445
+ "jobId": {
446
+ "type": "string"
447
+ },
448
+ "success": {
449
+ "type": "boolean"
450
+ },
451
+ "startTime": {
452
+ "type": "number"
453
+ },
454
+ "endTime": {
455
+ "type": "number"
456
+ }
457
+ }
458
+ },
459
+ "lastModifiedOn": {
460
+ "type": "number"
461
+ },
462
+ "lastModifiedBy": {
463
+ "type": "string"
464
+ }
465
+ }
466
+ },
366
467
  "dataset.list": {
367
468
  "type": "array",
368
469
  "items": {
@@ -825,6 +926,121 @@
825
926
  }
826
927
  }
827
928
  },
929
+ "job.get": {
930
+ "type": "object",
931
+ "additionalProperties": false,
932
+ "required": [
933
+ "id",
934
+ "state"
935
+ ],
936
+ "properties": {
937
+ "id": {
938
+ "type": "string"
939
+ },
940
+ "name": {
941
+ "type": "string"
942
+ },
943
+ "type": {
944
+ "type": "string"
945
+ },
946
+ "state": {
947
+ "type": "string"
948
+ },
949
+ "initiator": {
950
+ "type": "string"
951
+ },
952
+ "triggeredFrom": {
953
+ "type": "string"
954
+ },
955
+ "startTime": {
956
+ "type": "number"
957
+ },
958
+ "endTime": {
959
+ "type": "number"
960
+ },
961
+ "outputs": {
962
+ "type": "array",
963
+ "items": {
964
+ "type": "object",
965
+ "additionalProperties": false,
966
+ "properties": {
967
+ "type": {
968
+ "type": "string"
969
+ },
970
+ "id": {
971
+ "type": "string"
972
+ },
973
+ "projectKey": {
974
+ "type": "string"
975
+ },
976
+ "partition": {
977
+ "type": "string"
978
+ }
979
+ }
980
+ }
981
+ },
982
+ "progress": {
983
+ "type": "object",
984
+ "additionalProperties": {
985
+ "type": "number"
986
+ }
987
+ },
988
+ "error": {
989
+ "type": "object",
990
+ "additionalProperties": false,
991
+ "properties": {
992
+ "type": {
993
+ "type": "string"
994
+ },
995
+ "message": {
996
+ "type": "string"
997
+ }
998
+ }
999
+ },
1000
+ "activities": {
1001
+ "type": "array",
1002
+ "items": {
1003
+ "type": "object",
1004
+ "additionalProperties": false,
1005
+ "properties": {
1006
+ "id": {
1007
+ "type": "string"
1008
+ },
1009
+ "recipe": {
1010
+ "type": "string"
1011
+ },
1012
+ "recipeType": {
1013
+ "type": "string"
1014
+ },
1015
+ "state": {
1016
+ "type": "string"
1017
+ },
1018
+ "totalTime": {
1019
+ "type": "number"
1020
+ },
1021
+ "warnings": {
1022
+ "type": "number"
1023
+ },
1024
+ "error": {
1025
+ "type": "object",
1026
+ "additionalProperties": false,
1027
+ "properties": {
1028
+ "type": {
1029
+ "type": "string"
1030
+ },
1031
+ "message": {
1032
+ "type": "string"
1033
+ }
1034
+ }
1035
+ },
1036
+ "message": {
1037
+ "type": "string"
1038
+ }
1039
+ }
1040
+ }
1041
+ }
1042
+ }
1043
+ },
828
1044
  "job.list": {
829
1045
  "type": "array",
830
1046
  "items": {
@@ -1519,7 +1735,8 @@
1519
1735
  "type": "object",
1520
1736
  "required": [
1521
1737
  "projectKey",
1522
- "name"
1738
+ "name",
1739
+ "deniedPermissions"
1523
1740
  ],
1524
1741
  "properties": {
1525
1742
  "projectKey": {
@@ -1537,25 +1754,6 @@
1537
1754
  "projectAppType": {
1538
1755
  "type": "string"
1539
1756
  },
1540
- "creationTag": {
1541
- "additionalProperties": true,
1542
- "type": "object",
1543
- "properties": {
1544
- "versionNumber": {
1545
- "type": "number"
1546
- },
1547
- "lastModifiedOn": {
1548
- "anyOf": [
1549
- {
1550
- "type": "number"
1551
- },
1552
- {
1553
- "type": "string"
1554
- }
1555
- ]
1556
- }
1557
- }
1558
- },
1559
1757
  "ownerLogin": {
1560
1758
  "type": "string"
1561
1759
  },
@@ -1565,21 +1763,12 @@
1565
1763
  "type": "string"
1566
1764
  }
1567
1765
  },
1568
- "versionTag": {
1569
- "additionalProperties": true,
1570
- "type": "object",
1571
- "required": [
1572
- "versionNumber",
1573
- "lastModifiedOn"
1574
- ],
1575
- "properties": {
1576
- "versionNumber": {
1577
- "type": "number"
1578
- },
1579
- "lastModifiedOn": {
1580
- "type": "number"
1581
- }
1582
- }
1766
+ "deniedPermissions": {
1767
+ "type": "array",
1768
+ "items": {
1769
+ "type": "string"
1770
+ },
1771
+ "description": "Permission flags DSS reports as false; absent flags are granted."
1583
1772
  }
1584
1773
  }
1585
1774
  },
@@ -4,6 +4,24 @@
4
4
  * public endpoint writes; without it every downstream build fails.
5
5
  */
6
6
  export declare function assertDatasetTypeCreatable(dsType: string): void;
7
+ /**
8
+ * Dataset types that read from a SQL connection. These are the types DSS
9
+ * detects schemas for through `testAndDetectSettings/externalSQL` (the list
10
+ * dataikuapi's `DSSDataset.autodetect_settings` uses).
11
+ */
12
+ export declare const SQL_DATASET_TYPES: readonly ["JDBC", "PostgreSQL", "MySQL", "Vertica", "Snowflake", "Redshift", "Greenplum", "Teradata", "Oracle", "SQLServer", "SAPHANA", "Netezza", "BigQuery", "Athena", "hiveserver2", "Synapse", "FabricWarehouse", "Databricks", "DatabricksLakebase"];
13
+ export declare function isSqlDatasetType(dsType: string): boolean;
14
+ /**
15
+ * Validate the SQL source options (`table`/`dbSchema`/`catalog`/`query`) of a
16
+ * dataset creation against its type. Shared by `dataset create`, its plan, and
17
+ * the SDK so every entry point rejects the same combinations.
18
+ */
19
+ export declare function assertSqlDatasetSource(dsType: string, source: {
20
+ table?: string;
21
+ dbSchema?: string;
22
+ catalog?: string;
23
+ query?: string;
24
+ }): void;
7
25
  /** Wire body for POST /datasets/; shared with `dataset create --plan` so the plan matches the request. */
8
26
  export declare function buildDatasetCreateBody(opts: {
9
27
  projectKey: string;
@@ -13,6 +31,7 @@ export declare function buildDatasetCreateBody(opts: {
13
31
  table?: string;
14
32
  dbSchema?: string;
15
33
  catalog?: string;
34
+ query?: string;
16
35
  formatType?: string;
17
36
  formatParams?: Record<string, unknown>;
18
37
  managed?: boolean;
@@ -9,6 +9,60 @@ export function assertDatasetTypeCreatable(dsType) {
9
9
  return;
10
10
  throw new ClientValidationError("Inline (editable) datasets cannot be created here: the DSS public API cannot write their rows, and an Inline dataset without rows fails every downstream build (SourceDatasetNotReadyException). For a small static table, create an UploadedFiles dataset and upload a CSV with dss dataset upload-file, or write the rows from a python recipe into a managed dataset.", "validation_failed");
11
11
  }
12
+ /**
13
+ * Dataset types that read from a SQL connection. These are the types DSS
14
+ * detects schemas for through `testAndDetectSettings/externalSQL` (the list
15
+ * dataikuapi's `DSSDataset.autodetect_settings` uses).
16
+ */
17
+ export const SQL_DATASET_TYPES = [
18
+ "JDBC",
19
+ "PostgreSQL",
20
+ "MySQL",
21
+ "Vertica",
22
+ "Snowflake",
23
+ "Redshift",
24
+ "Greenplum",
25
+ "Teradata",
26
+ "Oracle",
27
+ "SQLServer",
28
+ "SAPHANA",
29
+ "Netezza",
30
+ "BigQuery",
31
+ "Athena",
32
+ "hiveserver2",
33
+ "Synapse",
34
+ "FabricWarehouse",
35
+ "Databricks",
36
+ "DatabricksLakebase",
37
+ ];
38
+ export function isSqlDatasetType(dsType) {
39
+ const lower = dsType.toLowerCase();
40
+ return SQL_DATASET_TYPES.some((type) => type.toLowerCase() === lower);
41
+ }
42
+ /**
43
+ * Validate the SQL source options (`table`/`dbSchema`/`catalog`/`query`) of a
44
+ * dataset creation against its type. Shared by `dataset create`, its plan, and
45
+ * the SDK so every entry point rejects the same combinations.
46
+ */
47
+ export function assertSqlDatasetSource(dsType, source) {
48
+ const hasTable = source.table !== undefined;
49
+ const hasQuery = source.query !== undefined;
50
+ if (hasTable && hasQuery) {
51
+ throw new ClientValidationError("Pass either a table or a query for a SQL dataset, not both.", "conflicting_input_sources");
52
+ }
53
+ if (!hasTable && (source.dbSchema !== undefined || source.catalog !== undefined)) {
54
+ throw new ClientValidationError("A schema or catalog only applies together with a table.", "validation_failed", "Add --table, or drop --schema/--catalog (a query names its own tables).");
55
+ }
56
+ if (hasTable && source.table.trim() === "") {
57
+ throw new ClientValidationError("table must be a non-empty table name.", "invalid_flag_value");
58
+ }
59
+ if (hasQuery && source.query.trim() === "") {
60
+ throw new ClientValidationError("query must be a non-empty SQL query.", "invalid_flag_value");
61
+ }
62
+ if ((hasTable || hasQuery) && !isSqlDatasetType(dsType)) {
63
+ throw new ClientValidationError(`A table or query only applies to SQL dataset types, not "${dsType}".`, "validation_failed", `SQL types: ${SQL_DATASET_TYPES.join(", ")}.`);
64
+ }
65
+ }
12
66
  /** Wire body for POST /datasets/; shared with `dataset create --plan` so the plan matches the request. */
13
67
  export function buildDatasetCreateBody(opts) {
14
68
  assertDatasetTypeCreatable(opts.dsType);
@@ -23,6 +77,21 @@ export function buildDatasetCreateBody(opts) {
23
77
  if (!opts.connection) {
24
78
  throw new ClientValidationError("connection is required unless dsType is UploadedFiles.");
25
79
  }
80
+ if (opts.query !== undefined) {
81
+ if (opts.table !== undefined) {
82
+ throw new ClientValidationError("Pass either a table or a query for a SQL dataset, not both.", "conflicting_input_sources");
83
+ }
84
+ if (opts.query.trim() === "") {
85
+ throw new ClientValidationError("query must be a non-empty SQL query.", "invalid_flag_value");
86
+ }
87
+ return {
88
+ projectKey: opts.projectKey,
89
+ name: opts.datasetName,
90
+ type: opts.dsType,
91
+ params: { connection: opts.connection, mode: "query", query: opts.query, },
92
+ managed: opts.managed ?? false,
93
+ };
94
+ }
26
95
  if (opts.table) {
27
96
  const params = {
28
97
  connection: opts.connection,