dataiku-sdk 3.8.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.
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Compact default results of the four large read commands (`job get`,
3
+ * `dataset info`, `dataset get`, `project get`), plus the single dispatch
4
+ * point for compacting command results (`shapeCommandResult`).
5
+ *
6
+ * The SDK resources keep returning the raw DSS objects; only the CLI compacts.
7
+ * `--full` returns the DSS object unchanged, and `--fields` projects DSS paths
8
+ * out of it (exactly like the compact `*.list` shapes in list-output.ts).
9
+ *
10
+ * Imports no schema graph: the discovery schemas below are plain data.
11
+ */
12
+ import { isRecord, } from "../utils/records.js";
13
+ import { shapeListResult, } from "./list-output.js";
14
+ import { DATASET_GET_DROPPED_KEYS, PROJECT_GET_DROPPED_KEYS, } from "./read-shapes.js";
15
+ function record(value) {
16
+ return isRecord(value) ? value : {};
17
+ }
18
+ function nonEmptyString(value) {
19
+ return typeof value === "string" && value.length > 0 ? value : undefined;
20
+ }
21
+ /** Drop `undefined` members so absent optional fields are omitted, never null. */
22
+ function defined(fields) {
23
+ return Object.fromEntries(Object.entries(fields).filter(([, value,]) => value !== undefined));
24
+ }
25
+ /** `{ type, message }` of a DSS error object (`error`, `firstFailure`); undefined when empty. */
26
+ function errorSummary(value) {
27
+ if (!isRecord(value))
28
+ return undefined;
29
+ const shaped = defined({
30
+ type: nonEmptyString(value["errorType"]),
31
+ message: nonEmptyString(value["detailedMessage"]) ?? nonEmptyString(value["message"]),
32
+ });
33
+ return Object.keys(shaped).length > 0 ? shaped : undefined;
34
+ }
35
+ /**
36
+ * Output id/project of a job output across DSS shapes: `targetDataset` +
37
+ * `targetDatasetProjectKey`, `targetManagedFolder`, `targetSavedModel`, ...
38
+ * and the plain `{ id, projectKey }` form.
39
+ */
40
+ function outputTarget(output) {
41
+ const targetKey = Object.keys(output).find((key) => /^target[A-Z]/.test(key) && !key.endsWith("ProjectKey") && key !== "targetPartition"
42
+ && typeof output[key] === "string");
43
+ return {
44
+ id: targetKey === undefined ? output["id"] : output[targetKey],
45
+ projectKey: targetKey === undefined ? output["projectKey"] : output[`${targetKey}ProjectKey`],
46
+ };
47
+ }
48
+ function shapeJobOutput(output, jobProjectKey) {
49
+ const fields = record(output);
50
+ const { id, projectKey, } = outputTarget(fields);
51
+ const partition = fields["targetPartition"] ?? fields["partition"];
52
+ return defined({
53
+ type: fields["type"],
54
+ id,
55
+ projectKey: typeof projectKey === "string" && projectKey !== jobProjectKey
56
+ ? projectKey
57
+ : undefined,
58
+ partition: typeof partition === "string" && partition !== "NP" ? partition : undefined,
59
+ });
60
+ }
61
+ function shapeJobActivity(activity) {
62
+ const fields = record(activity);
63
+ return defined({
64
+ id: fields["activityId"],
65
+ recipe: fields["recipeName"],
66
+ recipeType: fields["recipeType"],
67
+ state: fields["state"],
68
+ totalTime: fields["totalTime"],
69
+ warnings: record(fields["warnings"])["totalCount"],
70
+ error: errorSummary(fields["firstFailure"]),
71
+ message: nonEmptyString(fields["message"]),
72
+ });
73
+ }
74
+ /** `job get`: state, outputs, progress, and the failure; never the log tail. */
75
+ function shapeJobGet(result) {
76
+ if (!isRecord(result))
77
+ return result;
78
+ const base = record(result["baseStatus"]);
79
+ const def = record(base["def"]);
80
+ const activities = record(base["activities"]);
81
+ return defined({
82
+ id: def["id"],
83
+ name: def["name"],
84
+ type: def["type"],
85
+ state: base["state"],
86
+ initiator: nonEmptyString(def["initiator"])
87
+ ?? nonEmptyString(record(result["initiator"])["login"]),
88
+ triggeredFrom: def["triggeredFrom"],
89
+ startTime: base["jobStartTime"],
90
+ endTime: base["jobEndTime"],
91
+ outputs: Array.isArray(def["outputs"])
92
+ ? def["outputs"].map((output) => shapeJobOutput(output, def["projectKey"]))
93
+ : undefined,
94
+ progress: result["globalState"],
95
+ error: errorSummary(result["error"]),
96
+ activities: Object.values(activities).map(shapeJobActivity),
97
+ });
98
+ }
99
+ /** Extra column keys worth keeping when set (DSS sends most as ""/defaults). */
100
+ const COLUMN_EXTRA_KEYS = ["meaning", "comment",];
101
+ function shapeColumn(column) {
102
+ const fields = record(column);
103
+ const shaped = { name: fields["name"], type: fields["type"], };
104
+ for (const key of COLUMN_EXTRA_KEYS) {
105
+ const value = nonEmptyString(fields[key]);
106
+ if (value !== undefined)
107
+ shaped[key] = value;
108
+ }
109
+ return shaped;
110
+ }
111
+ function shapeLastBuild(value) {
112
+ if (!isRecord(value))
113
+ return undefined;
114
+ const shaped = defined({
115
+ jobId: value["jobId"],
116
+ success: value["buildSuccess"],
117
+ startTime: value["buildStartTime"],
118
+ endTime: value["buildEndTime"],
119
+ });
120
+ return Object.keys(shaped).length > 0 ? shaped : undefined;
121
+ }
122
+ /** `dataset info`: what the dataset is, its schema, flow neighbours, and freshness. */
123
+ function shapeDatasetInfo(result) {
124
+ if (!isRecord(result))
125
+ return result;
126
+ const dataset = record(result["dataset"]);
127
+ const params = record(dataset["params"]);
128
+ const columns = record(dataset["schema"])["columns"];
129
+ const tags = dataset["tags"];
130
+ const quality = record(result["dataQualityStatus"]);
131
+ const versioning = record(result["versioning"]);
132
+ const timeline = record(result["timeline"]);
133
+ return defined({
134
+ name: result["name"] ?? dataset["name"],
135
+ type: result["type"] ?? dataset["type"],
136
+ managed: dataset["managed"],
137
+ connection: params["connection"] ?? params["uploadConnection"],
138
+ formatType: dataset["formatType"],
139
+ partitioned: result["partitioned"],
140
+ schema: Array.isArray(columns) ? columns.map(shapeColumn) : undefined,
141
+ recipes: Array.isArray(result["recipes"])
142
+ ? result["recipes"].map((recipe) => record(recipe)["name"])
143
+ : undefined,
144
+ buildable: result["buildable"],
145
+ upstreamBuildable: result["upstreamBuildable"],
146
+ downstreamBuildable: result["downstreamBuildable"],
147
+ tags: Array.isArray(tags) && tags.length > 0 ? tags : undefined,
148
+ dataQuality: Object.keys(quality).length > 0 && quality["numberItems"] !== 0
149
+ ? result["dataQualityStatus"]
150
+ : undefined,
151
+ lastBuild: shapeLastBuild(result["lastBuild"]),
152
+ lastModifiedOn: versioning["lastModifiedOn"] ?? timeline["lastModifiedOn"],
153
+ lastModifiedBy: record(versioning["lastModifiedBy"] ?? timeline["lastModifiedBy"])["login"],
154
+ });
155
+ }
156
+ /**
157
+ * Recursively remove empty strings, arrays and objects (including objects that
158
+ * become empty). `false`, `0` and `null` are kept. Array positions are
159
+ * preserved: an element that empties out stays as its empty value.
160
+ */
161
+ function pruneEmpty(value) {
162
+ if (typeof value === "string")
163
+ return value.length === 0 ? undefined : value;
164
+ if (Array.isArray(value)) {
165
+ if (value.length === 0)
166
+ return undefined;
167
+ return value.map((item) => pruneEmpty(item) ?? (typeof item === "string" ? "" : Array.isArray(item) ? [] : {}));
168
+ }
169
+ if (isRecord(value)) {
170
+ const pruned = {};
171
+ for (const [key, member,] of Object.entries(value)) {
172
+ const kept = pruneEmpty(member);
173
+ if (kept !== undefined)
174
+ pruned[key] = kept;
175
+ }
176
+ return Object.keys(pruned).length === 0 ? undefined : pruned;
177
+ }
178
+ return value;
179
+ }
180
+ function withoutKeys(source, keys) {
181
+ return Object.fromEntries(Object.entries(source).filter(([key,]) => !keys.includes(key)));
182
+ }
183
+ /** `dataset get`: the settings object minus metadata and empty/default noise. */
184
+ function shapeDatasetGet(result) {
185
+ if (!isRecord(result))
186
+ return result;
187
+ const settings = withoutKeys(result, DATASET_GET_DROPPED_KEYS);
188
+ const formatParams = settings["formatParams"];
189
+ if (isRecord(formatParams)) {
190
+ settings["formatParams"] = withoutKeys(formatParams, ["hiveSeparators",]);
191
+ }
192
+ return pruneEmpty(settings) ?? {};
193
+ }
194
+ const PERMISSION_KEY = /^can[A-Z]/;
195
+ /**
196
+ * `project get`: as `dataset get`, with the boolean permission flags
197
+ * (`can*`, `isProjectAdmin`) collapsed to the denied ones. Absent from the
198
+ * list means granted, so nothing is lost.
199
+ */
200
+ function shapeProjectGet(result) {
201
+ if (!isRecord(result))
202
+ return result;
203
+ const settings = withoutKeys(result, PROJECT_GET_DROPPED_KEYS);
204
+ const deniedPermissions = [];
205
+ for (const [key, value,] of Object.entries(settings)) {
206
+ if (typeof value !== "boolean" || !(PERMISSION_KEY.test(key) || key === "isProjectAdmin")) {
207
+ continue;
208
+ }
209
+ if (!value)
210
+ deniedPermissions.push(key);
211
+ delete settings[key];
212
+ }
213
+ const pruned = record(pruneEmpty(settings));
214
+ return { ...pruned, deniedPermissions, };
215
+ }
216
+ /** Compact read shapers by `resource.action`. */
217
+ const READ_SHAPERS = {
218
+ "job.get": shapeJobGet,
219
+ "dataset.info": shapeDatasetInfo,
220
+ "dataset.get": shapeDatasetGet,
221
+ "project.get": shapeProjectGet,
222
+ };
223
+ /**
224
+ * Compact a command result for agents: `*.list` items (filter, cap, compact
225
+ * fields) and the four large read commands. `--full` and `--fields` bypass
226
+ * the read shapers so the DSS object (or a projection of it) is returned.
227
+ */
228
+ export function shapeCommandResult(resource, action, result, flags) {
229
+ if (action === "list")
230
+ return shapeListResult(resource, result, flags);
231
+ const shaper = READ_SHAPERS[`${resource}.${action}`];
232
+ if (!shaper || flags["full"] === true || typeof flags["fields"] === "string")
233
+ return result;
234
+ return shaper(result);
235
+ }
@@ -49,7 +49,7 @@ export declare function buildVersionPayload(input: {
49
49
  export declare const PACKAGE_ROOT: string | undefined;
50
50
  export declare const CLI_VERSION: string;
51
51
  export declare const CLI_GIT_REVISION: string | undefined;
52
- export declare const AGENT_CONTRACT_VERSION = 3;
52
+ export declare const AGENT_CONTRACT_VERSION = 4;
53
53
  export declare const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
54
- export declare const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v3.json";
54
+ export declare const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v4.json";
55
55
  export declare function cliVersionResult(): CliVersionPayload;
@@ -204,9 +204,9 @@ export function buildVersionPayload(input) {
204
204
  export const PACKAGE_ROOT = findPackageRoot();
205
205
  export const CLI_VERSION = packageVersion(PACKAGE_ROOT);
206
206
  export const CLI_GIT_REVISION = gitRevision(PACKAGE_ROOT);
207
- export const AGENT_CONTRACT_VERSION = 3;
207
+ export const AGENT_CONTRACT_VERSION = 4;
208
208
  export const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
209
- export const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v3.json";
209
+ export const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v4.json";
210
210
  export function cliVersionResult() {
211
211
  // Only code actually loaded from dist/ may report dist provenance; an
212
212
  // inherited DSS_LOAD_SOURCE on a source run is ignored.
package/dist/src/cli.js CHANGED
@@ -7,8 +7,8 @@ import { commands, } from "./cli/commands/index.js";
7
7
  import { ambientProjectKey, loadEnvFile, } from "./cli/env.js";
8
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";
@@ -1022,8 +1022,7 @@ async function runBatch(flags) {
1022
1022
  await preflightCleanupLedgerForFlags(client, stepFlags);
1023
1023
  const stepArgs = positional.slice(2);
1024
1024
  result = await meta.handler(client, stepArgs, stepFlags);
1025
- if (action === "list")
1026
- result = shapeListResult(resource, result, stepFlags);
1025
+ result = shapeCommandResult(resource, action, result, stepFlags);
1027
1026
  await recordCleanupLedgerEntry(client, resource, action, stepArgs, stepFlags, result, projectKey);
1028
1027
  }
1029
1028
  const failureExitCode = commandFailureExitCode(result);
@@ -1301,13 +1300,14 @@ function errorExitCode(err) {
1301
1300
  }
1302
1301
  function buildErrorReport(err, contextOverride) {
1303
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, } : {};
1304
1305
  const exitCode = errorExitCode(err);
1305
1306
  if (err instanceof ClientValidationError
1306
1307
  && (err.code === "ambiguous_outcome" || err.code === "assertion_failed"
1307
1308
  || err.code === "long_running_failure")) {
1308
1309
  return {
1309
1310
  type: "error",
1310
- ok: false,
1311
1311
  error: err.message,
1312
1312
  code: err.code,
1313
1313
  category: "dss",
@@ -1315,13 +1315,12 @@ function buildErrorReport(err, contextOverride) {
1315
1315
  retryable: false,
1316
1316
  ...(err.hint ? { hint: err.hint, } : {}),
1317
1317
  ...(err.details ? { details: err.details, } : {}),
1318
- ...context,
1318
+ ...scope,
1319
1319
  };
1320
1320
  }
1321
1321
  if (err instanceof UsageError || err instanceof ClientValidationError) {
1322
1322
  return {
1323
1323
  type: "error",
1324
- ok: false,
1325
1324
  error: err.message,
1326
1325
  code: err.code,
1327
1326
  category: err.code === "target_absence_unverifiable"
@@ -1333,26 +1332,24 @@ function buildErrorReport(err, contextOverride) {
1333
1332
  ...(err.code === "target_absence_unverifiable" ? { retryable: false, } : {}),
1334
1333
  ...(err.hint ? { hint: err.hint, } : {}),
1335
1334
  ...(err.details ? { details: err.details, } : {}),
1336
- ...context,
1335
+ ...scope,
1337
1336
  };
1338
1337
  }
1339
1338
  if (err instanceof CommandResultFailure) {
1340
1339
  return {
1341
1340
  type: "error",
1342
- ok: false,
1343
1341
  error: err.message,
1344
1342
  code: err.code,
1345
1343
  category: "dss",
1346
1344
  exitCode: err.exitCode,
1347
1345
  details: boundedFailureResult(err.result),
1348
- ...context,
1346
+ ...scope,
1349
1347
  };
1350
1348
  }
1351
1349
  if (isAmbiguousMutationFailure(err, context)) {
1352
1350
  const method = err.retry?.method.toUpperCase() ?? "mutation";
1353
1351
  return {
1354
1352
  type: "error",
1355
- ok: false,
1356
1353
  error: `The ${method} request failed after dispatch; the mutation outcome is unknown.`,
1357
1354
  code: "ambiguous_outcome",
1358
1355
  category: "dss",
@@ -1369,7 +1366,7 @@ function buildErrorReport(err, contextOverride) {
1369
1366
  ...err.details,
1370
1367
  ...(err.retry ? { retry: err.retry, } : {}),
1371
1368
  },
1372
- ...context,
1369
+ ...scope,
1373
1370
  };
1374
1371
  }
1375
1372
  if (err instanceof DataikuError) {
@@ -1391,7 +1388,6 @@ function buildErrorReport(err, contextOverride) {
1391
1388
  : err.retryHint;
1392
1389
  return {
1393
1390
  type: "error",
1394
- ok: false,
1395
1391
  error: dssMessage ? `${err.safeMessage}: ${dssMessage}` : err.safeMessage,
1396
1392
  code: dataikuErrorCode(err.category),
1397
1393
  category: "dss",
@@ -1401,18 +1397,17 @@ function buildErrorReport(err, contextOverride) {
1401
1397
  retryable: err.retryable,
1402
1398
  requestId: err.requestId ?? requestIdFromBody(err.body),
1403
1399
  ...(Object.keys(details).length > 0 ? { details, } : {}),
1404
- ...context,
1400
+ ...scope,
1405
1401
  };
1406
1402
  }
1407
1403
  const message = err instanceof Error ? err.message : String(err);
1408
1404
  return {
1409
1405
  type: "error",
1410
- ok: false,
1411
1406
  error: message,
1412
1407
  code: "internal_error",
1413
1408
  category: "internal",
1414
1409
  exitCode,
1415
- ...context,
1410
+ ...scope,
1416
1411
  };
1417
1412
  }
1418
1413
  function writeErrorReport(err) {
@@ -1524,7 +1519,7 @@ async function main() {
1524
1519
  assertCleanupLedgerSupported(resource, action, flags);
1525
1520
  await preflightCleanupLedgerForFlags(client, flags);
1526
1521
  const handled = await actionMeta.handler(client, args, flags);
1527
- const result = action === "list" ? shapeListResult(resource, handled, flags) : handled;
1522
+ const result = shapeCommandResult(resource, action, handled, flags);
1528
1523
  await recordCleanupLedgerEntry(client, resource, action, args, flags, result, projectKey);
1529
1524
  const failureExitCode = commandFailureExitCode(result);
1530
1525
  if (failureExitCode !== undefined)
@@ -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
  },