dataiku-sdk 3.3.0 → 3.5.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/README.md +6 -4
- package/dist/build-metadata.json +1 -1
- package/dist/src/cli/command-syntax.json +14524 -0
- package/dist/src/cli/commands/analysis.js +6 -9
- package/dist/src/cli/commands/api-deployer.js +15 -33
- package/dist/src/cli/commands/api-service.js +17 -27
- package/dist/src/cli/commands/app.js +22 -39
- package/dist/src/cli/commands/bundle.js +15 -24
- package/dist/src/cli/commands/business-app.js +10 -17
- package/dist/src/cli/commands/code-env.js +23 -36
- package/dist/src/cli/commands/code.js +5 -5
- package/dist/src/cli/commands/connection.js +12 -22
- package/dist/src/cli/commands/continuous-activity.js +6 -9
- package/dist/src/cli/commands/dashboard.js +8 -13
- package/dist/src/cli/commands/data-collection.js +9 -16
- package/dist/src/cli/commands/data-quality.js +14 -26
- package/dist/src/cli/commands/dataset.js +28 -51
- package/dist/src/cli/commands/discussion.js +7 -10
- package/dist/src/cli/commands/doctor.js +3 -3
- package/dist/src/cli/commands/flow-zone.js +8 -17
- package/dist/src/cli/commands/folder.js +12 -20
- package/dist/src/cli/commands/future.js +7 -10
- package/dist/src/cli/commands/group.js +7 -11
- package/dist/src/cli/commands/insight.js +7 -11
- package/dist/src/cli/commands/job.js +17 -25
- package/dist/src/cli/commands/knowledge-bank.js +5 -6
- package/dist/src/cli/commands/llm.js +5 -8
- package/dist/src/cli/commands/macro.js +11 -18
- package/dist/src/cli/commands/meaning.js +7 -11
- package/dist/src/cli/commands/metrics.js +7 -10
- package/dist/src/cli/commands/ml-task.js +12 -20
- package/dist/src/cli/commands/model-evaluation-store.js +7 -11
- package/dist/src/cli/commands/notebook.js +15 -27
- package/dist/src/cli/commands/plugin.js +35 -68
- package/dist/src/cli/commands/project-deployer.js +10 -22
- package/dist/src/cli/commands/project-folder.js +10 -17
- package/dist/src/cli/commands/project-git.js +39 -71
- package/dist/src/cli/commands/project-library.js +15 -23
- package/dist/src/cli/commands/project.js +9 -26
- package/dist/src/cli/commands/recipe.d.ts +16 -0
- package/dist/src/cli/commands/recipe.js +83 -94
- package/dist/src/cli/commands/saved-model.js +23 -39
- package/dist/src/cli/commands/scenario.js +21 -35
- package/dist/src/cli/commands/sql.js +4 -4
- package/dist/src/cli/commands/statistics.js +11 -18
- package/dist/src/cli/commands/streaming-endpoint.js +7 -11
- package/dist/src/cli/commands/user.js +11 -22
- package/dist/src/cli/commands/variable.js +3 -4
- package/dist/src/cli/commands/webapp.js +10 -16
- package/dist/src/cli/commands/wiki.js +9 -13
- package/dist/src/cli/commands/workspace.js +8 -14
- package/dist/src/cli/contract.d.ts +5 -15
- package/dist/src/cli/contract.js +179 -461
- package/dist/src/cli/doctor.js +1 -0
- package/dist/src/cli/env.d.ts +2 -0
- package/dist/src/cli/env.js +15 -1
- package/dist/src/cli/helpers/app-permissions.js +2 -1
- package/dist/src/cli/helpers/flow-zone.js +2 -1
- package/dist/src/cli/helpers/recipe.js +2 -1
- package/dist/src/cli/output-schemas.d.ts +24 -0
- package/dist/src/cli/output-schemas.js +233 -0
- package/dist/src/cli/syntax.d.ts +83 -0
- package/dist/src/cli/syntax.js +217 -0
- package/dist/src/cli/types.d.ts +3 -0
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +4 -2
- package/dist/src/cli/version.js +2 -0
- package/dist/src/cli.js +37 -64
- package/dist/src/client.d.ts +10 -1
- package/dist/src/client.js +27 -11
- package/dist/src/errors.d.ts +12 -1
- package/dist/src/errors.js +34 -7
- package/dist/src/generated/action-output-schemas.json +1735 -0
- package/dist/src/index.d.ts +1 -3
- package/dist/src/index.js +2 -2
- package/dist/src/resources/api-services.js +2 -3
- package/dist/src/resources/applications.js +2 -4
- package/dist/src/resources/code-envs.js +2 -2
- package/dist/src/resources/data-quality.js +4 -3
- package/dist/src/resources/datasets.d.ts +13 -0
- package/dist/src/resources/datasets.js +42 -8
- package/dist/src/resources/flow-zones.d.ts +6 -2
- package/dist/src/resources/flow-zones.js +10 -5
- package/dist/src/resources/folders.js +4 -2
- package/dist/src/resources/futures.d.ts +2 -0
- package/dist/src/resources/futures.js +39 -27
- package/dist/src/resources/insights.js +3 -2
- package/dist/src/resources/jobs.js +41 -31
- package/dist/src/resources/macros.js +2 -4
- package/dist/src/resources/ml-tasks.js +28 -19
- package/dist/src/resources/notebooks.js +2 -3
- package/dist/src/resources/plugins.d.ts +0 -2
- package/dist/src/resources/plugins.js +0 -3
- package/dist/src/resources/project-git.d.ts +6 -12
- package/dist/src/resources/project-git.js +31 -58
- package/dist/src/resources/project-library.d.ts +0 -2
- package/dist/src/resources/project-library.js +2 -7
- package/dist/src/resources/projects.d.ts +4 -2
- package/dist/src/resources/projects.js +37 -36
- package/dist/src/resources/recipes.d.ts +28 -0
- package/dist/src/resources/recipes.js +116 -84
- package/dist/src/resources/sql.js +8 -3
- package/dist/src/resources/statistics.js +2 -1
- package/dist/src/resources/variables.js +2 -1
- package/dist/src/resources/webapps.js +2 -3
- package/dist/src/schemas.d.ts +1 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/skill.js +6 -7
- package/dist/src/utils/cleanup-ledger.js +9 -8
- package/dist/src/utils/git-reference.d.ts +12 -0
- package/dist/src/utils/git-reference.js +38 -0
- package/dist/src/utils/polling.d.ts +3 -0
- package/dist/src/utils/polling.js +3 -0
- package/dist/src/utils/project-archive.js +2 -2
- package/dist/src/utils/stable-hash.d.ts +3 -1
- package/dist/src/utils/stable-hash.js +2 -0
- package/package.json +8 -8
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +3 -2
- package/skills/dataiku-dss/references/app-releases.md +2 -3
- package/skills/dataiku-dss/references/authentication.md +7 -7
- package/skills/dataiku-dss/references/coding.md +1 -1
- package/skills/dataiku-dss/references/mutations.md +1 -1
- package/skills/dataiku-dss/references/troubleshooting.md +2 -2
- package/dist/src/utils/pagination.d.ts +0 -8
- package/dist/src/utils/pagination.js +0 -23
|
@@ -2,6 +2,7 @@ import { sanitizeErrorSecrets, sanitizeSecrets, } from "../../utils/secret-sanit
|
|
|
2
2
|
import { requiredJsonInput, } from "../coerce.js";
|
|
3
3
|
import { executionMode, } from "../flags.js";
|
|
4
4
|
import { readIfExists, skipResult, } from "../output.js";
|
|
5
|
+
import { commandUsage, withUsage, } from "../syntax.js";
|
|
5
6
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
6
7
|
/**
|
|
7
8
|
* Keys of a connection payload that carry credentials. CLI plan/error/output
|
|
@@ -76,12 +77,11 @@ function requireConnectionName(value, usage) {
|
|
|
76
77
|
}
|
|
77
78
|
return trimmed;
|
|
78
79
|
}
|
|
79
|
-
export const connectionCommands = {
|
|
80
|
+
export const connectionCommands = withUsage("connection", {
|
|
80
81
|
list: {
|
|
81
82
|
handler: (c, _a, f) => c.connections.list({
|
|
82
83
|
type: f["type"],
|
|
83
84
|
}),
|
|
84
|
-
usage: "dss connection list [--type TYPE]",
|
|
85
85
|
description: "List all connection names, optionally filtered by connection type.",
|
|
86
86
|
examples: ["dss connection list", "dss connection list --type Filesystem",],
|
|
87
87
|
},
|
|
@@ -90,24 +90,22 @@ export const connectionCommands = {
|
|
|
90
90
|
mode: f["mode"],
|
|
91
91
|
projectKey: f["project-key"],
|
|
92
92
|
}),
|
|
93
|
-
usage: "dss connection infer [--mode fast|rich] [--project-key KEY]",
|
|
94
93
|
description: "List connections with inferred types and metadata.",
|
|
95
94
|
examples: ["dss connection infer", "dss connection infer --mode rich",],
|
|
96
95
|
},
|
|
97
96
|
get: {
|
|
98
97
|
handler: async (c, a) => {
|
|
99
|
-
const usage = "
|
|
98
|
+
const usage = commandUsage("connection", "get");
|
|
100
99
|
requireArgs(a, 1, usage);
|
|
101
100
|
const details = await c.connections.adminGet(requireConnectionName(a[0], usage));
|
|
102
101
|
return sanitizeConnectionSecrets(details);
|
|
103
102
|
},
|
|
104
|
-
usage: "dss connection get <name>",
|
|
105
103
|
description: "Get a connection by name (admin). Credential-bearing params (e.g. params.password) are redacted in output.",
|
|
106
104
|
examples: ["dss connection get postgres",],
|
|
107
105
|
},
|
|
108
106
|
create: {
|
|
109
107
|
handler: async (c, _a, f) => {
|
|
110
|
-
const usage = "
|
|
108
|
+
const usage = commandUsage("connection", "create");
|
|
111
109
|
const body = requiredConnectionJson(f, usage);
|
|
112
110
|
if (executionMode(f).dryRun) {
|
|
113
111
|
return {
|
|
@@ -127,7 +125,6 @@ export const connectionCommands = {
|
|
|
127
125
|
throw sanitizeConnectionError(error, connectionSecretsFromBody(body));
|
|
128
126
|
}
|
|
129
127
|
},
|
|
130
|
-
usage: "dss connection create (--data JSON|--data-file PATH|--stdin) [--dry-run]",
|
|
131
128
|
description: 'Create a connection (admin). Body is the Connection definition, e.g. {"name":"new-connection","type":"PostgreSQL","params":{"host":"...","user":"...","password":"..."}}. Secrets are never echoed in errors or output.',
|
|
132
129
|
examples: [
|
|
133
130
|
`dss connection create --data '{"name":"new-connection","type":"PostgreSQL","params":{"db":"dbname","user":"myuser","password":"s3cret"}}'`,
|
|
@@ -135,7 +132,7 @@ export const connectionCommands = {
|
|
|
135
132
|
},
|
|
136
133
|
update: {
|
|
137
134
|
handler: async (c, a, f) => {
|
|
138
|
-
const usage = "
|
|
135
|
+
const usage = commandUsage("connection", "update");
|
|
139
136
|
requireArgs(a, 1, usage);
|
|
140
137
|
const name = requireConnectionName(a[0], usage);
|
|
141
138
|
const body = requiredConnectionJson(f, usage);
|
|
@@ -160,7 +157,6 @@ export const connectionCommands = {
|
|
|
160
157
|
throw sanitizeConnectionError(error, secrets);
|
|
161
158
|
}
|
|
162
159
|
},
|
|
163
|
-
usage: "dss connection update <name> (--data JSON|--data-file PATH|--stdin) [--dry-run]",
|
|
164
160
|
description: "Update a connection (admin). The body MUST be the Connection object obtained from `dss connection get` (PUT semantics); type and name cannot be modified. Use --dry-run to preview the merged result without writing.",
|
|
165
161
|
examples: [
|
|
166
162
|
`dss connection update postgres --data '{"type":"PostgreSQL","name":"postgres","allowWrite":true,"params":{"db":"dbname","user":"myuser","password":"s3cret"}}'`,
|
|
@@ -168,7 +164,7 @@ export const connectionCommands = {
|
|
|
168
164
|
},
|
|
169
165
|
delete: {
|
|
170
166
|
handler: async (c, a, f) => {
|
|
171
|
-
const usage = "
|
|
167
|
+
const usage = commandUsage("connection", "delete");
|
|
172
168
|
requireArgs(a, 1, usage);
|
|
173
169
|
const name = requireConnectionName(a[0], usage);
|
|
174
170
|
if (executionMode(f).dryRun) {
|
|
@@ -191,18 +187,16 @@ export const connectionCommands = {
|
|
|
191
187
|
}
|
|
192
188
|
return c.connections.adminDelete(name);
|
|
193
189
|
},
|
|
194
|
-
usage: "dss connection delete <name> [--if-exists] [--dry-run]",
|
|
195
190
|
description: "Delete a connection (admin). Destructive: per the DSS docs no check is performed that the connection is not in use by a dataset.",
|
|
196
191
|
examples: ["dss connection delete old-conn", "dss connection delete old-conn --if-exists",],
|
|
197
192
|
},
|
|
198
193
|
test: {
|
|
199
194
|
handler: async (c, a) => {
|
|
200
|
-
const usage = "
|
|
195
|
+
const usage = commandUsage("connection", "test");
|
|
201
196
|
requireArgs(a, 1, usage);
|
|
202
197
|
const result = await c.connections.adminTest(requireConnectionName(a[0], usage));
|
|
203
198
|
return sanitizeConnectionSecrets(result);
|
|
204
199
|
},
|
|
205
|
-
usage: "dss connection test <name>",
|
|
206
200
|
description: "Test whether a connection is available (uses the same GET /connections/{name}/test route as the official Python client; returns connectionOK, errors when testing is unsupported for the type). Credential-bearing fields are redacted in output.",
|
|
207
201
|
examples: ["dss connection test postgres",],
|
|
208
202
|
},
|
|
@@ -210,14 +204,13 @@ export const connectionCommands = {
|
|
|
210
204
|
handler: (c, _a, f) => {
|
|
211
205
|
const connection = f["connection"];
|
|
212
206
|
if (!connection) {
|
|
213
|
-
throw new UsageError(
|
|
207
|
+
throw new UsageError(`--connection is required. Usage: ${commandUsage("connection", "schemas")}`);
|
|
214
208
|
}
|
|
215
209
|
return c.connections.schemas({
|
|
216
210
|
connection,
|
|
217
211
|
projectKey: f["project-key"],
|
|
218
212
|
});
|
|
219
213
|
},
|
|
220
|
-
usage: "dss connection schemas --connection CONN [--project-key KEY]",
|
|
221
214
|
description: "List schemas in a SQL connection.",
|
|
222
215
|
examples: ["dss connection schemas --connection ATHENA_CONN --project-key MYPROJ",],
|
|
223
216
|
},
|
|
@@ -225,7 +218,7 @@ export const connectionCommands = {
|
|
|
225
218
|
handler: (c, _a, f) => {
|
|
226
219
|
const connection = f["connection"];
|
|
227
220
|
if (!connection) {
|
|
228
|
-
throw new UsageError(
|
|
221
|
+
throw new UsageError(`--connection is required. Usage: ${commandUsage("connection", "tables")}`);
|
|
229
222
|
}
|
|
230
223
|
return c.connections.tables({
|
|
231
224
|
connection,
|
|
@@ -234,7 +227,6 @@ export const connectionCommands = {
|
|
|
234
227
|
projectKey: f["project-key"],
|
|
235
228
|
});
|
|
236
229
|
},
|
|
237
|
-
usage: "dss connection tables --connection CONN [--catalog CATALOG] [--schema SCHEMA] [--project-key KEY]",
|
|
238
230
|
description: "List importable tables in a SQL connection, optionally scoped by catalog and schema.",
|
|
239
231
|
examples: [
|
|
240
232
|
"dss connection tables --connection ATHENA_CONN --schema analytics --project-key MYPROJ",
|
|
@@ -242,7 +234,7 @@ export const connectionCommands = {
|
|
|
242
234
|
},
|
|
243
235
|
"prepare-import": {
|
|
244
236
|
handler: async (c, _a, f) => {
|
|
245
|
-
const usage = "
|
|
237
|
+
const usage = commandUsage("connection", "prepare-import");
|
|
246
238
|
const body = requiredJsonInput(f, `--data, --data-file, or --stdin is required (tables-import request). Usage: ${usage}`);
|
|
247
239
|
const keys = body["keys"];
|
|
248
240
|
if (!Array.isArray(keys) || keys.length === 0) {
|
|
@@ -273,7 +265,6 @@ export const connectionCommands = {
|
|
|
273
265
|
projectKey: f["project-key"],
|
|
274
266
|
});
|
|
275
267
|
},
|
|
276
|
-
usage: "dss connection prepare-import (--data JSON|--data-file PATH|--stdin) [--project-key KEY] [--dry-run]",
|
|
277
268
|
description: 'Prepare the import of selected SQL or Hive tables (WRITE_CONF). Body is {"keys":[...]} per the DSS tables-import docs. Returns a DSS future; poll with `dss future wait <jobId>`.',
|
|
278
269
|
examples: [
|
|
279
270
|
`dss connection prepare-import --data '{"keys":[{"connectionName":"postgres","name":"my_table"}]}' --project-key MYPROJ`,
|
|
@@ -281,7 +272,7 @@ export const connectionCommands = {
|
|
|
281
272
|
},
|
|
282
273
|
"execute-import": {
|
|
283
274
|
handler: async (c, _a, f) => {
|
|
284
|
-
const usage = "
|
|
275
|
+
const usage = commandUsage("connection", "execute-import");
|
|
285
276
|
const body = requiredJsonInput(f, `--data, --data-file, or --stdin is required (tables-import request). Usage: ${usage}`);
|
|
286
277
|
const sql = body["sqlImportCandidates"];
|
|
287
278
|
const hive = body["hiveImportCandidates"];
|
|
@@ -311,10 +302,9 @@ export const connectionCommands = {
|
|
|
311
302
|
projectKey: f["project-key"],
|
|
312
303
|
});
|
|
313
304
|
},
|
|
314
|
-
usage: "dss connection execute-import (--data JSON|--data-file PATH|--stdin) [--project-key KEY] [--dry-run]",
|
|
315
305
|
description: "Perform an import from SQL/Hive table candidates (WRITE_CONF). Body carries sqlImportCandidates and/or hiveImportCandidates per the DSS tables-import docs. Returns a DSS future.",
|
|
316
306
|
examples: [
|
|
317
307
|
`dss connection execute-import --data '{"sqlImportCandidates":[{"connectionName":"pgsql","table":"my_table","checked":false,"datasetName":"imported_from_db","existingDatasetsNames":[]}]}' --project-key MYPROJ`,
|
|
318
308
|
],
|
|
319
309
|
},
|
|
320
|
-
};
|
|
310
|
+
});
|
|
@@ -1,38 +1,35 @@
|
|
|
1
1
|
import { jsonInput, } from "../coerce.js";
|
|
2
|
+
import { commandUsage, withUsage, } from "../syntax.js";
|
|
2
3
|
import { requireArgs, } from "../usage.js";
|
|
3
|
-
export const continuousActivityCommands = {
|
|
4
|
+
export const continuousActivityCommands = withUsage("continuous-activity", {
|
|
4
5
|
list: {
|
|
5
6
|
handler: (c, _a, f) => c.continuousActivities.list(f["project-key"]),
|
|
6
|
-
usage: "dss continuous-activity list [--project-key KEY]",
|
|
7
7
|
description: "List continuous activities in a project.",
|
|
8
8
|
examples: ["dss continuous-activity list",],
|
|
9
9
|
},
|
|
10
10
|
status: {
|
|
11
11
|
handler: (c, a, f) => {
|
|
12
|
-
requireArgs(a, 1, "
|
|
12
|
+
requireArgs(a, 1, commandUsage("continuous-activity", "status"));
|
|
13
13
|
return c.continuousActivities.getStatus(a[0], f["project-key"]);
|
|
14
14
|
},
|
|
15
|
-
usage: "dss continuous-activity status <recipeId> [--project-key KEY]",
|
|
16
15
|
description: "Get a continuous recipe's desired and effective state.",
|
|
17
16
|
examples: ["dss continuous-activity status compute_stream",],
|
|
18
17
|
},
|
|
19
18
|
start: {
|
|
20
19
|
handler: (c, a, f) => {
|
|
21
|
-
requireArgs(a, 1, "
|
|
20
|
+
requireArgs(a, 1, commandUsage("continuous-activity", "start"));
|
|
22
21
|
return c.continuousActivities.start(a[0], jsonInput(f), f["project-key"]);
|
|
23
22
|
},
|
|
24
|
-
usage: "dss continuous-activity start <recipeId> [--data JSON|--data-file PATH|--stdin] [--project-key KEY]",
|
|
25
23
|
description: "Start a continuous recipe (optional loop restart params via JSON).",
|
|
26
24
|
examples: ["dss continuous-activity start compute_stream",],
|
|
27
25
|
},
|
|
28
26
|
stop: {
|
|
29
27
|
handler: async (c, a, f) => {
|
|
30
|
-
requireArgs(a, 1, "
|
|
28
|
+
requireArgs(a, 1, commandUsage("continuous-activity", "stop"));
|
|
31
29
|
await c.continuousActivities.stop(a[0], f["project-key"]);
|
|
32
30
|
return { stopped: a[0], };
|
|
33
31
|
},
|
|
34
|
-
usage: "dss continuous-activity stop <recipeId> [--project-key KEY]",
|
|
35
32
|
description: "Stop a continuous recipe.",
|
|
36
33
|
examples: ["dss continuous-activity stop compute_stream",],
|
|
37
34
|
},
|
|
38
|
-
};
|
|
35
|
+
});
|
|
@@ -4,26 +4,25 @@ import { deepMerge, } from "../../utils/deep-merge.js";
|
|
|
4
4
|
import { jsonInput, parseBooleanOption, } from "../coerce.js";
|
|
5
5
|
import { executionMode, } from "../flags.js";
|
|
6
6
|
import { readIfExists, skipResult, } from "../output.js";
|
|
7
|
+
import { commandUsage, withUsage, } from "../syntax.js";
|
|
7
8
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
8
|
-
export const dashboardCommands = {
|
|
9
|
+
export const dashboardCommands = withUsage("dashboard", {
|
|
9
10
|
list: {
|
|
10
11
|
handler: (c, _a, f) => c.dashboards.list(f["project-key"]),
|
|
11
|
-
usage: "dss dashboard list [--project-key KEY]",
|
|
12
12
|
description: "List project dashboards.",
|
|
13
13
|
examples: ["dss dashboard list",],
|
|
14
14
|
},
|
|
15
15
|
get: {
|
|
16
16
|
handler: (c, a, f) => {
|
|
17
|
-
requireArgs(a, 1, "
|
|
17
|
+
requireArgs(a, 1, commandUsage("dashboard", "get"));
|
|
18
18
|
return c.dashboards.get(a[0], f["project-key"]);
|
|
19
19
|
},
|
|
20
|
-
usage: "dss dashboard get <id> [--project-key KEY]",
|
|
21
20
|
description: "Get dashboard definition.",
|
|
22
21
|
examples: ["dss dashboard get DASHBOARD_ID",],
|
|
23
22
|
},
|
|
24
23
|
export: {
|
|
25
24
|
handler: async (c, a, f) => {
|
|
26
|
-
requireArgs(a, 1, "
|
|
25
|
+
requireArgs(a, 1, commandUsage("dashboard", "export"));
|
|
27
26
|
const outputPath = f["output"];
|
|
28
27
|
if (typeof outputPath !== "string" || !outputPath.trim()) {
|
|
29
28
|
throw new UsageError("--output PATH is required.", "missing_required_flag");
|
|
@@ -86,7 +85,6 @@ export const dashboardCommands = {
|
|
|
86
85
|
slideIndex,
|
|
87
86
|
};
|
|
88
87
|
},
|
|
89
|
-
usage: "dss dashboard export <id> --output PATH [--paper-size SIZE] [--orientation PORTRAIT|LANDSCAPE] [--file-type PDF] [--slide-index N] [--project-key KEY]",
|
|
90
88
|
description: "Render a dashboard page as PDF. Requires DSS graphics export to be configured.",
|
|
91
89
|
examples: [
|
|
92
90
|
"dss dashboard export DASHBOARD_ID --output dashboard.pdf",
|
|
@@ -101,7 +99,7 @@ export const dashboardCommands = {
|
|
|
101
99
|
const name = flagName ?? (typeof settingsName === "string" ? settingsName : undefined);
|
|
102
100
|
const listed = parseBooleanOption(f["listed"], "--listed");
|
|
103
101
|
if (!name) {
|
|
104
|
-
throw new UsageError(
|
|
102
|
+
throw new UsageError(`--name or dashboard settings containing a string name are required. Usage: ${commandUsage("dashboard", "create")}`);
|
|
105
103
|
}
|
|
106
104
|
const payload = { ...(settings ?? { pages: [], }), name, };
|
|
107
105
|
if (listed !== undefined)
|
|
@@ -131,7 +129,6 @@ export const dashboardCommands = {
|
|
|
131
129
|
});
|
|
132
130
|
return { created: created.id, resource: "dashboard", ...created, };
|
|
133
131
|
},
|
|
134
|
-
usage: "dss dashboard create (--name NAME|--data JSON|--data-file PATH|--stdin) [--listed true|false] [--if-not-exists] [--dry-run] [--project-key KEY]",
|
|
135
132
|
description: "Create a dashboard from raw settings or minimal name/listed fields. Defaults to an empty pages array.",
|
|
136
133
|
examples: [
|
|
137
134
|
"dss dashboard create --name 'Agent dashboard' --listed true --dry-run",
|
|
@@ -140,7 +137,7 @@ export const dashboardCommands = {
|
|
|
140
137
|
},
|
|
141
138
|
update: {
|
|
142
139
|
handler: async (c, a, f) => {
|
|
143
|
-
requireArgs(a, 1, "
|
|
140
|
+
requireArgs(a, 1, commandUsage("dashboard", "update"));
|
|
144
141
|
const name = f["name"];
|
|
145
142
|
const listed = parseBooleanOption(f["listed"], "--listed");
|
|
146
143
|
const data = jsonInput(f);
|
|
@@ -163,13 +160,12 @@ export const dashboardCommands = {
|
|
|
163
160
|
projectKey: f["project-key"],
|
|
164
161
|
});
|
|
165
162
|
},
|
|
166
|
-
usage: "dss dashboard update <id> (--name NAME|--listed true|false|--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
|
|
167
163
|
description: "Update dashboard settings via merge.",
|
|
168
164
|
examples: ["dss dashboard update DASHBOARD_ID --listed true --dry-run",],
|
|
169
165
|
},
|
|
170
166
|
delete: {
|
|
171
167
|
handler: async (c, a, f) => {
|
|
172
|
-
requireArgs(a, 1, "
|
|
168
|
+
requireArgs(a, 1, commandUsage("dashboard", "delete"));
|
|
173
169
|
const pk = f["project-key"];
|
|
174
170
|
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
175
171
|
const current = await readIfExists(() => c.dashboards.get(a[0], pk));
|
|
@@ -182,8 +178,7 @@ export const dashboardCommands = {
|
|
|
182
178
|
await c.dashboards.delete(a[0], pk);
|
|
183
179
|
return { deleted: a[0], resource: "dashboard", };
|
|
184
180
|
},
|
|
185
|
-
usage: "dss dashboard delete <id> [--if-exists] [--dry-run] [--project-key KEY]",
|
|
186
181
|
description: "Delete a dashboard.",
|
|
187
182
|
examples: ["dss dashboard delete DASHBOARD_ID --dry-run",],
|
|
188
183
|
},
|
|
189
|
-
};
|
|
184
|
+
});
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { requiredJsonInput, } from "../coerce.js";
|
|
2
2
|
import { executionMode, } from "../flags.js";
|
|
3
3
|
import { planResult, } from "../output.js";
|
|
4
|
+
import { commandUsage, withUsage, } from "../syntax.js";
|
|
4
5
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
6
|
const PLAN_FAILURE_CODES = { usage: 1, error: 2, transient: 3, };
|
|
6
7
|
function collectionEndpoint(dataCollectionId, suffix = "") {
|
|
7
8
|
return `/public/api/data-collections/${encodeURIComponent(dataCollectionId)}${suffix}`;
|
|
8
9
|
}
|
|
9
|
-
export const dataCollectionCommands = {
|
|
10
|
+
export const dataCollectionCommands = withUsage("data-collection", {
|
|
10
11
|
list: {
|
|
11
12
|
handler: (c) => c.dataCollections.list(),
|
|
12
|
-
usage: "dss data-collection list",
|
|
13
13
|
description: "List data collections on which the API key has READ privilege.",
|
|
14
14
|
examples: ["dss data-collection list",],
|
|
15
15
|
},
|
|
16
16
|
get: {
|
|
17
17
|
handler: (c, a) => {
|
|
18
|
-
requireArgs(a, 1, "
|
|
18
|
+
requireArgs(a, 1, commandUsage("data-collection", "get"));
|
|
19
19
|
return c.dataCollections.get(a[0]);
|
|
20
20
|
},
|
|
21
|
-
usage: "dss data-collection get <dataCollectionId>",
|
|
22
21
|
description: "Get collection settings. Permissions are omitted for non-admins.",
|
|
23
22
|
examples: ["dss data-collection get OjVsTQ3O",],
|
|
24
23
|
},
|
|
@@ -38,7 +37,6 @@ export const dataCollectionCommands = {
|
|
|
38
37
|
}
|
|
39
38
|
return c.dataCollections.create(body);
|
|
40
39
|
},
|
|
41
|
-
usage: "dss data-collection create (--data JSON|--data-file PATH|--stdin) [--dry-run]",
|
|
42
40
|
description: "Create a collection (creator becomes admin; requires mayCreateDataCollections).",
|
|
43
41
|
examples: [
|
|
44
42
|
'dss data-collection create --data \'{"displayName":"My Collection"}\'',
|
|
@@ -46,7 +44,7 @@ export const dataCollectionCommands = {
|
|
|
46
44
|
},
|
|
47
45
|
"settings-set": {
|
|
48
46
|
handler: async (c, a, f) => {
|
|
49
|
-
requireArgs(a, 1, "
|
|
47
|
+
requireArgs(a, 1, commandUsage("data-collection", "settings-set"));
|
|
50
48
|
const settings = requiredJsonInput(f, "--data, --data-file, or --stdin is required (collection settings).");
|
|
51
49
|
if (executionMode(f).dryRun) {
|
|
52
50
|
return planResult("data-collection", "settings-set", {
|
|
@@ -63,7 +61,6 @@ export const dataCollectionCommands = {
|
|
|
63
61
|
const updated = await c.dataCollections.update(a[0], settings);
|
|
64
62
|
return { updated: a[0], resource: "data-collection", ...updated, };
|
|
65
63
|
},
|
|
66
|
-
usage: "dss data-collection settings-set <dataCollectionId> (--data JSON|--data-file PATH|--stdin) [--dry-run]",
|
|
67
64
|
description: "Update collection settings; only send settings obtained through get. Admin required.",
|
|
68
65
|
examples: [
|
|
69
66
|
"dss data-collection settings-set OjVsTQ3O --data-file settings.json",
|
|
@@ -71,7 +68,7 @@ export const dataCollectionCommands = {
|
|
|
71
68
|
},
|
|
72
69
|
delete: {
|
|
73
70
|
validate: (a) => {
|
|
74
|
-
const usage = "
|
|
71
|
+
const usage = commandUsage("data-collection", "delete");
|
|
75
72
|
requireArgs(a, 1, usage);
|
|
76
73
|
if (a[0].trim().length === 0) {
|
|
77
74
|
throw new UsageError(`<dataCollectionId> must be a non-empty identifier. Usage: ${usage}`);
|
|
@@ -92,16 +89,14 @@ export const dataCollectionCommands = {
|
|
|
92
89
|
await c.dataCollections.delete(a[0]);
|
|
93
90
|
return { deleted: a[0], resource: "data-collection", };
|
|
94
91
|
},
|
|
95
|
-
usage: "dss data-collection delete <dataCollectionId> [--dry-run]",
|
|
96
92
|
description: "Permanently delete a collection. Admin required.",
|
|
97
93
|
examples: ["dss data-collection delete OjVsTQ3O --dry-run",],
|
|
98
94
|
},
|
|
99
95
|
"list-objects": {
|
|
100
96
|
handler: (c, a) => {
|
|
101
|
-
requireArgs(a, 1, "
|
|
97
|
+
requireArgs(a, 1, commandUsage("data-collection", "list-objects"));
|
|
102
98
|
return c.dataCollections.listObjects(a[0]);
|
|
103
99
|
},
|
|
104
|
-
usage: "dss data-collection list-objects <dataCollectionId>",
|
|
105
100
|
description: "List the objects in a collection.",
|
|
106
101
|
examples: ["dss data-collection list-objects OjVsTQ3O",],
|
|
107
102
|
},
|
|
@@ -111,7 +106,7 @@ export const dataCollectionCommands = {
|
|
|
111
106
|
const collectionId = a[0]
|
|
112
107
|
?? (typeof object.dataCollectionId === "string" ? object.dataCollectionId : undefined);
|
|
113
108
|
if (!collectionId) {
|
|
114
|
-
throw new UsageError(
|
|
109
|
+
throw new UsageError(`Usage: ${commandUsage("data-collection", "add-object")}`);
|
|
115
110
|
}
|
|
116
111
|
const reference = { ...object, };
|
|
117
112
|
delete reference.dataCollectionId;
|
|
@@ -130,7 +125,6 @@ export const dataCollectionCommands = {
|
|
|
130
125
|
await c.dataCollections.addObject(collectionId, reference);
|
|
131
126
|
return { added: reference.id, dataCollectionId: collectionId, resource: "data-collection", };
|
|
132
127
|
},
|
|
133
|
-
usage: "dss data-collection add-object <dataCollectionId> (--data JSON|--data-file PATH|--stdin) [--dry-run]",
|
|
134
128
|
description: "Add an object (e.g. {type:'DATASET',projectKey:'K',id:'name'}) to a collection. Contributor required.",
|
|
135
129
|
examples: [
|
|
136
130
|
'dss data-collection add-object OjVsTQ3O --data \'{"type":"DATASET","projectKey":"MYPROJECT","id":"orders"}\'',
|
|
@@ -138,7 +132,7 @@ export const dataCollectionCommands = {
|
|
|
138
132
|
},
|
|
139
133
|
"remove-dataset": {
|
|
140
134
|
handler: async (c, a, f) => {
|
|
141
|
-
requireArgs(a, 3, "
|
|
135
|
+
requireArgs(a, 3, commandUsage("data-collection", "remove-dataset"));
|
|
142
136
|
if (executionMode(f).dryRun) {
|
|
143
137
|
return planResult("data-collection", "remove-dataset", {
|
|
144
138
|
asyncKind: "none",
|
|
@@ -157,8 +151,7 @@ export const dataCollectionCommands = {
|
|
|
157
151
|
await c.dataCollections.removeDataset(a[0], a[1], a[2]);
|
|
158
152
|
return { removed: a[2], projectKey: a[1], dataCollectionId: a[0], resource: "data-collection", };
|
|
159
153
|
},
|
|
160
|
-
usage: "dss data-collection remove-dataset <dataCollectionId> <projectKey> <datasetName> [--dry-run]",
|
|
161
154
|
description: "Remove a dataset from a collection. Contributor required.",
|
|
162
155
|
examples: ["dss data-collection remove-dataset OjVsTQ3O MYPROJECT DATASET1",],
|
|
163
156
|
},
|
|
164
|
-
};
|
|
157
|
+
});
|
|
@@ -2,6 +2,7 @@ import { deepMerge, } from "../../utils/deep-merge.js";
|
|
|
2
2
|
import { num, parseBooleanOption, requiredJsonInput, } from "../coerce.js";
|
|
3
3
|
import { executionMode, } from "../flags.js";
|
|
4
4
|
import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
|
|
5
|
+
import { commandUsage, withUsage, } from "../syntax.js";
|
|
5
6
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
6
7
|
/**
|
|
7
8
|
* A data quality result counts as passing only when it actually carries a
|
|
@@ -15,37 +16,34 @@ function resultReportsOk(result) {
|
|
|
15
16
|
.map((value) => value.trim().toUpperCase());
|
|
16
17
|
return verdicts.length > 0 && verdicts.every((value) => value === "OK");
|
|
17
18
|
}
|
|
18
|
-
export const dataQualityCommands = {
|
|
19
|
+
export const dataQualityCommands = withUsage("data-quality", {
|
|
19
20
|
rules: {
|
|
20
21
|
handler: (c, a, f) => {
|
|
21
|
-
requireArgs(a, 1, "
|
|
22
|
+
requireArgs(a, 1, commandUsage("data-quality", "rules"));
|
|
22
23
|
return c.dataQuality.listRules(a[0], f["project-key"]);
|
|
23
24
|
},
|
|
24
|
-
usage: "dss data-quality rules <dataset> [--project-key KEY]",
|
|
25
25
|
description: "List data quality rules for a dataset.",
|
|
26
26
|
examples: ["dss data-quality rules orders",],
|
|
27
27
|
},
|
|
28
28
|
"get-rule": {
|
|
29
29
|
handler: (c, a, f) => {
|
|
30
|
-
requireArgs(a, 2, "
|
|
30
|
+
requireArgs(a, 2, commandUsage("data-quality", "get-rule"));
|
|
31
31
|
return c.dataQuality.getRule(a[0], a[1], f["project-key"]);
|
|
32
32
|
},
|
|
33
|
-
usage: "dss data-quality get-rule <dataset> <rule-id> [--project-key KEY]",
|
|
34
33
|
description: "Get one data quality rule by id.",
|
|
35
34
|
examples: ["dss data-quality get-rule orders RULE_ID",],
|
|
36
35
|
},
|
|
37
36
|
status: {
|
|
38
37
|
handler: (c, a, f) => {
|
|
39
|
-
requireArgs(a, 1, "
|
|
38
|
+
requireArgs(a, 1, commandUsage("data-quality", "status"));
|
|
40
39
|
return c.dataQuality.status(a[0], f["project-key"]);
|
|
41
40
|
},
|
|
42
|
-
usage: "dss data-quality status <dataset> [--project-key KEY]",
|
|
43
41
|
description: "Get the aggregate data quality status for a dataset.",
|
|
44
42
|
examples: ["dss data-quality status orders",],
|
|
45
43
|
},
|
|
46
44
|
"create-rule": {
|
|
47
45
|
handler: async (c, a, f) => {
|
|
48
|
-
requireArgs(a, 1, "
|
|
46
|
+
requireArgs(a, 1, commandUsage("data-quality", "create-rule"));
|
|
49
47
|
const config = requiredJsonInput(f, "--data, --data-file, or --stdin is required.");
|
|
50
48
|
const pk = f["project-key"];
|
|
51
49
|
const identity = typeof config.id === "string"
|
|
@@ -88,7 +86,6 @@ export const dataQualityCommands = {
|
|
|
88
86
|
...created,
|
|
89
87
|
};
|
|
90
88
|
},
|
|
91
|
-
usage: "dss data-quality create-rule <dataset> (--data JSON|--data-file PATH|--stdin) [--if-not-exists] [--dry-run] [--project-key KEY]",
|
|
92
89
|
description: "Create a data quality rule from raw rule config.",
|
|
93
90
|
examples: [
|
|
94
91
|
'dss data-quality create-rule orders --data \'{"type":"RecordCountInRangeRule","softMinimum":1,"softMinimumEnabled":true,"displayName":"Has rows"}\' --dry-run',
|
|
@@ -96,7 +93,7 @@ export const dataQualityCommands = {
|
|
|
96
93
|
},
|
|
97
94
|
"update-rule": {
|
|
98
95
|
handler: async (c, a, f) => {
|
|
99
|
-
requireArgs(a, 2, "
|
|
96
|
+
requireArgs(a, 2, commandUsage("data-quality", "update-rule"));
|
|
100
97
|
const data = requiredJsonInput(f, "--data, --data-file, or --stdin is required.");
|
|
101
98
|
if (executionMode(f).dryRun) {
|
|
102
99
|
const current = await c.dataQuality.getRule(a[0], a[1], f["project-key"]);
|
|
@@ -116,7 +113,6 @@ export const dataQualityCommands = {
|
|
|
116
113
|
projectKey: f["project-key"],
|
|
117
114
|
});
|
|
118
115
|
},
|
|
119
|
-
usage: "dss data-quality update-rule <dataset> <rule-id> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
|
|
120
116
|
description: "Update a data quality rule via GET-before-PUT merge.",
|
|
121
117
|
examples: [
|
|
122
118
|
"dss data-quality update-rule orders RULE_ID --data '{\"enabled\":false}' --dry-run",
|
|
@@ -124,7 +120,7 @@ export const dataQualityCommands = {
|
|
|
124
120
|
},
|
|
125
121
|
"delete-rule": {
|
|
126
122
|
handler: async (c, a, f) => {
|
|
127
|
-
requireArgs(a, 2, "
|
|
123
|
+
requireArgs(a, 2, commandUsage("data-quality", "delete-rule"));
|
|
128
124
|
const pk = f["project-key"];
|
|
129
125
|
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
130
126
|
const current = await readIfExists(() => c.dataQuality.getRule(a[0], a[1], pk));
|
|
@@ -144,38 +140,35 @@ export const dataQualityCommands = {
|
|
|
144
140
|
await c.dataQuality.deleteRule(a[0], a[1], pk);
|
|
145
141
|
return { deleted: a[1], dataset: a[0], resource: "data-quality", };
|
|
146
142
|
},
|
|
147
|
-
usage: "dss data-quality delete-rule <dataset> <rule-id> [--if-exists] [--dry-run] [--project-key KEY]",
|
|
148
143
|
description: "Delete a data quality rule.",
|
|
149
144
|
examples: ["dss data-quality delete-rule orders RULE_ID --dry-run",],
|
|
150
145
|
},
|
|
151
146
|
"status-by-partition": {
|
|
152
147
|
handler: (c, a, f) => {
|
|
153
|
-
requireArgs(a, 1, "
|
|
148
|
+
requireArgs(a, 1, commandUsage("data-quality", "status-by-partition"));
|
|
154
149
|
return c.dataQuality.statusByPartition(a[0], {
|
|
155
150
|
includeAllPartitions: f["include-all-partitions"] === true,
|
|
156
151
|
projectKey: f["project-key"],
|
|
157
152
|
});
|
|
158
153
|
},
|
|
159
|
-
usage: "dss data-quality status-by-partition <dataset> [--include-all-partitions] [--project-key KEY]",
|
|
160
154
|
description: "Get data quality status by dataset partition.",
|
|
161
155
|
examples: ["dss data-quality status-by-partition orders --include-all-partitions",],
|
|
162
156
|
},
|
|
163
157
|
"last-results": {
|
|
164
158
|
handler: (c, a, f) => {
|
|
165
|
-
requireArgs(a, 1, "
|
|
159
|
+
requireArgs(a, 1, commandUsage("data-quality", "last-results"));
|
|
166
160
|
return c.dataQuality.lastResults(a[0], {
|
|
167
161
|
partition: f["partition"],
|
|
168
162
|
ruleId: f["rule-id"],
|
|
169
163
|
projectKey: f["project-key"],
|
|
170
164
|
});
|
|
171
165
|
},
|
|
172
|
-
usage: "dss data-quality last-results <dataset> [--partition P] [--rule-id ID] [--project-key KEY]",
|
|
173
166
|
description: "Get latest data quality rule results for a dataset.",
|
|
174
167
|
examples: ["dss data-quality last-results orders",],
|
|
175
168
|
},
|
|
176
169
|
"assert-results": {
|
|
177
170
|
handler: async (c, a, f) => {
|
|
178
|
-
requireArgs(a, 1, "
|
|
171
|
+
requireArgs(a, 1, commandUsage("data-quality", "assert-results"));
|
|
179
172
|
const ruleId = f["rule-id"];
|
|
180
173
|
const pk = f["project-key"];
|
|
181
174
|
const results = await c.dataQuality.lastResults(a[0], { ruleId, projectKey: pk, });
|
|
@@ -196,7 +189,6 @@ export const dataQualityCommands = {
|
|
|
196
189
|
...(selected.length === 0 ? { reason: "no_results", } : {}),
|
|
197
190
|
};
|
|
198
191
|
},
|
|
199
|
-
usage: "dss data-quality assert-results <dataset> [--rule-id ID] [--project-key KEY]",
|
|
200
192
|
description: "Assert the latest data quality results: at least one selected rule must exist and every selected result must report OK. Returns { satisfied, checked, failed } with failed rule ids/outcomes only; a failed assertion exits 4 with assertion_failed.",
|
|
201
193
|
examples: [
|
|
202
194
|
"dss data-quality assert-results orders",
|
|
@@ -205,7 +197,7 @@ export const dataQualityCommands = {
|
|
|
205
197
|
},
|
|
206
198
|
history: {
|
|
207
199
|
handler: (c, a, f) => {
|
|
208
|
-
requireArgs(a, 1, "
|
|
200
|
+
requireArgs(a, 1, commandUsage("data-quality", "history"));
|
|
209
201
|
return c.dataQuality.history(a[0], {
|
|
210
202
|
minTimestamp: num(f["min-timestamp"], "--min-timestamp"),
|
|
211
203
|
maxTimestamp: num(f["max-timestamp"], "--max-timestamp"),
|
|
@@ -215,7 +207,6 @@ export const dataQualityCommands = {
|
|
|
215
207
|
projectKey: f["project-key"],
|
|
216
208
|
});
|
|
217
209
|
},
|
|
218
|
-
usage: "dss data-quality history <dataset> [--rule-id ID] [--min-timestamp MS] [--max-timestamp MS] [--results-per-page N] [--page N] [--project-key KEY]",
|
|
219
210
|
description: "Get data quality rule execution history.",
|
|
220
211
|
examples: ["dss data-quality history orders --results-per-page 100",],
|
|
221
212
|
},
|
|
@@ -224,7 +215,6 @@ export const dataQualityCommands = {
|
|
|
224
215
|
onlyMonitored: parseBooleanOption(f["only-monitored"], "--only-monitored"),
|
|
225
216
|
projectKey: f["project-key"],
|
|
226
217
|
}),
|
|
227
|
-
usage: "dss data-quality project-status [--only-monitored true|false] [--project-key KEY]",
|
|
228
218
|
description: "Get project-level data quality status by dataset.",
|
|
229
219
|
examples: ["dss data-quality project-status --only-monitored false",],
|
|
230
220
|
},
|
|
@@ -234,13 +224,12 @@ export const dataQualityCommands = {
|
|
|
234
224
|
maxTimestamp: num(f["max-timestamp"], "--max-timestamp"),
|
|
235
225
|
projectKey: f["project-key"],
|
|
236
226
|
}),
|
|
237
|
-
usage: "dss data-quality project-timeline [--min-timestamp MS] [--max-timestamp MS] [--project-key KEY]",
|
|
238
227
|
description: "Get project-level data quality timeline aggregates.",
|
|
239
228
|
examples: ["dss data-quality project-timeline --min-timestamp 1714521600000",],
|
|
240
229
|
},
|
|
241
230
|
compute: {
|
|
242
231
|
handler: async (c, a, f) => {
|
|
243
|
-
requireArgs(a, 1, "
|
|
232
|
+
requireArgs(a, 1, commandUsage("data-quality", "compute"));
|
|
244
233
|
const pk = f["project-key"];
|
|
245
234
|
const options = {
|
|
246
235
|
partition: f["partition"],
|
|
@@ -268,11 +257,10 @@ export const dataQualityCommands = {
|
|
|
268
257
|
return c.dataQuality.computeRulesAndWait(a[0], options);
|
|
269
258
|
return c.dataQuality.computeRules(a[0], options);
|
|
270
259
|
},
|
|
271
|
-
usage: "dss data-quality compute <dataset> [--partition P] [--rule-id ID] [--wait] [--timeout MS] [--poll-interval MS] [--dry-run] [--project-key KEY]",
|
|
272
260
|
description: "Start data quality rule computation, optionally waiting on the returned DSS future.",
|
|
273
261
|
examples: [
|
|
274
262
|
"dss data-quality compute orders --dry-run",
|
|
275
263
|
"dss data-quality compute orders --wait",
|
|
276
264
|
],
|
|
277
265
|
},
|
|
278
|
-
};
|
|
266
|
+
});
|